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