Wi-Fi 检查上网连通性流程

 都知道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
WifiPickerTracker
那 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状态机里,至此,整个流程大致完毕。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TI推出了针对物联网应用的集成了ARM Cortex-M4 MCU 的无线MCU器件SimpleLink CC3200,这是业界第一个具有内置Wi-Fi通性的MCU。 CC3200(CC3200中文数据手册)器件是一个完整平台解决方案,其中包括软件、示例应用、工具、用户和编程指南、参考设计以及 TI E2E 支持社区。CC3200采用易于布局布线的四方扁平无引线 (QFN) 封装。 CC3200 芯片特性: CC3200MCU子系统包含一个运行频率为 80MHz 的行业标准 ARM Cortex-M4 内核,并且包含多种外设,主要有一个快速并行摄像头接口,I2S,SD/MMC,UART,SPI,I2C 和四通道模数转换器 (ADC)等外设接口。 Wi-Fi 网络处理器子系统特有一个片上 Wi-Fi ,并且包含一个额外的专用 ARM MCU,包含 802.11 b/g/n 射频、基带和具有强大加密引擎的 MAC,以实现支持 256 位加密的快速、安全互联网连接等。 CC3200器件的更多特性可以点击查看(CC3200数据手册)。 图1 CC3200芯片内部结构 CC3200目前广泛的应用于物联网,例如家用电器、互联网网关、智能插座和仪表计量、智能能源、安防系统等。 CC3200评估板: 此评估板是基于CC3200的应用评估板CC3200 LaunchPad 评估板硬件组成如下: -SimpleLink CC3200单芯片Wi-Fi解决方案 -2*20pin LaunchPad 标准扩展引脚 -基于FTDI的 JTAG仿真,支持串口Flash编程 -虚拟串口,通过PC的USB口进行UART通信,以上两个功能由芯片FT2232完成 -测试用的板上内置天线设计 -2个用户按键和3个LED指示灯 -Micro USB接口,用于供电和调试 -加速度和温度传感器 -电流测量接口 硬件实物布局图如下: 图2 CC3200评估板实物图 评估板中用到的关键器件包括: TI的SimpleLink Wi-Fi和IoT解决方案,单芯片无线MCU:CC3200(CC3200数据手册) TI的采用WCSP封装的红外热电堆无触点温度传感器:TMP006(TMP006数据手册) TI的用于超高速USB 3.0接口的2通道ESD解决方案:TPD2EUSB30(TPD2EUSB30数据手册) TI的具有可配置电压转换和3态输出的单位双电源总线收发器:SN74LVC1T45D(SN74LVC1T45D数据手册) TI的具有三态输出的四路总线缓冲器:SN74LVC125APWR(SN74LVC125APWR数据手册) TI的1.0A、可调节电压、单输出LDO:TPS79601(TPS79601数据手册) 附件提供了评估板硬件应用手册,可以迅速上手CC3200。 附件中还包含了CC3200开发板原理图(PDF版本)、PCB(Eagle版本)、gerber文件、开发板材料清单(Excel版本)、测试例程、CC3200 SDK安装文件。 目前此款评估板在市面上暂无出售,附件提供的gerber文件可以用于打样。
当您遇到网络异常无法上网时,可以按照以下流程进行诊断: 1. 检查网络连接: - 确保您的计算机或设备已正确连接到网络,例如通过以太网或Wi-Fi。 - 检查网络线缆或无线连接是否正常连接并且没有物理损坏。 2. 检查本地设备设置: - 确保您的设备的IP地址、子网掩码、默认网关和DNS服务器设置正确。 - 如果使用静态IP,请检查您是否正确配置了相关参数。 3. Ping测试: - 打开命令提示符或终端窗口,尝试通过ping命令测试与其他设备或服务器的连通性。 - 使用以下命令测试与默认网关的连通性:`ping <默认网关IP地址>`。 - 使用以下命令测试与外部服务器的连通性:`ping <外部服务器IP地址>`。 4. 检查DNS设置: - 如果您无法访问网站或域名,请检查您的DNS设置。 - 尝试使用以下命令来测试DNS解析:`nslookup <域名>`。如果返回了IP地址,则DNS解析正常。 5. 检查防火墙和安全软件: - 检查您的防火墙和安全软件设置,确保它们没有阻止网络连接。 6. 重启网络设备: - 重启您的路由器、交换机或其他网络设备,有时这可以解决临时的网络问题。 7. 使用其他设备进行测试: - 如果您有其他设备可以连接到同一网络,请尝试使用其他设备进行测试。如果其他设备可以正常上网,可能是您的设备存在问题。 8. 联系网络管理员或服务提供商: - 如果以上步骤都无法解决问题,建议您联系网络管理员或互联网服务提供商(ISP)寻求进一步的帮助和支持。 请注意,上述流程是一般性的诊断步骤,具体情况可能会有所不同。在诊断过程中,您可以根据实际情况进行调整和进一步的排查。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值