美文网首页systemui
SystemUI状态栏wifi和sim icon显示"x"号或者

SystemUI状态栏wifi和sim icon显示"x"号或者

作者: 按捉老虎 | 来源:发表于2018-08-29 16:47 被阅读0次

    如题,项目开发或者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"/>   
    
    

    相关文章

      网友评论

        本文标题:SystemUI状态栏wifi和sim icon显示"x"号或者

        本文链接:https://www.haomeiwen.com/subject/ntskwftx.html