让我们从最开始的enableWifi开始分析
public boolean enableNetwork(int netId, boolean disableOthers) { final boolean pin = disableOthers && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP; if (pin) { NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) .build(); NetworkPinner.pin(mContext, request); } boolean success; try { success = mService.enableNetwork(netId, disableOthers); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } if (pin && !success) { NetworkPinner.unpin(); } return success; }
Android 4.4的enableNetwork
public boolean enableNetwork(int netId, boolean disableOthers) { try { return mService.enableNetwork(netId, disableOthers); } catch (RemoteException e) { return false; } }
通过binder机制调用了WifiManagerService的enableNetwork方法,对比Android 4.4的源码我们发现Android最新源码多了NetworkPinner,我们先来分析一下NetworkPinner的作用。如果disableOthers为TRUE && Tartget版本低于Android5.0, pin 为TRUE,可能跟Android 5.0之前版本兼容有关,我们看一下NetworkPinner的介绍。
/** * A class that pins a process to the first network that satisfies a particular NetworkRequest. * * We use this to maintain compatibility with pre-M apps that call WifiManager.enableNetwork() * to connect to a Wi-Fi network that has no Internet access, and then assume that they will be * able to use that network because it's the system default. * * In order to maintain compatibility with apps that call setProcessDefaultNetwork themselves, * we try not to set the default network unless they have already done so, and we try not to * clear the default network unless we set it ourselves. * * This should maintain behaviour that's compatible with L, which would pin the whole system to * any wifi network that was created via enableNetwork(..., true) until that network * disconnected. * * Note that while this hack allows network traffic to flow, it is quite limited. For example: * * 1. setProcessDefaultNetwork only affects this process, so: * - Any subprocesses spawned by this process will not be pinned to Wi-Fi. * - If this app relies on any other apps on the device also being on Wi-Fi, that won't work * either, because other apps on the device will not be pinned. * 2. The behaviour of other APIs is not modified. For example: * - getActiveNetworkInfo will return the system default network, not Wi-Fi. * - There will be no CONNECTIVITY_ACTION broadcasts about TYPE_WIFI. * - getProcessDefaultNetwork will not return null, so if any apps are relying on that, they * will be surprised as well. * * This class is a per-process singleton because the process default network is a per-process * singleton. * */
大意是NetworkPinner是为了适配pre-M的app,当调用enableNetwork去连接网络不通的WiFi的情况下,确保他们使用这个网络,因为这是系统默认的。
public boolean enableNetwork(int netId, boolean disableOthers) { enforceChangePermission(); if (mWifiStateMachineChannel != null) { return mWifiStateMachine.syncEnableNetwork(mWifiStateMachineChannel, netId, disableOthers); } else { Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); return false; } }
public boolean syncEnableNetwork(AsyncChannel channel, int netId, boolean disableOthers) { Message resultMsg = channel.sendMessageSynchronously(CMD_ENABLE_NETWORK, netId, disableOthers ? 1 : 0); boolean result = (resultMsg.arg1 != FAILURE); resultMsg.recycle(); return result; }
我们看到了AsyncChannel,在WifiManager中也出现了,但是并没有被启用,因为启用它的方法都变成了hide,Android 的WiFi机制将消息系统从Proxy移到了Stub。
private class WifiStateMachineHandler extends Handler { private AsyncChannel mWsmChannel; WifiStateMachineHandler(Looper looper) { super(looper); mWsmChannel = new AsyncChannel(); mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler()); } ..... }
public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) { if (DBG) log("connected srcHandler to the dstMessenger E"); // Initialize source fields mSrcContext = srcContext; mSrcHandler = srcHandler; mSrcMessenger = new Messenger(mSrcHandler); // Initialize destination fields mDstMessenger = dstMessenger; linkToDeathMonitor(); if (DBG) log("connected srcHandler to the dstMessenger X"); } /** * Connect two local Handlers. * * @param srcContext is the context of the source * @param srcHandler is the hander to receive CONNECTED & DISCONNECTED * messages * @param dstHandler is the hander to send messages to. */ public void connect(Context srcContext, Handler srcHandler, Handler dstHandler) { connect(srcContext, srcHandler, new Messenger(dstHandler)); }
AsyncChannel connect的作用是连接两个handler,形成一个handleMessage的责任链模式。
public void sendMessage(Message msg) { msg.replyTo = mSrcMessenger; try { mDstMessenger.send(msg); } catch (RemoteException e) { replyDisconnected(STATUS_SEND_UNSUCCESSFUL); } }
在WifiServiceImpl发送的消息会被发送到WifiStateMachine,我们继续看一下里面的状态机是如何处理这个消息的。
case CMD_ENABLE_NETWORK: // Only the current foreground user can modify networks. if (!mWifiConfigManager.isCurrentUserProfile( UserHandle.getUserId(message.sendingUid))) { loge("Only the current foreground user can modify networks " + " currentUserId=" + mWifiConfigManager.getCurrentUserId() + " sendingUserId=" + UserHandle.getUserId(message.sendingUid)); replyToMessage(message, message.what, FAILURE); break; } boolean disableOthers = message.arg2 == 1; netId = message.arg1; config = mWifiConfigManager.getWifiConfiguration(netId); if (config == null) { loge("No network with id = " + netId); messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; replyToMessage(message, message.what, FAILURE); break; } // disable other only means select this network, does not mean all other // networks need to be disabled if (disableOthers) { // Remember time of last connection attempt lastConnectAttemptTimestamp = System.currentTimeMillis(); mWifiConnectionStatistics.numWifiManagerJoinAttempt++; } // Cancel auto roam requests autoRoamSetBSSID(netId, "any"); ok = mWifiConfigManager.enableNetwork( config, disableOthers, message.sendingUid); if (!ok) { messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; } else { if (disableOthers) { mTargetNetworkId = netId; } mWifiConnectivityManager.forceConnectivityScan(); } replyToMessage(message, message.what, ok ? SUCCESS : FAILURE); break;
只有在ConnectModeState状态中进行了有效的处理,主要的部分是
ok = mWifiConfigManager.enableNetwork( config, disableOthers, message.sendingUid);
WifiConfigManager去设置这个networkId的WiFi的优先级。
/** * Enable a network. Note that there is no saveConfig operation. * This function is retained for compatibility with the public * API. The more powerful selectNetwork()/saveNetwork() is used by the * state machine for connecting to a network * * @param config network to be enabled * @return {@code true} if it succeeds, {@code false} otherwise */ boolean enableNetwork(WifiConfiguration config, boolean disableOthers, int uid) { if (config == null) { return false; } updateNetworkSelectionStatus( config, WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLE); setLatestUserSelectedConfiguration(config); boolean ret = true; if (disableOthers) { ret = selectNetworkWithoutBroadcast(config.networkId); if (sVDBG) { localLogNetwork("enableNetwork(disableOthers=true, uid=" + uid + ") ", config.networkId); } updateLastConnectUid(config, uid); writeKnownNetworkHistory(); sendConfiguredNetworksChangedBroadcast(); } else { if (sVDBG) localLogNetwork("enableNetwork(disableOthers=false) ", config.networkId); sendConfiguredNetworksChangedBroadcast(config, WifiManager.CHANGE_REASON_CONFIG_CHANGE); } return ret; }
如果disableOthers为true进入selectNetworkWithoutBroadcast
/** * Selects the specified network for connection. This involves * updating the priority of all the networks and enabling the given * network while disabling others. * * Selecting a network will leave the other networks disabled and * a call to enableAllNetworks() needs to be issued upon a connection * or a failure event from supplicant * * @param config network to select for connection * @param updatePriorities makes config highest priority network * @return false if the network id is invalid */ boolean selectNetwork(WifiConfiguration config, boolean updatePriorities, int uid) { if (sVDBG) localLogNetwork("selectNetwork", config.networkId); if (config.networkId == INVALID_NETWORK_ID) return false; if (!WifiConfigurationUtil.isVisibleToAnyProfile(config, mUserManager.getProfiles(mCurrentUserId))) { loge("selectNetwork " + Integer.toString(config.networkId) + ": Network config is not " + "visible to current user."); return false; } // Reset the priority of each network at start or if it goes too high. if (mLastPriority == -1 || mLastPriority > 1000000) { if (updatePriorities) { for (WifiConfiguration config2 : mConfiguredNetworks.valuesForCurrentUser()) { if (config2.networkId != INVALID_NETWORK_ID) { setNetworkPriorityNative(config2, 0); } } } mLastPriority = 0; } // Set to the highest priority and save the configuration. if (updatePriorities) { setNetworkPriorityNative(config, ++mLastPriority); } if (config.isPasspoint()) { // Set the SSID for the underlying WPA supplicant network entry corresponding to this // Passpoint profile to the SSID of the BSS selected by QNS. |config.SSID| is set by // selectQualifiedNetwork.selectQualifiedNetwork(), when the qualified network selected // is a Passpoint network. logd("Setting SSID for WPA supplicant network " + config.networkId + " to " + config.SSID); setSSIDNative(config, config.SSID); } mWifiConfigStore.enableHS20(config.isPasspoint()); if (updatePriorities) { saveConfig(); } updateLastConnectUid(config, uid); writeKnownNetworkHistory(); /* Enable the given network while disabling all other networks */ selectNetworkWithoutBroadcast(config.networkId); /* Avoid saving the config & sending a broadcast to prevent settings * from displaying a disabled list of networks */ return true; }
这个方法改变了WiFi的优先级,会将其他的WiFi设置为disable
private void sendConfiguredNetworksChangedBroadcast(WifiConfiguration network, int reason) { Intent intent = new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED, false); intent.putExtra(WifiManager.EXTRA_WIFI_CONFIGURATION, network); intent.putExtra(WifiManager.EXTRA_CHANGE_REASON, reason); mContext.sendBroadcastAsUser(intent, UserHandle.ALL); }
设置优先级后, 发送WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION的广播去更新状态机。
private void performTransitions() {//状态机切换
所有状态机的基类是StateMachine,处理所有的message,使用方法performTranstions切换状态机。
有关状态机切换的资料可以参考:http://blog.csdn.net/tankai19880619/article/details/42294815
我们再分析一个场景,WiFi正在连接A,然后我们点击连接B,此时状态机处于connecting状态,我们看这个状态(ConnectModeState)如何处理消息。
@Override public boolean processMessage(Message message) { WifiConfiguration config; int netId; boolean ok; boolean didDisconnect; String bssid; String ssid; NetworkUpdateResult result; logStateAndMessage(message, this); switch (message.what) { case WifiMonitor.ASSOCIATION_REJECTION_EVENT: ...... break; case WifiMonitor.AUTHENTICATION_FAILURE_EVENT: ...... break; case WifiMonitor.SSID_TEMP_DISABLED: ...... break; case WifiMonitor.SSID_REENABLED: Log.d(TAG, "Supplicant SSID reenable:" + mWifiConfigManager.getWifiConfiguration(message.arg1)); // Do not re-enable it in Quality Network Selection since framework has its own // Algorithm of disable/enable break; case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT: SupplicantState state = handleSupplicantStateChange(message); // A driver/firmware hang can now put the interface in a down state. // We detect the interface going down and recover from it if (!SupplicantState.isDriverActive(state)) { if (mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) { handleNetworkDisconnect(); } log("Detected an interface down, restart driver"); transitionTo(mDriverStoppedState); sendMessage(CMD_START_DRIVER); break; } // Supplicant can fail to report a NETWORK_DISCONNECTION_EVENT // when authentication times out after a successful connection, // we can figure this from the supplicant state. If supplicant // state is DISCONNECTED, but the mNetworkInfo says we are not // disconnected, we need to handle a disconnection if (!linkDebouncing && state == SupplicantState.DISCONNECTED && mNetworkInfo.getState() != NetworkInfo.State.DISCONNECTED) { if (DBG) log("Missed CTRL-EVENT-DISCONNECTED, disconnect"); handleNetworkDisconnect(); transitionTo(mDisconnectedState); } // If we have COMPLETED a connection to a BSSID, start doing // DNAv4/DNAv6 -style probing for on-link neighbors of // interest (e.g. routers); harmless if none are configured. if (state == SupplicantState.COMPLETED) { mIpManager.confirmConfiguration(); } break;
SUPPLICANT_STATE_CHANGE_EVENT(请求状态改变),如果状态不是SupplicantState.DISCONNECTED,断开连接handleNetworkDisconnect。
private void handleNetworkDisconnect() { if (DBG) log("handleNetworkDisconnect: Stopping DHCP and clearing IP" + " stack:" + Thread.currentThread().getStackTrace()[2].getMethodName() + " - " + Thread.currentThread().getStackTrace()[3].getMethodName() + " - " + Thread.currentThread().getStackTrace()[4].getMethodName() + " - " + Thread.currentThread().getStackTrace()[5].getMethodName()); stopRssiMonitoringOffload(); clearCurrentConfigBSSID("handleNetworkDisconnect"); stopIpManager(); /* Reset data structures */ mWifiScoreReport = null; mWifiInfo.reset(); linkDebouncing = false; /* Reset roaming parameters */ mAutoRoaming = false; setNetworkDetailedState(DetailedState.DISCONNECTED); if (mNetworkAgent != null) { mNetworkAgent.sendNetworkInfo(mNetworkInfo); mNetworkAgent = null; } mWifiConfigManager.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED); /* Clear network properties */ clearLinkProperties(); /* Cend event to CM & network change broadcast */ sendNetworkStateChangeBroadcast(mLastBssid); /* Cancel auto roam requests */ autoRoamSetBSSID(mLastNetworkId, "any"); mLastBssid = null; registerDisconnected(); mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID; }
class L2ConnectedState extends State { @Override public void enter() { mRssiPollToken++; if (mEnableRssiPolling) { sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0); } if (mNetworkAgent != null) { loge("Have NetworkAgent when entering L2Connected"); setNetworkDetailedState(DetailedState.DISCONNECTED); } setNetworkDetailedState(DetailedState.CONNECTING); mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext, "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter, mLinkProperties, 60, mNetworkMisc); // We must clear the config BSSID, as the wifi chipset may decide to roam // from this point on and having the BSSID specified in the network block would // cause the roam to faile and the device to disconnect clearCurrentConfigBSSID("L2ConnectedState"); mCountryCode.setReadyForChange(false); mWifiMetrics.setWifiState(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED); }
mNetworkAgent是在L2ConnectedState中创建,用于发送EVENT_NETWORK_INFO_CHANGED消息和NetworkInfo给ConnectivityService。如果连上了某个WIFI,那么此时的WIFI状态机将会进入L2ConnectedState状态。
private void sendNetworkStateChangeBroadcast(String bssid) { Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, new NetworkInfo(mNetworkInfo)); intent.putExtra(WifiManager.EXTRA_LINK_PROPERTIES, new LinkProperties(mLinkProperties)); if (bssid != null) intent.putExtra(WifiManager.EXTRA_BSSID, bssid); if (mNetworkInfo.getDetailedState() == DetailedState.VERIFYING_POOR_LINK || mNetworkInfo.getDetailedState() == DetailedState.CONNECTED) { // We no longer report MAC address to third-parties and our code does // not rely on this broadcast, so just send the default MAC address. fetchRssiLinkSpeedAndFrequencyNative(); WifiInfo sentWifiInfo = new WifiInfo(mWifiInfo); sentWifiInfo.setMacAddress(WifiInfo.DEFAULT_MAC_ADDRESS); intent.putExtra(WifiManager.EXTRA_WIFI_INFO, sentWifiInfo); } mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); }
当WiFi状态改变时都会调用sendNetworkStateChangeBroadcast发送NETWORK_STATE_CHANGED_ACTION广播,从代码看只有在连接成功的时候才会附带WifiInfo。