Android -- Wifi的save()操作
代码主要来自android 6.0
当我们在Settings中设置静态IP连接时,会调用到WifiManager::save()函数:
有注释可知,save函数会把传入WifiConfiguration信息代表的网络保存进wpa_supplicant.conf文件中;如果网络是已经存在的,则会更新信息;如果是新添加的,则会重新添加到配置文件中。
- /**
- * 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);
- }
WifiServiceImpl::ClientHandler处理该消息:
主要做消息转发处理,看WifiStateMachine中的处理流程。ConnectModeState状态处理该消息:
如果检测出IP配置信息有变化,则会切换到ObtainingIpState状态,并触发autojoin流程。如果用户使用的是静态IP,看ObtainingIpState的enter()函数:
看else分支,在设置静态IP之前,调用stopDhcp()停掉当前的dhcp流程,获取到上层传入的静态IP配置信息:
- /* 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;
- }
- 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;
- 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);
- }
- }
- }
- 如果config为null,则认为静态配置失败
- 调用mNwService.setInterfaceConfig(),设置配置的IP地址信息
- 组装DhcpResults对象,并发送CMD_STATIC_IP_SUCCESS消息,更新网络状态
CMD_STATIC_IP_SUCCESS在ObtainingIpState被处理:
将当前静态IP设置的DhcpResults赋给状态机的成员变量mDchpResults,最后调用updateLinkProperties()更新连接配置信息:
跟踪CMD_IP_CONFIGURATION_SUCCESSFUL消息。ObtainingIpState不处理该消息,其父状态L2ConnectedState处理:
静态IP配置成功后,调用handleSuccessfulIpConfiguration()做之后的一些准备工作;调用sendConnectedState(),发送WifiManager.NETWORK_STATE_CHANGED_ACTION广播,通知其他组件网络连接状态的变化(此处是CONNECTED)。最后将Wifi状态机切换到ConnectedSate,代表网络已经连接完成。
- 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);
- }
- // 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;
- case CMD_IP_CONFIGURATION_SUCCESSFUL:
- handleSuccessfulIpConfiguration();
- sendConnectedState();
- transitionTo(mConnectedState);
- break;