安卓中的移动网络图标并不单一接受一个监听器的信息,图标的确定流程拉得很长,在systemui中也分布在几个不同的位置。此前开发中只用了 PhoneStateListener 提供的信号格数变化,导致了自制的状态栏图标并不准确。如今客户需求贴卡版本的机器,重新梳理移动网络图标的显示逻辑被提上日程。
显示控制
首先,真正操作状态栏上移动图标变化的文件是这个:
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
128 public void applyMobileState(MobileIconState state) {
129 if (state == null) {
130 setVisibility(View.GONE);
131 mState = null;
132 return;
133 }
134
135 if (mState == null) {
136 mState = state.copy();
137 initViewState();
138 return;
139 }
140
141 if (!mState.equals(state)) {
142 updateState(state.copy());
143 }
144 }
这个方法是更新图标的必经之路。
初始化和更新图标的方法大同小异,在这里仅做updateState()的分析,有兴趣的朋友可以去源代码中对比。
170 private void updateState(MobileIconState state) {
171 setContentDescription(state.contentDescription);
172 if (mState.visible != state.visible) {
173 mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE);
174 }
175 if (mState.strengthId != state.strengthId) {
176 mMobileDrawable.setLevel(state.strengthId);
177 }
178 if (mState.typeId != state.typeId) {
179 if (state.typeId != 0) {
180 mMobileType.setContentDescription(state.typeContentDescription);
181 mMobileType.setImageResource(state.typeId);
182 mMobileType.setVisibility(View.VISIBLE);
183 } else {
184 mMobileType.setVisibility(View.GONE);
185 }
186 }
187
188 mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
189 mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
190 mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
191 mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
192 mInoutContainer.setVisibility((state.activityIn || state.activityOut)
193 ? View.VISIBLE : View.GONE);
194
195 mState = state;
196 }
流程分析如上,本文不研究小图标(漫游、上传/下载速度等),很多功能已经不用了。文本描述是指“无信号”“3g”“4g”等的说明文字,不作重点分析。
数据传输
我们可以发现,移动网络图标有3个重要指标:可见性、信号强度、信号类型。而这些指标来自于方法参数中的三个内部变量。
那么这个方法何时启用呢?搜索systemui相关代码,我们在这个地方找到了调用。
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
375 public void onSetMobileIcon(int viewIndex, MobileIconState state) {
376 StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex);
377 if (view != null) {
378 view.applyMobileState(state);
379 }
380
381 if (mIsInDemoMode) {
382 mDemoStatusIcons.updateMobileState(state);
383 }
384 }
同文件中的调用:
348 public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
349 switch (holder.getType()) {
350 case TYPE_ICON:
351 onSetIcon(viewIndex, holder.getIcon());
352 return;
353 case TYPE_WIFI:
354 onSetSignalIcon(viewIndex, holder.getWifiState());
355 return;
356
357 case TYPE_MOBILE:
358 onSetMobileIcon(viewIndex, holder.getMobileState());
359 default:
360 break;
361 }
362 }
这个方法在相关的impl中这样调用:
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
303 private void handleSet(int index, StatusBarIconHolder holder) {
304 int viewIndex = getViewIndex(index, holder.getTag());
305 mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
306 mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
307 }
有关移动信号的调用如下:
192 /**
193 * Accept a list of MobileIconStates, which all live in the same slot(?!), and then are sorted
194 * by subId. Don't worry this definitely makes sense and works.
195 * @param slot da slot
196 * @param iconStates All of the mobile icon states
197 */
198 @Override
199 public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
200 Slot mobileSlot = getSlot(slot);
201 int slotIndex = getSlotIndex(slot);
202
203 for (MobileIconState state : iconStates) {
204 StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
205 if (holder == null) {
206 holder = StatusBarIconHolder.fromMobileIconState(state);
207 setIcon(slotIndex, holder);
208 } else {
209 holder.setMobileState(state);
210 handleSet(slotIndex, holder);
211 }
212 }
213 }
方法名很直接说了:setMobileIcons,设置移动信号的图标。由于sim卡可能不止一张,此处使用for循环。
这个方法在如下代码中被调用:
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
182 @Override
183 public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
184 int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
185 String description, boolean isWide, int subId, boolean roaming) {
186 MobileIconState state = getState(subId);
187 if (state == null) {
188 return;
189 }
190
191 // Visibility of the data type indicator changed
192 boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0);
193
194 state.visible = statusIcon.visible && !mBlockMobile;
195 state.strengthId = statusIcon.icon;
196 state.typeId = statusType;
197 state.contentDescription = statusIcon.contentDescription;
198 state.typeContentDescription = typeContentDescription;
199 state.roaming = roaming;
200 state.activityIn = activityIn && mActivityEnabled;
201 state.activityOut = activityOut && mActivityEnabled;
202
203 // Always send a copy to maintain value type semantics
204 mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
205
206 if (typeChanged) {
207 WifiIconState wifiCopy = mWifiIconState.copy();
208 updateShowWifiSignalSpacer(wifiCopy);
209 if (!Objects.equals(wifiCopy, mWifiIconState)) {
210 updateWifiIconWithState(wifiCopy);
211 mWifiIconState = wifiCopy;
212 }
213 }
214 }
这里我们终于看到了关键的三个指标的来源:
这个方法的调用在移动信号的控制器中。
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
277 @Override
278 public void notifyListeners(SignalCallback callback) {
279 MobileIconGroup icons = getIcons();
280
281 String contentDescription = getStringIfExists(getContentDescription());
282 String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
283 if (mCurrentState.inetCondition == 0) {
284 dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
285 }
286 final boolean dataDisabled = mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
287 && mCurrentState.userSetup;
288
289 // Show icon in QS when we are connected or data is disabled.
290 boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
291 IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
292 getCurrentIconId(), contentDescription);
293
294 int qsTypeIcon = 0;
295 IconState qsIcon = null;
296 String description = null;
297 // Only send data sim callbacks to QS.
298 if (mCurrentState.dataSim) {
299 qsTypeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mQsDataType : 0;
300 qsIcon = new IconState(mCurrentState.enabled
301 && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
302 description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
303 }
304 boolean activityIn = mCurrentState.dataConnected
305 && !mCurrentState.carrierNetworkChangeMode
306 && mCurrentState.activityIn;
307 boolean activityOut = mCurrentState.dataConnected
308 && !mCurrentState.carrierNetworkChangeMode
309 && mCurrentState.activityOut;
310 showDataIcon &= mCurrentState.isDefault || dataDisabled;
311 int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mDataType : 0;
312 callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
313 activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
314 mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);
315 }
直到这个文件,我们进入了信号决定环节。
图标决定
通过上一章的notifyListeners()方法,我们找到了两个参数的来源:
相关代码抄录:
291 IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
292 getCurrentIconId(), contentDescription);
286 final boolean dataDisabled = mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
287 && mCurrentState.userSetup;
.............
.............
310 showDataIcon &= mCurrentState.isDefault || dataDisabled;
311 int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.mDataType : 0;
我们可以发现,这两个值的决定与一个状态有紧密联系:mCurrentState,除去与它相关的值,还有如下几个因素:
getCurrentIconId(), contentDescription,mConfig,icons
我们一项一项分析:
第一项是一个方法,可以看出,它决定了移动网络数据信号格数的图标。
247 @Override
248 public int getCurrentIconId() {
249 if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) {
250 return SignalDrawable.getCarrierChangeState(getNumLevels());
251 } else if (mCurrentState.connected) {
252 int level = mCurrentState.level;
253 if (mConfig.inflateSignalStrengths) {
254 level++;
255 }
256 boolean dataDisabled = mCurrentState.userSetup
257 && mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED;
258 boolean noInternet = mCurrentState.inetCondition == 0;
259 boolean cutOut = dataDisabled || noInternet;
260 return SignalDrawable.getState(level, getNumLevels(), cutOut);
261 } else if (mCurrentState.enabled) {
262 return SignalDrawable.getEmptyState(getNumLevels());
263 } else {
264 return 0;
265 }
266 }
该方法的运行流程如下:
另外,设置正常信号格时,如果判断无网络,则会另外生成一种带叉号的信号格,表示网络异常。
第二项是图标描述,此处省略。第三项mConfig在控制器被构造时会传入一个变量,后续也会使用一个公开方法修改:
113 public void setConfiguration(Config config) {
114 mConfig = config;
115 mapIconSets();
116 updateTelephony();
117 }
第四项icons略有波折,最后在父类中找到了它的定义:icons=mCurrentState.iconGroup;
现在,所有依赖的信息都很明确了:
数据的初始化和监听
我们已经分析出正是这些原始数据影响着图标的显示,因此,当它们发生变化时,图标也会相应改变,触发监听器回调到状态栏控制处。现在我们一一分析这些原始数据的来源和变化。
mConfig
初始化:
在移动网络控制器被实例化时,初始的mconfig来自网络控制器构造器的同名变量:
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
147 /**
148 * Construct this controller object and register for updates.
149 */
150 public NetworkControllerImpl(Context context, Looper bgLooper,
151 DeviceProvisionedController deviceProvisionedController) {
152 this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
153 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
154 (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
155 SubscriptionManager.from(context), Config.readConfig(context), bgLooper,
156 new CallbackHandler(),
157 new AccessPointControllerImpl(context),
158 new DataUsageController(context),
159 new SubscriptionDefaults(),
160 deviceProvisionedController);
........
162 }
163
164 @VisibleForTesting
165 NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
166 TelephonyManager telephonyManager, WifiManager wifiManager,
167 SubscriptionManager subManager, Config config, Looper bgLooper,
168 CallbackHandler callbackHandler,
169 AccessPointControllerImpl accessPointController,
170 DataUsageController dataUsageController,
171 SubscriptionDefaults defaultsHandler,
172 DeviceProvisionedController deviceProvisionedController) {
........
174 mConfig = config;
........
........
255 }
观察到此处可以认为:
mConfig = Config.readConfig(context);
监听:
该控制器实现了广播接受者的接口,并且在相应广播中更改了mConfig的值。
435 @Override
436 public void onReceive(Context context, Intent intent) {
...........
...........
441 switch (action) {
...........
...........
478 case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
479 mConfig = Config.readConfig(mContext);
480 mReceiverHandler.post(this::handleConfigurationChanged);
481 break;
...........
...........
498 }
总结mConfig的变化都是由Config.readConfig()传达的,Config类是何方神圣呢?原来是用来读写的内部类。
1024 @VisibleForTesting
1025 static class Config {
1026 boolean showAtLeast3G = false;
1027 boolean alwaysShowCdmaRssi = false;
1028 boolean show4gForLte = false;
1029 boolean hideLtePlus = false;
1030 boolean hspaDataDistinguishable;
1031 boolean inflateSignalStrengths = false;
1032 boolean alwaysShowDataRatIcon = false;
1033
1034 static Config readConfig(Context context) {
1035 Config config = new Config();
1036 Resources res = context.getResources();
1037
1038 config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
1039 config.alwaysShowCdmaRssi =
1040 res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
1041 config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
1042 config.hspaDataDistinguishable =
1043 res.getBoolean(R.bool.config_hspa_data_distinguishable);
1044 config.hideLtePlus = res.getBoolean(R.bool.config_hideLtePlus);
1045 config.inflateSignalStrengths = res.getBoolean(R.bool.config_inflateSignalStrength);
1046
1047 CarrierConfigManager configMgr = (CarrierConfigManager)
1048 context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
1049 PersistableBundle b = configMgr.getConfig();
1050 if (b != null) {
1051 config.alwaysShowDataRatIcon = b.getBoolean(
1052 CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
1053 }
1054 return config;
1055 }
1056 }
1057}
使用该内部类,在创建时和收到特定广播时就能确定mConfig了。
isDefault & inetCondition
这两项放在一起,是因为在MobileSignalController.java中,两样是一起确定值的,并且前者还影响后者。
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
133 @Override
134 public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
135 boolean isValidated = validatedTransports.get(mTransportType);
136 mCurrentState.isDefault = connectedTransports.get(mTransportType);
137 // Only show this as not having connectivity if we are default.
138 mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0;
139 notifyListenersIfNecessary();
140 }
这两样不存在初始值,全由该方法定义。
这个方法在网络控制器中被调用:
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
697 /**
698 * Update the Inet conditions and what network we are connected to.
699 */
700 private void updateConnectivity() {
701 mConnectedTransports.clear();
702 mValidatedTransports.clear();
703 for (NetworkCapabilities nc :
704 mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
705 for (int transportType : nc.getTransportTypes()) {
706 mConnectedTransports.set(transportType);
707 if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
708 mValidatedTransports.set(transportType);
709 }
710 }
711 }
712
713 if (CHATTY) {
714 Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
715 Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
716 }
717
718 mInetCondition = !mValidatedTransports.isEmpty();
719
720 pushConnectivityToSignals();
721 }
722
723 /**
724 * Pushes the current connectivity state to all SignalControllers.
725 */
726 private void pushConnectivityToSignals() {
727 // We want to update all the icons, all at once, for any condition change
728 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
729 MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
730 mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
731 }
732 mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
733 mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
734 }
pushConnectivityToSignals()在识别sim卡时会被调用一次,作为初始化,后续都由updateConnectivity()更新。而updateConnectivity()在初始化时作为网络变化的回调:
224 ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
225 private Network mLastNetwork;
226 private NetworkCapabilities mLastNetworkCapabilities;
227
228 @Override
229 public void onCapabilitiesChanged(
230 Network network, NetworkCapabilities networkCapabilities) {
231 boolean lastValidated = (mLastNetworkCapabilities != null) &&
232 mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
233 boolean validated =
234 networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
235
236 // This callback is invoked a lot (i.e. when RSSI changes), so avoid updating
237 // icons when connectivity state has remained the same.
238 if (network.equals(mLastNetwork) &&
239 networkCapabilities.equalsTransportTypes(mLastNetworkCapabilities) &&
240 validated == lastValidated) {
241 return;
242 }
243 mLastNetwork = network;
244 mLastNetworkCapabilities = networkCapabilities;
245 updateConnectivity();
246 }
247 };
此外,收到对应广播时,该方法也会被调用
435 @Override
436 public void onReceive(Context context, Intent intent) {
.........
.........
440 final String action = intent.getAction();
441 switch (action) {
442 case ConnectivityManager.CONNECTIVITY_ACTION:
443 case ConnectivityManager.INET_CONDITION_ACTION:
444 updateConnectivity();
445 break;
..........
..........
497 }
498 }
enabled
mCurrentState.enabled实际上是一个只初始化一次的值,在NetworkControllerImpl.java的构造器中是这样的:
164 @VisibleForTesting
165 NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
166 TelephonyManager telephonyManager, WifiManager wifiManager,
167 SubscriptionManager subManager, Config config, Looper bgLooper,
168 CallbackHandler callbackHandler,
169 AccessPointControllerImpl accessPointController,
170 DataUsageController dataUsageController,
171 SubscriptionDefaults defaultsHandler,
172 DeviceProvisionedController deviceProvisionedController) {
173 mContext = context;
.......
.......
182 mHasMobileDataFeature =
183 mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
.......
.......
255 }
可以理解为sim卡是否支持移动网络。
airplaneMode
顾名思义,飞行模式。
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
123 public void setAirplaneMode(boolean airplaneMode) {
124 mCurrentState.airplaneMode = airplaneMode;
125 notifyListenersIfNecessary();
126 }
该方法的调用同样在网络控制器中:
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
651 private void updateAirplaneMode(boolean force) {
652 boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
653 Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
654 if (airplaneMode != mAirplaneMode || force) {
655 mAirplaneMode = airplaneMode;
656 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
657 MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
658 mobileSignalController.setAirplaneMode(mAirplaneMode);
659 }
660 notifyListeners();
661 }
662 }
updateAirplaneMode()方法在初始化时和识sim卡时会被调用,此外则接收飞行模式的广播进行变化:
435 @Override
436 public void onReceive(Context context, Intent intent) {
.......
.......
440 final String action = intent.getAction();
441 switch (action) {
........
........
446 case Intent.ACTION_AIRPLANE_MODE_CHANGED:
447 refreshLocale();
448 updateAirplaneMode(false);
449 break;
........
........
497 }
498 }
userSetUp
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
128 public void setUserSetupComplete(boolean userSetup) {
129 mCurrentState.userSetup = userSetup;
130 notifyListenersIfNecessary();
131 }
这个变量同样在网络控制器中被设置:
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
626 private void setUserSetupComplete(final boolean userSetup) {
627 mReceiverHandler.post(() -> handleSetUserSetupComplete(userSetup));
628 }
629
630 private void handleSetUserSetupComplete(boolean userSetup) {
631 mUserSetup = userSetup;
632 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
633 MobileSignalController controller = mMobileSignalControllers.valueAt(i);
634 controller.setUserSetupComplete(mUserSetup);
635 }
636 }
除了在初始化和sim卡状态变化时重新设置该变量,构造器中还添加了如下监听:
216 deviceProvisionedController.addCallback(new DeviceProvisionedListener() {
217 @Override
218 public void onUserSetupChanged() {
219 setUserSetupComplete(deviceProvisionedController.isUserSetup(
220 deviceProvisionedController.getCurrentUser()));
221 }
222 });
iconGroup
icongroup的更新放在移动网络控制器中的 ** 重要方法updateTelephony() **中,移动网络本身的回调触发之后都会调用该方法。(updateTelephony方法不做省略,之后会引用到)
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
442 /**
443 * Updates the current state based on mServiceState, mSignalStrength, mDataNetType,
444 * mDataState, and mSimState. It should be called any time one of these is updated.
445 * This will call listeners if necessary.
446 */
447 private final void updateTelephony() {
448 if (DEBUG) {
449 Log.d(mTag, "updateTelephonySignalStrength: hasService=" + hasService()
450 + " ss=" + mSignalStrength);
451 }
452 mCurrentState.connected = hasService() && mSignalStrength != null;
453 if (mCurrentState.connected) {
454 if (!mSignalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
455 mCurrentState.level = mSignalStrength.getCdmaLevel();
456 } else {
457 mCurrentState.level = mSignalStrength.getLevel();
458 }
459 }
460 if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
461 mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
462 } else {
463 mCurrentState.iconGroup = mDefaultIcons;
464 }
465 mCurrentState.dataConnected = mCurrentState.connected
466 && mDataState == TelephonyManager.DATA_CONNECTED;
467
468 mCurrentState.roaming = isRoaming();
469 if (isCarrierNetworkChangeActive()) {
470 mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
471 } else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) {
472 mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
473 }
474 if (isEmergencyOnly() != mCurrentState.isEmergency) {
475 mCurrentState.isEmergency = isEmergencyOnly();
476 mNetworkController.recalculateEmergency();
477 }
478 // Fill in the network name if we think we have it.
479 if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null
480 && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) {
481 mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
482 }
483
484 notifyListenersIfNecessary();
485 }
iconGroup和sim卡相关,在识卡时这个标志就会被确定下来。
connected & level
该变量的设置同样在上一节的updateTelephony()中,判断卡是否能通话/上网,并且同时能读到信号强度。
322 private boolean hasService() {
323 if (mServiceState != null) {
324 // Consider the device to be in service if either voice or data
325 // service is available. Some SIM cards are marketed as data-only
326 // and do not support voice service, and on these SIM cards, we
327 // want to show signal bars for data service as well as the "no
328 // service" or "emergency calls only" text that indicates that voice
329 // is not available.
330 switch (mServiceState.getVoiceRegState()) {
331 case ServiceState.STATE_POWER_OFF:
332 return false;
333 case ServiceState.STATE_OUT_OF_SERVICE:
334 case ServiceState.STATE_EMERGENCY_ONLY:
335 return mServiceState.getDataRegState() == ServiceState.STATE_IN_SERVICE;
336 default:
337 return true;
338 }
339 } else {
340 return false;
341 }
342 }
这里有两个新的值:卡服务状态mServiceState和卡信号强度mSignalStrength。这两者的确定有赖于PhoneStateListener 的回调:
510 class MobilePhoneStateListener extends PhoneStateListener {
511 public MobilePhoneStateListener(int subId, Looper looper) {
512 super(subId, looper);
513 }
514
515 @Override
516 public void onSignalStrengthsChanged(SignalStrength signalStrength) {
517 if (DEBUG) {
518 Log.d(mTag, "onSignalStrengthsChanged signalStrength=" + signalStrength +
519 ((signalStrength == null) ? "" : (" level=" + signalStrength.getLevel())));
520 }
521 mSignalStrength = signalStrength;
522 updateTelephony();
523 }
524
525 @Override
526 public void onServiceStateChanged(ServiceState state) {
527 if (DEBUG) {
528 Log.d(mTag, "onServiceStateChanged voiceState=" + state.getVoiceRegState()
529 + " dataState=" + state.getDataRegState());
530 }
531 mServiceState = state;
532 if (state != null) {
533 mDataNetType = state.getDataNetworkType();
534 if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
535 mServiceState.isUsingCarrierAggregation()) {
536 mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
537 }
538 }
539 updateTelephony();
540 }
541
542 @Override
543 public void onDataConnectionStateChanged(int state, int networkType) {
544 if (DEBUG) {
545 Log.d(mTag, "onDataConnectionStateChanged: state=" + state
546 + " type=" + networkType);
547 }
548 mDataState = state;
549 mDataNetType = networkType;
550 if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE && mServiceState != null &&
551 mServiceState.isUsingCarrierAggregation()) {
552 mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
553 }
554 updateTelephony();
555 }
556
557 @Override
558 public void onDataActivity(int direction) {
559 if (DEBUG) {
560 Log.d(mTag, "onDataActivity: direction=" + direction);
561 }
562 setActivity(direction);
563 }
564
565 @Override
566 public void onCarrierNetworkChange(boolean active) {
567 if (DEBUG) {
568 Log.d(mTag, "onCarrierNetworkChange: active=" + active);
569 }
570 mCurrentState.carrierNetworkChangeMode = active;
571
572 updateTelephony();
573 }
574 };
确定有连接后,判断信号强度才有意义,否则不改变level的值。
总结
移动网络的显示控制异常复杂,实际复刻时还有许多难以言说的细节没能在文中言明。例如监听器的设置时机,图标的更新频率等,前后处理了一个月才自测完成。本文仅仅从监听与数据传输的角度阐明状态栏展示移动网络图标的流程,至于识别sim卡,安卓系统为之作了封装,寻到深处是各种位运算,以作者的能力暂时没能参悟,不细说了。
最后:如有指教或者意见,欢迎在评论区指出。