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