• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ACTIVE_DATA_SUB_CHANGED;
20 import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ON_SIM_STATE_CHANGED;
21 import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ON_TELEPHONY_CAPABLE;
22 import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_REFRESH_CARRIER_INFO;
23 
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.pm.PackageManager;
28 import android.content.res.Resources;
29 import android.os.Trace;
30 import android.telephony.ServiceState;
31 import android.telephony.SubscriptionInfo;
32 import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
33 import android.telephony.TelephonyManager;
34 import android.text.TextUtils;
35 import android.util.Log;
36 
37 import androidx.annotation.Nullable;
38 import androidx.annotation.VisibleForTesting;
39 
40 import com.android.keyguard.logging.CarrierTextManagerLogger;
41 import com.android.settingslib.WirelessUtils;
42 import com.android.systemui.R;
43 import com.android.systemui.dagger.qualifiers.Background;
44 import com.android.systemui.dagger.qualifiers.Main;
45 import com.android.systemui.keyguard.WakefulnessLifecycle;
46 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository;
47 import com.android.systemui.telephony.TelephonyListenerManager;
48 
49 import java.util.Arrays;
50 import java.util.List;
51 import java.util.Objects;
52 import java.util.concurrent.Executor;
53 import java.util.concurrent.atomic.AtomicBoolean;
54 
55 import javax.inject.Inject;
56 
57 /**
58  * Controller that generates text including the carrier names and/or the status of all the SIM
59  * interfaces in the device. Through a callback, the updates can be retrieved either as a list or
60  * separated by a given separator {@link CharSequence}.
61  *
62  * @deprecated use {@link com.android.systemui.statusbar.pipeline.wifi} instead
63  */
64 @Deprecated
65 public class CarrierTextManager {
66     private static final boolean DEBUG = KeyguardConstants.DEBUG;
67     private static final String TAG = "CarrierTextController";
68 
69     private final boolean mIsEmergencyCallCapable;
70     private final Executor mMainExecutor;
71     private final Executor mBgExecutor;
72     private boolean mTelephonyCapable;
73     private final boolean mShowMissingSim;
74     private final boolean mShowAirplaneMode;
75     private final AtomicBoolean mNetworkSupported = new AtomicBoolean();
76     @VisibleForTesting
77     protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
78     private final CarrierTextManagerLogger mLogger;
79     private final WifiRepository mWifiRepository;
80     private final boolean[] mSimErrorState;
81     private final int mSimSlotsNumber;
82     @Nullable // Check for nullability before dispatching
83     private CarrierTextCallback mCarrierTextCallback;
84     private final Context mContext;
85     private final TelephonyManager mTelephonyManager;
86     private final CharSequence mSeparator;
87     private final TelephonyListenerManager mTelephonyListenerManager;
88     private final WakefulnessLifecycle mWakefulnessLifecycle;
89     private final WakefulnessLifecycle.Observer mWakefulnessObserver =
90             new WakefulnessLifecycle.Observer() {
91                 @Override
92                 public void onFinishedWakingUp() {
93                     final CarrierTextCallback callback = mCarrierTextCallback;
94                     if (callback != null) callback.finishedWakingUp();
95                 }
96 
97                 @Override
98                 public void onStartedGoingToSleep() {
99                     final CarrierTextCallback callback = mCarrierTextCallback;
100                     if (callback != null) callback.startedGoingToSleep();
101                 }
102             };
103 
104     @VisibleForTesting
105     protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() {
106         @Override
107         public void onRefreshCarrierInfo() {
108             mLogger.logUpdateCarrierTextForReason(REASON_REFRESH_CARRIER_INFO);
109             updateCarrierText();
110         }
111 
112         @Override
113         public void onTelephonyCapable(boolean capable) {
114             mLogger.logUpdateCarrierTextForReason(REASON_ON_TELEPHONY_CAPABLE);
115             mTelephonyCapable = capable;
116             updateCarrierText();
117         }
118 
119         public void onSimStateChanged(int subId, int slotId, int simState) {
120             if (slotId < 0 || slotId >= mSimSlotsNumber) {
121                 Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId
122                         + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable));
123                 return;
124             }
125 
126             mLogger.logUpdateCarrierTextForReason(REASON_ON_SIM_STATE_CHANGED);
127             if (getStatusForIccState(simState) == CarrierTextManager.StatusMode.SimIoError) {
128                 mSimErrorState[slotId] = true;
129                 updateCarrierText();
130             } else if (mSimErrorState[slotId]) {
131                 mSimErrorState[slotId] = false;
132                 updateCarrierText();
133             }
134         }
135     };
136 
137     private final ActiveDataSubscriptionIdListener mPhoneStateListener =
138             new ActiveDataSubscriptionIdListener() {
139         @Override
140         public void onActiveDataSubscriptionIdChanged(int subId) {
141             if (mNetworkSupported.get() && mCarrierTextCallback != null) {
142                 mLogger.logUpdateCarrierTextForReason(REASON_ACTIVE_DATA_SUB_CHANGED);
143                 updateCarrierText();
144             }
145         }
146     };
147 
148     /**
149      * The status of this lock screen. Primarily used for widgets on LockScreen.
150      */
151     @VisibleForTesting
152     protected enum StatusMode {
153         Normal, // Normal case (sim card present, it's not locked)
154         NetworkLocked, // SIM card is 'network locked'.
155         SimMissing, // SIM card is missing.
156         SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access
157         SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times
158         SimLocked, // SIM card is currently locked
159         SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure
160         SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM.
161         SimIoError, // SIM card is faulty
162         SimRestricted, // SIM Card restricted, present but not usable due to carrier restrictions.
163         SimUnknown // SIM card is unknown
164     }
165 
166     /**
167      * Controller that provides updates on text with carriers names or SIM status.
168      * Used by {@link CarrierText}.
169      *
170      * @param separator Separator between different parts of the text
171      */
CarrierTextManager( Context context, CharSequence separator, boolean showAirplaneMode, boolean showMissingSim, WifiRepository wifiRepository, TelephonyManager telephonyManager, TelephonyListenerManager telephonyListenerManager, WakefulnessLifecycle wakefulnessLifecycle, @Main Executor mainExecutor, @Background Executor bgExecutor, KeyguardUpdateMonitor keyguardUpdateMonitor, CarrierTextManagerLogger logger)172     private CarrierTextManager(
173             Context context,
174             CharSequence separator,
175             boolean showAirplaneMode,
176             boolean showMissingSim,
177             WifiRepository wifiRepository,
178             TelephonyManager telephonyManager,
179             TelephonyListenerManager telephonyListenerManager,
180             WakefulnessLifecycle wakefulnessLifecycle,
181             @Main Executor mainExecutor,
182             @Background Executor bgExecutor,
183             KeyguardUpdateMonitor keyguardUpdateMonitor,
184             CarrierTextManagerLogger logger) {
185 
186         mContext = context;
187         mIsEmergencyCallCapable = telephonyManager.isVoiceCapable();
188 
189         mShowAirplaneMode = showAirplaneMode;
190         mShowMissingSim = showMissingSim;
191         mWifiRepository = wifiRepository;
192         mTelephonyManager = telephonyManager;
193         mSeparator = separator;
194         mTelephonyListenerManager = telephonyListenerManager;
195         mWakefulnessLifecycle = wakefulnessLifecycle;
196         mSimSlotsNumber = getTelephonyManager().getSupportedModemCount();
197         mSimErrorState = new boolean[mSimSlotsNumber];
198         mMainExecutor = mainExecutor;
199         mBgExecutor = bgExecutor;
200         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
201         mLogger = logger;
202         mBgExecutor.execute(() -> {
203             boolean supported = mContext.getPackageManager()
204                     .hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
205             if (supported && mNetworkSupported.compareAndSet(false, supported)) {
206                 // This will set/remove the listeners appropriately. Note that it will never double
207                 // add the listeners.
208                 handleSetListening(mCarrierTextCallback);
209             }
210         });
211     }
212 
getTelephonyManager()213     private TelephonyManager getTelephonyManager() {
214         return mTelephonyManager;
215     }
216 
217     /**
218      * Checks if there are faulty cards. Adds the text depending on the slot of the card
219      *
220      * @param text:   current carrier text based on the sim state
221      * @param carrierNames names order by subscription order
222      * @param subOrderBySlot array containing the sub index for each slot ID
223      * @param noSims: whether a valid sim card is inserted
224      * @return text
225      */
updateCarrierTextWithSimIoError(CharSequence text, CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims)226     private CharSequence updateCarrierTextWithSimIoError(CharSequence text,
227             CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) {
228         final CharSequence carrier = "";
229         CharSequence carrierTextForSimIOError = getCarrierTextForSimState(
230                 TelephonyManager.SIM_STATE_CARD_IO_ERROR, carrier);
231         // mSimErrorState has the state of each sim indexed by slotID.
232         for (int index = 0; index < getTelephonyManager().getActiveModemCount(); index++) {
233             if (!mSimErrorState[index]) {
234                 continue;
235             }
236             // In the case when no sim cards are detected but a faulty card is inserted
237             // overwrite the text and only show "Invalid card"
238             if (noSims) {
239                 return concatenate(carrierTextForSimIOError,
240                         getContext().getText(
241                                 com.android.internal.R.string.emergency_calls_only),
242                         mSeparator);
243             } else if (subOrderBySlot[index] != -1) {
244                 int subIndex = subOrderBySlot[index];
245                 // prepend "Invalid card" when faulty card is inserted in slot 0 or 1
246                 carrierNames[subIndex] = concatenate(carrierTextForSimIOError,
247                         carrierNames[subIndex],
248                         mSeparator);
249             } else {
250                 // concatenate "Invalid card" when faulty card is inserted in other slot
251                 text = concatenate(text, carrierTextForSimIOError, mSeparator);
252             }
253 
254         }
255         return text;
256     }
257 
258     /**
259      * This may be called internally after retrieving the correct value of {@code mNetworkSupported}
260      * (assumed false to start). In that case, the following happens:
261      * <ul>
262      *     <li> If there was a registered callback, and the network is supported, it will register
263      *          listeners.
264      *     <li> If there was not a registered callback, it will try to remove unregistered listeners
265      *          which is a no-op
266      * </ul>
267      *
268      * This call will always be processed in a background thread.
269      */
handleSetListening(CarrierTextCallback callback)270     private void handleSetListening(CarrierTextCallback callback) {
271         if (callback != null) {
272             mCarrierTextCallback = callback;
273             if (mNetworkSupported.get()) {
274                 // Keyguard update monitor expects callbacks from main thread
275                 mMainExecutor.execute(() -> {
276                     mKeyguardUpdateMonitor.registerCallback(mCallback);
277                     mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
278                 });
279                 mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener);
280             } else {
281                 // Don't listen and clear out the text when the device isn't a phone.
282                 mMainExecutor.execute(() -> callback.updateCarrierInfo(
283                         new CarrierTextCallbackInfo("", null, false, null)
284                 ));
285             }
286         } else {
287             mCarrierTextCallback = null;
288             mMainExecutor.execute(() -> {
289                 mKeyguardUpdateMonitor.removeCallback(mCallback);
290                 mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
291             });
292             mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener);
293         }
294     }
295 
296     /**
297      * Sets the listening status of this controller. If the callback is null, it is set to
298      * not listening.
299      *
300      * @param callback Callback to provide text updates
301      */
setListening(CarrierTextCallback callback)302     public void setListening(CarrierTextCallback callback) {
303         mBgExecutor.execute(() -> handleSetListening(callback));
304     }
305 
getSubscriptionInfo()306     protected List<SubscriptionInfo> getSubscriptionInfo() {
307         return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo();
308     }
309 
updateCarrierText()310     protected void updateCarrierText() {
311         Trace.beginSection("CarrierTextManager#updateCarrierText");
312         boolean allSimsMissing = true;
313         boolean anySimReadyAndInService = false;
314         CharSequence displayText = null;
315         List<SubscriptionInfo> subs = getSubscriptionInfo();
316 
317         final int numSubs = subs.size();
318         final int[] subsIds = new int[numSubs];
319         // This array will contain in position i, the index of subscription in slot ID i.
320         // -1 if no subscription in that slot
321         final int[] subOrderBySlot = new int[mSimSlotsNumber];
322         for (int i = 0; i < mSimSlotsNumber; i++) {
323             subOrderBySlot[i] = -1;
324         }
325         final CharSequence[] carrierNames = new CharSequence[numSubs];
326         mLogger.logUpdate(numSubs);
327 
328         for (int i = 0; i < numSubs; i++) {
329             int subId = subs.get(i).getSubscriptionId();
330             carrierNames[i] = "";
331             subsIds[i] = subId;
332             subOrderBySlot[subs.get(i).getSimSlotIndex()] = i;
333             int simState = mKeyguardUpdateMonitor.getSimState(subId);
334             CharSequence carrierName = subs.get(i).getCarrierName();
335             CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName);
336             mLogger.logUpdateLoopStart(subId, simState, String.valueOf(carrierName));
337             if (carrierTextForSimState != null) {
338                 allSimsMissing = false;
339                 carrierNames[i] = carrierTextForSimState;
340             }
341             if (simState == TelephonyManager.SIM_STATE_READY) {
342                 Trace.beginSection("WFC check");
343                 ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId);
344                 if (ss != null && ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE) {
345                     // hack for WFC (IWLAN) not turning off immediately once
346                     // Wi-Fi is disassociated or disabled
347                     if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
348                             || mWifiRepository.isWifiConnectedWithValidSsid()) {
349                         mLogger.logUpdateWfcCheck();
350                         anySimReadyAndInService = true;
351                     }
352                 }
353                 Trace.endSection();
354             }
355         }
356         // Only create "No SIM card" if no cards with CarrierName && no wifi when some sim is READY
357         // This condition will also be true always when numSubs == 0
358         if (allSimsMissing && !anySimReadyAndInService) {
359             if (numSubs != 0) {
360                 // Shows "No SIM card | Emergency calls only" on devices that are voice-capable.
361                 // This depends on mPlmn containing the text "Emergency calls only" when the radio
362                 // has some connectivity. Otherwise, it should be null or empty and just show
363                 // "No SIM card"
364                 // Grab the first subscripton, because they all should contain the emergency text,
365                 // described above.
366                 displayText = makeCarrierStringOnEmergencyCapable(
367                         getMissingSimMessage(), subs.get(0).getCarrierName());
368             } else {
369                 // We don't have a SubscriptionInfo to get the emergency calls only from.
370                 // Grab it from the old sticky broadcast if possible instead. We can use it
371                 // here because no subscriptions are active, so we don't have
372                 // to worry about MSIM clashing.
373                 CharSequence text =
374                         getContext().getText(com.android.internal.R.string.emergency_calls_only);
375                 Intent i = getContext().registerReceiver(null,
376                         new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED));
377                 if (i != null) {
378                     String spn = "";
379                     String plmn = "";
380                     if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)) {
381                         spn = i.getStringExtra(TelephonyManager.EXTRA_SPN);
382                     }
383                     if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) {
384                         plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN);
385                     }
386                     mLogger.logUpdateFromStickyBroadcast(plmn, spn);
387                     if (Objects.equals(plmn, spn)) {
388                         text = plmn;
389                     } else {
390                         text = concatenate(plmn, spn, mSeparator);
391                     }
392                 }
393                 displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text);
394             }
395         }
396 
397         if (TextUtils.isEmpty(displayText)) displayText = joinNotEmpty(mSeparator, carrierNames);
398 
399         displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot,
400                 allSimsMissing);
401 
402         boolean airplaneMode = false;
403         // APM (airplane mode) != no carrier state. There are carrier services
404         // (e.g. WFC = Wi-Fi calling) which may operate in APM.
405         if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) {
406             displayText = getAirplaneModeMessage();
407             airplaneMode = true;
408         }
409 
410         final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
411                 displayText,
412                 carrierNames,
413                 !allSimsMissing,
414                 subsIds,
415                 airplaneMode);
416         mLogger.logCallbackSentFromUpdate(info);
417         postToCallback(info);
418         Trace.endSection();
419     }
420 
421     @VisibleForTesting
postToCallback(CarrierTextCallbackInfo info)422     protected void postToCallback(CarrierTextCallbackInfo info) {
423         final CarrierTextCallback callback = mCarrierTextCallback;
424         if (callback != null) {
425             mMainExecutor.execute(() -> callback.updateCarrierInfo(info));
426         }
427     }
428 
getContext()429     private Context getContext() {
430         return mContext;
431     }
432 
getMissingSimMessage()433     private String getMissingSimMessage() {
434         return mShowMissingSim && mTelephonyCapable
435                 ? getContext().getString(R.string.keyguard_missing_sim_message_short) : "";
436     }
437 
getAirplaneModeMessage()438     private String getAirplaneModeMessage() {
439         return mShowAirplaneMode
440                 ? getContext().getString(R.string.airplane_mode) : "";
441     }
442 
443     /**
444      * Top-level function for creating carrier text. Makes text based on simState, PLMN
445      * and SPN as well as device capabilities, such as being emergency call capable.
446      *
447      * @return Carrier text if not in missing state, null otherwise.
448      */
getCarrierTextForSimState(int simState, CharSequence text)449     private CharSequence getCarrierTextForSimState(int simState, CharSequence text) {
450         CharSequence carrierText = null;
451         CarrierTextManager.StatusMode status = getStatusForIccState(simState);
452         switch (status) {
453             case Normal:
454                 carrierText = text;
455                 break;
456 
457             case SimNotReady:
458                 // Null is reserved for denoting missing, in this case we have nothing to display.
459                 carrierText = ""; // nothing to display yet.
460                 break;
461 
462             case NetworkLocked:
463                 carrierText = makeCarrierStringOnEmergencyCapable(
464                         mContext.getText(R.string.keyguard_network_locked_message), text);
465                 break;
466 
467             case SimMissing:
468                 carrierText = null;
469                 break;
470 
471             case SimPermDisabled:
472                 carrierText = makeCarrierStringOnEmergencyCapable(
473                         getContext().getText(
474                                 R.string.keyguard_permanent_disabled_sim_message_short),
475                         text);
476                 break;
477 
478             case SimMissingLocked:
479                 carrierText = null;
480                 break;
481 
482             case SimLocked:
483                 carrierText = makeCarrierStringOnLocked(
484                         getContext().getText(R.string.keyguard_sim_locked_message),
485                         text);
486                 break;
487 
488             case SimPukLocked:
489                 carrierText = makeCarrierStringOnLocked(
490                         getContext().getText(R.string.keyguard_sim_puk_locked_message),
491                         text);
492                 break;
493             case SimIoError:
494                 carrierText = makeCarrierStringOnEmergencyCapable(
495                         getContext().getText(R.string.keyguard_sim_error_message_short),
496                         text);
497                 break;
498             case SimRestricted: // fall through
499             case SimUnknown:
500                 carrierText = null;
501                 break;
502         }
503 
504         return carrierText;
505     }
506 
507     /*
508      * Add emergencyCallMessage to carrier string only if phone supports emergency calls.
509      */
makeCarrierStringOnEmergencyCapable( CharSequence simMessage, CharSequence emergencyCallMessage)510     private CharSequence makeCarrierStringOnEmergencyCapable(
511             CharSequence simMessage, CharSequence emergencyCallMessage) {
512         if (mIsEmergencyCallCapable) {
513             return concatenate(simMessage, emergencyCallMessage, mSeparator);
514         }
515         return simMessage;
516     }
517 
518     /*
519      * Add "SIM card is locked" in parenthesis after carrier name, so it is easily associated in
520      * DSDS
521      */
makeCarrierStringOnLocked(CharSequence simMessage, CharSequence carrierName)522     private CharSequence makeCarrierStringOnLocked(CharSequence simMessage,
523             CharSequence carrierName) {
524         final boolean simMessageValid = !TextUtils.isEmpty(simMessage);
525         final boolean carrierNameValid = !TextUtils.isEmpty(carrierName);
526         if (simMessageValid && carrierNameValid) {
527             return mContext.getString(R.string.keyguard_carrier_name_with_sim_locked_template,
528                     carrierName, simMessage);
529         } else if (simMessageValid) {
530             return simMessage;
531         } else if (carrierNameValid) {
532             return carrierName;
533         } else {
534             return "";
535         }
536     }
537 
538     /**
539      * Determine the current status of the lock screen given the SIM state and other stuff.
540      */
541     @VisibleForTesting
getStatusForIccState(int simState)542     protected CarrierTextManager.StatusMode getStatusForIccState(int simState) {
543         if (!mKeyguardUpdateMonitor.isDeviceProvisioned()
544                 && (simState == TelephonyManager.SIM_STATE_ABSENT
545                         || simState == TelephonyManager.SIM_STATE_PERM_DISABLED)) {
546             return CarrierTextManager.StatusMode.SimMissingLocked;
547         }
548 
549         switch (simState) {
550             case TelephonyManager.SIM_STATE_ABSENT:
551                 return CarrierTextManager.StatusMode.SimMissing;
552             case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
553                 return CarrierTextManager.StatusMode.NetworkLocked;
554             case TelephonyManager.SIM_STATE_NOT_READY:
555                 return CarrierTextManager.StatusMode.SimNotReady;
556             case TelephonyManager.SIM_STATE_PIN_REQUIRED:
557                 return CarrierTextManager.StatusMode.SimLocked;
558             case TelephonyManager.SIM_STATE_PUK_REQUIRED:
559                 return CarrierTextManager.StatusMode.SimPukLocked;
560             case TelephonyManager.SIM_STATE_READY:
561                 return CarrierTextManager.StatusMode.Normal;
562             case TelephonyManager.SIM_STATE_PERM_DISABLED:
563                 return CarrierTextManager.StatusMode.SimPermDisabled;
564             case TelephonyManager.SIM_STATE_UNKNOWN:
565                 return CarrierTextManager.StatusMode.SimUnknown;
566             case TelephonyManager.SIM_STATE_CARD_IO_ERROR:
567                 return CarrierTextManager.StatusMode.SimIoError;
568             case TelephonyManager.SIM_STATE_CARD_RESTRICTED:
569                 return CarrierTextManager.StatusMode.SimRestricted;
570         }
571         return CarrierTextManager.StatusMode.SimUnknown;
572     }
573 
concatenate(CharSequence plmn, CharSequence spn, CharSequence separator)574     private static CharSequence concatenate(CharSequence plmn, CharSequence spn,
575             CharSequence separator) {
576         final boolean plmnValid = !TextUtils.isEmpty(plmn);
577         final boolean spnValid = !TextUtils.isEmpty(spn);
578         if (plmnValid && spnValid) {
579             return new StringBuilder().append(plmn).append(separator).append(spn).toString();
580         } else if (plmnValid) {
581             return plmn;
582         } else if (spnValid) {
583             return spn;
584         } else {
585             return "";
586         }
587     }
588 
589     /**
590      * Joins the strings in a sequence using a separator. Empty strings are discarded with no extra
591      * separator added so there are no extra separators that are not needed.
592      */
joinNotEmpty(CharSequence separator, CharSequence[] sequences)593     private static CharSequence joinNotEmpty(CharSequence separator, CharSequence[] sequences) {
594         int length = sequences.length;
595         if (length == 0) return "";
596         StringBuilder sb = new StringBuilder();
597         for (int i = 0; i < length; i++) {
598             if (!TextUtils.isEmpty(sequences[i])) {
599                 if (!TextUtils.isEmpty(sb)) {
600                     sb.append(separator);
601                 }
602                 sb.append(sequences[i]);
603             }
604         }
605         return sb.toString();
606     }
607 
append(List<CharSequence> list, CharSequence string)608     private static List<CharSequence> append(List<CharSequence> list, CharSequence string) {
609         if (!TextUtils.isEmpty(string)) {
610             list.add(string);
611         }
612         return list;
613     }
614 
getCarrierHelpTextForSimState(int simState, String plmn, String spn)615     private CharSequence getCarrierHelpTextForSimState(int simState,
616             String plmn, String spn) {
617         int carrierHelpTextId = 0;
618         CarrierTextManager.StatusMode status = getStatusForIccState(simState);
619         switch (status) {
620             case NetworkLocked:
621                 carrierHelpTextId = R.string.keyguard_instructions_when_pattern_disabled;
622                 break;
623 
624             case SimMissing:
625                 carrierHelpTextId = R.string.keyguard_missing_sim_instructions_long;
626                 break;
627 
628             case SimPermDisabled:
629                 carrierHelpTextId = R.string.keyguard_permanent_disabled_sim_instructions;
630                 break;
631 
632             case SimMissingLocked:
633                 carrierHelpTextId = R.string.keyguard_missing_sim_instructions;
634                 break;
635 
636             case Normal:
637             case SimLocked:
638             case SimPukLocked:
639                 break;
640         }
641 
642         return mContext.getText(carrierHelpTextId);
643     }
644 
645     /** Injectable Buildeer for {@#link CarrierTextManager}. */
646     public static class Builder {
647         private final Context mContext;
648         private final String mSeparator;
649         private final WifiRepository mWifiRepository;
650         private final TelephonyManager mTelephonyManager;
651         private final TelephonyListenerManager mTelephonyListenerManager;
652         private final WakefulnessLifecycle mWakefulnessLifecycle;
653         private final Executor mMainExecutor;
654         private final Executor mBgExecutor;
655         private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
656         private final CarrierTextManagerLogger mLogger;
657         private boolean mShowAirplaneMode;
658         private boolean mShowMissingSim;
659         private String mDebugLocation;
660 
661         @Inject
Builder( Context context, @Main Resources resources, @Nullable WifiRepository wifiRepository, TelephonyManager telephonyManager, TelephonyListenerManager telephonyListenerManager, WakefulnessLifecycle wakefulnessLifecycle, @Main Executor mainExecutor, @Background Executor bgExecutor, KeyguardUpdateMonitor keyguardUpdateMonitor, CarrierTextManagerLogger logger)662         public Builder(
663                 Context context,
664                 @Main Resources resources,
665                 @Nullable WifiRepository wifiRepository,
666                 TelephonyManager telephonyManager,
667                 TelephonyListenerManager telephonyListenerManager,
668                 WakefulnessLifecycle wakefulnessLifecycle,
669                 @Main Executor mainExecutor,
670                 @Background Executor bgExecutor,
671                 KeyguardUpdateMonitor keyguardUpdateMonitor,
672                 CarrierTextManagerLogger logger) {
673             mContext = context;
674             mSeparator = resources.getString(
675                     com.android.internal.R.string.kg_text_message_separator);
676             mWifiRepository = wifiRepository;
677             mTelephonyManager = telephonyManager;
678             mTelephonyListenerManager = telephonyListenerManager;
679             mWakefulnessLifecycle = wakefulnessLifecycle;
680             mMainExecutor = mainExecutor;
681             mBgExecutor = bgExecutor;
682             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
683             mLogger = logger;
684         }
685 
686         /** */
setShowAirplaneMode(boolean showAirplaneMode)687         public Builder setShowAirplaneMode(boolean showAirplaneMode) {
688             mShowAirplaneMode = showAirplaneMode;
689             return this;
690         }
691 
692         /** */
setShowMissingSim(boolean showMissingSim)693         public Builder setShowMissingSim(boolean showMissingSim) {
694             mShowMissingSim = showMissingSim;
695             return this;
696         }
697 
698         /**
699          * To help disambiguate logs, set a location to be used in the LogBuffer calls, e.g.:
700          * "keyguard" or "keyguard emergency status bar"
701          */
setDebugLocationString(String debugLocationString)702         public Builder setDebugLocationString(String debugLocationString) {
703             mDebugLocation = debugLocationString;
704             return this;
705         }
706 
707         /** Create a CarrierTextManager. */
build()708         public CarrierTextManager build() {
709             mLogger.setLocation(mDebugLocation);
710             return new CarrierTextManager(
711                     mContext, mSeparator, mShowAirplaneMode, mShowMissingSim, mWifiRepository,
712                     mTelephonyManager, mTelephonyListenerManager, mWakefulnessLifecycle,
713                     mMainExecutor, mBgExecutor, mKeyguardUpdateMonitor, mLogger);
714         }
715     }
716 
717     /**
718      * Data structure for passing information to CarrierTextController subscribers
719      */
720     public static final class CarrierTextCallbackInfo {
721         public final CharSequence carrierText;
722         public final CharSequence[] listOfCarriers;
723         public final boolean anySimReady;
724         public final int[] subscriptionIds;
725         public boolean airplaneMode;
726 
727         @VisibleForTesting
CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds)728         public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
729                 boolean anySimReady, int[] subscriptionIds) {
730             this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false);
731         }
732 
733         @VisibleForTesting
CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds, boolean airplaneMode)734         public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
735                 boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) {
736             this.carrierText = carrierText;
737             this.listOfCarriers = listOfCarriers;
738             this.anySimReady = anySimReady;
739             this.subscriptionIds = subscriptionIds;
740             this.airplaneMode = airplaneMode;
741         }
742 
743         @Override
toString()744         public String toString() {
745             return "CarrierTextCallbackInfo{"
746                     + "carrierText=" + carrierText
747                     + ", listOfCarriers=" + Arrays.toString(listOfCarriers)
748                     + ", anySimReady=" + anySimReady
749                     + ", subscriptionIds=" + Arrays.toString(subscriptionIds)
750                     + ", airplaneMode=" + airplaneMode
751                     + '}';
752         }
753     }
754 
755     /**
756      * Callback to communicate to Views
757      */
758     public interface CarrierTextCallback {
759         /**
760          * Provides updated carrier information.
761          */
updateCarrierInfo(CarrierTextCallbackInfo info)762         default void updateCarrierInfo(CarrierTextCallbackInfo info) {};
763 
764         /**
765          * Notifies the View that the device is going to sleep
766          */
startedGoingToSleep()767         default void startedGoingToSleep() {};
768 
769         /**
770          * Notifies the View that the device finished waking up
771          */
finishedWakingUp()772         default void finishedWakingUp() {};
773     }
774 }
775