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 com.android.settingslib.mobile.MobileMappings.getDefaultIcons;
19 import static com.android.settingslib.mobile.MobileMappings.getIconKey;
20 import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 import android.database.ContentObserver;
25 import android.net.NetworkCapabilities;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.provider.Settings.Global;
29 import android.telephony.AccessNetworkConstants;
30 import android.telephony.CellSignalStrength;
31 import android.telephony.CellSignalStrengthCdma;
32 import android.telephony.ServiceState;
33 import android.telephony.SignalStrength;
34 import android.telephony.SubscriptionInfo;
35 import android.telephony.SubscriptionManager;
36 import android.telephony.TelephonyManager;
37 import android.telephony.ims.ImsException;
38 import android.telephony.ims.ImsMmTelManager;
39 import android.telephony.ims.ImsReasonInfo;
40 import android.telephony.ims.ImsRegistrationAttributes;
41 import android.telephony.ims.RegistrationManager.RegistrationCallback;
42 import android.text.Html;
43 import android.text.TextUtils;
44 import android.util.Log;
45 
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.settingslib.AccessibilityContentDescriptions;
48 import com.android.settingslib.SignalIcon.MobileIconGroup;
49 import com.android.settingslib.graph.SignalDrawable;
50 import com.android.settingslib.mobile.MobileMappings.Config;
51 import com.android.settingslib.mobile.MobileStatusTracker;
52 import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus;
53 import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
54 import com.android.settingslib.mobile.TelephonyIcons;
55 import com.android.settingslib.net.SignalStrengthUtil;
56 import com.android.systemui.R;
57 import com.android.systemui.flags.FeatureFlags;
58 import com.android.systemui.util.CarrierConfigTracker;
59 
60 import java.io.PrintWriter;
61 import java.text.SimpleDateFormat;
62 import java.util.BitSet;
63 import java.util.List;
64 import java.util.Map;
65 
66 /**
67  * Monitors the mobile signal changes and update the SysUI icons.
68  */
69 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
70     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
71     private static final int STATUS_HISTORY_SIZE = 64;
72     private static final int IMS_TYPE_WWAN = 1;
73     private static final int IMS_TYPE_WLAN = 2;
74     private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
75     private final TelephonyManager mPhone;
76     private final CarrierConfigTracker mCarrierConfigTracker;
77     private final ImsMmTelManager mImsMmTelManager;
78     private final SubscriptionDefaults mDefaults;
79     private final String mNetworkNameDefault;
80     private final String mNetworkNameSeparator;
81     private final ContentObserver mObserver;
82     private final boolean mProviderModelBehavior;
83     private final boolean mProviderModelSetting;
84     private final Handler mReceiverHandler;
85     private int mImsType = IMS_TYPE_WWAN;
86     // Save entire info for logging, we only use the id.
87     final SubscriptionInfo mSubscriptionInfo;
88     private Map<String, MobileIconGroup> mNetworkToIconLookup;
89 
90     private int mLastLevel;
91     private MobileIconGroup mDefaultIcons;
92     private Config mConfig;
93     @VisibleForTesting
94     boolean mInflateSignalStrengths = false;
95     private int mLastWwanLevel;
96     private int mLastWlanLevel;
97     private int mLastWlanCrossSimLevel;
98     @VisibleForTesting
99     MobileStatusTracker mMobileStatusTracker;
100 
101     // Save the previous STATUS_HISTORY_SIZE states for logging.
102     private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
103     // Where to copy the next state into.
104     private int mMobileStatusHistoryIndex;
105 
106     private final MobileStatusTracker.Callback mMobileCallback =
107             new MobileStatusTracker.Callback() {
108                 private String mLastStatus;
109 
110                 @Override
111                 public void onMobileStatusChanged(boolean updateTelephony,
112                         MobileStatus mobileStatus) {
113                     if (Log.isLoggable(mTag, Log.DEBUG)) {
114                         Log.d(mTag, "onMobileStatusChanged="
115                                 + " updateTelephony=" + updateTelephony
116                                 + " mobileStatus=" + mobileStatus.toString());
117                     }
118                     String currentStatus = mobileStatus.toString();
119                     if (!currentStatus.equals(mLastStatus)) {
120                         mLastStatus = currentStatus;
121                         String status = new StringBuilder()
122                                 .append(SSDF.format(System.currentTimeMillis())).append(",")
123                                 .append(currentStatus)
124                                 .toString();
125                         recordLastMobileStatus(status);
126                     }
127                     updateMobileStatus(mobileStatus);
128                     if (updateTelephony) {
129                         updateTelephony();
130                     } else {
131                         notifyListenersIfNecessary();
132                     }
133                 }
134             };
135 
136     private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
137         @Override
138         public void onRegistered(ImsRegistrationAttributes attributes) {
139             Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
140             int imsTransportType = attributes.getTransportType();
141             int registrationAttributes = attributes.getAttributeFlags();
142             if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
143                 mImsType = IMS_TYPE_WWAN;
144                 IconState statusIcon = new IconState(
145                         true,
146                         getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
147                         getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
148                 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
149             } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
150                 if (registrationAttributes == 0) {
151                     mImsType = IMS_TYPE_WLAN;
152                     IconState statusIcon = new IconState(
153                             true,
154                             getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
155                             getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
156                     notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
157                 } else if (registrationAttributes
158                         == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
159                     mImsType = IMS_TYPE_WLAN_CROSS_SIM;
160                     IconState statusIcon = new IconState(
161                             true,
162                             getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
163                             getCallStrengthDescription(
164                                     mLastWlanCrossSimLevel, /* isWifi= */false));
165                     notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
166                 }
167             }
168         }
169 
170         @Override
171         public void onUnregistered(ImsReasonInfo info) {
172             Log.d(mTag, "onDeregistered: " + "info=" + info);
173             mImsType = IMS_TYPE_WWAN;
174             IconState statusIcon = new IconState(
175                     true,
176                     getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
177                     getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
178             notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
179         }
180     };
181 
182     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
183     // need listener lists anymore.
MobileSignalController( Context context, Config config, boolean hasMobileData, TelephonyManager phone, CallbackHandler callbackHandler, NetworkControllerImpl networkController, SubscriptionInfo info, SubscriptionDefaults defaults, Looper receiverLooper, CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags )184     public MobileSignalController(
185             Context context,
186             Config config,
187             boolean hasMobileData,
188             TelephonyManager phone,
189             CallbackHandler callbackHandler,
190             NetworkControllerImpl networkController,
191             SubscriptionInfo info,
192             SubscriptionDefaults defaults,
193             Looper receiverLooper,
194             CarrierConfigTracker carrierConfigTracker,
195             FeatureFlags featureFlags
196     ) {
197         super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
198                 NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
199                 networkController);
200         mCarrierConfigTracker = carrierConfigTracker;
201         mConfig = config;
202         mPhone = phone;
203         mDefaults = defaults;
204         mSubscriptionInfo = info;
205         mNetworkNameSeparator = getTextIfExists(
206                 R.string.status_bar_network_name_separator).toString();
207         mNetworkNameDefault = getTextIfExists(
208                 com.android.internal.R.string.lockscreen_carrier_default).toString();
209         mReceiverHandler = new Handler(receiverLooper);
210 
211         mNetworkToIconLookup = mapIconSets(mConfig);
212         mDefaultIcons = getDefaultIcons(mConfig);
213 
214         String networkName = info.getCarrierName() != null ? info.getCarrierName().toString()
215                 : mNetworkNameDefault;
216         mLastState.networkName = mCurrentState.networkName = networkName;
217         mLastState.networkNameData = mCurrentState.networkNameData = networkName;
218         mLastState.enabled = mCurrentState.enabled = hasMobileData;
219         mLastState.iconGroup = mCurrentState.iconGroup = mDefaultIcons;
220         mObserver = new ContentObserver(new Handler(receiverLooper)) {
221             @Override
222             public void onChange(boolean selfChange) {
223                 updateTelephony();
224             }
225         };
226         mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
227         mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
228                 info, mDefaults, mMobileCallback);
229         mProviderModelBehavior = featureFlags.isCombinedStatusBarSignalIconsEnabled();
230         mProviderModelSetting = featureFlags.isProviderModelSettingEnabled();
231     }
232 
setConfiguration(Config config)233     void setConfiguration(Config config) {
234         mConfig = config;
235         updateInflateSignalStrength();
236         mNetworkToIconLookup = mapIconSets(mConfig);
237         mDefaultIcons = getDefaultIcons(mConfig);
238         updateTelephony();
239     }
240 
setAirplaneMode(boolean airplaneMode)241     void setAirplaneMode(boolean airplaneMode) {
242         mCurrentState.airplaneMode = airplaneMode;
243         notifyListenersIfNecessary();
244     }
245 
setUserSetupComplete(boolean userSetup)246     void setUserSetupComplete(boolean userSetup) {
247         mCurrentState.userSetup = userSetup;
248         notifyListenersIfNecessary();
249     }
250 
251     @Override
updateConnectivity(BitSet connectedTransports, BitSet validatedTransports)252     public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
253         boolean isValidated = validatedTransports.get(mTransportType);
254         mCurrentState.isDefault = connectedTransports.get(mTransportType);
255         // Only show this as not having connectivity if we are default.
256         mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0;
257         notifyListenersIfNecessary();
258     }
259 
setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode)260     void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
261         mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode;
262         updateTelephony();
263     }
264 
265     /**
266      * Start listening for phone state changes.
267      */
registerListener()268     public void registerListener() {
269         mMobileStatusTracker.setListening(true);
270         mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),
271                 true, mObserver);
272         mContext.getContentResolver().registerContentObserver(Global.getUriFor(
273                 Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
274                 true, mObserver);
275         if (mProviderModelBehavior) {
276             mReceiverHandler.post(mTryRegisterIms);
277         }
278     }
279 
280     // There is no listener to monitor whether the IMS service is ready, so we have to retry the
281     // IMS registration.
282     private final Runnable mTryRegisterIms = new Runnable() {
283         private static final int MAX_RETRY = 12;
284         private int mRetryCount;
285 
286         @Override
287         public void run() {
288             try {
289                 mRetryCount++;
290                 mImsMmTelManager.registerImsRegistrationCallback(
291                         mReceiverHandler::post, mRegistrationCallback);
292                 Log.d(mTag, "registerImsRegistrationCallback succeeded");
293             } catch (RuntimeException | ImsException e) {
294                 if (mRetryCount < MAX_RETRY) {
295                     Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
296                     // Wait for 5 seconds to retry
297                     mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
298                 }
299             }
300         }
301     };
302 
303     /**
304      * Stop listening for phone state changes.
305      */
unregisterListener()306     public void unregisterListener() {
307         mMobileStatusTracker.setListening(false);
308         mContext.getContentResolver().unregisterContentObserver(mObserver);
309         mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
310     }
311 
updateInflateSignalStrength()312     private void updateInflateSignalStrength() {
313         mInflateSignalStrengths = SignalStrengthUtil.shouldInflateSignalStrength(mContext,
314                 mSubscriptionInfo.getSubscriptionId());
315     }
316 
getNumLevels()317     private int getNumLevels() {
318         if (mInflateSignalStrengths) {
319             return CellSignalStrength.getNumSignalStrengthLevels() + 1;
320         }
321         return CellSignalStrength.getNumSignalStrengthLevels();
322     }
323 
324     @Override
getCurrentIconId()325     public int getCurrentIconId() {
326         if (mCurrentState.iconGroup == TelephonyIcons.CARRIER_NETWORK_CHANGE) {
327             return SignalDrawable.getCarrierChangeState(getNumLevels());
328         } else if (mCurrentState.connected) {
329             int level = mCurrentState.level;
330             if (mInflateSignalStrengths) {
331                 level++;
332             }
333             boolean dataDisabled = mCurrentState.userSetup
334                     && (mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
335                     || (mCurrentState.iconGroup == TelephonyIcons.NOT_DEFAULT_DATA
336                             && mCurrentState.defaultDataOff));
337             boolean noInternet = mCurrentState.inetCondition == 0;
338             boolean cutOut = dataDisabled || noInternet;
339             return SignalDrawable.getState(level, getNumLevels(), cutOut);
340         } else if (mCurrentState.enabled) {
341             return SignalDrawable.getEmptyState(getNumLevels());
342         } else {
343             return 0;
344         }
345     }
346 
347     @Override
getQsCurrentIconId()348     public int getQsCurrentIconId() {
349         return getCurrentIconId();
350     }
351 
352     @Override
notifyListeners(SignalCallback callback)353     public void notifyListeners(SignalCallback callback) {
354         // If the device is on carrier merged WiFi, we should let WifiSignalController to control
355         // the SysUI states.
356         if (mNetworkController.isCarrierMergedWifi(mSubscriptionInfo.getSubscriptionId())) {
357             return;
358         }
359         MobileIconGroup icons = getIcons();
360 
361         String contentDescription = getTextIfExists(getContentDescription()).toString();
362         CharSequence dataContentDescriptionHtml = getTextIfExists(icons.dataContentDescription);
363 
364         //TODO: Hacky
365         // The data content description can sometimes be shown in a text view and might come to us
366         // as HTML. Strip any styling here so that listeners don't have to care
367         CharSequence dataContentDescription = Html.fromHtml(
368                 dataContentDescriptionHtml.toString(), 0).toString();
369         if (mCurrentState.inetCondition == 0) {
370             dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
371         }
372 
373         final QsInfo qsInfo = getQsInfo(contentDescription, icons.dataType);
374         final SbInfo sbInfo = getSbInfo(contentDescription, icons.dataType);
375 
376         MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
377                 sbInfo.icon,
378                 qsInfo.icon,
379                 sbInfo.ratTypeIcon,
380                 qsInfo.ratTypeIcon,
381                 mCurrentState.hasActivityIn(),
382                 mCurrentState.hasActivityOut(),
383                 dataContentDescription,
384                 dataContentDescriptionHtml,
385                 qsInfo.description,
386                 mSubscriptionInfo.getSubscriptionId(),
387                 mCurrentState.roaming,
388                 sbInfo.showTriangle);
389         callback.setMobileDataIndicators(mobileDataIndicators);
390     }
391 
getQsInfo(String contentDescription, int dataTypeIcon)392     private QsInfo getQsInfo(String contentDescription, int dataTypeIcon) {
393         int qsTypeIcon = 0;
394         IconState qsIcon = null;
395         CharSequence qsDescription = null;
396 
397         boolean pm = mProviderModelSetting || mProviderModelBehavior;
398         if (mCurrentState.dataSim) {
399             // If using provider model behavior, only show QS icons if the state is also default
400             if (pm && !mCurrentState.isDefault) {
401                 return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
402             }
403 
404             if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) {
405                 qsTypeIcon = dataTypeIcon;
406             }
407 
408             boolean qsIconVisible = mCurrentState.enabled && !mCurrentState.isEmergency;
409             qsIcon = new IconState(qsIconVisible, getQsCurrentIconId(), contentDescription);
410 
411             if (!mCurrentState.isEmergency) {
412                 qsDescription = mCurrentState.networkName;
413             }
414         }
415 
416         return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
417     }
418 
getSbInfo(String contentDescription, int dataTypeIcon)419     private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
420         final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
421         boolean showTriangle = false;
422         int typeIcon = 0;
423         IconState statusIcon = null;
424 
425         if (mProviderModelBehavior) {
426             boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
427                     && (mCurrentState.dataSim && mCurrentState.isDefault);
428             typeIcon =
429                     (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
430             showDataIconStatusBar |= mCurrentState.roaming;
431             statusIcon = new IconState(
432                     showDataIconStatusBar && !mCurrentState.airplaneMode,
433                     getCurrentIconId(), contentDescription);
434 
435             showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
436         } else {
437             statusIcon = new IconState(
438                     mCurrentState.enabled && !mCurrentState.airplaneMode,
439                     getCurrentIconId(), contentDescription);
440 
441             boolean showDataIconInStatusBar =
442                     (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
443             typeIcon =
444                     (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
445             showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
446         }
447 
448         return new SbInfo(showTriangle, typeIcon, statusIcon);
449     }
450 
451     @Override
cleanState()452     protected MobileState cleanState() {
453         return new MobileState();
454     }
455 
isInService()456     public boolean isInService() {
457         return mCurrentState.isInService();
458     }
459 
getNetworkNameForCarrierWiFi()460     String getNetworkNameForCarrierWiFi() {
461         return mPhone.getSimOperatorName();
462     }
463 
isRoaming()464     private boolean isRoaming() {
465         // During a carrier change, roaming indications need to be suppressed.
466         if (isCarrierNetworkChangeActive()) {
467             return false;
468         }
469         if (mCurrentState.isCdma()) {
470             return mPhone.getCdmaEnhancedRoamingIndicatorDisplayNumber()
471                     != TelephonyManager.ERI_OFF;
472         } else {
473             return mCurrentState.isRoaming();
474         }
475     }
476 
isCarrierNetworkChangeActive()477     private boolean isCarrierNetworkChangeActive() {
478         return mCurrentState.carrierNetworkChangeMode;
479     }
480 
handleBroadcast(Intent intent)481     void handleBroadcast(Intent intent) {
482         String action = intent.getAction();
483         if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) {
484             updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false),
485                     intent.getStringExtra(TelephonyManager.EXTRA_SPN),
486                     intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN),
487                     intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false),
488                     intent.getStringExtra(TelephonyManager.EXTRA_PLMN));
489             notifyListenersIfNecessary();
490         } else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
491             updateDataSim();
492             notifyListenersIfNecessary();
493         }
494     }
495 
updateDataSim()496     private void updateDataSim() {
497         int activeDataSubId = mDefaults.getActiveDataSubId();
498         if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
499             mCurrentState.dataSim = activeDataSubId == mSubscriptionInfo.getSubscriptionId();
500         } else {
501             // There doesn't seem to be a data sim selected, however if
502             // there isn't a MobileSignalController with dataSim set, then
503             // QS won't get any callbacks and will be blank.  Instead
504             // lets just assume we are the data sim (which will basically
505             // show one at random) in QS until one is selected.  The user
506             // should pick one soon after, so we shouldn't be in this state
507             // for long.
508             mCurrentState.dataSim = true;
509         }
510     }
511 
512     /**
513      * Updates the network's name based on incoming spn and plmn.
514      */
updateNetworkName(boolean showSpn, String spn, String dataSpn, boolean showPlmn, String plmn)515     void updateNetworkName(boolean showSpn, String spn, String dataSpn,
516             boolean showPlmn, String plmn) {
517         if (CHATTY) {
518             Log.d("CarrierLabel", "updateNetworkName showSpn=" + showSpn
519                     + " spn=" + spn + " dataSpn=" + dataSpn
520                     + " showPlmn=" + showPlmn + " plmn=" + plmn);
521         }
522         StringBuilder str = new StringBuilder();
523         StringBuilder strData = new StringBuilder();
524         if (showPlmn && plmn != null) {
525             str.append(plmn);
526             strData.append(plmn);
527         }
528         if (showSpn && spn != null) {
529             if (str.length() != 0) {
530                 str.append(mNetworkNameSeparator);
531             }
532             str.append(spn);
533         }
534         if (str.length() != 0) {
535             mCurrentState.networkName = str.toString();
536         } else {
537             mCurrentState.networkName = mNetworkNameDefault;
538         }
539         if (showSpn && dataSpn != null) {
540             if (strData.length() != 0) {
541                 strData.append(mNetworkNameSeparator);
542             }
543             strData.append(dataSpn);
544         }
545         if (strData.length() != 0) {
546             mCurrentState.networkNameData = strData.toString();
547         } else {
548             mCurrentState.networkNameData = mNetworkNameDefault;
549         }
550     }
551 
552     /**
553      * Extracts the CellSignalStrengthCdma from SignalStrength then returns the level
554      */
getCdmaLevel(SignalStrength signalStrength)555     private int getCdmaLevel(SignalStrength signalStrength) {
556         List<CellSignalStrengthCdma> signalStrengthCdma =
557                 signalStrength.getCellSignalStrengths(CellSignalStrengthCdma.class);
558         if (!signalStrengthCdma.isEmpty()) {
559             return signalStrengthCdma.get(0).getLevel();
560         }
561         return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
562     }
563 
updateMobileStatus(MobileStatus mobileStatus)564     private void updateMobileStatus(MobileStatus mobileStatus) {
565         int lastVoiceState = mCurrentState.getVoiceServiceState();
566         mCurrentState.setFromMobileStatus(mobileStatus);
567 
568         notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
569         if (mProviderModelBehavior) {
570             maybeNotifyCallStateChanged(lastVoiceState);
571         }
572     }
573 
574     /** Call state changed is only applicable when provider model behavior is true */
maybeNotifyCallStateChanged(int lastVoiceState)575     private void maybeNotifyCallStateChanged(int lastVoiceState) {
576         int currentVoiceState = mCurrentState.getVoiceServiceState();
577         if (lastVoiceState == currentVoiceState) {
578             return;
579         }
580         // Only update the no calling Status in the below scenarios
581         // 1. The first valid voice state has been received
582         // 2. The voice state has been changed and either the last or current state is
583         //    ServiceState.STATE_IN_SERVICE
584         if (lastVoiceState == -1
585                 || (lastVoiceState == ServiceState.STATE_IN_SERVICE
586                         || currentVoiceState == ServiceState.STATE_IN_SERVICE)) {
587             boolean isNoCalling = mCurrentState.isNoCalling();
588             isNoCalling &= !hideNoCalling();
589             IconState statusIcon = new IconState(isNoCalling,
590                     R.drawable.ic_qs_no_calling_sms,
591                     getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
592             notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
593         }
594     }
595 
updateNoCallingState()596     void updateNoCallingState() {
597         int currentVoiceState = mCurrentState.getVoiceServiceState();
598         boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
599         isNoCalling &= !hideNoCalling();
600         IconState statusIcon = new IconState(isNoCalling,
601                 R.drawable.ic_qs_no_calling_sms,
602                 getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
603         notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
604     }
605 
hideNoCalling()606     private boolean hideNoCalling() {
607         return mNetworkController.hasDefaultNetwork()
608                 && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId());
609     }
610 
getCallStrengthIcon(int level, boolean isWifi)611     private int getCallStrengthIcon(int level, boolean isWifi) {
612         return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
613                 : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
614     }
615 
getCallStrengthDescription(int level, boolean isWifi)616     private String getCallStrengthDescription(int level, boolean isWifi) {
617         return isWifi
618                 ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
619                         .toString()
620                 : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
621                         .toString();
622     }
623 
refreshCallIndicator(SignalCallback callback)624     void refreshCallIndicator(SignalCallback callback) {
625         boolean isNoCalling = mCurrentState.isNoCalling();
626         isNoCalling &= !hideNoCalling();
627         IconState statusIcon = new IconState(isNoCalling,
628                 R.drawable.ic_qs_no_calling_sms,
629                 getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
630         callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
631 
632         switch (mImsType) {
633             case IMS_TYPE_WWAN:
634                 statusIcon = new IconState(
635                         true,
636                         getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
637                         getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
638                 break;
639             case IMS_TYPE_WLAN:
640                 statusIcon = new IconState(
641                         true,
642                         getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
643                         getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
644                 break;
645             case IMS_TYPE_WLAN_CROSS_SIM:
646                 statusIcon = new IconState(
647                         true,
648                         getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
649                         getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
650         }
651         callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
652     }
653 
notifyWifiLevelChange(int level)654     void notifyWifiLevelChange(int level) {
655         if (!mProviderModelBehavior) {
656             return;
657         }
658         mLastWlanLevel = level;
659         if (mImsType != IMS_TYPE_WLAN) {
660             return;
661         }
662         IconState statusIcon = new IconState(
663                 true,
664                 getCallStrengthIcon(level, /* isWifi= */true),
665                 getCallStrengthDescription(level, /* isWifi= */true));
666         notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
667     }
668 
notifyDefaultMobileLevelChange(int level)669     void notifyDefaultMobileLevelChange(int level) {
670         if (!mProviderModelBehavior) {
671             return;
672         }
673         mLastWlanCrossSimLevel = level;
674         if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
675             return;
676         }
677         IconState statusIcon = new IconState(
678                 true,
679                 getCallStrengthIcon(level, /* isWifi= */false),
680                 getCallStrengthDescription(level, /* isWifi= */false));
681         notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
682     }
683 
notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength)684     void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
685         if (!mProviderModelBehavior) {
686             return;
687         }
688         int newLevel = getSignalLevel(signalStrength);
689         if (newLevel != mLastLevel) {
690             mLastLevel = newLevel;
691             mLastWwanLevel = newLevel;
692             if (mImsType == IMS_TYPE_WWAN) {
693                 IconState statusIcon = new IconState(
694                         true,
695                         getCallStrengthIcon(newLevel, /* isWifi= */false),
696                         getCallStrengthDescription(newLevel, /* isWifi= */false));
697                 notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
698             }
699             if (mCurrentState.dataSim) {
700                 mNetworkController.notifyDefaultMobileLevelChange(newLevel);
701             }
702         }
703     }
704 
getSignalLevel(SignalStrength signalStrength)705     int getSignalLevel(SignalStrength signalStrength) {
706         if (signalStrength == null) {
707             return 0;
708         }
709         if (!signalStrength.isGsm() && mConfig.alwaysShowCdmaRssi) {
710             return getCdmaLevel(signalStrength);
711         } else {
712             return signalStrength.getLevel();
713         }
714     }
715 
716     /**
717      * Updates the current state based on ServiceState, SignalStrength, DataState,
718      * TelephonyDisplayInfo, and sim state.  It should be called any time one of these is updated.
719      * This will call listeners if necessary.
720      */
updateTelephony()721     private void updateTelephony() {
722         if (Log.isLoggable(mTag, Log.DEBUG)) {
723             Log.d(mTag, "updateTelephonySignalStrength: hasService="
724                     + mCurrentState.isInService()
725                     + " ss=" + mCurrentState.signalStrength
726                     + " displayInfo=" + mCurrentState.telephonyDisplayInfo);
727         }
728         checkDefaultData();
729         mCurrentState.connected = mCurrentState.isInService();
730         if (mCurrentState.connected) {
731             mCurrentState.level = getSignalLevel(mCurrentState.signalStrength);
732         }
733 
734         String iconKey = getIconKey(mCurrentState.telephonyDisplayInfo);
735         if (mNetworkToIconLookup.get(iconKey) != null) {
736             mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
737         } else {
738             mCurrentState.iconGroup = mDefaultIcons;
739         }
740         mCurrentState.dataConnected = mCurrentState.isDataConnected();
741 
742         mCurrentState.roaming = isRoaming();
743         if (isCarrierNetworkChangeActive()) {
744             mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
745         } else if (isDataDisabled() && !mConfig.alwaysShowDataRatIcon) {
746             if (mSubscriptionInfo.getSubscriptionId() != mDefaults.getDefaultDataSubId()) {
747                 mCurrentState.iconGroup = TelephonyIcons.NOT_DEFAULT_DATA;
748             } else {
749                 mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
750             }
751         }
752         if (mCurrentState.isEmergencyOnly() != mCurrentState.isEmergency) {
753             mCurrentState.isEmergency = mCurrentState.isEmergencyOnly();
754             mNetworkController.recalculateEmergency();
755         }
756         // Fill in the network name if we think we have it.
757         if (mCurrentState.networkName.equals(mNetworkNameDefault)
758                 && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) {
759             mCurrentState.networkName = mCurrentState.getOperatorAlphaShort();
760         }
761         // If this is the data subscription, update the currentState data name
762         if (mCurrentState.networkNameData.equals(mNetworkNameDefault)
763                 && mCurrentState.dataSim
764                 && !TextUtils.isEmpty(mCurrentState.getOperatorAlphaShort())) {
765             mCurrentState.networkNameData = mCurrentState.getOperatorAlphaShort();
766         }
767 
768         notifyListenersIfNecessary();
769     }
770 
771     /**
772      * If we are controlling the NOT_DEFAULT_DATA icon, check the status of the other one
773      */
checkDefaultData()774     private void checkDefaultData() {
775         if (mCurrentState.iconGroup != TelephonyIcons.NOT_DEFAULT_DATA) {
776             mCurrentState.defaultDataOff = false;
777             return;
778         }
779 
780         mCurrentState.defaultDataOff = mNetworkController.isDataControllerDisabled();
781     }
782 
onMobileDataChanged()783     void onMobileDataChanged() {
784         checkDefaultData();
785         notifyListenersIfNecessary();
786     }
787 
isDataDisabled()788     boolean isDataDisabled() {
789         return !mPhone.isDataConnectionAllowed();
790     }
791 
792     @VisibleForTesting
setActivity(int activity)793     void setActivity(int activity) {
794         mCurrentState.activityIn = activity == TelephonyManager.DATA_ACTIVITY_INOUT
795                 || activity == TelephonyManager.DATA_ACTIVITY_IN;
796         mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
797                 || activity == TelephonyManager.DATA_ACTIVITY_OUT;
798         notifyListenersIfNecessary();
799     }
800 
recordLastMobileStatus(String mobileStatus)801     private void recordLastMobileStatus(String mobileStatus) {
802         mMobileStatusHistory[mMobileStatusHistoryIndex] = mobileStatus;
803         mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
804     }
805 
806     @VisibleForTesting
setImsType(int imsType)807     void setImsType(int imsType) {
808         mImsType = imsType;
809     }
810 
811     @Override
dump(PrintWriter pw)812     public void dump(PrintWriter pw) {
813         super.dump(pw);
814         pw.println("  mSubscription=" + mSubscriptionInfo + ",");
815         pw.println("  mProviderModelSetting=" + mProviderModelSetting + ",");
816         pw.println("  mProviderModelBehavior=" + mProviderModelBehavior + ",");
817         pw.println("  mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
818         pw.println("  isDataDisabled=" + isDataDisabled() + ",");
819         pw.println("  mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
820         pw.println("  MobileStatusHistory");
821         int size = 0;
822         for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
823             if (mMobileStatusHistory[i] != null) {
824                 size++;
825             }
826         }
827         // Print out the previous states in ordered number.
828         for (int i = mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - 1;
829                 i >= mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - size; i--) {
830             pw.println("  Previous MobileStatus("
831                     + (mMobileStatusHistoryIndex + STATUS_HISTORY_SIZE - i) + "): "
832                     + mMobileStatusHistory[i & (STATUS_HISTORY_SIZE - 1)]);
833         }
834     }
835 
836     /** Box for QS icon info */
837     private static final class QsInfo {
838         final int ratTypeIcon;
839         final IconState icon;
840         final CharSequence description;
841 
QsInfo(int typeIcon, IconState iconState, CharSequence desc)842         QsInfo(int typeIcon, IconState iconState, CharSequence desc) {
843             ratTypeIcon = typeIcon;
844             icon = iconState;
845             description = desc;
846         }
847     }
848 
849     /** Box for StatusBar icon info */
850     private static final class SbInfo {
851         final boolean showTriangle;
852         final int ratTypeIcon;
853         final IconState icon;
854 
SbInfo(boolean show, int typeIcon, IconState iconState)855         SbInfo(boolean show, int typeIcon, IconState iconState) {
856             showTriangle = show;
857             ratTypeIcon = typeIcon;
858             icon = iconState;
859         }
860     }
861 }
862