Android Wi-Fi save()流程

本文详细解析了Android系统中设置静态IP的过程,包括调用WifiManager::save()函数保存网络配置,通过WifiConfigStore处理配置信息,以及最终通过native方法进行实际保存。此外,还介绍了在静态IP配置成功后如何更新连接状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当我们在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 == nullthrow 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;

通过调用WifiConfigStore中的saveNetwork方法进行save操作.

/**
 * Add/update the specified configuration and save config
 *
 * @param config WifiConfiguration to be saved
 * @return network update result
 */

NetworkUpdateResult saveNetwork(WifiConfiguration config, int uid) {
 WifiConfiguration conf;

 // A new network cannot have null SSID
 if (config == null || (config.networkId == INVALID_NETWORK_ID &&
   config.SSID == null)) {
  return new NetworkUpdateResult(INVALID_NETWORK_ID);
 }
 if (VDBG) localLog("WifiConfigStore: saveNetwork netId", config.networkId);
 if (VDBG) {
  loge("WifiConfigStore saveNetwork, size=" + mConfiguredNetworks.size()
    + " SSID=" + config.SSID
    + " Uid=" + Integer.toString(config.creatorUid)
    + "/" + Integer.toString(config.lastUpdateUid));
 }

 if (mDeletedEphemeralSSIDs.remove(config.SSID)) {
  if (VDBG) {
   loge("WifiConfigStore: removed from ephemeral blacklist: " + config.SSID);
  }
  // NOTE: This will be flushed to disk as part of the addOrUpdateNetworkNative call
  // below, since we're creating/modifying a config.
 }

 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 a new network */
 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);

   // reenable autojoin, since new information has been provided
   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;
}

最终还是通过调用native中的方法进行实际的保存操作.

public boolean saveConfig() {
 return doBooleanCommand("SAVE_CONFIG");
}

之后就是通过wpa_supplicant进行保存.

再回到上边.当保存成功之后.如果检测出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,代表网络已经连接完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值