今天我们来讲讲大家比较熟悉的状态栏wifi icon和数据icon显示逻辑。
一般在正常使用情况下,当手机连接wifi的时候,状态栏就会显示wifi icon,而当wifi断开的时候,状态栏就会显示4G 3G等等的数据icon。那么显示wifi或者4G的依据是什么呢?手机在某些网络状态下,会不会有wifi和4G icon同时显示的现象呢?下面我们就带着这个疑问,去了解下wifi和4G icon显示的逻辑。
- 首先我们看下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的显示就讲完了,逻辑还是很简单清晰的
- 下面我们就需要重点关注下数据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是何方神圣,
这也是写本文的最终用意。
- 数据业务的被动刷新
你有否想过,在数据业务开启的情况下,手机又连接了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上报的流程,各位看官可以跟着往下走;当然如果觉得比较枯燥乏味,可以拿好贵重物品欢快的离场了。
- 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