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配置信息:
- 如果config为null,则认为静态配置失败
- 调用mNwService.setInterfaceConfig(),设置配置的IP地址信息
- 组装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,代表网络已经连接完成。