一 前言
在前面一篇简单地说明了用户在WifiSettings界面选择一个AP显示配置AP参数的对话框的过程,当用户在对话框中选择好加密方式和输入密码之后,点击确定按钮,Android就会去连接这个AP,这一篇将主要分析连接AP的过程(AP参数没有保存的这种情况)。
二 图示调用流程
为了调用流程图更简洁明了,整个调用流程图示将以一条主线走下去,对于其它的一些方法调用将不在流程图显示,更详细更细节地可以看下面的代码具体流程。
三 代码具体流程
1 应用层
1.1 packages/apps/settings/WifiSettings.java
当填写完配置相关参数后,点击确定按钮会监听到操作。
@Override
public void onSubmit(WifiDialog dialog) {
if (mDialog != null) {
submit(mDialog.getController());
}
}
看submit。
/* package */ void submit(WifiConfigController configController) {
final WifiConfiguration config = configController.getConfig();
if (config == null) {
if (mSelectedAccessPoint != null
&& mSelectedAccessPoint.isSaved()) {
connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */);
}
} else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
mWifiManager.save(config, mSaveListener);
} else {
mWifiManager.save(config, mSaveListener);
if (mSelectedAccessPoint != null) { // Not an "Add network"
connect(config, false /* isSavedNetwork */);
}
}
mWifiTracker.resumeScanning();
}
看mWifiManager.save。
2 框架层
2.1 frameworks/base/wifi/java/android/net/wifi/WifiManager.java
/**
* Save the given network to the list of configured networks for the
* foreground user. If the network already exists, the configuration
* is updated. Any new network is enabled by default.
*
* For a new network, this function is used instead of a
* sequence of addNetwork() and enableNetwork().
*
* For an existing network, it accomplishes the task of updateNetwork()
*
* This API will cause reconnect if the crecdentials of the current active
* connection has been changed.
*
* @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");
getChannel().sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
通过异步通道AsyncChannel来调用WifiServiceImpl的ClientHandler来处理SAVE_NETWORK消息。
2.2 frameworks/base/wifi/java/android/net/wifi/WifiServiceImpl.java
/**
* Handles client connections
*/
private class ClientHandler extends WifiHandler {
ClientHandler(String tag, Looper looper) {
super(tag, looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
...
case WifiManager.SAVE_NETWORK: {
if (checkChangePermissionAndReplyIfNotAuthorized(
msg, WifiManager.SAVE_NETWORK_FAILED)) {
WifiConfiguration config = (WifiConfiguration) msg.obj;
int networkId = msg.arg1;
Slog.d(TAG, "SAVE"
+ " nid=" + Integer.toString(networkId)
+ " config=" + config
+ " uid=" + msg.sendingUid
+ " name="
+ mContext.getPackageManager().getNameForUid(msg.sendingUid));
if (config != null) {
/* Command is forwarded to state machine */
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else {
Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
WifiManager.INVALID_ARGS);
}
}
break;
}
...
}
}
...
}
mWifiStateMachine.sendMessage发送消息直接让WifiStateMachine状态机来处理。
2.3 frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java
public WifiStateMachine(Context context, FrameworkFacade facade, Looper looper,
UserManager userManager, WifiInjector wifiInjector,
BackupManagerProxy backupManagerProxy, WifiCountryCode countryCode,
WifiNative wifiNative,
WrongPasswordNotifier wrongPasswordNotifier,
SarManager sarManager) {
...
// CHECKSTYLE:OFF IndentationCheck
addState(mDefaultState);
addState(mConnectModeState, mDefaultState);
addState(mL2ConnectedState, mConnectModeState);
addState(mObtainingIpState, mL2ConnectedState);
addState(mConnectedState, mL2ConnectedState);
addState(mRoamingState, mL2ConnectedState);
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
// CHECKSTYLE:ON IndentationCheck
setInitialState(mDefaultState);
setLogRecSize(NUM_LOG_RECS_NORMAL);
setLogOnlyTransitions(false);
//start the state machine
start();
// Learn the initial state of whether the screen is on.
// We update this field when we receive broadcasts from the system.
handleScreenStateChanged(powerManager.isInteractive());
}
WifiStateMachine的状态图为
WifiStateMachine此时处于DisconnectedState状态,但这个状态里没有做什么,接着ConnectModeState会进行处理。
class ConnectModeState extends State {
...
@Override
public boolean processMessage(Message message) {
WifiConfiguration config;
int netId;
boolean ok;
boolean didDisconnect;
String bssid;
String ssid;
NetworkUpdateResult result;
Set<Integer> removedNetworkIds;
int reasonCode;
boolean timedOut;
logStateAndMessage(message, this);
switch (message.what) {
...
case WifiManager.SAVE_NETWORK:
result = saveNetworkConfigAndSendReply(message);
netId = result.getNetworkId();
if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) {
if (result.hasCredentialChanged()) {
config = (WifiConfiguration) message.obj;
// The network credentials changed and we're connected to this network,
// start a new connection with the updated credentials.
logi("SAVE_NETWORK credential changed for config=" + config.configKey()
+ ", Reconnecting.");
startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
} else {
if (result.hasProxyChanged()) {
log("Reconfiguring proxy on connection");
mIpClient.setHttpProxy(
getCurrentWifiConfiguration().getHttpProxy());
}
if (result.hasIpChanged()) {
// The current 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(b/36576642): clear addresses and disable IPv6
// to simplify obtainingIpState.
transitionTo(mObtainingIpState);
}
}
}
break;
...
}
return HANDLED;
}
}
}
首先会调用saveNetworkConfigAndSendReply来保存连接配置参数并发送广播。
/**
* Private method to handle calling WifiConfigManager to add & enable network configs and reply
* to the message from the sender of the outcome.
*
* @return NetworkUpdateResult with networkId of the added/updated configuration. Will return
* {@link WifiConfiguration#INVALID_NETWORK_ID} in case of error.
*/
private NetworkUpdateResult saveNetworkConfigAndSendReply(Message message) {
WifiConfiguration config = (WifiConfiguration) message.obj;
if (config == null) {
loge("SAVE_NETWORK with null configuration "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName());
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
NetworkUpdateResult result =
mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
if (!result.isSuccess()) {
loge("SAVE_NETWORK adding/updating config=" + config + " failed");
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
return result;
}
if (!mWifiConfigManager.enableNetwork(
result.getNetworkId(), false, message.sendingUid)) {
loge("SAVE_NETWORK enabling config=" + config + " failed");
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR);
return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID);
}
broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED);
return result;
}
调用mWifiConfigManager.addOrUpdateNetwork进行配置参数的保存,调用 broadcastWifiCredentialChanged发送广播。
/**
* Notify interested parties if a wifi config has been changed.
*
* @param wifiCredentialEventType WIFI_CREDENTIAL_SAVED or WIFI_CREDENTIAL_FORGOT
* @param config Must have a WifiConfiguration object to succeed
* TODO: b/35258354 investigate if this can be removed. Is the broadcast sent by
* WifiConfigManager sufficient?
*/
private void broadcastWifiCredentialChanged(int wifiCredentialEventType,
WifiConfiguration config) {
if (config != null && config.preSharedKey != null) {
Intent intent = new Intent(WifiManager.WIFI_CREDENTIAL_CHANGED_ACTION);
intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_SSID, config.SSID);
intent.putExtra(WifiManager.EXTRA_WIFI_CREDENTIAL_EVENT_TYPE,
wifiCredentialEventType);
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT,
android.Manifest.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE);
}
}
除了对配置AP参数的保存之外,回过头来再看startConnectToNetwork。
/**
* Automatically connect to the network specified
*
* @param networkId ID of the network to connect to
* @param uid UID of the app triggering the connection.
* @param bssid BSSID of the network
*/
public void startConnectToNetwork(int networkId, int uid, String bssid) {
sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
}
发送CMD_START_CONNECT消息,ConnectModeState会处理CMD_START_CONNECT 消息。
case CMD_START_CONNECT:
/* connect command coming from auto-join */
netId = message.arg1;
int uid = message.arg2;
bssid = (String) message.obj;
synchronized (mWifiReqCountLock) {
if (!hasConnectionRequests()) {
if (mNetworkAgent == null) {
loge("CMD_START_CONNECT but no requests and not connected,"
+ " bailing");
break;
} else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
loge("CMD_START_CONNECT but no requests and connected, but app "
+ "does not have sufficient permissions, bailing");
break;
}
}
}
config = mWifiConfigManager.getConfiguredNetworkWithoutMasking(netId);
logd("CMD_START_CONNECT sup state "
+ mSupplicantStateTracker.getSupplicantStateName()
+ " my state " + getCurrentState().getName()
+ " nid=" + Integer.toString(netId)
+ " roam=" + Boolean.toString(mIsAutoRoaming));
if (config == null) {
loge("CMD_START_CONNECT and no config, bail out...");
break;
}
mTargetNetworkId = netId;
setTargetBssid(config, bssid);
if (mEnableConnectedMacRandomization.get()) {
configureRandomizedMacAddress(config);
}
String currentMacAddress = mWifiNative.getMacAddress(mInterfaceName);
mWifiInfo.setMacAddress(currentMacAddress);
Log.i(TAG, "Connecting with " + currentMacAddress + " as the mac address");
reportConnectionAttemptStart(config, mTargetRoamBSSID,
WifiMetricsProto.ConnectionEvent.ROAM_UNRELATED);
if (mWifiNative.connectToNetwork(mInterfaceName, config)) {
mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT, config);
lastConnectAttemptTimestamp = mClock.getWallClockMillis();
targetWificonfiguration = config;
mIsAutoRoaming = false;
if (getCurrentState() != mDisconnectedState) {
transitionTo(mDisconnectingState);
}
} else {
loge("CMD_START_CONNECT Failed to start connection to network " + config);
reportConnectionAttemptEnd(
WifiMetrics.ConnectionEvent.FAILURE_CONNECT_NETWORK_FAILED,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.ERROR);
break;
}
break;
看mWifiNative.connectToNetwork。
2.4 frameworks/base/wifi/java/android/net/wifi/WifiNative.java
/**
* Add the provided network configuration to wpa_supplicant and initiate connection to it.
* This method does the following:
* 1. Abort any ongoing scan to unblock the connection request.
* 2. Remove any existing network in wpa_supplicant(This implicitly triggers disconnect).
* 3. Add a new network to wpa_supplicant.
* 4. Save the provided configuration to wpa_supplicant.
* 5. Select the new network in wpa_supplicant.
* 6. Triggers reconnect command to wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param configuration WifiConfiguration parameters for the provided network.
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean connectToNetwork(@NonNull String ifaceName, WifiConfiguration configuration) {
// Abort ongoing scan before connect() to unblock connection request.
mWificondControl.abortScan(ifaceName);
return mSupplicantStaIfaceHal.connectToNetwork(ifaceName, configuration);
}
如注释所示,这个方法做了6件事:
(1) 中止任何正在进行的扫描以免阻塞连接请求
(2)移除wpa_supplicant里的所有现有网络(这会隐式触发断开连接)
(3)在wpa_supplicant里添加一个新的网络
(4)在wpa_supplicant中保存提供的configuration
(5)在wpa_supplicant中选择新的网络
(6)触发wpa_supplicant 的重新连接命令
看mSupplicantStaIfaceHal.connectToNetwork。
2.5 frameworks/base/wifi/java/android/net/wifi/SupplicantStaIfaceHal.java
/**
* Add the provided network configuration to wpa_supplicant and initiate connection to it.
* This method does the following:
* 1. If |config| is different to the current supplicant network, removes all supplicant
* networks and saves |config|.
* 2. Select the new network in wpa_supplicant.
*
* @param ifaceName Name of the interface.
* @param config WifiConfiguration parameters for the provided network.
* @return {@code true} if it succeeds, {@code false} otherwise
*/
public boolean connectToNetwork(@NonNull String ifaceName, @NonNull WifiConfiguration config) {
synchronized (mLock) {
logd("connectToNetwork " + config.configKey());
WifiConfiguration currentConfig = getCurrentNetworkLocalConfig(ifaceName);
if (WifiConfigurationUtil.isSameNetwork(config, currentConfig)) {
String networkSelectionBSSID = config.getNetworkSelectionStatus()
.getNetworkSelectionBSSID();
String networkSelectionBSSIDCurrent =
currentConfig.getNetworkSelectionStatus().getNetworkSelectionBSSID();
if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
logd("Network is already saved, will not trigger remove and add operation.");
} else {
logd("Network is already saved, but need to update BSSID.");
if (!setCurrentNetworkBssid(
ifaceName,
config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
loge("Failed to set current network BSSID.");
return false;
}
mCurrentNetworkLocalConfigs.put(ifaceName, new WifiConfiguration(config));
}
} else {
mCurrentNetworkRemoteHandles.remove(ifaceName);
mCurrentNetworkLocalConfigs.remove(ifaceName);
if (!removeAllNetworks(ifaceName)) {
loge("Failed to remove existing networks");
return false;
}
Pair<SupplicantStaNetworkHal, WifiConfiguration> pair =
addNetworkAndSaveConfig(ifaceName, config);
if (pair == null) {
loge("Failed to add/save network configuration: " + config.configKey());
return false;
}
mCurrentNetworkRemoteHandles.put(ifaceName, pair.first);
mCurrentNetworkLocalConfigs.put(ifaceName, pair.second);
}
SupplicantStaNetworkHal networkHandle =
checkSupplicantStaNetworkAndLogFailure(ifaceName, "connectToNetwork");
if (networkHandle == null || !networkHandle.select()) {
loge("Failed to select network configuration: " + config.configKey());
return false;
}
return true;
}
}
首先添加网络addNetworkAndSaveConfig。
/**
* Add a network configuration to wpa_supplicant.
*
* @param config Config corresponding to the network.
* @return a Pair object including SupplicantStaNetworkHal and WifiConfiguration objects
* for the current network.
*/
private Pair<SupplicantStaNetworkHal, WifiConfiguration>
addNetworkAndSaveConfig(@NonNull String ifaceName, WifiConfiguration config) {
synchronized (mLock) {
logi("addSupplicantStaNetwork via HIDL");
if (config == null) {
loge("Cannot add NULL network!");
return null;
}
SupplicantStaNetworkHal network = addNetwork(ifaceName);
if (network == null) {
loge("Failed to add a network!");
return null;
}
boolean saveSuccess = false;
try {
saveSuccess = network.saveWifiConfiguration(config);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Exception while saving config params: " + config, e);
}
if (!saveSuccess) {
loge("Failed to save variables for: " + config.configKey());
if (!removeAllNetworks(ifaceName)) {
loge("Failed to remove all networks on failure.");
}
return null;
}
return new Pair(network, new WifiConfiguration(config));
}
}
看 addNetwork。
/**
* Adds a new network.
*
* @return The ISupplicantNetwork object for the new network, or null if the call fails
*/
private SupplicantStaNetworkHal addNetwork(@NonNull String ifaceName) {
synchronized (mLock) {
final String methodStr = "addNetwork";
ISupplicantStaIface iface = checkSupplicantStaIfaceAndLogFailure(ifaceName, methodStr);
if (iface == null) return null;
Mutable<ISupplicantNetwork> newNetwork = new Mutable<>();
try {
iface.addNetwork((SupplicantStatus status,
ISupplicantNetwork network) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
newNetwork.value = network;
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
if (newNetwork.value != null) {
return getStaNetworkMockable(
ifaceName,
ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder()));
} else {
return null;
}
}
}
添加网络就暂时只跟到Java框架层,回头再看保存配置network.saveWifiConfiguration。
2.6 frameworks/base/wifi/java/android/net/wifi/SupplicantStaNetworkHal.java
/**
* Save an entire WifiConfiguration to wpa_supplicant via HIDL.
*
* @param config WifiConfiguration object to be saved.
* @return true if succeeds, false otherwise.
* @throws IllegalArgumentException on malformed configuration params.
*/
public boolean saveWifiConfiguration(WifiConfiguration config) {
synchronized (mLock) {
if (config == null) return false;
/** SSID */
if (config.SSID != null) {
if (!setSsid(NativeUtil.decodeSsid(config.SSID))) {
Log.e(TAG, "failed to set SSID: " + config.SSID);
return false;
}
}
/** BSSID */
String bssidStr = config.getNetworkSelectionStatus().getNetworkSelectionBSSID();
if (bssidStr != null) {
byte[] bssid = NativeUtil.macAddressToByteArray(bssidStr);
if (!setBssid(bssid)) {
Log.e(TAG, "failed to set BSSID: " + bssidStr);
return false;
}
}
/** Pre Shared Key */
// This can either be quoted ASCII passphrase or hex string for raw psk.
if (config.preSharedKey != null) {
if (config.preSharedKey.startsWith("\"")) {
if (!setPskPassphrase(NativeUtil.removeEnclosingQuotes(config.preSharedKey))) {
Log.e(TAG, "failed to set psk passphrase");
return false;
}
} else {
if (!setPsk(NativeUtil.hexStringToByteArray(config.preSharedKey))) {
Log.e(TAG, "failed to set psk");
return false;
}
}
}
/** Wep Keys */
boolean hasSetKey = false;
if (config.wepKeys != null) {
for (int i = 0; i < config.wepKeys.length; i++) {
if (config.wepKeys[i] != null) {
if (!setWepKey(
i, NativeUtil.hexOrQuotedStringToBytes(config.wepKeys[i]))) {
Log.e(TAG, "failed to set wep_key " + i);
return false;
}
hasSetKey = true;
}
}
}
/** Wep Tx Key Idx */
if (hasSetKey) {
if (!setWepTxKeyIdx(config.wepTxKeyIndex)) {
Log.e(TAG, "failed to set wep_tx_keyidx: " + config.wepTxKeyIndex);
return false;
}
}
/** HiddenSSID */
if (!setScanSsid(config.hiddenSSID)) {
Log.e(TAG, config.SSID + ": failed to set hiddenSSID: " + config.hiddenSSID);
return false;
}
/** RequirePMF */
if (!setRequirePmf(config.requirePMF)) {
Log.e(TAG, config.SSID + ": failed to set requirePMF: " + config.requirePMF);
return false;
}
/** Key Management Scheme */
if (config.allowedKeyManagement.cardinality() != 0) {
// Add FT flags if supported.
BitSet keyMgmtMask = addFastTransitionFlags(config.allowedKeyManagement);
if (!setKeyMgmt(wifiConfigurationToSupplicantKeyMgmtMask(keyMgmtMask))) {
Log.e(TAG, "failed to set Key Management");
return false;
}
}
/** Security Protocol */
if (config.allowedProtocols.cardinality() != 0
&& !setProto(wifiConfigurationToSupplicantProtoMask(config.allowedProtocols))) {
Log.e(TAG, "failed to set Security Protocol");
return false;
}
/** Auth Algorithm */
if (config.allowedAuthAlgorithms.cardinality() != 0
&& !setAuthAlg(wifiConfigurationToSupplicantAuthAlgMask(
config.allowedAuthAlgorithms))) {
Log.e(TAG, "failed to set AuthAlgorithm");
return false;
}
/** Group Cipher */
if (config.allowedGroupCiphers.cardinality() != 0
&& !setGroupCipher(wifiConfigurationToSupplicantGroupCipherMask(
config.allowedGroupCiphers))) {
Log.e(TAG, "failed to set Group Cipher");
return false;
}
/** Pairwise Cipher*/
if (config.allowedPairwiseCiphers.cardinality() != 0
&& !setPairwiseCipher(wifiConfigurationToSupplicantPairwiseCipherMask(
config.allowedPairwiseCiphers))) {
Log.e(TAG, "failed to set PairwiseCipher");
return false;
}
/** metadata: FQDN + ConfigKey + CreatorUid */
final Map<String, String> metadata = new HashMap<String, String>();
if (config.isPasspoint()) {
metadata.put(ID_STRING_KEY_FQDN, config.FQDN);
}
metadata.put(ID_STRING_KEY_CONFIG_KEY, config.configKey());
metadata.put(ID_STRING_KEY_CREATOR_UID, Integer.toString(config.creatorUid));
if (!setIdStr(createNetworkExtra(metadata))) {
Log.e(TAG, "failed to set id string");
return false;
}
/** UpdateIdentifier */
if (config.updateIdentifier != null
&& !setUpdateIdentifier(Integer.parseInt(config.updateIdentifier))) {
Log.e(TAG, "failed to set update identifier");
return false;
}
// Finish here if no EAP config to set
if (config.enterpriseConfig != null
&& config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) {
if (!saveWifiEnterpriseConfig(config.SSID, config.enterpriseConfig)) {
return false;
}
}
// Now that the network is configured fully, start listening for callback events.
mISupplicantStaNetworkCallback =
new SupplicantStaNetworkHalCallback(config.networkId, config.SSID);
if (!registerCallback(mISupplicantStaNetworkCallback)) {
Log.e(TAG, "Failed to register callback");
return false;
}
return true;
}
}
整个AP连接过程暂时就跟到Java框架层。