SystemUI开发总结-移动网络图标显示逻辑

安卓中的移动网络图标并不单一接受一个监听器的信息,图标的确定流程拉得很长,在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卡,安卓系统为之作了封装,寻到深处是各种位运算,以作者的能力暂时没能参悟,不细说了。
最后:如有指教或者意见,欢迎在评论区指出。

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值