SystemUI状态栏wifi和sim|SystemUI状态栏wifi和sim icon显示"x"号或者"!"号现象分析

如题,项目开发或者Google Nexus等亲儿子机器上,StatusBar经常会看到wifi和sim icon显示"x"号或者"!"号
首先给出结论,有以上的现象主要是因为默认访问Google服务器失败,Google源生逻辑则会在信号塔旁边显示"x"号或者"!"号提醒用户。
一般正常情况下,如果手机可以上网,就可以连接google服务器,但是由于国内对google做了限制,无法访问,才会出现这种情况。
下面则从代码逻辑中详细分析出现以上现象的原因

  1. 为了方便理解和说明,本文就以wifi icon为例子,sim icon流程和wifi icon完全是一致的
    首先StatusBar中刷新wifi信号显示都是在WifiSignalController中完成的,下面我们来看下WifiSignalController是怎么处理wifi信号的
public WifiSignalController(Context context, boolean hasMobileData, CallbackHandler callbackHandler, NetworkControllerImpl networkController) { super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI, callbackHandler, networkController); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mWifiTracker = new WifiStatusTracker(mWifiManager); mHasMobileData = https://www.it610.com/article/hasMobileData; Handler handler = new WifiHandler(Looper.getMainLooper()); mWifiChannel = new AsyncChannel(); Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger(); if (wifiMessenger != null) { mWifiChannel.connect(context, handler, wifiMessenger); } // WiFi only has one state. mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup("Wi-Fi Icons", WifiIcons.WIFI_SIGNAL_STRENGTH, WifiIcons.QS_WIFI_SIGNAL_STRENGTH, AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH, WifiIcons.WIFI_NO_NETWORK, WifiIcons.QS_WIFI_NO_NETWORK, WifiIcons.WIFI_NO_NETWORK, WifiIcons.QS_WIFI_NO_NETWORK, AccessibilityContentDescriptions.WIFI_NO_CONNECTION ); }

以上就是WifiSignalController的构造函数,也是最主要的初始化流程,在初始化的时候实例化了WifiManager和WifiStatusTracker等用于追踪wifi信息,最终通过WifiStatusTracker handleBroadcast处理并且刷新信号
public void handleBroadcast(Intent intent) { String action = intent.getAction(); if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN); enabled = state == WifiManager.WIFI_STATE_ENABLED; } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) { final NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); connecting = networkInfo != null && !networkInfo.isConnected() && networkInfo.isConnectedOrConnecting(); connected = networkInfo != null && networkInfo.isConnected(); // If Connected grab the signal strength and ssid. if (connected) { // try getting it out of the intent first WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null ? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) : mWifiManager.getConnectionInfo(); if (info != null) { ssid = getSsid(info); } else { ssid = null; } } else if (!connected) { ssid = null; } } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { // Default to -200 as its below WifiManager.MIN_RSSI. rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); level = WifiManager.calculateSignalLevel(rssi, 5); } }

本文我们重点关注的是构造函数中的mCurrentState.iconGroup,这个iconGroup就是wifi信号UI显示的所有要素
mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup( "Wi-Fi Icons", WifiIcons.WIFI_SIGNAL_STRENGTH, WifiIcons.QS_WIFI_SIGNAL_STRENGTH, AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH, WifiIcons.WIFI_NO_NETWORK, WifiIcons.QS_WIFI_NO_NETWORK, WifiIcons.WIFI_NO_NETWORK, WifiIcons.QS_WIFI_NO_NETWORK, AccessibilityContentDescriptions.WIFI_NO_CONNECTION );

其中最核心的就是WifiIcons.WIFI_SIGNAL_STRENGTH,我们来看下这个常量的定义,在WifiIcons中
public class WifiIcons { static final int[][] WIFI_SIGNAL_STRENGTH = { { R.drawable.stat_sys_wifi_signal_0, R.drawable.stat_sys_wifi_signal_1, R.drawable.stat_sys_wifi_signal_2, R.drawable.stat_sys_wifi_signal_3, R.drawable.stat_sys_wifi_signal_4 }, { R.drawable.stat_sys_wifi_signal_0_fully, R.drawable.stat_sys_wifi_signal_1_fully, R.drawable.stat_sys_wifi_signal_2_fully, R.drawable.stat_sys_wifi_signal_3_fully, R.drawable.stat_sys_wifi_signal_4_fully } };

WIFI_SIGNAL_STRENGTH就是一个二维数组,这个数组里面就定义了不同状态栏下的wifi信号强度
这个数组就是本文的核心部分,第一个数组就是带有"x"号或者"!"号的wifi icon,第二个数组就是正常显示的wifi icon
下面我们就看下这个WIFI_SIGNAL_STRENGTH是在哪里被引用的
  1. 回到WifiSignalController中,看下wifi信号刷新的地方
@Override public void notifyListeners(SignalCallback callback) { // only show wifi in the cluster if connected or if wifi-only boolean wifiVisible = mCurrentState.enabled && (mCurrentState.connected || !mHasMobileData); String wifiDesc = wifiVisible ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; String contentDescription = getStringIfExists(getContentDescription()); if (mCurrentState.inetCondition == 0) { contentDescription += ("," + mContext.getString(R.string.accessibility_quick_settings_no_internet)); }IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription); IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(), contentDescription); callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut, wifiDesc, mCurrentState.isTransient); }/** * Extract wifi state directly from broadcasts about changes in wifi state. */ public void handleBroadcast(Intent intent) { mWifiTracker.handleBroadcast(intent); mCurrentState.enabled = mWifiTracker.enabled; mCurrentState.connected = mWifiTracker.connected; mCurrentState.ssid = mWifiTracker.ssid; mCurrentState.rssi = mWifiTracker.rssi; mCurrentState.level = mWifiTracker.level; notifyListenersIfNecessary(); }

当handleBroadcast数据刷新执行完毕后,就会调用notifyListeners来刷新信号,其中
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
就是wifi icon刷新的一个bean类
public static class IconState { public final boolean visible; public final int icon; public final String contentDescription; public IconState(boolean visible, int icon, String contentDescription) { this.visible = visible; this.icon = icon; this.contentDescription = contentDescription; }public IconState(boolean visible, int icon, int contentDescription, Context context) { this(visible, icon, context.getString(contentDescription)); } }

看到IconState的构造函数就相当清楚了,wifi icon其实就是这个icon,也就是上文的getCurrentIconId()
下面就来看下getCurrentIconId()实现的逻辑
  1. 在WifiSignalController的父类SignalController中可以看到
/** * Gets the signal icon for SB based on current state of connected, enabled, and level. */ public int getCurrentIconId() { if (mCurrentState.connected) { return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level]; } else if (mCurrentState.enabled) { return getIcons().mSbDiscState; } else { return getIcons().mSbNullState; } }public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0; notifyListenersIfNecessary(); }

wifi信号数组主要是受这个mCurrentState.inetCondition变量控制,
当mCurrentState.inetCondition == 1是就显示正常的wifi icon,
而当mCurrentState.inetCondition == 0时,就显示本文中的带"x"号或者"!"的icon
接着往上找就会发现,在NetworkControllerImpl中pushConnectivityToSignals调用了
updateConnectivity
/** * Pushes the current connectivity state to all SignalControllers. */ private void pushConnectivityToSignals() { // We want to update all the icons, all at once, for any condition change for (int i = 0; i < mMobileSignalControllers.size(); i++) { MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i); mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); } mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); }

下面就继续深究下mValidatedTransports变量的实现逻辑
  1. 在NetworkControllerImpl中,当接收到ConnectivityManager.CONNECTIVITY_ACTION或者ConnectivityManager.INET_CONDITION_ACTION广播的时候,触发了updateConnectivity的刷新
@Override public void onReceive(Context context, Intent intent) { if (CHATTY) { Log.d(TAG, "onReceive: intent=" + intent); } final String action = intent.getAction(); if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) || action.equals(ConnectivityManager.INET_CONDITION_ACTION)) { updateConnectivity(); } /** * Update the Inet conditions and what network we are connected to. */ private void updateConnectivity() { mConnectedTransports.clear(); mValidatedTransports.clear(); for (NetworkCapabilities nc : mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) { for (int transportType : nc.getTransportTypes()) { mConnectedTransports.set(transportType); if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) { mValidatedTransports.set(transportType); } } }if (CHATTY) { Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports); Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports); }mInetCondition = !mValidatedTransports.isEmpty(); pushConnectivityToSignals(); }

Bingo!!!到这里就找到Systemui中StatusBar最终显示带"x"号或者"!"的原因了,就是在
for (NetworkCapabilities nc : mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) { for (int transportType : nc.getTransportTypes()) { mConnectedTransports.set(transportType); if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) { mValidatedTransports.set(transportType); } } }

这个函数中,起决定性因素的nc.hasCapability(NET_CAPABILITY_VALIDATED)当这个函数返回false的时候就会导致在SignalController中mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
取到0,而当nc.hasCapability(NET_CAPABILITY_VALIDATED)== true,取到正确状态后,就取到1
有兴趣的朋友在这个函数里面加点log信息,逻辑就一目了然了
到这里Systemui分析就告一段落了,基本可以排除是Systemui逻辑问题,接下来就需要framework查收了
  1. 当然如果想进阶分析下framework为什么上报这种状态的话,可以跟着往下走
    从上面的nc.hasCapability(NET_CAPABILITY_VALIDATED)开始
/** * Tests for the presence of a capabilitity on this instance. * * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for. * @return {@code true} if set on this instance. */ public boolean hasCapability(int capability) { if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { return false; } return ((mNetworkCapabilities & (1 << capability)) != 0); }/** * Adds the given capability to this {@code NetworkCapability} instance. * Multiple capabilities may be applied sequentially.Note that when searching * for a network to satisfy a request, all capabilities requested must be satisfied. * * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added. * @return This NetworkCapabilities instance, to facilitate chaining. * @hide */ public NetworkCapabilities addCapability(int capability) { if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) { throw new IllegalArgumentException("NetworkCapability out of range"); } mNetworkCapabilities |= 1 << capability; return this; }

在NetworkCapabilities中可以看到hasCapability主要是看mNetworkCapabilities的逻辑,理所当然的就需要看
addCapability函数的实现了,当务之急就是需要找到addCapability(NET_CAPABILITY_VALIDATED)调用的地方
继续查找源码,发现这个函数实现是在ConnectivityService中调用
/** * Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities} * augmented with any stateful capabilities implied from {@code networkAgent} * (e.g., validated status and captive portal status). * * @param oldScore score of the network before any of the changes that prompted us *to call this function. * @param nai the network having its capabilities updated. * @param networkCapabilities the new network capabilities. */ private void updateCapabilities( int oldScore, NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) { // Once a NetworkAgent is connected, complain if some immutable capabilities are removed. if (nai.everConnected && !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities( networkCapabilities)) { // TODO: consider not complaining when a network agent degrade its capabilities if this // does not cause any request (that is not a listen) currently matching that agent to // stop being matched by the updated agent. String diff = nai.networkCapabilities.describeImmutableDifferences(networkCapabilities); if (!TextUtils.isEmpty(diff)) { Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff); } }// Don't modify caller's NetworkCapabilities. networkCapabilities = new NetworkCapabilities(networkCapabilities); if (nai.lastValidated) { networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED); } else { networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED); }private boolean maybeHandleNetworkMonitorMessage(Message msg) { switch (msg.what) { default: return false; case NetworkMonitor.EVENT_NETWORK_TESTED: { final NetworkAgentInfo nai; synchronized (mNetworkForNetId) { nai = mNetworkForNetId.get(msg.arg2); } if (nai != null) { final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID); final boolean wasValidated = nai.lastValidated; if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") + (msg.obj == null ? "" : " with redirect to " + (String)msg.obj)); if (valid != nai.lastValidated) { final int oldScore = nai.getCurrentScore(); nai.lastValidated = valid; nai.everValidated |= valid; updateCapabilities(oldScore, nai, nai.networkCapabilities); // If score has changed, rebroadcast to NetworkFactories. b/17726566 if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); }............. ............. .............

如上在NetworkMonitor.EVENT_NETWORK_TESTED message中
final boolean valid =
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
的值决定了nai.lastValidated的逻辑
这样的话逻辑就比较清晰了,接着继续查找NetworkMonitor
  1. 在NetworkMonitor中,sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
    NETWORK_TEST_RESULT_VALID, mNetId, null))的地方如下
// Being in the ValidatedState State indicates a Network is: // - Successfully validated, or // - Wanted "as is" by the user, or // - Does not satisfy the default NetworkRequest and so validation has been skipped. private class ValidatedState extends State { @Override public void enter() { maybeLogEvaluationResult( networkEventType(validationStage(), EvaluationResult.VALIDATED)); mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_VALID, mNetId, null)); mValidations++; }@Override public boolean processMessage(Message message) { switch (message.what) { case CMD_NETWORK_CONNECTED: transitionTo(mValidatedState); return HANDLED; default: return NOT_HANDLED; } } }

接着往下看,睁大眼睛看,精华的部分要出现了O(∩_∩)O哈哈~
// Note: This call to isCaptivePortal() could take up to a minute. Resolving the // server's IP addresses could hit the DNS timeout, and attempting connections // to each of the server's several IP addresses (currently one IPv4 and one // IPv6) could each take SOCKET_TIMEOUT_MS.During this time this StateMachine // will be unresponsive. isCaptivePortal() could be executed on another Thread // if this is found to cause problems. CaptivePortalProbeResult probeResult = isCaptivePortal(); if (probeResult.isSuccessful()) { transitionTo(mValidatedState); } else if (probeResult.isPortal()) { mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl)); mLastPortalProbeResult = probeResult; transitionTo(mCaptivePortalState); } else { final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); sendMessageDelayed(msg, mReevaluateDelayMs); logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED); mConnectivityServiceHandler.sendMessage(obtainMessage( EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl)); if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) { // Don't continue to blame UID forever. TrafficStats.clearThreadStatsUid(); } mReevaluateDelayMs *= 2; if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) { mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS; } }

在这个函数中CaptivePortalProbeResult probeResult = isCaptivePortal();
这个关键的部分决定了当前的网络状态,然后根据状态isSuccessful isPortal send不同的message
继续isCaptivePortal()函数中的实现逻辑
@VisibleForTesting protected CaptivePortalProbeResult isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) { validationLog("Validation disabled."); return CaptivePortalProbeResult.SUCCESS; }URL pacUrl = null; URL httpsUrl = mCaptivePortalHttpsUrl; URL httpUrl = mCaptivePortalHttpUrl; // On networks with a PAC instead of fetching a URL that should result in a 204 // response, we instead simply fetch the PAC script.This is done for a few reasons: // 1. At present our PAC code does not yet handle multiple PACs on multiple networks //until something like https://android-review.googlesource.com/#/c/115180/ lands. //Network.openConnection() will ignore network-specific PACs and instead fetch //using NO_PROXY.If a PAC is in place, the only fetch we know will succeed with //NO_PROXY is the fetch of the PAC itself. // 2. To proxy the generate_204 fetch through a PAC would require a number of things //happen before the fetch can commence, namely: //a) the PAC script be fetched //b) a PAC script resolver service be fired up and resolve the captive portal //server. //Network validation could be delayed until these prerequisities are satisifed or //could simply be left to race them.Neither is an optimal solution. // 3. PAC scripts are sometimes used to block or restrict Internet access and may in //fact block fetching of the generate_204 URL which would lead to false negative //results for network validation. final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy(); if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) { pacUrl = makeURL(proxyInfo.getPacFileUrl().toString()); if (pacUrl == null) { return CaptivePortalProbeResult.FAILED; } }if ((pacUrl == null) && (httpUrl == null || httpsUrl == null)) { return CaptivePortalProbeResult.FAILED; }long startTime = SystemClock.elapsedRealtime(); final CaptivePortalProbeResult result; if (pacUrl != null) { result = sendDnsAndHttpProbes(null, pacUrl, ValidationProbeEvent.PROBE_PAC); } else if (mUseHttps) { result = sendParallelHttpProbes(proxyInfo, httpsUrl, httpUrl); } else { result = sendDnsAndHttpProbes(proxyInfo, httpUrl, ValidationProbeEvent.PROBE_HTTP); }long endTime = SystemClock.elapsedRealtime(); sendNetworkConditionsBroadcast(true /* response received */, result.isPortal() /* isCaptivePortal */, startTime, endTime); return result; }

在这个函数中,主要工作就是通过 send HTTP请求,最后通过返回值确认CaptivePortalProbeResult.SUCCESS还是CaptivePortalProbeResult.FAILED从而决定上面的sendMessage发送NETWORK_TEST_RESULT_INVALID还是
NETWORK_VALIDATION_FAILED
此函数中核心的两个URL httpsUrl = mCaptivePortalHttpsUrl和URL httpUrl = mCaptivePortalHttpUrl,他们的定义如下:
// Default configuration values for captive portal detection probes. // TODO: append a random length parameter to the default HTTPS url. // TODO: randomize browser version ids in the default User-Agent String. private static final String DEFAULT_HTTPS_URL="https://www.google.com/generate_204"; private static final String DEFAULT_HTTP_URL= "http://connectivitycheck.gstatic.com/generate_204"; private static String getCaptivePortalServerHttpsUrl(Context context) { return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL); }public static String getCaptivePortalServerHttpUrl(Context context) { return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL); }

(首先给出结论,有以上的现象主要是因为默认访问Google服务器失败,Google源生逻辑则会在信号塔旁边显示"x"号或者"!"号提醒用户。)
到这里我们就找到了开头的结论的出处
写在最后
所以说,出现标题中的现象,其实按照源生逻辑是属于正常现象,因为你连不上谷爹的服务器了吗
当然如果项目开发中并不想看到此这个现象,想去除显示"x"号或者"!"号,那么我们再看了上面的原理之后,解决办法也就有了方案
  • 头痛医头脚痛医脚方案: 在Systemui 中修改
static final int[][] WIFI_SIGNAL_STRENGTH = { { R.drawable.stat_sys_wifi_signal_0, R.drawable.stat_sys_wifi_signal_1, R.drawable.stat_sys_wifi_signal_2, R.drawable.stat_sys_wifi_signal_3, R.drawable.stat_sys_wifi_signal_4 }, { R.drawable.stat_sys_wifi_signal_0_fully, R.drawable.stat_sys_wifi_signal_1_fully, R.drawable.stat_sys_wifi_signal_2_fully, R.drawable.stat_sys_wifi_signal_3_fully, R.drawable.stat_sys_wifi_signal_4_fully } }; public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0; notifyListenersIfNecessary(); }

可以把二维数组改成元素一致的,或者让mCurrentState.inetCondition == 1
当然此方案笔者是不大推荐的,只能当做是快速验证的临时方案
  • 药到病除彻底根治方案:在framework中修改
@VisibleForTesting protected CaptivePortalProbeResult isCaptivePortal() { if (!mIsCaptivePortalCheckEnabled) { validationLog("Validation disabled."); return CaptivePortalProbeResult.SUCCESS; } ................... ................... ...................

我们看到在NetworkMonitor中,其实framework给我们提供了一个mIsCaptivePortalCheckEnabled,让该函数直接返回SUCCESS.
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT) != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;

该变量就定义在NetworkMonitor构造函数中,看到这里就明了了,其实就是一个数据库值
/** * Don't attempt to detect captive portals. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; /** * When detecting a captive portal, display a notification that * prompts the user to sign in. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; /** * When detecting a captive portal, immediately disconnect from the * network and do not reconnect to that network in the future. * * @hide */ public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; /** * What to do when connecting a network that presents a captive portal. * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. * * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. * @hide */ public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; /** * Setting to turn off captive portal detection. Feature is enabled by * default and the setting needs to be set to 0 to disable it. * * @deprecated use CAPTIVE_PORTAL_MODE_IGNORE to disable captive portal detection * @hide */ @Deprecated public static final String CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled";

【SystemUI状态栏wifi和sim|SystemUI状态栏wifi和sim icon显示"x"号或者"!"号现象分析】所以只需要在SettingsProvider的配置文件中,将该数据库配置下就可以了

    推荐阅读