WifiSettings
@Override
public void onSubmit(WifiDialog dialog) {
if (mDialog != null) {
submit(mDialog.getController());
}
}
/* 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();
}
protected void connect(final WifiConfiguration config, boolean isSavedNetwork) {
// Log subtype if configuration is a saved network.
mMetricsFeatureProvider.action(getVisibilityLogger(), MetricsEvent.ACTION_WIFI_CONNECT,
isSavedNetwork);
mWifiManager.connect(config, mConnectListener);
mClickedConnect = true;
}
WifiManager
/**
* Connect to a network with the given configuration. The network also
* gets added to the list of configured networks for the foreground user.
*
* For a new network, this function is used instead of a
* sequence of addNetwork(), enableNetwork(), and reconnect()
*
* @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
*/
@SystemApi
public void connect(WifiConfiguration config, ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
// Use INVALID_NETWORK_ID for arg1 when passing a config object
// arg1 is used to pass network id when the network already exists
getChannel().sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
putListener(listener), config);
}
WifiServiceImpl
case WifiManager.CONNECT_NETWORK: {
if (checkChangePermissionAndReplyIfNotAuthorized(
msg, WifiManager.CONNECT_NETWORK_FAILED)) {
WifiConfiguration config = (WifiConfiguration) msg.obj;
int networkId = msg.arg1;
Slog.d(TAG, "CONNECT "
+ " 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 if (config == null
&& networkId != WifiConfiguration.INVALID_NETWORK_ID) {
mWifiStateMachine.sendMessage(Message.obtain(msg));
} else {
Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.INVALID_ARGS);
}
}
break;
}
WifiStateMachine
class ConnectModeState extends State {
...
case WifiManager.CONNECT_NETWORK:
/**
* The connect message can contain a network id passed as arg1 on message or
* or a config passed as obj on message.
* For a new network, a config is passed to create and connect.
* For an existing network, a network id is passed
*/
netId = message.arg1;
config = (WifiConfiguration) message.obj;
boolean hasCredentialChanged = false;
// New network addition.
if (config != null) {
result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid);
if (!result.isSuccess()) {
loge("CONNECT_NETWORK adding/updating config=" + config + " failed");
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.ERROR);
break;
}
netId = result.getNetworkId();
hasCredentialChanged = result.hasCredentialChanged();
}
if (!connectToUserSelectNetwork(
netId, message.sendingUid, hasCredentialChanged)) {
messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL;
replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
WifiManager.NOT_AUTHORIZED);
break;
}
mWifiMetrics.logStaEvent(StaEvent.TYPE_CONNECT_NETWORK, config);
broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config);
replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
break;
这边发现在连接之前会更新一下WifiConfigManager里的config,看了下主要会把连接的uid的更新名字和时间更新到config里去。
/**
* Create a new internal WifiConfiguration object by copying over parameters from the provided
* external configuration to a copy of the existing internal WifiConfiguration object.
*
* @param internalConfig WifiConfiguration object in our internal map.
* @param externalConfig WifiConfiguration object provided from the external API.
* @return Copy of existing WifiConfiguration object with parameters merged from the provided
* configuration.
*/
private WifiConfiguration updateExistingInternalWifiConfigurationFromExternal(
WifiConfiguration internalConfig, WifiConfiguration externalConfig, int uid) {
WifiConfiguration newInternalConfig = new WifiConfiguration(internalConfig);
// Copy over all the public elements from the provided configuration.
mergeWithInternalWifiConfiguration(newInternalConfig, externalConfig);
// Add debug information for network update.
newInternalConfig.lastUpdateUid = uid;
newInternalConfig.lastUpdateName = mContext.getPackageManager().getNameForUid(uid);
newInternalConfig.updateTime = createDebugTimeStampString(mClock.getWallClockMillis());
return newInternalConfig;
}
再回头看下连接流程
/**
* Initiates connection to a network specified by the user/app. This method checks if the
* requesting app holds the NETWORK_SETTINGS permission.
*
* @param netId Id network to initiate connection.
* @param uid UID of the app requesting the connection.
* @param forceReconnect Whether to force a connection even if we're connected to the same
* network currently.
*/
private boolean connectToUserSelectNetwork(int netId, int uid, boolean forceReconnect) {
logd("connectToUserSelectNetwork netId " + netId + ", uid " + uid
+ ", forceReconnect = " + forceReconnect);
if (mWifiConfigManager.getConfiguredNetwork(netId) == null) {
loge("connectToUserSelectNetwork Invalid network Id=" + netId);
return false;
}
if (!mWifiConfigManager.enableNetwork(netId, true, uid)
|| !mWifiConfigManager.updateLastConnectUid(netId, uid)) {
logi("connectToUserSelectNetwork Allowing uid " + uid
+ " with insufficient permissions to connect=" + netId);
} else if (mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
// Note user connect choice here, so that it will be considered in the next network
// selection.
mWifiConnectivityManager.setUserConnectChoice(netId);
}
if (!forceReconnect && mWifiInfo.getNetworkId() == netId) {
// We're already connected to the user specified network, don't trigger a
// reconnection unless it was forced.
logi("connectToUserSelectNetwork already connecting/connected=" + netId);
} else {
mWifiConnectivityManager.prepareForForcedConnection(netId);
startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
}
return true;
}
稍微看下setUserConnectChoice,大概意思是将该次连接的网络优先级提到最高,下次连接优先考虑。
WifiConnectivityManager
/**
* Handler when user specifies a particular network to connect to
*/
public void setUserConnectChoice(int netId) {
localLog("setUserConnectChoice: netId=" + netId);
mNetworkSelector.setUserConnectChoice(netId);
}
WifiNetworkSelector
/**
* This API is called when user explicitly selects a network. Currently, it is used in following
* cases:
* (1) User explicitly chooses to connect to a saved network.
* (2) User saves a network after adding a new network.
* (3) User saves a network after modifying a saved network.
* Following actions will be triggered:
* 1. If this network is disabled, we need re-enable it again.
* 2. This network is favored over all the other networks visible in latest network
* selection procedure.
*
* @param netId ID for the network chosen by the user
* @return true -- There is change made to connection choice of any saved network.
* false -- There is no change made to connection choice of any saved network.
*/
public boolean setUserConnectChoice(int netId) {
localLog("userSelectNetwork: network ID=" + netId);
WifiConfiguration selected = mWifiConfigManager.getConfiguredNetwork(netId);
if (selected == null || selected.SSID == null) {
localLog("userSelectNetwork: Invalid configuration with nid=" + netId);
return false;
}
// Enable the network if it is disabled.
if (!selected.getNetworkSelectionStatus().isNetworkEnabled()) {
mWifiConfigManager.updateNetworkSelectionStatus(netId,
WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE);
}
boolean change = false;
String key = selected.configKey();
// This is only used for setting the connect choice timestamp for debugging purposes.
long currentTime = mClock.getWallClockMillis();
List savedNetworks = mWifiConfigManager.getSavedNetworks();
for (WifiConfiguration network : savedNetworks) {
WifiConfiguration.NetworkSelectionStatus status = network.getNetworkSelectionStatus();
if (network.networkId == selected.networkId) {
if (status.getConnectChoice() != null) {
localLog("Remove user selection preference of " + status.getConnectChoice()
+ " Set Time: " + status.getConnectChoiceTimestamp() + " from "
+ network.SSID + " : " + network.networkId);
mWifiConfigManager.clearNetworkConnectChoice(network.networkId);
change = true;
}
continue;
}
if (status.getSeenInLastQualifiedNetworkSelection()
&& (status.getConnectChoice() == null
|| !status.getConnectChoice().equals(key))) {
localLog("Add key: " + key + " Set Time: " + currentTime + " to "
+ toNetworkString(network));
mWifiConfigManager.setNetworkConnectChoice(network.networkId, key, currentTime);
change = true;
}
}
return change;
}
WifiConfigManager
/**
* Set the {@link NetworkSelectionStatus#mConnectChoice} &
* {@link NetworkSelectionStatus#mConnectChoiceTimestamp} for the provided network.
*
* This is invoked by Network Selector when the user overrides the currently connected network
* choice.
*
* @param networkId network ID corresponding to the network.
* @param connectChoiceConfigKey ConfigKey corresponding to the network which was chosen over
* this network.
* @param timestamp timestamp at which the choice was made.
* @return true if the network was found, false otherwise.
*/
public boolean setNetworkConnectChoice(
int networkId, String connectChoiceConfigKey, long timestamp) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Set network connect choice " + connectChoiceConfigKey + " for " + networkId);
}
WifiConfiguration config = getInternalConfiguredNetwork(networkId);
if (config == null) {
return false;
}
config.getNetworkSelectionStatus().setConnectChoice(connectChoiceConfigKey);
config.getNetworkSelectionStatus().setConnectChoiceTimestamp(timestamp);
saveToStore(false);
return true;
}
WifiConfiguration.java
/**
* set user preferred choice over this configuration
* @param newConnectChoice, the configKey of user preferred choice over this configuration
*/
public void setConnectChoice(String newConnectChoice) {
mConnectChoice = newConnectChoice;
}
这个choice会在如下类中起作用,具体逻辑待续
/**
* Overrides the {@code candidate} chosen by the {@link #mEvaluators} with the user chosen
* {@link WifiConfiguration} if one exists.
*
* @return the user chosen {@link WifiConfiguration} if one exists, {@code candidate} otherwise
*/
private WifiConfiguration overrideCandidateWithUserConnectChoice(
@NonNull WifiConfiguration candidate) {
WifiConfiguration tempConfig = candidate;
WifiConfiguration originalCandidate = candidate;
ScanResult scanResultCandidate = candidate.getNetworkSelectionStatus().getCandidate();
while (tempConfig.getNetworkSelectionStatus().getConnectChoice() != null) {
String key = tempConfig.getNetworkSelectionStatus().getConnectChoice();
tempConfig = mWifiConfigManager.getConfiguredNetwork(key);
if (tempConfig != null) {
WifiConfiguration.NetworkSelectionStatus tempStatus =
tempConfig.getNetworkSelectionStatus();
if (tempStatus.getCandidate() != null && tempStatus.isNetworkEnabled()) {
scanResultCandidate = tempStatus.getCandidate();
candidate = tempConfig;
}
} else {
localLog("Connect choice: " + key + " has no corresponding saved config.");
break;
}
}
if (candidate != originalCandidate) {
localLog("After user selection adjustment, the final candidate is:"
+ WifiNetworkSelector.toNetworkString(candidate) + " : "
+ scanResultCandidate.BSSID);
}
return candidate;
}
继续梳理连接流程
/**
* 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);
}
class ConnectModeState extends State {
...
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;
1)校验下是否有连接请求
2)获取下密码未隐藏的config,只可以在WiFi 堆栈中调用,api中不要调用,与普通获取config的区别在于密码有没有被替换为*
/**
* Helper method to mask all passwords/keys from the provided WifiConfiguration object. This
* is needed when the network configurations are being requested via the public WifiManager
* API's.
* This currently masks the following elements: psk, wepKeys & enterprise config password.
*/
private void maskPasswordsInWifiConfiguration(WifiConfiguration configuration) {
if (!TextUtils.isEmpty(configuration.preSharedKey)) {
configuration.preSharedKey = PASSWORD_MASK;
}
if (configuration.wepKeys != null) {
for (int i = 0; i < configuration.wepKeys.length; i++) {
if (!TextUtils.isEmpty(configuration.wepKeys[i])) {
configuration.wepKeys[i] = PASSWORD_MASK;
}
}
}
if (!TextUtils.isEmpty(configuration.enterpriseConfig.getPassword())) {
configuration.enterpriseConfig.setPassword(PASSWORD_MASK);
}
}
3)连接的时候有没有使用随机mac,以隐藏mac
if (mEnableConnectedMacRandomization.get()) {
configureRandomizedMacAddress(config);
}
/**
* Dynamically change the MAC address to use the locally randomized
* MAC address generated for each network.
* @param config WifiConfiguration with mRandomizedMacAddress to change into. If the address
* is masked out or not set, it will generate a new random MAC address.
*/
private void configureRandomizedMacAddress(WifiConfiguration config) {
if (config == null) {
Log.e(TAG, "No config to change MAC address to");
return;
}
MacAddress currentMac = MacAddress.fromString(mWifiNative.getMacAddress(mInterfaceName));
MacAddress newMac = config.getOrCreateRandomizedMacAddress();
mWifiConfigManager.setNetworkRandomizedMacAddress(config.networkId, newMac);
if (!WifiConfiguration.isValidMacAddressForRandomization(newMac)) {
Log.wtf(TAG, "Config generated an invalid MAC address");
} else if (currentMac.equals(newMac)) {
Log.d(TAG, "No changes in MAC address");
} else {
mWifiMetrics.logStaEvent(StaEvent.TYPE_MAC_CHANGE, config);
boolean setMacSuccess =
mWifiNative.setMacAddress(mInterfaceName, newMac);
Log.d(TAG, "ConnectedMacRandomization SSID(" + config.getPrintableSsid()
+ "). setMacAddress(" + newMac.toString() + ") from "
+ currentMac.toString() + " = " + setMacSuccess);
}
}
WifiNative
/**
* 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);
}
SupplicantStaIfaceHal
/**
* 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 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;
}
}
SupplicantStaNetworkHal
/**
* Trigger a connection to this network.
*
* @return true if it succeeds, false otherwise.
*/
public boolean select() {
synchronized (mLock) {
final String methodStr = "select";
if (!checkISupplicantStaNetworkAndLogFailure(methodStr)) return false;
try {
SupplicantStatus status = mISupplicantStaNetwork.select();
return checkStatusAndLogFailure(status, methodStr);
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
return false;
}
}
}
通过hidl调用到supplicant
sta_network
Return StaNetwork::select(select_cb _hidl_cb)
{
return validateAndCall(
this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
&StaNetwork::selectInternal, _hidl_cb);
}
SupplicantStatus StaNetwork::selectInternal()
{
struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
if (wpa_ssid->disabled == 2) {
return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
}
struct wpa_supplicant *wpa_s = retrieveIfacePtr();
wpa_s->scan_min_time.sec = 0;
wpa_s->scan_min_time.usec = 0;
// Make sure that the supplicant is updated to the latest
// MAC address, which might have changed due to MAC randomization.
wpa_supplicant_update_mac_addr(wpa_s);
wpa_supplicant_select_network(wpa_s, wpa_ssid);
return {SupplicantStatusCode::SUCCESS, ""};
}
Android P流程和Android O总体没变化,状态机那边主要是启动的时候根据模式抽出来了,连接流程还是在WifiStateMachine里。流程图就不重新画了,和Android O一模一样的。