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

今天我们来讲讲大家比较熟悉的状态栏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

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

推荐阅读更多精彩内容