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 = 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";
    

所以只需要在SettingsProvider的配置文件中,将该数据库配置下就可以了

<!-- Default for Settings.Global.CAPTIVE_PORTAL_MODE   DISABLE -->
<setting type="global" name="captive_portal_mode" value="0"/>

<!-- Default for Settings.Global.CAPTIVE_PORTAL_MODE   ENABLE -->
<setting type="global" name="captive_portal_mode" value="1"/>   

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容