1 /*
2  * Copyright (C) 2019 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 
17 package com.android.keyguard;
18 
19 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
20 import static android.telephony.PhoneStateListener.LISTEN_NONE;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.net.ConnectivityManager;
26 import android.net.wifi.WifiManager;
27 import android.os.Handler;
28 import android.telephony.PhoneStateListener;
29 import android.telephony.ServiceState;
30 import android.telephony.SubscriptionInfo;
31 import android.telephony.SubscriptionManager;
32 import android.telephony.TelephonyManager;
33 import android.text.TextUtils;
34 import android.util.Log;
35 
36 import com.android.internal.telephony.IccCardConstants;
37 import com.android.internal.telephony.TelephonyIntents;
38 import com.android.settingslib.WirelessUtils;
39 import com.android.systemui.Dependency;
40 import com.android.systemui.R;
41 import com.android.systemui.keyguard.WakefulnessLifecycle;
42 
43 import java.util.List;
44 import java.util.Objects;
45 
46 import androidx.annotation.VisibleForTesting;
47 
48 /**
49  * Controller that generates text including the carrier names and/or the status of all the SIM
50  * interfaces in the device. Through a callback, the updates can be retrieved either as a list or
51  * separated by a given separator {@link CharSequence}.
52  */
53 public class CarrierTextController {
54     private static final boolean DEBUG = KeyguardConstants.DEBUG;
55     private static final String TAG = "CarrierTextController";
56 
57     private final boolean mIsEmergencyCallCapable;
58     private boolean mTelephonyCapable;
59     private boolean mShowMissingSim;
60     private boolean mShowAirplaneMode;
61     @VisibleForTesting
62     protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
63     private WifiManager mWifiManager;
64     private boolean[] mSimErrorState;
65     private final int mSimSlotsNumber;
66     private CarrierTextCallback mCarrierTextCallback;
67     private Context mContext;
68     private CharSequence mSeparator;
69     private WakefulnessLifecycle mWakefulnessLifecycle;
70     private final WakefulnessLifecycle.Observer mWakefulnessObserver =
71             new WakefulnessLifecycle.Observer() {
72                 @Override
73                 public void onFinishedWakingUp() {
74                     mCarrierTextCallback.finishedWakingUp();
75                 }
76 
77                 @Override
78                 public void onStartedGoingToSleep() {
79                     mCarrierTextCallback.startedGoingToSleep();
80                 }
81             };
82 
83     @VisibleForTesting
84     protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
85         @Override
86         public void onRefreshCarrierInfo() {
87             if (DEBUG) {
88                 Log.d(TAG, "onRefreshCarrierInfo(), mTelephonyCapable: "
89                         + Boolean.toString(mTelephonyCapable));
90             }
91             updateCarrierText();
92         }
93 
94         @Override
95         public void onTelephonyCapable(boolean capable) {
96             if (DEBUG) {
97                 Log.d(TAG, "onTelephonyCapable() mTelephonyCapable: "
98                         + Boolean.toString(capable));
99             }
100             mTelephonyCapable = capable;
101             updateCarrierText();
102         }
103 
104         public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) {
105             if (slotId < 0 || slotId >= mSimSlotsNumber) {
106                 Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId
107                         + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
108                 return;
109             }
110 
111             if (DEBUG) Log.d(TAG, "onSimStateChanged: " + getStatusForIccState(simState));
112             if (getStatusForIccState(simState) == CarrierTextController.StatusMode.SimIoError) {
113                 mSimErrorState[slotId] = true;
114                 updateCarrierText();
115             } else if (mSimErrorState[slotId]) {
116                 mSimErrorState[slotId] = false;
117                 updateCarrierText();
118             }
119         }
120     };
121 
122     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
123     private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
124         @Override
125         public void onActiveDataSubscriptionIdChanged(int subId) {
126             mActiveMobileDataSubscription = subId;
127             if (mKeyguardUpdateMonitor != null) {
128                 updateCarrierText();
129             }
130         }
131     };
132 
133     /**
134      * The status of this lock screen. Primarily used for widgets on LockScreen.
135      */
136     private enum StatusMode {
137         Normal, // Normal case (sim card present, it's not locked)
138         NetworkLocked, // SIM card is 'network locked'.
139         SimMissing, // SIM card is missing.
140         SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
141         SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
142         SimLocked, // SIM card is currently locked
143         SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
144         SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM.
145         SimIoError, // SIM card is faulty
146         SimUnknown // SIM card is unknown
147     }
148 
149     /**
150      * Controller that provides updates on text with carriers names or SIM status.
151      * Used by {@link CarrierText}.
152      *
153      * @param separator Separator between different parts of the text
154      */
CarrierTextController(Context context, CharSequence separator, boolean showAirplaneMode, boolean showMissingSim)155     public CarrierTextController(Context context, CharSequence separator, boolean showAirplaneMode,
156             boolean showMissingSim) {
157         mContext = context;
158         mIsEmergencyCallCapable = getTelephonyManager().isVoiceCapable();
159 
160         mShowAirplaneMode = showAirplaneMode;
161         mShowMissingSim = showMissingSim;
162 
163         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
164         mSeparator = separator;
165         mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
166         mSimSlotsNumber = getTelephonyManager().getSupportedModemCount();
167         mSimErrorState = new boolean[mSimSlotsNumber];
168     }
169 
getTelephonyManager()170     private TelephonyManager getTelephonyManager() {
171         return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
172     }
173 
174     /**
175      * Checks if there are faulty cards. Adds the text depending on the slot of the card
176      *
177      * @param text:   current carrier text based on the sim state
178      * @param carrierNames names order by subscription order
179      * @param subOrderBySlot array containing the sub index for each slot ID
180      * @param noSims: whether a valid sim card is inserted
181      * @return text
182      */
updateCarrierTextWithSimIoError(CharSequence text, CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims)183     private CharSequence updateCarrierTextWithSimIoError(CharSequence text,
184             CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) {
185         final CharSequence carrier = "";
186         CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
187                 IccCardConstants.State.CARD_IO_ERROR, carrier);
188         // mSimErrorState has the state of each sim indexed by slotID.
189         for (int index = 0; index < getTelephonyManager().getActiveModemCount(); index++) {
190             if (!mSimErrorState[index]) {
191                 continue;
192             }
193             // In the case when no sim cards are detected but a faulty card is inserted
194             // overwrite the text and only show "Invalid card"
195             if (noSims) {
196                 return concatenate(carrierTextForSimIOError,
197                         getContext().getText(
198                                 com.android.internal.R.string.emergency_calls_only),
199                         mSeparator);
200             } else if (subOrderBySlot[index] != -1) {
201                 int subIndex = subOrderBySlot[index];
202                 // prepend "Invalid card" when faulty card is inserted in slot 0 or 1
203                 carrierNames[subIndex] = concatenate(carrierTextForSimIOError,
204                         carrierNames[subIndex],
205                         mSeparator);
206             } else {
207                 // concatenate "Invalid card" when faulty card is inserted in other slot
208                 text = concatenate(text, carrierTextForSimIOError, mSeparator);
209             }
210 
211         }
212         return text;
213     }
214 
215     /**
216      * Sets the listening status of this controller. If the callback is null, it is set to
217      * not listening
218      *
219      * @param callback Callback to provide text updates
220      */
setListening(CarrierTextCallback callback)221     public void setListening(CarrierTextCallback callback) {
222         TelephonyManager telephonyManager = getTelephonyManager();
223         if (callback != null) {
224             mCarrierTextCallback = callback;
225             if (ConnectivityManager.from(mContext).isNetworkSupported(
226                     ConnectivityManager.TYPE_MOBILE)) {
227                 mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
228                 mKeyguardUpdateMonitor.registerCallback(mCallback);
229                 mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
230                 telephonyManager.listen(mPhoneStateListener,
231                         LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
232             } else {
233                 // Don't listen and clear out the text when the device isn't a phone.
234                 mKeyguardUpdateMonitor = null;
235                 callback.updateCarrierInfo(new CarrierTextCallbackInfo("", null, false, null));
236             }
237         } else {
238             mCarrierTextCallback = null;
239             if (mKeyguardUpdateMonitor != null) {
240                 mKeyguardUpdateMonitor.removeCallback(mCallback);
241                 mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
242             }
243             telephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
244         }
245     }
246 
getSubscriptionInfo()247     protected List<SubscriptionInfo> getSubscriptionInfo() {
248         return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
249     }
250 
updateCarrierText()251     protected void updateCarrierText() {
252         boolean allSimsMissing = true;
253         boolean anySimReadyAndInService = false;
254         CharSequence displayText = null;
255         List<SubscriptionInfo> subs = getSubscriptionInfo();
256 
257         final int numSubs = subs.size();
258         final int[] subsIds = new int[numSubs];
259         // This array will contain in position i, the index of subscription in slot ID i.
260         // -1 if no subscription in that slot
261         final int[] subOrderBySlot = new int[mSimSlotsNumber];
262         for (int i = 0; i < mSimSlotsNumber; i++) {
263             subOrderBySlot[i] = -1;
264         }
265         final CharSequence[] carrierNames = new CharSequence[numSubs];
266         if (DEBUG) Log.d(TAG, "updateCarrierText(): " + numSubs);
267 
268         for (int i = 0; i < numSubs; i++) {
269             int subId = subs.get(i).getSubscriptionId();
270             carrierNames[i] = "";
271             subsIds[i] = subId;
272             subOrderBySlot[subs.get(i).getSimSlotIndex()] = i;
273             IccCardConstants.State simState = mKeyguardUpdateMonitor.getSimState(subId);
274             CharSequence carrierName = subs.get(i).getCarrierName();
275             CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
276             if (DEBUG) {
277                 Log.d(TAG, "Handling (subId=" + subId + "): " + simState + " " + carrierName);
278             }
279             if (carrierTextForSimState != null) {
280                 allSimsMissing = false;
281                 carrierNames[i] = carrierTextForSimState;
282             }
283             if (simState == IccCardConstants.State.READY) {
284                 ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
285                 if (ss != null && ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE) {
286                     // hack for WFC (IWLAN) not turning off immediately once
287                     // Wi-Fi is disassociated or disabled
288                     if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
289                             || (mWifiManager.isWifiEnabled()
290                             && mWifiManager.getConnectionInfo() != null
291                             && mWifiManager.getConnectionInfo().getBSSID() != null)) {
292                         if (DEBUG) {
293                             Log.d(TAG, "SIM ready and in service: subId=" + subId + ", ss=" + ss);
294                         }
295                         anySimReadyAndInService = true;
296                     }
297                 }
298             }
299         }
300         // Only create "No SIM card" if no cards with CarrierName && no wifi when some sim is READY
301         // This condition will also be true always when numSubs == 0
302         if (allSimsMissing && !anySimReadyAndInService) {
303             if (numSubs != 0) {
304                 // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
305                 // This depends on mPlmn containing the text "Emergency calls only" when the radio
306                 // has some connectivity. Otherwise, it should be null or empty and just show
307                 // "No SIM card"
308                 // Grab the first subscripton, because they all should contain the emergency text,
309                 // described above.
310                 displayText = makeCarrierStringOnEmergencyCapable(
311                         getMissingSimMessage(), subs.get(0).getCarrierName());
312             } else {
313                 // We don't have a SubscriptionInfo to get the emergency calls only from.
314                 // Grab it from the old sticky broadcast if possible instead. We can use it
315                 // here because no subscriptions are active, so we don't have
316                 // to worry about MSIM clashing.
317                 CharSequence text =
318                         getContext().getText(com.android.internal.R.string.emergency_calls_only);
319                 Intent i = getContext().registerReceiver(null,
320                         new IntentFilter(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION));
321                 if (i != null) {
322                     String spn = "";
323                     String plmn = "";
324                     if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) {
325                         spn = i.getStringExtra(TelephonyIntents.EXTRA_SPN);
326                     }
327                     if (i.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) {
328                         plmn = i.getStringExtra(TelephonyIntents.EXTRA_PLMN);
329                     }
330                     if (DEBUG) Log.d(TAG, "Getting plmn/spn sticky brdcst " + plmn + "/" + spn);
331                     if (Objects.equals(plmn, spn)) {
332                         text = plmn;
333                     } else {
334                         text = concatenate(plmn, spn, mSeparator);
335                     }
336                 }
337                 displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text);
338             }
339         }
340 
341         if (TextUtils.isEmpty(displayText)) displayText = joinNotEmpty(mSeparator, carrierNames);
342 
343         displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot,
344                 allSimsMissing);
345 
346         boolean airplaneMode = false;
347         // APM (airplane mode) != no carrier state. There are carrier services
348         // (e.g. WFC = Wi-Fi calling) which may operate in APM.
349         if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
350             displayText = getAirplaneModeMessage();
351             airplaneMode = true;
352         }
353 
354         final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
355                 displayText,
356                 carrierNames,
357                 !allSimsMissing,
358                 subsIds,
359                 airplaneMode);
360         postToCallback(info);
361     }
362 
363     @VisibleForTesting
postToCallback(CarrierTextCallbackInfo info)364     protected void postToCallback(CarrierTextCallbackInfo info) {
365         Handler handler = Dependency.get(Dependency.MAIN_HANDLER);
366         final CarrierTextCallback callback = mCarrierTextCallback;
367         if (callback != null) {
368             handler.post(() -> callback.updateCarrierInfo(info));
369         }
370     }
371 
getContext()372     private Context getContext() {
373         return mContext;
374     }
375 
getMissingSimMessage()376     private String getMissingSimMessage() {
377         return mShowMissingSim && mTelephonyCapable
378                 ? getContext().getString(R.string.keyguard_missing_sim_message_short) : "";
379     }
380 
getAirplaneModeMessage()381     private String getAirplaneModeMessage() {
382         return mShowAirplaneMode
383                 ? getContext().getString(R.string.airplane_mode) : "";
384     }
385 
386     /**
387      * Top-level function for creating carrier text. Makes text based on simState, PLMN
388      * and SPN as well as device capabilities, such as being emergency call capable.
389      *
390      * @return Carrier text if not in missing state, null otherwise.
391      */
getCarrierTextForSimState(IccCardConstants.State simState, CharSequence text)392     private CharSequence getCarrierTextForSimState(IccCardConstants.State simState,
393             CharSequence text) {
394         CharSequence carrierText = null;
395         CarrierTextController.StatusMode status = getStatusForIccState(simState);
396         switch (status) {
397             case Normal:
398                 carrierText = text;
399                 break;
400 
401             case SimNotReady:
402                 // Null is reserved for denoting missing, in this case we have nothing to display.
403                 carrierText = ""; // nothing to display yet.
404                 break;
405 
406             case NetworkLocked:
407                 carrierText = makeCarrierStringOnEmergencyCapable(
408                         mContext.getText(R.string.keyguard_network_locked_message), text);
409                 break;
410 
411             case SimMissing:
412                 carrierText = null;
413                 break;
414 
415             case SimPermDisabled:
416                 carrierText = makeCarrierStringOnEmergencyCapable(
417                         getContext().getText(
418                                 R.string.keyguard_permanent_disabled_sim_message_short),
419                         text);
420                 break;
421 
422             case SimMissingLocked:
423                 carrierText = null;
424                 break;
425 
426             case SimLocked:
427                 carrierText = makeCarrierStringOnLocked(
428                         getContext().getText(R.string.keyguard_sim_locked_message),
429                         text);
430                 break;
431 
432             case SimPukLocked:
433                 carrierText = makeCarrierStringOnLocked(
434                         getContext().getText(R.string.keyguard_sim_puk_locked_message),
435                         text);
436                 break;
437             case SimIoError:
438                 carrierText = makeCarrierStringOnEmergencyCapable(
439                         getContext().getText(R.string.keyguard_sim_error_message_short),
440                         text);
441                 break;
442             case SimUnknown:
443                 carrierText = null;
444                 break;
445         }
446 
447         return carrierText;
448     }
449 
450     /*
451      * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
452      */
makeCarrierStringOnEmergencyCapable( CharSequence simMessage, CharSequence emergencyCallMessage)453     private CharSequence makeCarrierStringOnEmergencyCapable(
454             CharSequence simMessage, CharSequence emergencyCallMessage) {
455         if (mIsEmergencyCallCapable) {
456             return concatenate(simMessage, emergencyCallMessage, mSeparator);
457         }
458         return simMessage;
459     }
460 
461     /*
462      * Add "SIM card is locked" in parenthesis after carrier name, so it is easily associated in
463      * DSDS
464      */
makeCarrierStringOnLocked(CharSequence simMessage, CharSequence carrierName)465     private CharSequence makeCarrierStringOnLocked(CharSequence simMessage,
466             CharSequence carrierName) {
467         final boolean simMessageValid = !TextUtils.isEmpty(simMessage);
468         final boolean carrierNameValid = !TextUtils.isEmpty(carrierName);
469         if (simMessageValid && carrierNameValid) {
470             return mContext.getString(R.string.keyguard_carrier_name_with_sim_locked_template,
471                     carrierName, simMessage);
472         } else if (simMessageValid) {
473             return simMessage;
474         } else if (carrierNameValid) {
475             return carrierName;
476         } else {
477             return "";
478         }
479     }
480 
481     /**
482      * Determine the current status of the lock screen given the SIM state and other stuff.
483      */
getStatusForIccState(IccCardConstants.State simState)484     private CarrierTextController.StatusMode getStatusForIccState(IccCardConstants.State simState) {
485         // Since reading the SIM may take a while, we assume it is present until told otherwise.
486         if (simState == null) {
487             return CarrierTextController.StatusMode.Normal;
488         }
489 
490         final boolean missingAndNotProvisioned =
491                 !KeyguardUpdateMonitor.getInstance(mContext).isDeviceProvisioned()
492                         && (simState == IccCardConstants.State.ABSENT
493                         || simState == IccCardConstants.State.PERM_DISABLED);
494 
495         // Assume we're NETWORK_LOCKED if not provisioned
496         simState = missingAndNotProvisioned ? IccCardConstants.State.NETWORK_LOCKED : simState;
497         switch (simState) {
498             case ABSENT:
499                 return CarrierTextController.StatusMode.SimMissing;
500             case NETWORK_LOCKED:
501                 return CarrierTextController.StatusMode.SimMissingLocked;
502             case NOT_READY:
503                 return CarrierTextController.StatusMode.SimNotReady;
504             case PIN_REQUIRED:
505                 return CarrierTextController.StatusMode.SimLocked;
506             case PUK_REQUIRED:
507                 return CarrierTextController.StatusMode.SimPukLocked;
508             case READY:
509                 return CarrierTextController.StatusMode.Normal;
510             case PERM_DISABLED:
511                 return CarrierTextController.StatusMode.SimPermDisabled;
512             case UNKNOWN:
513                 return CarrierTextController.StatusMode.SimUnknown;
514             case CARD_IO_ERROR:
515                 return CarrierTextController.StatusMode.SimIoError;
516         }
517         return CarrierTextController.StatusMode.SimUnknown;
518     }
519 
concatenate(CharSequence plmn, CharSequence spn, CharSequence separator)520     private static CharSequence concatenate(CharSequence plmn, CharSequence spn,
521             CharSequence separator) {
522         final boolean plmnValid = !TextUtils.isEmpty(plmn);
523         final boolean spnValid = !TextUtils.isEmpty(spn);
524         if (plmnValid && spnValid) {
525             return new StringBuilder().append(plmn).append(separator).append(spn).toString();
526         } else if (plmnValid) {
527             return plmn;
528         } else if (spnValid) {
529             return spn;
530         } else {
531             return "";
532         }
533     }
534 
535     /**
536      * Joins the strings in a sequence using a separator. Empty strings are discarded with no extra
537      * separator added so there are no extra separators that are not needed.
538      */
joinNotEmpty(CharSequence separator, CharSequence[] sequences)539     private static CharSequence joinNotEmpty(CharSequence separator, CharSequence[] sequences) {
540         int length = sequences.length;
541         if (length == 0) return "";
542         StringBuilder sb = new StringBuilder();
543         for (int i = 0; i < length; i++) {
544             if (!TextUtils.isEmpty(sequences[i])) {
545                 if (!TextUtils.isEmpty(sb)) {
546                     sb.append(separator);
547                 }
548                 sb.append(sequences[i]);
549             }
550         }
551         return sb.toString();
552     }
553 
append(List<CharSequence> list, CharSequence string)554     private static List<CharSequence> append(List<CharSequence> list, CharSequence string) {
555         if (!TextUtils.isEmpty(string)) {
556             list.add(string);
557         }
558         return list;
559     }
560 
getCarrierHelpTextForSimState(IccCardConstants.State simState, String plmn, String spn)561     private CharSequence getCarrierHelpTextForSimState(IccCardConstants.State simState,
562             String plmn, String spn) {
563         int carrierHelpTextId = 0;
564         CarrierTextController.StatusMode status = getStatusForIccState(simState);
565         switch (status) {
566             case NetworkLocked:
567                 carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
568                 break;
569 
570             case SimMissing:
571                 carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
572                 break;
573 
574             case SimPermDisabled:
575                 carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
576                 break;
577 
578             case SimMissingLocked:
579                 carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
580                 break;
581 
582             case Normal:
583             case SimLocked:
584             case SimPukLocked:
585                 break;
586         }
587 
588         return mContext.getText(carrierHelpTextId);
589     }
590 
591     /**
592      * Data structure for passing information to CarrierTextController subscribers
593      */
594     public static final class CarrierTextCallbackInfo {
595         public final CharSequence carrierText;
596         public final CharSequence[] listOfCarriers;
597         public final boolean anySimReady;
598         public final int[] subscriptionIds;
599         public boolean airplaneMode;
600 
601         @VisibleForTesting
CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds)602         public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
603                 boolean anySimReady, int[] subscriptionIds) {
604             this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false);
605         }
606 
607         @VisibleForTesting
CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds, boolean airplaneMode)608         public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
609                 boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) {
610             this.carrierText = carrierText;
611             this.listOfCarriers = listOfCarriers;
612             this.anySimReady = anySimReady;
613             this.subscriptionIds = subscriptionIds;
614             this.airplaneMode = airplaneMode;
615         }
616     }
617 
618     /**
619      * Callback to communicate to Views
620      */
621     public interface CarrierTextCallback {
622         /**
623          * Provides updated carrier information.
624          */
updateCarrierInfo(CarrierTextCallbackInfo info)625         default void updateCarrierInfo(CarrierTextCallbackInfo info) {};
626 
627         /**
628          * Notifies the View that the device is going to sleep
629          */
startedGoingToSleep()630         default void startedGoingToSleep() {};
631 
632         /**
633          * Notifies the View that the device finished waking up
634          */
finishedWakingUp()635         default void finishedWakingUp() {};
636     }
637 }
638