都知道Wi-Fi连接成功后需要做连通性的一个校验,如果校验成功,则表示网络畅通,反之就是网络受阻,我们看下Android 13这块代码是否有什么变化,http://aospxref.com/android-13.0.0_r3/
具体的代码位置:http://aospxref.com/android-13.0.0_r3/xref/packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
状态机关系如下:
addState(mDefaultState);
addState(mMaybeNotifyState, mDefaultState);
addState(mEvaluatingState, mMaybeNotifyState);
addState(mProbingState, mEvaluatingState);
addState(mWaitingForNextProbeState, mEvaluatingState);
addState(mCaptivePortalState, mMaybeNotifyState);
addState(mEvaluatingPrivateDnsState, mDefaultState);
addState(mEvaluatingBandwidthState, mDefaultState);
addState(mValidatedState, mDefaultState);
setInitialState(mDefaultState);
处于 ValidatedState 状态表示网络已成功验证,或用户希望“按原样”验证,或不满足默认的NetworkRequest,因此已跳过验证。
处于 MaybeNotifyState 状态表示可能已通知用户需要登录。此状态注意在退出状态时清除通知。
处于 CaptivePortalState 状态表示检测到 CaptivePortal,并向用户显示登录通知
处于 EvaluatingState 状态表示正在评估网络的 Internet 连接性,或者用户已指出该网络是不需要的。
处于 WaitingForNextProbe 状态表示评估探测失败,状态从ProbingState转换。这确保状态机仅在探测进行时处于ProbingState,而不是在等待执行下一个探测时,这允许ProbingState延迟大多数消息,直到探测完成。
为了记录全,这里我们从如何检测到portal网络开始看,当一个网络连接上,我们看下此刻干了啥:
http://aospxref.com/android-13.0.0_r3/xref/packages/apps/Settings/src/com/android/settings/wifi/WifiSettings.java#updateWifiEntryPreferences()
private void updateWifiEntryPreferences() {
// in case state has changed
if (mWifiPickerTracker.getWifiState() != WifiManager.WIFI_STATE_ENABLED) {
return;
}
//如果此时状态不是已连接的,就直接返回,是的就继续往下,这里只看和登录相关的
final WifiEntry connectedEntry = mWifiPickerTracker.getConnectedWifiEntry();
pref.setOnPreferenceClickListener(preference -> {
if (connectedEntry.canSignIn()) { ---> 去WifiPickerTracker看下这个方法
connectedEntry.signIn(null /* callback */);
}
connectedEntry 此时通过mWifiPickerTracker.getConnectedWifiEntry()拿到的是 mConnectedWifiEntry
那 mConnectedWifiEntry 是指的啥呢?代码里搜下,看这段代码:
protected void updateWifiEntries() {
synchronized (mLock) {
// 从标准缓存中查找已连接的 Wi-Fi 条目
mConnectedWifiEntry = mStandardWifiEntryCache.stream().filter(entry -> {
final @WifiEntry.ConnectedState int connectedState = entry.getConnectedState();
return connectedState == CONNECTED_STATE_CONNECTED
|| connectedState == CONNECTED_STATE_CONNECTING;
}).findAny().orElse(null /* other */);
//mConnectedWifiEntry没有,再看看是不是SuggestedWifiEntry缓存
if (mConnectedWifiEntry == null) {
mConnectedWifiEntry = mSuggestedWifiEntryCache.stream().filter(entry -> {
final @WifiEntry.ConnectedState int connectedState = entry.getConnectedState();
return connectedState == CONNECTED_STATE_CONNECTED
|| connectedState == CONNECTED_STATE_CONNECTING;
}).findAny().orElse(null /* other */);
}
// 如果仍然没有找到已连接的 Wi-Fi 条目,检查 Passpoint 缓存
if (mConnectedWifiEntry == null) {
mConnectedWifiEntry = mPasspointWifiEntryCache.values().stream().filter(entry -> {
final @WifiEntry.ConnectedState int connectedState = entry.getConnectedState();
return connectedState == CONNECTED_STATE_CONNECTED
|| connectedState == CONNECTED_STATE_CONNECTING;
}).findAny().orElse(null /* other */);
}
// 如果有网络请求的条目,并且该条目的连接状态不是断开连接,将其设置为已连接的 Wi-Fi 条目
if (mConnectedWifiEntry == null && mNetworkRequestEntry != null
&& mNetworkRequestEntry.getConnectedState() != CONNECTED_STATE_DISCONNECTED) {
mConnectedWifiEntry = mNetworkRequestEntry;
}
// 设置已连接的 Wi-Fi 条目为默认网络
if (mConnectedWifiEntry != null) {
mConnectedWifiEntry.setIsDefaultNetwork(mIsWifiDefaultRoute);
}
// 清空 Wi-Fi 条目列表
mWifiEntries.clear();
// 获取具有可见建议的扫描结果键集合
final Set<ScanResultKey> scanResultKeysWithVisibleSuggestions =
mSuggestedWifiEntryCache.stream()
.filter(entry -> entry.isUserShareable()
|| entry == mConnectedWifiEntry)
.map(entry -> entry.getStandardWifiEntryKey().getScanResultKey())
.collect(Collectors.toSet());
// 获取 Passpoint SSID 的 UTF-8 编码集合
Set<String> passpointUtf8Ssids = new ArraySet<>();
for (PasspointWifiEntry passpointWifiEntry : mPasspointWifiEntryCache.values()) {
passpointUtf8Ssids.addAll(passpointWifiEntry.getAllUtf8Ssids());
}
// 更新标准 Wi-Fi 条目列表
for (StandardWifiEntry entry : mStandardWifiEntryCache) {
entry.updateAdminRestrictions();
if (entry == mConnectedWifiEntry) {
continue;
}
// 过滤掉未保存的条目,已存在于可见建议中的条目,以及已存在于 Passpoint 缓存中的条目
if (!entry.isSaved()) {
if (scanResultKeysWithVisibleSuggestions
.contains(entry.getStandardWifiEntryKey().getScanResultKey())) {
continue;
}
if (passpointUtf8Ssids.contains(entry.getSsid())) {
continue;
}
}
// 将符合条件的标准 Wi-Fi 条目添加到列表中
mWifiEntries.add(entry);
}
// 添加断开连接的建议 Wi-Fi 条目到列表中
mWifiEntries.addAll(mSuggestedWifiEntryCache.stream().filter(entry ->
entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED
&& entry.isUserShareable()).collect(toList()));
// 添加断开连接的 Passpoint Wi-Fi 条目到列表中
mWifiEntries.addAll(mPasspointWifiEntryCache.values().stream().filter(entry ->
entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED).collect(toList()));
// 添加断开连接的 OUS Wi-Fi 条目到列表中
mWifiEntries.addAll(mOsuWifiEntryCache.values().stream().filter(entry ->
entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED
&& !entry.isAlreadyProvisioned()).collect(toList()));
// 添加断开连接的上下文 Wi-Fi 条目到列表中
mWifiEntries.addAll(getContextualWifiEntries().stream().filter(entry ->
entry.getConnectedState() == CONNECTED_STATE_DISCONNECTED).collect(toList()));
// 根据排序规则对 Wi-Fi 条目列表进行排序
Collections.sort(mWifiEntries, WifiEntry.WIFI_PICKER_COMPARATOR);
// 如果不清楚可以看这句log,就知道 mConnectedWifiEntry 被赋值哪一个了
if (isVerboseLoggingEnabled()) {
Log.v(TAG, "Connected WifiEntry: " + mConnectedWifiEntry);
Log.v(TAG, "Updated WifiEntries: " + Arrays.toString(mWifiEntries.toArray()));
}
}
// 通知监听器 Wi-Fi 条目已更改
notifyOnWifiEntriesChanged();
}
贴下这个log可以参考下:
WifiPickerTracker: Connected WifiEntry: StandardWifiEntry:{"SCAN_RESULT_KEY":"{\"SSID\":\"wifitest\",\"SECURITY_TYPES\":[0,6]}","IS_TARGETING_NEW_NETWORKS":true},title:wifitest,summary:Obtaining IP address…,isSaved:true,isSubscription:false,
isSuggestion:false,level:4X,security[0],connected:false,connectedInfo:null,isValidated:false,isDefaultNetwork:false
此时我们知道是StandardWifiEntry,所以看下这里面的 canSignIn() 和 signIn() 方法:
http://aospxref.com/android-13.0.0_r3/xref/frameworks/opt/net/wifi/libs/WifiTrackerLib/src/com/android/wifitrackerlib/StandardWifiEntry.java#canSignIn()
@Override
public synchronized boolean canSignIn() {
return mNetworkCapabilities != null && mNetworkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
}
@Override
public void signIn(@Nullable SignInCallback callback) {
if (canSignIn()) {
// canSignIn() implies that this WifiEntry is the currently connected network, so use
// getCurrentNetwork() to start the captive portal app.
NonSdkApiWrapper.startCaptivePortalApp(mContext.getSystemService(ConnectivityManager.class),
mWifiManager.getCurrentNetwork());
}
}
启动CaptivePortalApp,接下来就是一步步的调用
http://aospxref.com/android-13.0.0_r3/xref/frameworks/opt/net/wifi/libs/WifiTrackerLib/src/com/android/wifitrackerlib/
NonSdkApiWrapper.java#startCaptivePortalApp
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Connectivity/framework/src/android/net/
ConnectivityManager.java#startCaptivePortalApp
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Connectivity/service/src/com/android/server/
ConnectivityService.java#startCaptivePortalApp
5032 public void startCaptivePortalApp(Network network) {
5033 enforceNetworkStackOrSettingsPermission();
5034 mHandler.post(() -> {
5035 NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
5036 if (nai == null) return;
5037 if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return;
5038 nai.networkMonitor().launchCaptivePortalApp(); ---> 启动captivePortalApp
5039 });
5040 }
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java#753
751 * Request the captive portal application to be launched.
752 */
753 public void launchCaptivePortalApp() {
754 sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
755 }
CMD_LAUNCH_CAPTIVE_PORTAL_APP 消息在哪个状态机里面处理呢?最好的办法就是看log,如果没有log我们只能根据逻辑来推理,那下面我们来结合代码推理下,众所周知,当网络状态发生变化是会通知CS去 updateNetworkInfo,这里就会去调用NetworkMonitor
networkAgent.networkMonitor().notifyNetworkConnected(params);
初始状态我们从文章的一开始就知道了,是DefaultState,所以会在这个状态里面处理 CMD_NETWORK_CONNECTED 消息
913 private class DefaultState extends State {
914 @Override
915 public void enter() {
916 // Register configuration broadcast here instead of constructor to prevent start() was
917 // not called yet when the broadcast is received and cause crash.
918 mContext.registerReceiver(mConfigurationReceiver,
919 new IntentFilter(ACTION_CONFIGURATION_CHANGED));
920 checkAndRenewResourceConfig(); ---> 这里是读取URL的配置(http、https)
921 Log.d(TAG, "Starting on network " + mNetwork
922 + " with capport HTTPS URL " + Arrays.toString(mCaptivePortalHttpsUrls)
923 + " and HTTP URL " + Arrays.toString(mCaptivePortalHttpUrls));
924 }
925
926 @Override
927 public boolean processMessage(Message message) {
928 switch (message.what) {
929 case CMD_NETWORK_CONNECTED:
930 updateConnectedNetworkAttributes(message);
931 logNetworkEvent(NetworkEvent.NETWORK_CONNECTED);
932 transitionTo(mEvaluatingState); ---> 转到这个状态里面
933 return HANDLED;
// 看下这个状态里面
1335 private class EvaluatingState extends State {
1336 private Uri mEvaluatingCapportUrl;
1337
1338 @Override
1339 public void enter() {
1340 // If we have already started to track time spent in EvaluatingState
1341 // don't reset the timer due simply to, say, commands or events that
1342 // cause us to exit and re-enter EvaluatingState.
1343 if (!mEvaluationTimer.isStarted()) {
1344 mEvaluationTimer.start(); ---> 如果还没有开始跟踪在 EvaluatingState 中花费的时间,就启动计时器
1345 }
1346
1347 // Check if the network is captive with Terms & Conditions page. The first network
1348 // evaluation for captive networks with T&Cs returns early but NetworkMonitor will then
1349 // keep checking for connectivity to determine when the T&Cs are cleared.
1350 if (isTermsAndConditionsCaptive(mInfoShim.getCaptivePortalData(mLinkProperties)) && mValidations == 0) {
1352 mLastPortalProbeResult = new CaptivePortalProbeResult(CaptivePortalProbeResult.PORTAL_CODE,
1354 mLinkProperties.getCaptivePortalData().getUserPortalUrl().toString(), null,
1356 CaptivePortalProbeResult.PROBE_UNKNOWN);
1357 mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,
1358 mLastPortalProbeResult.redirectUrl);
1359 transitionTo(mCaptivePortalState); ---> 转到 CaptivePortalState 状态
1360 return;
1361 }
1362 sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); ---> 向自身发送 CMD_REEVALUATE 消息,以便进行重新评估
1363 if (mUidResponsibleForReeval != INVALID_UID) {
1364 TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
1365 mUidResponsibleForReeval = INVALID_UID;
1366 }
1367 mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS;
1368 mEvaluateAttempts = 0;
1369 mEvaluatingCapportUrl = getCaptivePortalApiUrl(mLinkProperties);
1370 // Reset all current probe results to zero, but retain current validation state until
1371 // validation succeeds or fails.
1372 mEvaluationState.clearProbeResults();
1373 }
//省略了大部分代码
1376 public boolean processMessage(Message message) {
1377 switch (message.what) {
1378 case CMD_REEVALUATE: ---> 处理这个消息
1379 if (message.arg1 != mReevaluateToken || mUserDoesNotWant) {
1380 return HANDLED;
1381 }
1426 transitionTo(mProbingState); ---> 转到这个状态里
1427 return HANDLED;
private class ProbingState extends State {
1666 private Thread mThread;
1667
1668 @Override
1669 public void enter() {
1670 // When starting a full probe cycle here, record any pending stats (for example if
1671 // CMD_FORCE_REEVALUATE was called before evaluation finished, as can happen in
1672 // EvaluatingPrivateDnsState).
1673 maybeStopCollectionAndSendMetrics();
1674 // Restart the metrics collection timers. Metrics will be stopped and sent when the
1675 // validation attempt finishes (as success, failure or portal), or if it is interrupted
1676 // (by being restarted or if NetworkMonitor stops).
1677 startMetricsCollection();
1678 if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
1679 //Don't continue to blame UID forever.
1680 TrafficStats.clearThreadStatsUid();
1681 }
1682
1683 final int token = ++mProbeToken;
1684 final ValidationProperties deps = new ValidationProperties(mNetworkCapabilities);
1685 final URL fallbackUrl = nextFallbackUrl();
1686 final URL[] httpsUrls = Arrays.copyOf(mCaptivePortalHttpsUrls, mCaptivePortalHttpsUrls.length);
1688 final URL[] httpUrls = Arrays.copyOf( mCaptivePortalHttpUrls, mCaptivePortalHttpUrls.length);
// 这个就是我们比较熟悉的去做校验,如果完成了,就发送 CMD_PROBE_COMPLETE 消息
1690 mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0,
1691 isCaptivePortal(deps, httpsUrls, httpUrls, fallbackUrl))));
1692 mThread.start();
1693 }
我们知道 Android 系统访问 captive_portal 指定的地址,如果返回 http 204 则表明可以联通互联网。结束。如果返回 http 302 则可能是被认证拦截,则自动打开此 302 的地址,让用户认证,而 isCaptivePortal 如果检测到是302就表示我们需要去启动一个 CaptivePortalLoginActivity来填写账号和密码去sign-in,我们看下这块流程,继续看下这个流程,处理CMD_PROBE_COMPLETE消息
http://aospxref.com/android13.0.0_r3/xref/packages/modules/NetworkStack/src/com/android/server/connectivity/
NetworkMonitor.java#ProbingState
private static final int CMD_PROBE_COMPLETE = 16;
public boolean processMessage(Message message) {
1697 switch (message.what) {
1698 case CMD_PROBE_COMPLETE:
1699 // Ensure that CMD_PROBE_COMPLETE from stale threads are ignored.
1700 if (message.arg1 != mProbeToken) {
1701 return HANDLED;
1702 }
1703
1704 final CaptivePortalProbeResult probeResult = (CaptivePortalProbeResult) message.obj;
1706 mLastProbeTime = SystemClock.elapsedRealtime();
1707
1708 maybeWriteDataStallStats(probeResult);
1709
1710 if (probeResult.isSuccessful()) {
1711 // Transit EvaluatingPrivateDnsState to get to Validated
1712 // state (even if no Private DNS validation required).
1713 transitionTo(mEvaluatingPrivateDnsState);
1714 } else if (isTermsAndConditionsCaptive(
1715 mInfoShim.getCaptivePortalData(mLinkProperties))) {
1716 mLastPortalProbeResult = new CaptivePortalProbeResult(
1717 CaptivePortalProbeResult.PORTAL_CODE,
1718 mLinkProperties.getCaptivePortalData().getUserPortalUrl() .toString(), null,
1720 CaptivePortalProbeResult.PROBE_UNKNOWN);
1721 mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,
1722 mLastPortalProbeResult.redirectUrl);
1723 transitionTo(mCaptivePortalState);
1724 } else if (probeResult.isPortal()) { ---> 这里我们是portal 网络,直接看这个
// 注意下这个方法,是通知CS那边现在网络的状态,验证成功or失败
1725 mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID, probeResult.redirectUrl);
1727 mLastPortalProbeResult = probeResult;
1728 transitionTo(mCaptivePortalState); ---> 转到这个状态里
1729 } else if (probeResult.isPartialConnectivity()) {
1730 mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_PARTIAL,
1731 null /* redirectUrl */);
1732 maybeDisableHttpsProbing(mAcceptPartialConnectivity);
1733 if (mAcceptPartialConnectivity) {
1734 transitionTo(mEvaluatingPrivateDnsState);
1735 } else {
1736 transitionTo(mWaitingForNextProbeState);
1737 }
1738 } else {
1739 logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
1740 mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID,null /* redirectUrl */);
1742 transitionTo(mWaitingForNextProbeState);
1743 }
1744 return HANDLED;
// 这个状态里处理,发现就是去启动一个通知去让用户sign-in
private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = 11;
private class CaptivePortalState extends State {
1486 private static final String ACTION_LAUNCH_CAPTIVE_PORTAL_APP = "android.net.netmon.launchCaptivePortalApp";
1488
1489 @Override
1490 public void enter() {
1491 maybeLogEvaluationResult(networkEventType(validationStage(), EvaluationResult.CAPTIVE_PORTAL));
1493 // Don't annoy user with sign-in notifications.
1494 if (mDontDisplaySigninNotification) return;
1495 // Create a CustomIntentReceiver that sends us a
1496 // CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user
1497 // touches the notification.
1498 if (mLaunchCaptivePortalAppBroadcastReceiver == null) {
1499 // Wait for result.
1500 mLaunchCaptivePortalAppBroadcastReceiver = new CustomIntentReceiver(
1501 ACTION_LAUNCH_CAPTIVE_PORTAL_APP, new Random().nextInt(),
1502 CMD_LAUNCH_CAPTIVE_PORTAL_APP); ---> 这个消息是 11
1503 // Display the sign in notification.
1504 // Only do this once for every time we enter MaybeNotifyState. b/122164725
1505 showProvisioningNotification(mLaunchCaptivePortalAppBroadcastReceiver.mAction);
1506 }
1507 // Retest for captive portal occasionally.
1508 sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */, CAPTIVE_PORTAL_REEVALUATE_DELAY_MS);
1510 mValidations++;
1511 maybeStopCollectionAndSendMetrics();
1512 }
1513
1514 @Override
1515 public void exit() {
1516 removeMessages(CMD_CAPTIVE_PORTAL_RECHECK);
1517 }
1518 }
// CaptivePortalState 父亲状态处理这个消息:CMD_LAUNCH_CAPTIVE_PORTAL_APP
1281 // Being in the MaybeNotifyState State indicates the user may have been notified that sign-in
1282 // is required. This State takes care to clear the notification upon exit from the State.
1283 private class MaybeNotifyState extends State {
1284 @Override
1285 public boolean processMessage(Message message) {
1286 switch (message.what) {
1287 case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
1288 final Bundle appExtras = new Bundle();
1289 // OneAddressPerFamilyNetwork is not parcelable across processes.
1290 final Network network = new Network(mCleartextDnsNetwork);
1291 appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network);
1292 final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
1293 // Use redirect URL from AP if exists.
1294 final String portalUrl =
1295 (useRedirectUrlForPortal() && makeURL(probeRes.redirectUrl) != null)
1296 ? probeRes.redirectUrl : probeRes.detectUrl;
1297 appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, portalUrl);
1298 if (probeRes.probeSpec != null) {
1299 final String encodedSpec = probeRes.probeSpec.getEncodedSpec();
1300 appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
1301 }
1302 appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
1303 mCaptivePortalUserAgent);
1304 if (mNotifier != null) {
1305 mNotifier.notifyCaptivePortalValidationPending(network);
1306 }
1307 mCm.startCaptivePortalApp(network, appExtras); ---> 最终调用到CS里面的方法
1308 return HANDLED;
1309 default:
1310 return NOT_HANDLED;
1311 }
1312 }
最终调用CS里面的这个方法,去通过Intent action 启动 CaptivePortalLoginActivity
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java#5051
// 这句代码是CM里面的,为了方便直接和下面的放一起
public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
5051 public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
5052 mContext.enforceCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
5053 "ConnectivityService");
5054
5055 final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
5056 appIntent.putExtras(appExtras);
5057 appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
5058 new CaptivePortal(new CaptivePortalImpl(network).asBinder())); ---> 回调方法在下面
5059 appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
5060
5061 final long token = Binder.clearCallingIdentity();
5062 try {
5063 mContext.startActivityAsUser(appIntent, UserHandle.CURRENT);
5064 } finally {
5065 Binder.restoreCallingIdentity(token);
5066 }
5067 }
// CaptivePortalLoginActivity 的状态回调
private class CaptivePortalImpl extends ICaptivePortal.Stub {
5070 private final Network mNetwork;
5071
5072 private CaptivePortalImpl(Network network) {
5073 mNetwork = network;
5074 }
5075
5076 @Override
5077 public void appResponse(final int response) {
5078 if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
5079 enforceSettingsPermission();
5080 }
5081
5082 final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork);
5083 if (nm == null) return;
5084 nm.notifyCaptivePortalAppFinished(response); ---> 比如这里我们sign-in结束就会通知到NetworkMonitor那边
5085 }
5086
5087 @Override
5088 public void appRequest(final int request) {
5089 final NetworkMonitorManager nm = getNetworkMonitorManager(mNetwork);
5090 if (nm == null) return;
5091
5092 if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
5093 checkNetworkStackPermission();
5094 nm.forceReevaluation(mDeps.getCallingUid());
5095 }
5096 }
5097
5098 @Nullable
5099 private NetworkMonitorManager getNetworkMonitorManager(final Network network) {
5100 // getNetworkAgentInfoForNetwork is thread-safe
5101 final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
5102 if (nai == null) return null;
5103
5104 // nai.networkMonitor() is thread-safe
5105 return nai.networkMonitor();
5106 }
5107 }
下面就是activity结束收到的事件处理:
http://aospxref.com/android-13.0.0_r3/xref/packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java#notifyCaptivePortalAppFinished
private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = 9;
760 public void notifyCaptivePortalAppFinished(int response) {
761 sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
762 }
// 会在 DefaultState 状态里处理这个消息
case CMD_CAPTIVE_PORTAL_APP_FINISHED:
956 log("CaptivePortal App responded with " + message.arg1);
957
958 // If the user has seen and acted on a captive portal notification, and the
959 // captive portal app is now closed, disable HTTPS probes. This avoids the
960 // following pathological situation:
961 //
962 // 1. HTTP probe returns a captive portal, HTTPS probe fails or times out.
963 // 2. User opens the app and logs into the captive portal.
964 // 3. HTTP starts working, but HTTPS still doesn't work for some other reason -
965 // perhaps due to the network blocking HTTPS?
966 //
967 // In this case, we'll fail to validate the network even after the app is
968 // dismissed. There is now no way to use this network, because the app is now
969 // gone, so the user cannot select "Use this network as is".
970 mUseHttps = false;
971
972 switch (message.arg1) {
973 case APP_RETURN_DISMISSED:
974 sendMessage(CMD_FORCE_REEVALUATION, NO_UID, 0); ---> 再验证
975 break;
976 case APP_RETURN_WANTED_AS_IS:
977 mDontDisplaySigninNotification = true;
978 // If the user wants to use this network anyway, there is no need to
979 // perform the bandwidth check even if configured.
980 mIsBandwidthCheckPassedOrIgnored = true;
981 // If the user wants to use this network anyway, it should always
982 // be reported as validated, but other checks still need to be
983 // done. For example, it should still validate strict private DNS and
984 // show a notification if not available, because the network will
985 // be unusable for this additional reason.
986 mEvaluationState.setCaptivePortalWantedAsIs();
987 // A successful evaluation result should be reported immediately, so
988 // that the network stack may immediately use the validation in ranking
989 // without waiting for a possibly long private DNS or bandwidth eval
990 // step.
991 mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_VALID,
992 null);
993 // TODO: Distinguish this from a network that actually validates.
994 // Displaying the "x" on the system UI icon may still be a good idea.
995 transitionTo(mEvaluatingPrivateDnsState);
996 break;
997 case APP_RETURN_UNWANTED:
998 mDontDisplaySigninNotification = true;
999 mUserDoesNotWant = true;
1000 mEvaluationState.reportEvaluationResult(
1001 NETWORK_VALIDATION_RESULT_INVALID, null);
1002 // TODO: Should teardown network.
1003 mUidResponsibleForReeval = 0;
1004 transitionTo(mEvaluatingState);
1005 break;
1006 }
1007 return HANDLED;
现在再回到开头的那个问题,Setting那边调用下来的CMD_LAUNCH_CAPTIVE_PORTAL_APP 在哪个状态机里处理?会在MaybeNotifyState状态里处理,因为当 DefaultState里面处理了网络连接事件 CMD_NETWORK_CONNECTED就会转到 EvaluatingState 状态里面,然后自己给自己发送 CMD_REEVALUATE来进行网络评估,如果是需要评估的就转到 ProbingState 里面对URL进行判断,结果会随着CMD_PROBE_COMPLETE消息带过来,如果是protal网络的,就需要启动CaptivePortalLoginActivity去sign-in,用户sign-in结束,再进行一次校验,如果是非protal网络,就直接对URL连通性进行检测成功后都转到EvaluatingPrivateDnsState状态机里面,然后再给自己发送消息CMD_EVALUATE_PRIVATE_DNS来处理一些事物,验证都ok的话最终转到ValidatedState状态机里,至此,整个流程大致完毕。