1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.systemui.statusbar.connectivity;
17 
18 import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
19 
20 import android.content.Context;
21 import android.content.Intent;
22 import android.database.ContentObserver;
23 import android.net.NetworkCapabilities;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.provider.Settings.Global;
27 import android.telephony.CellSignalStrength;
28 import android.telephony.CellSignalStrengthCdma;
29 import android.telephony.SignalStrength;
30 import android.telephony.SubscriptionInfo;
31 import android.telephony.SubscriptionManager;
32 import android.telephony.TelephonyManager;
33 import android.text.Html;
34 import android.text.TextUtils;
35 import android.util.Log;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.settingslib.SignalIcon.MobileIconGroup;
39 import com.android.settingslib.graph.SignalDrawable;
40 import com.android.settingslib.mobile.MobileMappings.Config;
41 import com.android.settingslib.mobile.MobileStatusTracker;
42 import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus;
43 import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
44 import com.android.settingslib.mobile.TelephonyIcons;
45 import com.android.settingslib.net.SignalStrengthUtil;
46 import com.android.systemui.res.R;
47 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy;
48 import com.android.systemui.util.CarrierConfigTracker;
49 
50 import java.io.PrintWriter;
51 import java.text.SimpleDateFormat;
52 import java.util.BitSet;
53 import java.util.List;
54 import java.util.Map;
55 
56 /**
57  * Monitors the mobile signal changes and update the SysUI icons.
58  */
59 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
60     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
61     private static final int STATUS_HISTORY_SIZE = 64;
62     private final TelephonyManager mPhone;
63     private final CarrierConfigTracker mCarrierConfigTracker;
64     private final SubscriptionDefaults mDefaults;
65     private final MobileMappingsProxy mMobileMappingsProxy;
66     private final String mNetworkNameDefault;
67     private final String mNetworkNameSeparator;
68     private final ContentObserver mObserver;
69     // Save entire info for logging, we only use the id.
70     final SubscriptionInfo mSubscriptionInfo;
71     private Map<String, MobileIconGroup> mNetworkToIconLookup;
72 
73     private MobileIconGroup mDefaultIcons;
74     private Config mConfig;
75     @VisibleForTesting
76     boolean mInflateSignalStrengths = false;
77     @VisibleForTesting
78     final MobileStatusTracker mMobileStatusTracker;
79 
80     // Save the previous STATUS_HISTORY_SIZE states for logging.
81     private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
82     // Where to copy the next state into.
83     private int mMobileStatusHistoryIndex;
84 
85     private final MobileStatusTracker.Callback mMobileCallback =
86             new MobileStatusTracker.Callback() {
87                 private String mLastStatus;
88 
89                 @Override
90                 public void onMobileStatusChanged(boolean updateTelephony,
91                         MobileStatus mobileStatus) {
92                     if (DEBUG) {
93                         Log.d(mTag, "onMobileStatusChanged="
94                                 + " updateTelephony=" + updateTelephony
95                                 + " mobileStatus=" + mobileStatus.toString());
96                     }
97                     String currentStatus = mobileStatus.toString();
98                     if (!currentStatus.equals(mLastStatus)) {
99                         mLastStatus = currentStatus;
100                         String status = new StringBuilder()
101                                 .append(SSDF.format(System.currentTimeMillis())).append(",")
102                                 .append(currentStatus)
103                                 .toString();
104                         recordLastMobileStatus(status);
105                     }
106                     updateMobileStatus(mobileStatus);
107                     if (updateTelephony) {
108                         updateTelephony();
109                     } else {
110                         notifyListenersIfNecessary();
111                     }
112                 }
113             };
114 
115     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
116     // need listener lists anymore.
MobileSignalController( Context context, Config config, boolean hasMobileData, TelephonyManager phone, CallbackHandler callbackHandler, NetworkControllerImpl networkController, MobileMappingsProxy mobileMappingsProxy, SubscriptionInfo info, SubscriptionDefaults defaults, Looper receiverLooper, CarrierConfigTracker carrierConfigTracker, MobileStatusTrackerFactory mobileStatusTrackerFactory )117     public MobileSignalController(
118             Context context,
119             Config config,
120             boolean hasMobileData,
121             TelephonyManager phone,
122             CallbackHandler callbackHandler,
123             NetworkControllerImpl networkController,
124             MobileMappingsProxy mobileMappingsProxy,
125             SubscriptionInfo info,
126             SubscriptionDefaults defaults,
127             Looper receiverLooper,
128             CarrierConfigTracker carrierConfigTracker,
129             MobileStatusTrackerFactory mobileStatusTrackerFactory
130     ) {
131         super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
132                 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
133                 networkController);
134         mCarrierConfigTracker = carrierConfigTracker;
135         mConfig = config;
136         mPhone = phone;
137         mDefaults = defaults;
138         mSubscriptionInfo = info;
139         mMobileMappingsProxy = mobileMappingsProxy;
140         mNetworkNameSeparator = getTextIfExists(
141                 R.string.status_bar_network_name_separator).toString();
142         mNetworkNameDefault = getTextIfExists(
143                 com.android.internal.R.string.lockscreen_carrier_default).toString();
144 
145         mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig);
146         mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig);
147 
148         String networkName = info.getCarrierName() != null ? info.getCarrierName().toString()
149                 : mNetworkNameDefault;
150         mLastState.networkName = mCurrentState.networkName = networkName;
151         mLastState.networkNameData = mCurrentState.networkNameData = networkName;
152         mLastState.enabled = mCurrentState.enabled = hasMobileData;
153         mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
154         mObserver = new ContentObserver(new Handler(receiverLooper)) {
155             @Override
156             public void onChange(boolean selfChange) {
157                 updateTelephony();
158             }
159         };
160         mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback);
161     }
162 
setConfiguration(Config config)163     void setConfiguration(Config config) {
164         mConfig = config;
165         updateInflateSignalStrength();
166         mNetworkToIconLookup = mMobileMappingsProxy.mapIconSets(mConfig);
167         mDefaultIcons = mMobileMappingsProxy.getDefaultIcons(mConfig);
168         updateTelephony();
169     }
170 
setAirplaneMode(boolean airplaneMode)171     void setAirplaneMode(boolean airplaneMode) {
172         mCurrentState.airplaneMode = airplaneMode;
173         notifyListenersIfNecessary();
174     }
175 
setUserSetupComplete(boolean userSetup)176     void setUserSetupComplete(boolean userSetup) {
177         mCurrentState.userSetup = userSetup;
178         notifyListenersIfNecessary();
179     }
180 
181     @Override
updateConnectivity(BitSet connectedTransports, BitSet validatedTransports)182     public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
183         boolean isValidated = validatedTransports.get(mTransportType);
184         mCurrentState.isDefault = connectedTransports.get(mTransportType);
185         // Only show this as not having connectivity if we are default.
186         mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0;
187         notifyListenersIfNecessary();
188     }
189 
setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode)190     void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
191         mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode;
192         updateTelephony();
193     }
194 
195     /**
196      * Start listening for phone state changes.
197      */
registerListener()198     public void registerListener() {
199         mMobileStatusTracker.setListening(true);
200         mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),
201                 true, mObserver);
202         mContext.getContentResolver().registerContentObserver(Global.getUriFor(
203                 Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
204                 true, mObserver);
205     }
206 
207     /**
208      * Stop listening for phone state changes.
209      */
unregisterListener()210     public void unregisterListener() {
211         mMobileStatusTracker.setListening(false);
212         mContext.getContentResolver().unregisterContentObserver(mObserver);
213     }
214 
updateInflateSignalStrength()215     private void updateInflateSignalStrength() {
216         mInflateSignalStrengths = SignalStrengthUtil.shouldInflateSignalStrength(mContext,
217                 mSubscriptionInfo.getSubscriptionId());
218     }
219 
getNumLevels()220     private int getNumLevels() {
221         if (mInflateSignalStrengths) {
222             return CellSignalStrength.getNumSignalStrengthLevels() + 1;
223         }
224         return CellSignalStrength.getNumSignalStrengthLevels();
225     }
226 
227     @Override
getCurrentIconId()228     public int getCurrentIconId() {
229         if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) {
230             return SignalDrawable.getCarrierChangeState(getNumLevels());
231         } else if (mCurrentState.connected) {
232             int level = mCurrentState.level;
233             if (mInflateSignalStrengths) {
234                 level++;
235             }
236             boolean dataDisabled = mCurrentState.userSetup
237                     && (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
238                     || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA
239                             && mCurrentState.defaultDataOff));
240             boolean noInternet = mCurrentState.inetCondition == 0;
241             boolean cutOut = dataDisabled || noInternet;
242             return SignalDrawable.getState(level, getNumLevels(), cutOut);
243         } else if (mCurrentState.enabled) {
244             return SignalDrawable.getEmptyState(getNumLevels());
245         } else {
246             return 0;
247         }
248     }
249 
250     @Override
getQsCurrentIconId()251     public int getQsCurrentIconId() {
252         return getCurrentIconId();
253     }
254 
255     @Override
notifyListeners(SignalCallback callback)256     public void notifyListeners(SignalCallback callback) {
257         // If the device is on carrier merged WiFi, we should let WifiSignalController to control
258         // the SysUI states.
259         if (mNetworkController.isCarrierMergedWifi(mSubscriptionInfo.getSubscriptionId())) {
260             return;
261         }
262         MobileIconGroup icons = getIcons();
263 
264         String contentDescription = getTextIfExists(getContentDescription()).toString();
265         CharSequence dataContentDescriptionHtml = getTextIfExists(icons.dataContentDescription);
266 
267         //TODO: Hacky
268         // The data content description can sometimes be shown in a text view and might come to us
269         // as HTML. Strip any styling here so that listeners don't have to care
270         CharSequence dataContentDescription = Html.fromHtml(
271                 dataContentDescriptionHtml.toString(), 0).toString();
272         if (mCurrentState.inetCondition == 0) {
273             dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
274         }
275 
276         int iconId = mCurrentState.getNetworkTypeIcon(mContext);
277         final QsInfo qsInfo = getQsInfo(contentDescription, iconId);
278         final SbInfo sbInfo = getSbInfo(contentDescription, iconId);
279 
280         MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
281                 sbInfo.icon,
282                 qsInfo.icon,
283                 sbInfo.ratTypeIcon,
284                 qsInfo.ratTypeIcon,
285                 mCurrentState.hasActivityIn(),
286                 mCurrentState.hasActivityOut(),
287                 dataContentDescription,
288                 dataContentDescriptionHtml,
289                 qsInfo.description,
290                 mSubscriptionInfo.getSubscriptionId(),
291                 mCurrentState.roaming,
292                 sbInfo.showTriangle);
293         callback.setMobileDataIndicators(mobileDataIndicators);
294     }
295 
getQsInfo(String contentDescription, int dataTypeIcon)296     private QsInfo getQsInfo(String contentDescription, int dataTypeIcon) {
297         int qsTypeIcon = 0;
298         IconState qsIcon = null;
299         CharSequence qsDescription = null;
300 
301         if (mCurrentState.dataSim) {
302             // only show QS icons if the state is also default
303             if (!mCurrentState.isDefault) {
304                 return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
305             }
306 
307             if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) {
308                 qsTypeIcon = dataTypeIcon;
309             }
310 
311             boolean qsIconVisible = mCurrentState.enabled && !mCurrentState.isEmergency;
312             qsIcon = new IconState(qsIconVisible, getQsCurrentIconId(), contentDescription);
313 
314             if (!mCurrentState.isEmergency) {
315                 qsDescription = mCurrentState.networkName;
316             }
317         }
318 
319         return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
320     }
321 
getSbInfo(String contentDescription, int dataTypeIcon)322     private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
323         final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
324         IconState statusIcon = new IconState(
325                 mCurrentState.enabled && !mCurrentState.airplaneMode,
326                 getCurrentIconId(), contentDescription);
327 
328         boolean showDataIconInStatusBar =
329                 (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
330         int typeIcon =
331                 (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
332         boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
333 
334         return new SbInfo(showTriangle, typeIcon, statusIcon);
335     }
336 
337     @Override
cleanState()338     protected MobileState cleanState() {
339         return new MobileState();
340     }
341 
isInService()342     public boolean isInService() {
343         return mCurrentState.isInService();
344     }
345 
getNetworkNameForCarrierWiFi()346     String getNetworkNameForCarrierWiFi() {
347         return mPhone.getSimOperatorName();
348     }
349 
isRoaming()350     private boolean isRoaming() {
351         // During a carrier change, roaming indications need to be suppressed.
352         if (isCarrierNetworkChangeActive()) {
353             return false;
354         }
355         if (mCurrentState.isCdma()) {
356             return mPhone.getCdmaEnhancedRoamingIndicatorDisplayNumber()
357                     != TelephonyManager.ERI_OFF;
358         } else {
359             return mCurrentState.isRoaming();
360         }
361     }
362 
isCarrierNetworkChangeActive()363     private boolean isCarrierNetworkChangeActive() {
364         return mCurrentState.carrierNetworkChangeMode;
365     }
366 
handleBroadcast(Intent intent)367     void handleBroadcast(Intent intent) {
368         String action = intent.getAction();
369         if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) {
370             updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false),
371                     intent.getStringExtra(TelephonyManager.EXTRA_SPN),
372                     intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN),
373                     intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false),
374                     intent.getStringExtra(TelephonyManager.EXTRA_PLMN));
375             notifyListenersIfNecessary();
376         } else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
377             updateDataSim();
378             notifyListenersIfNecessary();
379         } else if (action.equals(TelephonyManager.ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED)) {
380             int carrierId = intent.getIntExtra(
381                     TelephonyManager.EXTRA_CARRIER_ID, UNKNOWN_CARRIER_ID);
382             mCurrentState.setCarrierId(carrierId);
383         }
384     }
385 
updateDataSim()386     private void updateDataSim() {
387         int activeDataSubId = mDefaults.getActiveDataSubId();
388         if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
389             mCurrentState.dataSim = activeDataSubId == mSubscriptionInfo.getSubscriptionId();
390         } else {
391             // There doesn't seem to be a data sim selected, however if
392             // there isn't a MobileSignalController with dataSim set, then
393             // QS won't get any callbacks and will be blank.  Instead
394             // lets just assume we are the data sim (which will basically
395             // show one at random) in QS until one is selected.  The user
396             // should pick one soon after, so we shouldn't be in this state
397             // for long.
398             mCurrentState.dataSim = true;
399         }
400     }
401 
402     /**
403      * Updates the network's name based on incoming spn and plmn.
404      */
updateNetworkName(boolean showSpn, String spn, String dataSpn, boolean showPlmn, String plmn)405     void updateNetworkName(boolean showSpn, String spn, String dataSpn,
406             boolean showPlmn, String plmn) {
407         if (CHATTY) {
408             Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn
409                     + " spn=" + spn + " dataSpn=" + dataSpn
410                     + " showPlmn=" + showPlmn + " plmn=" + plmn);
411         }
412         StringBuilder str = new StringBuilder();
413         StringBuilder strData = new StringBuilder();
414         if (showPlmn && plmn != null) {
415             str.append(plmn);
416             strData.append(plmn);
417         }
418         if (showSpn && spn != null) {
419             if (str.length() != 0) {
420                 str.append(mNetworkNameSeparator);
421             }
422             str.append(spn);
423         }
424         if (str.length() != 0) {
425             mCurrentState.networkName = str.toString();
426         } else {
427             mCurrentState.networkName = mNetworkNameDefault;
428         }
429         if (showSpn && dataSpn != null) {
430             if (strData.length() != 0) {
431                 strData.append(mNetworkNameSeparator);
432             }
433             strData.append(dataSpn);
434         }
435         if (strData.length() != 0) {
436             mCurrentState.networkNameData = strData.toString();
437         } else {
438             mCurrentState.networkNameData = mNetworkNameDefault;
439         }
440     }
441 
442     /**
443      * Extracts the CellSignalStrengthCdma from SignalStrength then returns the level
444      */
getCdmaLevel(SignalStrength signalStrength)445     private int getCdmaLevel(SignalStrength signalStrength) {
446         List<CellSignalStrengthCdma> signalStrengthCdma =
447                 signalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class);
448         if (!signalStrengthCdma.isEmpty()) {
449             return signalStrengthCdma.get(0).getLevel();
450         }
451         return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
452     }
453 
updateMobileStatus(MobileStatus mobileStatus)454     private void updateMobileStatus(MobileStatus mobileStatus) {
455         mCurrentState.setFromMobileStatus(mobileStatus);
456     }
457 
getSignalLevel(SignalStrength signalStrength)458     int getSignalLevel(SignalStrength signalStrength) {
459         if (signalStrength == null) {
460             return 0;
461         }
462         if (!signalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
463             return getCdmaLevel(signalStrength);
464         } else {
465             return signalStrength.getLevel();
466         }
467     }
468 
469     /**
470      * Updates the current state based on ServiceState, SignalStrength, DataState,
471      * TelephonyDisplayInfo, and sim state.  It should be called any time one of these is updated.
472      * This will call listeners if necessary.
473      */
updateTelephony()474     private void updateTelephony() {
475         if (DEBUG) {
476             Log.d(mTag, "updateTelephonySignalStrength: hasService="
477                     + mCurrentState.isInService()
478                     + " ss=" + mCurrentState.signalStrength
479                     + " displayInfo=" + mCurrentState.telephonyDisplayInfo);
480         }
481         checkDefaultData();
482         mCurrentState.connected = mCurrentState.isInService();
483         if (mCurrentState.connected) {
484             mCurrentState.level = getSignalLevel(mCurrentState.signalStrength);
485         }
486 
487         mCurrentState.setCarrierId(mPhone.getSimCarrierId());
488         String iconKey = mMobileMappingsProxy.getIconKey(mCurrentState.telephonyDisplayInfo);
489         if (mNetworkToIconLookup.get(iconKey) != null) {
490             mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
491         } else {
492             mCurrentState.iconGroup = mDefaultIcons;
493         }
494         mCurrentState.dataConnected = mCurrentState.isDataConnected();
495 
496         mCurrentState.roaming = isRoaming();
497         if (isCarrierNetworkChangeActive()) {
498             mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
499         } else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) {
500             if (mSubscriptionInfo.getSubscriptionId() != mDefaults.getDefaultDataSubId()) {
501                 mCurrentState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
502             } else {
503                 mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
504             }
505         }
506         if (mCurrentState.isEmergencyOnly() != mCurrentState.isEmergency) {
507             mCurrentState.isEmergency = mCurrentState.isEmergencyOnly();
508             mNetworkController.recalculateEmergency();
509         }
510         // Fill in the network name if we think we have it.
511         if (mCurrentState.networkName.equals(mNetworkNameDefault)
512                 && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) {
513             mCurrentState.networkName = mCurrentState.getOperatorAlphaShort();
514         }
515         // If this is the data subscription, update the currentState data name
516         if (mCurrentState.networkNameData.equals(mNetworkNameDefault)
517                 && mCurrentState.dataSim
518                 && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) {
519             mCurrentState.networkNameData = mCurrentState.getOperatorAlphaShort();
520         }
521 
522         notifyListenersIfNecessary();
523     }
524 
525     /**
526      * If we are controlling the NOT_DEFAULT_DATA icon, check the status of the other one
527      */
checkDefaultData()528     private void checkDefaultData() {
529         if (mCurrentState.iconGroup != TelephonyIcons.NOT_DEFAULT_DATA) {
530             mCurrentState.defaultDataOff = false;
531             return;
532         }
533 
534         mCurrentState.defaultDataOff = mNetworkController.isDataControllerDisabled();
535     }
536 
onMobileDataChanged()537     void onMobileDataChanged() {
538         checkDefaultData();
539         notifyListenersIfNecessary();
540     }
541 
isDataDisabled()542     boolean isDataDisabled() {
543         return !mPhone.isDataConnectionAllowed();
544     }
545 
546     @VisibleForTesting
setActivity(int activity)547     void setActivity(int activity) {
548         mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT
549                 || activity == TelephonyManager.DATA_ACTIVITY_IN;
550         mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
551                 || activity == TelephonyManager.DATA_ACTIVITY_OUT;
552         notifyListenersIfNecessary();
553     }
554 
recordLastMobileStatus(String mobileStatus)555     private void recordLastMobileStatus(String mobileStatus) {
556         mMobileStatusHistory[mMobileStatusHistoryIndex] = mobileStatus;
557         mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
558     }
559 
560     @Override
dump(PrintWriter pw)561     public void dump(PrintWriter pw) {
562         super.dump(pw);
563         pw.println("  mSubscription=" + mSubscriptionInfo + ",");
564         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
565         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
566         pw.println("  mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
567         pw.println("  mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening());
568         pw.println("  MobileStatusHistory");
569         int size = 0;
570         for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
571             if (mMobileStatusHistory[i] != null) {
572                 size++;
573             }
574         }
575         // Print out the previous states in ordered number.
576         for (int i = mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - 1;
577                 i >= mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - size; i--) {
578             pw.println("  Previous MobileStatus("
579                     + (mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - i) + "): "
580                     + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]);
581         }
582 
583         dumpTableData(pw);
584     }
585 
586     /** Box for QS icon info */
587     private static final class QsInfo {
588         final int ratTypeIcon;
589         final IconState icon;
590         final CharSequence description;
591 
QsInfo(int typeIcon, IconState iconState, CharSequence desc)592         QsInfo(int typeIcon, IconState iconState, CharSequence desc) {
593             ratTypeIcon = typeIcon;
594             icon = iconState;
595             description = desc;
596         }
597 
598         @Override
toString()599         public String toString() {
600             return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon;
601         }
602     }
603 
604     /** Box for status bar icon info */
605     private static final class SbInfo {
606         final boolean showTriangle;
607         final int ratTypeIcon;
608         final IconState icon;
609 
SbInfo(boolean show, int typeIcon, IconState iconState)610         SbInfo(boolean show, int typeIcon, IconState iconState) {
611             showTriangle = show;
612             ratTypeIcon = typeIcon;
613             icon = iconState;
614         }
615 
616         @Override
toString()617         public String toString() {
618             return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon
619                     + " icon=" + icon;
620         }
621     }
622 }
623