美文网首页
SystemUI状态栏wifi和数据icon显示逻辑分析

SystemUI状态栏wifi和数据icon显示逻辑分析

作者: 按捉老虎 | 来源:发表于2018-09-11 17:54 被阅读0次

    今天我们来讲讲大家比较熟悉的状态栏wifi icon和数据icon显示逻辑。
    一般在正常使用情况下,当手机连接wifi的时候,状态栏就会显示wifi icon,而当wifi断开的时候,状态栏就会显示4G 3G等等的数据icon。那么显示wifi或者4G的依据是什么呢?手机在某些网络状态下,会不会有wifi和4G icon同时显示的现象呢?下面我们就带着这个疑问,去了解下wifi和4G icon显示的逻辑。

    1. 首先我们看下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);
    
    

    在WifiSignalController中wifiVisible控制着wifi的visibility,而wifiVisible主要是受mCurrentState.enabled和mCurrentState.connected影响(mHasMobileData这个值可以忽略,这个值代表是否支持数据网络,显然手机项目这个值始终是true)

       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;
                Log.d("SIGNALICON", TAG + ",handleBroadcast,WIFI_STATE,state = " + state + ",enabled = " + 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();
                Log.d("SIGNALICON", TAG + ",handleBroadcast,NETWORK_STATE,connecting = " + connecting + ",connected = " + connected);
                .........................
                .........................
                .........................
                
    

    如上就是在WifiStatusTracker中通过广播消息取到的wifi的状态,到这里wifi的显示就讲完了,逻辑还是很简单清晰的

    1. 下面我们就需要重点关注下数据icon的显示逻辑了
          final boolean dataDisabled = (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED)
                    && mCurrentState.userSetup;
          boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
            Log.d("SIGNALICON", mTag + ",notifyListeners======dataDisabled = " + dataDisabled
                    + ",dataConnected = " + mCurrentState.dataConnected + ",showDataIcon= " + showDataIcon);
            IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
                    getCurrentIconId(), contentDescription);
    
            int qsTypeIcon = 0;
            IconState qsIcon = null;
            String description = null;
            // Only send data sim callbacks to QS.
            if (mCurrentState.dataSim) {
                qsTypeIcon = showDataIcon ? icons.mQsDataType : 0;
                qsIcon = new IconState(mCurrentState.enabled
                        && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
                description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
            }
            boolean activityIn = mCurrentState.dataConnected
                    && !mCurrentState.carrierNetworkChangeMode
                    && mCurrentState.activityIn;
            boolean activityOut = mCurrentState.dataConnected
                    && !mCurrentState.carrierNetworkChangeMode
                    && mCurrentState.activityOut;
            showDataIcon &= mCurrentState.isDefault || dataDisabled;
            Log.d("SIGNALICON", mTag + ",notifyListeners,dataDisabled = " + dataDisabled
                    + ",isDefault = " + mCurrentState.isDefault + ",showDataIcon= " + showDataIcon);
    
            Log.d("SIGNALICON", mTag + ",notifyListeners,showDataIcon= " + showDataIcon + ",mStyle = " + mStyle);
            int typeIcon = (showDataIcon && mStyle == STATUS_BAR_STYLE_ANDROID_DEFAULT) ? icons.mDataType : 0;
            int dataActivityId = showDataIcon && !showMobileActivity() ? icons.mActivityId : 0;
            callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                    activityIn, activityOut, dataActivityId,
                    icons.mStackedDataIcon, icons.mStackedVoiceIcon,
                    dataContentDescription, description, icons.mIsWide,
                    mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming,
                    networkIcon, volteIcon, showDataIcon);
    

    如上,在MobileSignalController中,就是核心的数据icon的显示,其中我们关注的是typeIcon,这个icon就是显示的4G,3G icon,所以我们详细分析下typeIcon 的逻辑

    int typeIcon = (showDataIcon && mStyle == STATUS_BAR_STYLE_ANDROID_DEFAULT) ? icons.mDataType : 0;
    

    从这里我们可以清晰的看到,typeIcon 是受showDataIcon的控制

    boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
    
    showDataIcon &= mCurrentState.isDefault || dataDisabled;
    

    可以看到showDataIcon主要有两处进行了赋值,那么这两次赋值分别代表了什么不同的意义呢?我们接着往下看

    在updateTelephony()函数中

        if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
                mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
            } else {
                mCurrentState.iconGroup = mDefaultIcons;
            }
            mCurrentState.dataConnected = mCurrentState.connected
                    && mDataState == TelephonyManager.DATA_CONNECTED;
    
            Log.d("SIGNALICON", mTag + ",updateTelephony,connected = " + mCurrentState.connected
                    + ", mDataState = " + mDataState + ",dataConnected = " + mCurrentState.dataConnected);
    
            mCurrentState.roaming = isRoaming();
            if (isCarrierNetworkChangeActive()) {
                mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
            } else if (isDataDisabled()) {
                mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
            }
    

    找到了mCurrentState.dataConnected和mCurrentState.iconGroup赋值的地方,我们主要关注mDataState 即可。

    在class MobilePhoneStateListener extends PhoneStateListener {}中我们找到了mDataState 的出处

        @Override
            public void onDataConnectionStateChanged(int state, int networkType) {
                mDataState = state;
                mDataNetType = networkType;
                Log.d("SIGNALICON", mTag + ",onDataConnectionStateChanged,state = " + state
                        + ", networkType = " + networkType);
                if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
                        mServiceState.isUsingCarrierAggregation()) {
                    mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
                }
                updateTelephony();
            }
    

    可见mDataState 是从PhoneStateListener callback中上报的,该值主要对应了不同的连接状态,可以看下framework定义。TelephonyManager.java中

        ** Data connection state: Unknown.  Used before we know the state.
         * @hide
         */
        public static final int DATA_UNKNOWN        = -1;
        /** Data connection state: Disconnected. IP traffic not available. */
        public static final int DATA_DISCONNECTED   = 0;
        /** Data connection state: Currently setting up a data connection. */
        public static final int DATA_CONNECTING     = 1;
        /** Data connection state: Connected. IP traffic should be available. */
        public static final int DATA_CONNECTED      = 2;
        /** Data connection state: Suspended. The connection is up, but IP
         * traffic is temporarily unavailable. For example, in a 2G network,
         * data activity may be suspended when a voice call arrives. */
        public static final int DATA_SUSPENDED      = 3;
    

    实际上这个callback就是用户主动开启和关闭数据业务的时候,framework上报的流程,我顺带加了点log有助于逻辑分析,下面我们就分几种情况看下对应的log

    • 开启数据业务
    09-11 16:38:45.477  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),onDataConnectionStateChanged,state = 2, networkType = 13
    09-11 16:38:45.509  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),updateTelephony,connected = true, mDataState = 2,dataConnected = true
    09-11 16:38:45.517  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:38:45.518  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = false,showDataIcon= false
    09-11 16:38:45.519  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= false,mStyle = 0
    09-11 16:38:45.529  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),updateTelephony,connected = true, mDataState = 2,dataConnected = true
    09-11 16:38:45.572  1524  1755 D SIGNALICON: NetworkController,updateConnectivity update transportType
    09-11 16:38:45.574  1524  1755 D SIGNALICON: NetworkController,updateConnectivity======transportType = 0
    09-11 16:38:45.575  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:38:45.576  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = true,showDataIcon= true
    09-11 16:38:45.576  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= true,mStyle = 0
    

    当主动开启数据业务的时候,callback上报了state = 2, networkType = 13状态,state代表数据连接状态而networkType 则代表当前的网络类型(3G和4G等等),紧接着updateTelephony函数中会根据state设置mCurrentState.dataConnected状态,最后在notifyListeners根据mCurrentState.dataConnected的状态设置了showDataIcon的值

    • 关闭数据业务
    09-11 16:40:32.840  1524  1755 D SIGNALICON: NetworkController,updateConnectivity update transportType
    09-11 16:40:32.843  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:40:32.843  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = false,showDataIcon= false
    09-11 16:40:32.843  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= false,mStyle = 0
    09-11 16:40:32.943  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),updateTelephony,connected = true, mDataState = 2,dataConnected = true
    09-11 16:40:33.172  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),onDataConnectionStateChanged,state = 0, networkType = 13
    09-11 16:40:33.181  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),updateTelephony,connected = true, mDataState = 0,dataConnected = false
    09-11 16:40:33.185  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = false,showDataIcon= false
    09-11 16:40:33.185  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = false,showDataIcon= false
    09-11 16:40:33.185  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= false,mStyle = 0
    09-11 16:40:36.461  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),updateTelephony,connected = true, mDataState = 0,dataConnected = false
    

    关闭数据业务的流程,大致和开启式一致的,只是中间的状态是相反的

    以上就是我们第二点要讲的,数据业务开关的流程,说白了状态栏就是依据onDataConnectionStateChanged callback来刷新UI,逻辑也是比较清晰明了的。

    细心的你可能就会发现,showDataIcon 不是有两处赋值的地方吗?

    boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
    showDataIcon &= mCurrentState.isDefault || dataDisabled;
    

    onDataConnectionStateChanged callback 只与第一次的赋值有关联,那第二次的赋值又与什么有关联呢?
    下面就是我们要重点讲的第三点,我们来看看第二个赋值中的mCurrentState.isDefault是何方神圣,
    这也是写本文的最终用意。

    1. 数据业务的被动刷新
      你有否想过,在数据业务开启的情况下,手机又连接了wifi,这时状态栏显示了wifi icon而4G icon是怎么消失的呢?
      首先我们看下wifi开启和关闭的log
    • wifi开启
    09-11 16:42:34.264  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast,WIFI_STATE,state = 2, enabled = false
    09-11 16:42:34.278  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast,WIFI_STATE,state = 3, enabled = true
    09-11 16:42:37.596  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:42:37.596  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = true,showDataIcon= true
    09-11 16:42:37.596  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= true,mStyle = 0
    09-11 16:42:39.608  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:42:39.609  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = true,showDataIcon= true
    09-11 16:42:39.609  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= true,mStyle = 0
    09-11 16:42:56.984  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast,NETWORK_STATE,connected = true
    09-11 16:42:57.096  1524  1755 D SIGNALICON: NetworkController,updateConnectivity update transportType
    09-11 16:42:57.097  1524  1755 D SIGNALICON: NetworkController,updateConnectivity======transportType = 1
    09-11 16:42:57.101  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:42:57.101  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = false,showDataIcon= false
    09-11 16:42:57.101  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= false,mStyle = 0
    
    
    • wifi关闭
    09-11 16:44:26.877  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast------>NETWORK_STATE,connected = false
    09-11 16:44:26.913  1524  1755 D SIGNALICON: NetworkController,updateConnectivity update transportType
    09-11 16:44:26.974  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast------>WIFI_STATE,state = 0, enabled = false
    09-11 16:44:27.007  1524  1755 D SIGNALICON: NetworkController,updateConnectivity update transportType
    09-11 16:44:27.010  1524  1755 D SIGNALICON: NetworkController,updateConnectivity======transportType = 0
    09-11 16:44:27.012  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:44:27.012  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = true,showDataIcon= true
    09-11 16:44:27.012  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= true,mStyle = 0
    09-11 16:44:27.622  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast------>WIFI_STATE,state = 1, enabled = false
    09-11 16:44:28.265  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:44:28.266  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = true,showDataIcon= true
    09-11 16:44:28.266  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,showDataIcon= true,mStyle = 0
    
    

    我们截取log其中一段看下

    wifi 开启
    09-11 16:42:56.984  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast,NETWORK_STATE,connected = true
    NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:42:57.101  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = false,showDataIcon= false
    
    wifi 关闭
    09-11 16:44:26.974  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast------>WIFI_STATE,state = 0, enabled = false
    NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:44:27.012  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = true,showDataIcon= true
    09-11 16:44:27.012  1524  1755 D SIGNALICON: 
    
    

    在wifi开启或者关闭的时候,showDataIcon第一次赋值的地方始终是true,这也是正常的,因为数据业务本来就是开着的,而且onDataConnectionStateChanged callback也没有上报刷新,然后到了第二次showDataIcon赋值的时候却出现变化了
    showDataIcon &= mCurrentState.isDefault || dataDisabled;
    逻辑里面是这么定义的,log里面也能看到,mCurrentState.isDefault导致了最后showDataIcon发生了变化。我们接着往下看

        @Override
        public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
            boolean isValidated = validatedTransports.get(mTransportType);
            mCurrentState.isDefault = connectedTransports.get(mTransportType);
            // Only show this as not having connectivity if we are default.
            mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0;
            notifyListenersIfNecessary();
        }
    
    

    还是在MobileSignalController中,我们找到了mCurrentState.isDefault的出处,看过我上一篇状态栏信号分析的同学
    SystemUI状态栏wifi和sim icon显示"x"号或者"!"号现象分析
    对这个函数一定很清楚了,有兴趣的可以再看下熟悉这之间的流程,那么我们长话短说。

    在NetworkControllerImpl中,当接收到ConnectivityManager.CONNECTIVITY_ACTION或者ConnectivityManager.INET_CONDITION_ACTION广播的时候,触发了updateConnectivity的刷新

    /**
         * Update the Inet conditions and what network we are connected to.
         */
        private void updateConnectivity() {
            mConnectedTransports.clear();
            mValidatedTransports.clear();
            Log.d("SIGNALICON", TAG + ",updateConnectivity update transportType");
            for (NetworkCapabilities nc :
                    mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
                for (int transportType : nc.getTransportTypes()) {
                    Log.d("SIGNALICON", TAG + ",updateConnectivity======transportType = " + transportType);
                    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();
        }
    
    /**
         * 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);
        }
    
    

    到这里我们就找到了mCurrentState.isDefault = connectedTransports.get(mTransportType)实现的逻辑,就是在收到信号刷新广播消息的时候,通过NetworkCapabilities中获取了transportType,而MobileSignalController在初始化的时候就传入了需要的数据类型NetworkCapabilities.TRANSPORT_CELLULAR

    public MobileSignalController(Context context, Config config, boolean hasMobileData,
                TelephonyManager phone, CallbackHandler callbackHandler,
                NetworkControllerImpl networkController, SubscriptionInfo info,
                SubscriptionDefaults defaults, Looper receiverLooper) {
            super("MobileSignalController(" + info.getSubscriptionId() + "_" + info.getSimSlotIndex() + ")", context,
                    NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
                    networkController);
                    ................
                    ................    
    

    SignalController对mTransportType进行了赋值

    public SignalController(String tag, Context context, int type, CallbackHandler callbackHandler,
                NetworkControllerImpl networkController) {
            mTag = TAG + "." + tag;
            mNetworkController = networkController;
            mTransportType = type;
            ...........
            ...........
    

    NetworkCapabilities中定义了信号类型的值:

    /**
         * Representing the transport type.  Apps should generally not care about transport.  A
         * request for a fast internet connection could be satisfied by a number of different
         * transports.  If any are specified here it will be satisfied a Network that matches
         * any of them.  If a caller doesn't care about the transport it should not specify any.
         */
        private long mTransportTypes;
    
        /**
         * Indicates this network uses a Cellular transport.
         */
        public static final int TRANSPORT_CELLULAR = 0;
    
        /**
         * Indicates this network uses a Wi-Fi transport.
         */
        public static final int TRANSPORT_WIFI = 1;
    
        /**
         * Indicates this network uses a Bluetooth transport.
         */
        public static final int TRANSPORT_BLUETOOTH = 2;
        
    

    我们再结合log确认下:

    wifi 开启
    09-11 16:42:56.984  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast,NETWORK_STATE,connected = true
    09-11 16:42:57.096  1524  1755 D SIGNALICON: NetworkController,updateConnectivity update transportType
    09-11 16:42:57.097  1524  1755 D SIGNALICON: NetworkController,updateConnectivity======transportType = 1
    09-11 16:42:57.101  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:42:57.101  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = false,showDataIcon= false
    
    wifi 关闭
    09-11 16:44:26.877  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast------>NETWORK_STATE,connected = false
    09-11 16:44:26.913  1524  1755 D SIGNALICON: NetworkController,updateConnectivity update transportType
    09-11 16:44:26.974  1524  1755 D SIGNALICON: WifiStatusTracker,handleBroadcast------>WIFI_STATE,state = 0, enabled = false
    09-11 16:44:27.007  1524  1755 D SIGNALICON: NetworkController,updateConnectivity update transportType
    09-11 16:44:27.010  1524  1755 D SIGNALICON: NetworkController,updateConnectivity======transportType = 0
    09-11 16:44:27.012  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners======dataDisabled = false,dataConnected = true,showDataIcon= true
    09-11 16:44:27.012  1524  1755 D SIGNALICON: NetworkController.MobileSignalController(2_1),notifyListeners,dataDisabled = false,isDefault = true,showDataIcon= true
    
    

    在wifi连接的时候上报了transportType = 1,导致isDefault = false;而wifi断开,数据连接上时,上报了transportType = 0,导致isDefault = true,log和我们的分析过程是完全吻合的。

    以上systemui模块刷新wifi和数据icon的分析就完结了,总结下:

    • 当wifi和数据连接同时打开,wifi连接的时候,framework会通过ConnectivityManager.CONNECTIVITY_ACTION或者ConnectivityManager.INET_CONDITION_ACTION广播,触发了updateConnectivity的刷新,然后取到了transportType = 1,从而isDefault = false,MobileSignalController中showDataIcon = fase,数据icon就隐藏了
    • 当wifi和数据连接同时打开,wifi断开,数据连接的时候,framework会也通过ConnectivityManager.CONNECTIVITY_ACTION或者ConnectivityManager.INET_CONDITION_ACTION广播,触发了updateConnectivity的刷新,然后取到了transportType = 0,从而isDefault = true,MobileSignalController中showDataIcon = true,数据icon就显示了。

    项目开发中有时候也会遇到wifi连接上了,wifi和4G同时显示的现象
    通过上面的分析,我们也就比较容易定位问题了,加点log确认下,当时wifi connected = true的时候,updateConnectivity是不是也取到了transportType = 0的状态,从项目经验中看90%是因为这个原因,貌似Android有这样一个机制,当wifi信号很微弱的时候,会自动切换到数据流量,以免影响用户体验。此时就需要framework协助进一步确认了

    如果想进阶了解下framework上报的流程,各位看官可以跟着往下走;当然如果觉得比较枯燥乏味,可以拿好贵重物品欢快的离场了。

    1. framework更新wifi和数据信号的流程

    既然wifi和数据显示的逻辑是受NetworkCapabilities 中的 TRANSPORT_CELLULAR和TRANSPORT_WIFI影响,而获取这个状态的入口是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);
                    .....................................
        
    

    那我们就从getDefaultNetworkCapabilitiesForUser和getTransportTypes它们入手

    在ConnectivityService中我们找到了getDefaultNetworkCapabilitiesForUser的实现

        @Override
        public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
            // The basic principle is: if an app's traffic could possibly go over a
            // network, without the app doing anything multinetwork-specific,
            // (hence, by "default"), then include that network's capabilities in
            // the array.
            //
            // In the normal case, app traffic only goes over the system's default
            // network connection, so that's the only network returned.
            //
            // With a VPN in force, some app traffic may go into the VPN, and thus
            // over whatever underlying networks the VPN specifies, while other app
            // traffic may go over the system default network (e.g.: a split-tunnel
            // VPN, or an app disallowed by the VPN), so the set of networks
            // returned includes the VPN's underlying networks and the system
            // default.
            enforceAccessPermission();
    
            HashMap<Network, NetworkCapabilities> result = new HashMap<Network, NetworkCapabilities>();
    
            NetworkAgentInfo nai = getDefaultNetwork();
            NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
            if (nc != null) {
                result.put(nai.network, nc);
            }
            ....................
       
    

    这个函数中NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai)就是从NetworkAgentInfo把信息取出来,最终给NetworkCapabilities设置mTransportTypes的地方

    private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
            if (nai != null) {
                synchronized (nai) {
                    if (nai.networkCapabilities != null) {
                        return new NetworkCapabilities(nai.networkCapabilities);
                    }
                }
            }
            return null;
        }   
    

    我们看NetworkCapabilities类中的以下两个函数

    public NetworkCapabilities(NetworkCapabilities nc) {
            if (nc != null) {
                mNetworkCapabilities = nc.mNetworkCapabilities;
                mTransportTypes = nc.mTransportTypes;
                mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
                mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
                mNetworkSpecifier = nc.mNetworkSpecifier;
                mSignalStrength = nc.mSignalStrength;
            }
        }
       
    
    /**
         * Gets all the transports set on this {@code NetworkCapability} instance.
         *
         * @return an array of {@code NetworkCapabilities.TRANSPORT_*} values
         *         for this instance.
         * @hide
         */
        public int[] getTransportTypes() {
            return BitUtils.unpackBits(mTransportTypes);
        }
       
    

    好了到这里,我们就找到了getTransportTypes的实现

    下面就主要关注ConnectivityService类中getDefaultNetworkCapabilitiesForUser函数是如何取到getDefaultNetwork()数据的的,因为后面的逻辑都是从这个函数中取数据的

    private NetworkAgentInfo getDefaultNetwork() {
            return getNetworkForRequest(mDefaultRequest.requestId);
        }
        
    private NetworkAgentInfo getNetworkForRequest(int requestId) {
            synchronized (mNetworkForRequestId) {
                return mNetworkForRequestId.get(requestId);
            }
        }
        
    private void setNetworkForRequest(int requestId, NetworkAgentInfo nai) {
            synchronized (mNetworkForRequestId) {
                mNetworkForRequestId.put(requestId, nai);
            }
        }
    /**
         * NetworkAgentInfo supporting a request by requestId.
         * These have already been vetted (their Capabilities satisfy the request)
         * and the are the highest scored network available.
         * the are keyed off the Requests requestId.
         */
        // NOTE: Accessed on multiple threads, must be synchronized on itself.
        @GuardedBy("mNetworkForRequestId")
        private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
                new SparseArray<NetworkAgentInfo>();
       
    

    在上面的函数中我们找到了mNetworkForRequestId就是一个SparseArray,setNetworkForRequest给mNetworkForRequestId赋值,getNetworkForRequest负责取值。

    • 信号类型的初始化

    下面我们关注setNetworkForRequest函数是哪里调用,然后往里面传值的,我们就能够找到出处了

    private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
                ReapUnvalidatedNetworks reapUnvalidatedNetworks, long now) {
                .................
                newNetwork.unlingerRequest(nri.request);
                        setNetworkForRequest(nri.request.requestId, newNetwork);
                        if (!newNetwork.addRequest(nri.request)) {
                            Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request);
                        }
                        addedRequests.add(nri);
                .................        
                }
                
    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
    
                ................
                updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
    
                // Consider network even though it is not yet validated.
                final long now = SystemClock.elapsedRealtime();
                rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
    
                // This has to happen after matching the requests, because callbacks are just requests.
                notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
                .................
    }
    

    我们看到在updateNetworkInfo中设置了最终的NetworkCapabilities,接着往下看

    private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
            if (VDBG) log("Got NetworkAgent Messenger");
            mNetworkAgentInfos.put(na.messenger, na);
            synchronized (mNetworkForNetId) {
                mNetworkForNetId.put(na.network.netId, na);
            }
            na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
            NetworkInfo networkInfo = na.networkInfo;
            na.networkInfo = null;
            updateNetworkInfo(na, networkInfo);
        }
    
    private class InternalHandler extends Handler {
            public InternalHandler(Looper looper) {
                super(looper);
            }
            
        case EVENT_REGISTER_NETWORK_AGENT: {
                        handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);
                        break;
                    }
    
    public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
                LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
                int currentScore, NetworkMisc networkMisc) {
            enforceConnectivityInternalPermission();
    
            LinkProperties lp = new LinkProperties(linkProperties);
            lp.ensureDirectlyConnectedRoutes();
            // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
            // satisfies mDefaultRequest.
            final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                    new Network(reserveNetId()), new NetworkInfo(networkInfo), lp,
                    new NetworkCapabilities(networkCapabilities), currentScore,
                    mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
            synchronized (this) {
                nai.networkMonitor.systemReady = mSystemReady;
            }
            addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network,
                    networkInfo.getExtraInfo());
            if (DBG) log("registerNetworkAgent " + nai);
            mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
            return nai.network.netId;
        }
    
    

    在registerNetworkAgent中完成了updateNetworkInfo的设置,仿佛离答案越来越近了有木有,那么这个函数是怎么设置的呢

    public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
                NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
            super(looper);
            LOG_TAG = logTag;
            mContext = context;
            if (ni == null || nc == null || lp == null) {
                throw new IllegalArgumentException();
            }
    
            if (VDBG) log("Registering NetworkAgent");
            ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
                    Context.CONNECTIVITY_SERVICE);
            netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
                    new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);
        }
    

    在NetworkAgent.java中我们找到了答案,就是在NetworkAgent初始化的时候注册的。我们看下NetworkAgent的注释

    /**
     * A Utility class for handling for communicating between bearer-specific
     * code and ConnectivityService.
     *
     * A bearer may have more than one NetworkAgent if it can simultaneously
     * support separate networks (IMS / Internet / MMS Apns on cellular, or
     * perhaps connections with different SSID or P2P for Wi-Fi).
     *
     * @hide
     */
    public abstract class NetworkAgent extends Handler {}
    

    英文大意就是:
    用于处理bearer-specific code 和 ConnectivityService通信的实用类,如果这个bearer可以承载多个网络的话,那它就会有多个NetworkAgent,比如 (IMS / Internet / MMS Apns on cellular, or perhaps connections with different SSID or P2P for Wi-Fi)

    到这里我们就能够大概猜到了,每个网络类型应该就对应着一个NetworkAgent,是不是酱紫呢?我们接着往下找

    private class WifiNetworkAgent extends NetworkAgent {
            public WifiNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
                    NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
                super(l, c, TAG, ni, nc, lp, score, misc);
            }
            .............
    }
            
    class L2ConnectedState extends State {
            @Override
            public void enter() {
                mRssiPollToken++;
                if (mEnableRssiPolling) {
                    sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
                }
                if (mNetworkAgent != null) {
                    loge("Have NetworkAgent when entering L2Connected");
                    setNetworkDetailedState(DetailedState.DISCONNECTED);
                }
                setNetworkDetailedState(DetailedState.CONNECTING);
    
                mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
                        "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
                        mLinkProperties, 60, mNetworkMisc);
               .............
    }
    
    public WifiStateMachine(Context context, FrameworkFacade facade, Looper looper,
                                UserManager userManager, WifiInjector wifiInjector,
                                BackupManagerProxy backupManagerProxy, WifiCountryCode countryCode,
                                WifiNative wifiNative,
                                WrongPasswordNotifier wrongPasswordNotifier) {
                                
            .......................
            mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
            mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
            mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
            mNetworkCapabilitiesFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
            mNetworkCapabilitiesFilter.setLinkUpstreamBandwidthKbps(1024 * 1024);
            mNetworkCapabilitiesFilter.setLinkDownstreamBandwidthKbps(1024 * 1024);
            .......................
                                }
    

    在WifiStateMachine.java中发现了初始化NetworkAgent 的地方,我们看到了熟悉的身影
    mNetworkCapabilitiesFilter.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    终于我们找到设置TRANSPORT_WIFI的地方

    private class DcNetworkAgent extends NetworkAgent {
            public DcNetworkAgent(Looper l, Context c, String TAG, NetworkInfo ni,
                    NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
                super(l, c, TAG, ni, nc, lp, score, misc);
            }
            .............
    }
            
    /**
         * The state machine is connected, expecting an EVENT_DISCONNECT.
         */
        private class DcActiveState extends State {
            @Override public void enter() {
                    .............
                final NetworkMisc misc = new NetworkMisc();
                final CarrierSignalAgent carrierSignalAgent = mPhone.getCarrierSignalAgent();
                if (carrierSignalAgent.hasRegisteredReceivers(TelephonyIntents
                        .ACTION_CARRIER_SIGNAL_REDIRECTED)) {
                    // carrierSignal Receivers will place the carrier-specific provisioning notification
                    misc.provisioningNotificationDisabled = true;
                }
                misc.subscriberId = mPhone.getSubscriberId();
    
                setNetworkRestriction();
                mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
                        "DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
                        50, misc);
               .............
    }
    
    NetworkCapabilities getNetworkCapabilities() {
            NetworkCapabilities result = new NetworkCapabilities();
            result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
            .......................
                                
                                }
    

    同理在数据网络DataConnection.java初始化的时候我们也找到了result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)设置的地方

    到这里初始化的地方就看的差不多了,下面我们再了解下信号变化时刷新的数据,NetworkAgentInfo是如何取到的。

    • 信号类型的更新

    我们再回到ConnectivityService中

    private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
            final NetworkInfo.State state = newInfo.getState();
            NetworkInfo oldInfo = null;
            final int oldScore = networkAgent.getCurrentScore();
            synchronized (networkAgent) {
                oldInfo = networkAgent.networkInfo;
                networkAgent.networkInfo = newInfo;
            }
            notifyLockdownVpn(networkAgent);
            .....................
    }
    
    
    private void maybeHandleNetworkAgentMessage(Message msg) {
                NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
                if (nai == null) {
                    if (VDBG) {
                        log(String.format("%s from unknown NetworkAgent", eventName(msg.what)));
                    }
                    return;
                }
                ....................
                case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
                        NetworkInfo info = (NetworkInfo) msg.obj;
                        updateNetworkInfo(nai, info);
                        break;
                    }
                ....................
    
    }
    
    private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
            if (VDBG) log("Got NetworkAgent Messenger");
            mNetworkAgentInfos.put(na.messenger, na);
            synchronized (mNetworkForNetId) {
                mNetworkForNetId.put(na.network.netId, na);
            }
            na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
            NetworkInfo networkInfo = na.networkInfo;
            na.networkInfo = null;
            updateNetworkInfo(na, networkInfo);
        }
    
    NetworkAgent.java
    /**
         * Called by the bearer code when it has new NetworkInfo data.
         */
        public void sendNetworkInfo(NetworkInfo networkInfo) {
            queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
        }
    

    我们可以看到在收到EVENT_NETWORK_INFO_CHANGED的message触发了updateNetworkInfo这就是信号刷新的地方,
    我们看到NetworkAgentInfo 就是从初始化的时候handleRegisterNetworkAgent函数中,HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos里面取出来的

    case EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED:
                        AsyncResult ar = (AsyncResult)msg.obj;
                        Pair<Integer, Integer> drsRatPair = (Pair<Integer, Integer>)ar.result;
                        mDataRegState = drsRatPair.first;
                        if (mRilRat != drsRatPair.second) {
                            updateTcpBufferSizes(drsRatPair.second);
                        }
                        mRilRat = drsRatPair.second;
                        if (DBG) {
                            log("DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED"
                                    + " drs=" + mDataRegState
                                    + " mRilRat=" + mRilRat);
                        }
                        ServiceState ss = mPhone.getServiceState();
                        int networkType = ss.getDataNetworkType();
                        mNetworkInfo.setSubtype(networkType,
                                TelephonyManager.getNetworkTypeName(networkType));
                        if (mNetworkAgent != null) {
                            updateNetworkInfoSuspendState();
                            mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
                            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
                            mNetworkAgent.sendLinkProperties(mLinkProperties);
                        }
                        break;
    
    

    DataConenection.java 中刷新信号类型的地方

    class ConnectModeState extends State {
            @Override
            public void enter() {
                if (!mWifiNative.removeAllNetworks()) {
                    loge("Failed to remove networks on entering connect mode");
                }
                mWifiInfo.reset();
                mWifiInfo.setSupplicantState(SupplicantState.DISCONNECTED);
                // Let the system know that wifi is available in client mode.
                setWifiState(WIFI_STATE_ENABLED);
    
                mNetworkInfo.setIsAvailable(true);
                if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
                ......................
            }
    
            @Override
            public void exit() {
                // Let the system know that wifi is not available since we are exiting client mode.
                mNetworkInfo.setIsAvailable(false);
                if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);
                ......................
            }
    }
    
    

    WifiStateMachine信号类型刷新的地方

    好了,到这里framework信号类型的初始化和更新就分析完毕了,中间过程虽然有点繁琐,但总的来说逻辑还是相对清晰的

    下面简单总结下吧:

    • Systemui中显示wifi和数据icon是根据ConnectivityService.java中上报的NetworkCapabilities状态来定
      当NetworkCapabilities getTransportTypes() 返回 TRANSPORT_CELLULAR(== 0)的时候就显示数据信号
      而NetworkCapabilities getTransportTypes() 返回 TRANSPORT_WIFI(== 1)的时候就显示wifi信号

    • framework信号类型初始化的时候,每一个信号都对应着一个NetworkAgent,WifiStateMachine 设置了TRANSPORT_WIFI,而DataConenection 设置了TRANSPORT_CELLULAR。

    分析就到这里了,文章若有什么分析不对的地方,欢迎大家指正,谢谢O(∩_∩)O

    相关文章

      网友评论

          本文标题:SystemUI状态栏wifi和数据icon显示逻辑分析

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