1 /*
2  * Copyright 2017 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.internal.telephony.uicc;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.app.AlertDialog;
22 import android.content.ActivityNotFoundException;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.res.Resources;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.os.PowerManager;
31 import android.os.UserHandle;
32 import android.telephony.SubscriptionInfo;
33 import android.telephony.SubscriptionManager;
34 import android.telephony.TelephonyManager;
35 import android.text.TextUtils;
36 import android.util.IndentingPrintWriter;
37 import android.util.Log;
38 import android.view.WindowManager;
39 
40 import com.android.internal.R;
41 import com.android.internal.telephony.CommandsInterface;
42 import com.android.internal.telephony.IccCardConstants;
43 import com.android.internal.telephony.Phone;
44 import com.android.internal.telephony.PhoneFactory;
45 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
46 import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode;
47 import com.android.internal.telephony.uicc.euicc.EuiccCard;
48 import com.android.internal.telephony.util.ArrayUtils;
49 import com.android.internal.telephony.util.TelephonyUtils;
50 import com.android.telephony.Rlog;
51 
52 import java.io.FileDescriptor;
53 import java.io.PrintWriter;
54 import java.lang.annotation.Retention;
55 import java.lang.annotation.RetentionPolicy;
56 import java.util.HashMap;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.stream.Collectors;
60 
61 /**
62  * This class represents a physical slot on the device.
63  */
64 public class UiccSlot extends Handler {
65     private static final String TAG = "UiccSlot";
66     private static final boolean DBG = true;
67 
68     public static final String EXTRA_ICC_CARD_ADDED =
69             "com.android.internal.telephony.uicc.ICC_CARD_ADDED";
70     public static final int INVALID_PHONE_ID = -1;
71 
72     @Retention(RetentionPolicy.SOURCE)
73     @IntDef(
74             prefix = {"VOLTAGE_CLASS_"},
75             value = {VOLTAGE_CLASS_UNKNOWN, VOLTAGE_CLASS_A, VOLTAGE_CLASS_B, VOLTAGE_CLASS_C})
76     public @interface VoltageClass {}
77 
78     public static final int VOLTAGE_CLASS_UNKNOWN = 0;
79     public static final int VOLTAGE_CLASS_A = 1;
80     public static final int VOLTAGE_CLASS_B = 2;
81     public static final int VOLTAGE_CLASS_C = 3;
82 
83     private final Object mLock = new Object();
84     private boolean mActive;
85     private boolean mStateIsUnknown = true;
86     private Context mContext;
87     private UiccCard mUiccCard;
88     private boolean mIsEuicc;
89     private @VoltageClass int mMinimumVoltageClass;
90     private String mEid;
91     private AnswerToReset mAtr;
92     private boolean mIsRemovable;
93     private MultipleEnabledProfilesMode mSupportedMepMode;
94 
95     // Map each available portIdx to phoneId
96     private HashMap<Integer, Integer> mPortIdxToPhoneId = new HashMap<>();
97     //Map each available portIdx with old radio state for state checking
98     private HashMap<Integer, Integer> mLastRadioState = new HashMap<>();
99     // Store iccId of each port.
100     private HashMap<Integer, String> mIccIds = new HashMap<>();
101     // IccCardStatus and IccSlotStatus events order is not guaranteed. Inorder to handle MEP mode,
102     // map each available portIdx with CardState for card state checking
103     private HashMap<Integer, CardState> mCardState = new HashMap<>();
104 
105     private static final int EVENT_CARD_REMOVED = 13;
106     private static final int EVENT_CARD_ADDED = 14;
107 
UiccSlot(Context c, boolean isActive)108     public UiccSlot(Context c, boolean isActive) {
109         if (DBG) log("Creating");
110         mContext = c;
111         mActive = isActive;
112         mSupportedMepMode = MultipleEnabledProfilesMode.NONE;
113     }
114 
115     /**
116      * Update slot. The main trigger for this is a change in the ICC Card status.
117      */
update(CommandsInterface ci, IccCardStatus ics, int phoneId, int slotIndex)118     public void update(CommandsInterface ci, IccCardStatus ics, int phoneId, int slotIndex) {
119         synchronized (mLock) {
120             mPortIdxToPhoneId.put(ics.mSlotPortMapping.mPortIndex, phoneId);
121             CardState oldState = mCardState.get(ics.mSlotPortMapping.mPortIndex);
122             mCardState.put(ics.mSlotPortMapping.mPortIndex, ics.mCardState);
123             mIccIds.put(ics.mSlotPortMapping.mPortIndex, ics.iccid);
124             parseAtr(ics.atr);
125             mIsRemovable = isSlotRemovable(slotIndex);
126             // Update supported MEP mode in IccCardStatus if the CardState is present.
127             if (ics.mCardState.isCardPresent()) {
128                 updateSupportedMepMode(ics.mSupportedMepMode);
129             }
130 
131             int radioState = ci.getRadioState();
132             if (DBG) {
133                 log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState);
134             }
135 
136             if (absentStateUpdateNeeded(oldState, ics.mSlotPortMapping.mPortIndex)) {
137                 updateCardStateAbsent(ci.getRadioState(), phoneId,
138                         ics.mSlotPortMapping.mPortIndex);
139             // Because mUiccCard may be updated in both IccCardStatus and IccSlotStatus, we need to
140             // create a new UiccCard instance in two scenarios:
141             //   1. mCardState is changing from ABSENT to non ABSENT.
142             //   2. The latest mCardState is not ABSENT, but there is no UiccCard instance.
143             } else if ((oldState == null || oldState == CardState.CARDSTATE_ABSENT
144                     || mUiccCard == null) && mCardState.get(ics.mSlotPortMapping.mPortIndex)
145                     != CardState.CARDSTATE_ABSENT) {
146                 // No notification while we are just powering up
147                 if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE
148                         && mLastRadioState.getOrDefault(ics.mSlotPortMapping.mPortIndex,
149                         TelephonyManager.RADIO_POWER_UNAVAILABLE)
150                         != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
151                     if (DBG) log("update: notify card added");
152                     sendMessage(obtainMessage(EVENT_CARD_ADDED, null));
153                 }
154 
155                 // card is present in the slot now; create new mUiccCard
156                 if (mUiccCard != null && (!mIsEuicc
157                         || ArrayUtils.isEmpty(mUiccCard.getUiccPortList()))) {
158                     loge("update: mUiccCard != null when card was present; disposing it now");
159                     mUiccCard.dispose();
160                     mUiccCard = null;
161                 }
162 
163                 if (!mIsEuicc) {
164                     // Uicc does not support MEP, passing false by default.
165                     mUiccCard = new UiccCard(mContext, ci, ics, phoneId, mLock,
166                             MultipleEnabledProfilesMode.NONE);
167                 } else {
168                     // The EID should be reported with the card status, but in case it's not we want
169                     // to catch that here
170                     if (TextUtils.isEmpty(ics.eid)) {
171                         loge("update: eid is missing. ics.eid="
172                                 + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, ics.eid));
173                     }
174                     if (mUiccCard == null) {
175                         mUiccCard = new EuiccCard(mContext, ci, ics, phoneId, mLock,
176                                 getSupportedMepMode());
177                     } else {
178                         // In MEP case, UiccCard instance is already created, just call update API.
179                         // UiccPort initialization is handled inside UiccCard.
180                         mUiccCard.update(mContext, ci, ics, phoneId);
181                     }
182                 }
183             } else {
184                 if (mUiccCard != null) {
185                     mUiccCard.update(mContext, ci, ics, phoneId);
186                 }
187             }
188             mLastRadioState.put(ics.mSlotPortMapping.mPortIndex, radioState);
189         }
190     }
191 
192     /**
193      * Update slot based on IccSlotStatus.
194      */
update(CommandsInterface[] ci, IccSlotStatus iss, int slotIndex)195     public void update(CommandsInterface[] ci, IccSlotStatus iss, int slotIndex) {
196         synchronized (mLock) {
197             IccSimPortInfo[] simPortInfos = iss.mSimPortInfos;
198             parseAtr(iss.atr);
199             mEid = iss.eid;
200             mIsRemovable = isSlotRemovable(slotIndex);
201 
202             for (int i = 0; i < simPortInfos.length; i++) {
203                 int phoneId = iss.mSimPortInfos[i].mLogicalSlotIndex;
204                 CardState oldState = mCardState.get(i);
205                 mCardState.put(i, iss.cardState);
206                 mIccIds.put(i, simPortInfos[i].mIccId);
207                 if (!iss.mSimPortInfos[i].mPortActive) {
208                     // TODO: (b/79432584) evaluate whether should broadcast card state change
209                     // even if it's inactive.
210                     UiccController.getInstance().updateSimStateForInactivePort(
211                             mPortIdxToPhoneId.getOrDefault(i, INVALID_PHONE_ID),
212                             iss.mSimPortInfos[i].mIccId);
213                     mLastRadioState.put(i, TelephonyManager.RADIO_POWER_UNAVAILABLE);
214                     if (mUiccCard != null) {
215                         // Dispose the port
216                         mUiccCard.disposePort(i);
217                     }
218                 } else {
219                     if (absentStateUpdateNeeded(oldState, i)) {
220                         int radioState = SubscriptionManager.isValidPhoneId(phoneId) ?
221                                 ci[phoneId].getRadioState() :
222                                 TelephonyManager.RADIO_POWER_UNAVAILABLE;
223                         updateCardStateAbsent(radioState, phoneId, i);
224                     }
225                     // TODO: (b/79432584) Create UiccCard or EuiccCard object here.
226                     // Right now It's OK not creating it because Card status update will do it.
227                     // But we should really make them symmetric.
228                 }
229             }
230             // From MEP, Card can have multiple ports. So dispose UiccCard only when all the
231             // ports are inactive.
232             if (!hasActivePort(simPortInfos)) {
233                 if (mActive) {
234                     mActive = false;
235                     nullifyUiccCard(true /* sim state is unknown */);
236                 }
237             } else {
238                 mActive = true;
239             }
240             mPortIdxToPhoneId.clear();
241             for (int i = 0; i < simPortInfos.length; i++) {
242                 // If port is not active, update with invalid phone id(i.e. -1)
243                 mPortIdxToPhoneId.put(i, simPortInfos[i].mPortActive ?
244                         simPortInfos[i].mLogicalSlotIndex : INVALID_PHONE_ID);
245             }
246             updateSupportedMepMode(iss.mSupportedMepMode);
247             // Since the MEP capability is related to supported MEP mode, thus need to
248             // update the flag after UiccCard creation.
249             if (mUiccCard != null) {
250                 mUiccCard.updateSupportedMepMode(getSupportedMepMode());
251             }
252         }
253     }
254 
updateSupportedMepMode(MultipleEnabledProfilesMode mode)255     private void updateSupportedMepMode(MultipleEnabledProfilesMode mode) {
256         mSupportedMepMode = mode;
257         // If SupportedMepMode is MultipleEnabledProfilesMode.NONE, validate ATR and
258         // num of ports to handle backward compatibility for < RADIO_HAL_VERSION_2_1.
259         if (mode == MultipleEnabledProfilesMode.NONE) {
260             // Even ATR suggest UICC supports multiple enabled profiles, MEP can be disabled per
261             // carrier restrictions, so checking the real number of ports reported from modem is
262             // necessary.
263             if (mPortIdxToPhoneId.size() > 1
264                     && mAtr != null && mAtr.isMultipleEnabledProfilesSupported()) {
265                 // Set MEP-B mode in case if modem sends wrong mode even though supports MEP.
266                 Log.i(TAG, "Modem does not send proper supported MEP mode or older HAL version");
267                 mSupportedMepMode = MultipleEnabledProfilesMode.MEP_B;
268             }
269         }
270     }
271 
hasActivePort(IccSimPortInfo[] simPortInfos)272     private boolean hasActivePort(IccSimPortInfo[] simPortInfos) {
273         for (IccSimPortInfo simPortInfo : simPortInfos) {
274             if (simPortInfo.mPortActive) {
275                 return true;
276             }
277         }
278         return false;
279     }
280 
281     /* Return valid phoneId if possible from the portIdx mapping*/
getAnyValidPhoneId()282     private int getAnyValidPhoneId() {
283         for (int phoneId : mPortIdxToPhoneId.values()) {
284             if (SubscriptionManager.isValidPhoneId(phoneId)) {
285                 return phoneId;
286             }
287         }
288         return INVALID_PHONE_ID;
289     }
290 
291     @NonNull
getPortList()292     public int[] getPortList() {
293         synchronized (mLock) {
294             return mPortIdxToPhoneId.keySet().stream().mapToInt(Integer::valueOf).toArray();
295         }
296     }
297 
298     /** Return whether the passing portIndex belong to this physical slot */
isValidPortIndex(int portIndex)299     public boolean isValidPortIndex(int portIndex) {
300         return mPortIdxToPhoneId.containsKey(portIndex);
301     }
302 
getPortIndexFromPhoneId(int phoneId)303     public int getPortIndexFromPhoneId(int phoneId) {
304         synchronized (mLock) {
305             for (Map.Entry<Integer, Integer> entry : mPortIdxToPhoneId.entrySet()) {
306                 if (entry.getValue() == phoneId) {
307                     return entry.getKey();
308                 }
309             }
310             return TelephonyManager.DEFAULT_PORT_INDEX;
311         }
312     }
313 
getPortIndexFromIccId(String iccId)314     public int getPortIndexFromIccId(String iccId) {
315         synchronized (mLock) {
316             for (Map.Entry<Integer, String> entry : mIccIds.entrySet()) {
317                 if (IccUtils.compareIgnoreTrailingFs(entry.getValue(), iccId)) {
318                     return entry.getKey();
319                 }
320             }
321             // If iccId is not found, return invalid port index.
322             return TelephonyManager.INVALID_PORT_INDEX;
323         }
324     }
325 
getPhoneIdFromPortIndex(int portIndex)326     public int getPhoneIdFromPortIndex(int portIndex) {
327         synchronized (mLock) {
328             return mPortIdxToPhoneId.getOrDefault(portIndex, INVALID_PHONE_ID);
329         }
330     }
331 
isPortActive(int portIdx)332     public boolean isPortActive(int portIdx) {
333         synchronized (mLock) {
334             return SubscriptionManager.isValidPhoneId(
335                     mPortIdxToPhoneId.getOrDefault(portIdx, INVALID_PHONE_ID));
336         }
337     }
338 
339     /* Returns true if multiple enabled profiles are supported */
isMultipleEnabledProfileSupported()340     public boolean isMultipleEnabledProfileSupported() {
341         synchronized (mLock) {
342             return mSupportedMepMode.isMepMode();
343         }
344     }
345 
absentStateUpdateNeeded(CardState oldState, int portIndex)346     private boolean absentStateUpdateNeeded(CardState oldState, int portIndex) {
347         return (oldState != CardState.CARDSTATE_ABSENT || mUiccCard != null)
348                 && mCardState.get(portIndex) == CardState.CARDSTATE_ABSENT;
349     }
350 
updateCardStateAbsent(int radioState, int phoneId, int portIndex)351     private void updateCardStateAbsent(int radioState, int phoneId, int portIndex) {
352         // No notification while we are just powering up
353         if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE
354                 && mLastRadioState.getOrDefault(
355                         portIndex, TelephonyManager.RADIO_POWER_UNAVAILABLE)
356                 != TelephonyManager.RADIO_POWER_UNAVAILABLE) {
357             if (DBG) log("update: notify card removed");
358             sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
359         }
360 
361         UiccController.getInstance().updateSimState(phoneId, IccCardConstants.State.ABSENT, null);
362         // no card present in the slot now; dispose port and then card if needed.
363         disposeUiccCardIfNeeded(false /* sim state is not unknown */, portIndex);
364         // If SLOT_STATUS is the last event, wrong subscription is getting invalidate during
365         // slot switch event. To avoid it, reset the phoneId corresponding to the portIndex.
366         mPortIdxToPhoneId.put(portIndex, INVALID_PHONE_ID);
367         mLastRadioState.put(portIndex, TelephonyManager.RADIO_POWER_UNAVAILABLE);
368     }
369 
370     // whenever we set mUiccCard to null, we lose the ability to differentiate between absent and
371     // unknown states. To mitigate this, we will us mStateIsUnknown to keep track. The sim is only
372     // unknown if we haven't heard from the radio or if the radio has become unavailable.
nullifyUiccCard(boolean stateUnknown)373     private void nullifyUiccCard(boolean stateUnknown) {
374         if (mUiccCard != null) {
375             mUiccCard.dispose();
376         }
377         mStateIsUnknown = stateUnknown;
378         mUiccCard = null;
379     }
380 
disposeUiccCardIfNeeded(boolean isStateUnknown, int portIndex)381     private void disposeUiccCardIfNeeded(boolean isStateUnknown, int portIndex) {
382         if (mUiccCard != null) {
383             // First dispose UiccPort corresponding to the portIndex
384             mUiccCard.disposePort(portIndex);
385             if (ArrayUtils.isEmpty(mUiccCard.getUiccPortList())) {
386                 // No UiccPort objects are found, safe to dispose the card
387                 nullifyUiccCard(isStateUnknown);
388             }
389         } else {
390             mStateIsUnknown = isStateUnknown;
391         }
392     }
393 
isStateUnknown()394     public boolean isStateUnknown() {
395         // CardState is not specific to any port index, use default port.
396         CardState cardState = mCardState.get(TelephonyManager.DEFAULT_PORT_INDEX);
397         if (cardState == null || cardState == CardState.CARDSTATE_ABSENT) {
398             // mStateIsUnknown is valid only in this scenario.
399             return mStateIsUnknown;
400         }
401         // if mUiccCard is null, assume the state to be UNKNOWN for now.
402         // The state may be known but since the actual card object is not available,
403         // it is safer to return UNKNOWN.
404         return mUiccCard == null;
405     }
406 
407     // Return true if a slot index is for removable UICCs or eUICCs
isSlotRemovable(int slotIndex)408     private boolean isSlotRemovable(int slotIndex) {
409         int[] euiccSlots = mContext.getResources()
410                 .getIntArray(com.android.internal.R.array.non_removable_euicc_slots);
411         if (euiccSlots == null) {
412             return true;
413         }
414         for (int euiccSlot : euiccSlots) {
415             if (euiccSlot == slotIndex) {
416                 return false;
417             }
418         }
419 
420         return true;
421     }
422 
checkIsEuiccSupported()423     private void checkIsEuiccSupported() {
424         if (mAtr == null) {
425             mIsEuicc = false;
426             return;
427         }
428         mIsEuicc = mAtr.isEuiccSupported();
429         log(" checkIsEuiccSupported : " + mIsEuicc);
430     }
431 
checkMinimumVoltageClass()432     private void checkMinimumVoltageClass() {
433         mMinimumVoltageClass = VOLTAGE_CLASS_UNKNOWN;
434         if (mAtr == null) {
435             return;
436         }
437         // Supported voltage classes are stored in the 5 least significant bits of the TA byte for
438         // global interface.
439         List<AnswerToReset.InterfaceByte> interfaceBytes = mAtr.getInterfaceBytes();
440         for (int i = 0; i < interfaceBytes.size() - 1; i++) {
441             if (interfaceBytes.get(i).getTD() != null
442                     && (interfaceBytes.get(i).getTD() & AnswerToReset.T_MASK)
443                             == AnswerToReset.T_VALUE_FOR_GLOBAL_INTERFACE
444                     && interfaceBytes.get(i + 1).getTA() != null) {
445                 byte ta = interfaceBytes.get(i + 1).getTA();
446                 if ((ta & 0x01) != 0) {
447                     mMinimumVoltageClass = VOLTAGE_CLASS_A;
448                 }
449                 if ((ta & 0x02) != 0) {
450                     mMinimumVoltageClass = VOLTAGE_CLASS_B;
451                 }
452                 if ((ta & 0x04) != 0) {
453                     mMinimumVoltageClass = VOLTAGE_CLASS_C;
454                 }
455                 return;
456             }
457         }
458         // Use default value - only class A
459         mMinimumVoltageClass = VOLTAGE_CLASS_A;
460     }
461 
parseAtr(String atr)462     private void parseAtr(String atr) {
463         mAtr = AnswerToReset.parseAtr(atr);
464         checkIsEuiccSupported();
465         checkMinimumVoltageClass();
466     }
467 
isEuicc()468     public boolean isEuicc() {
469         return mIsEuicc;
470     }
471 
472     @VoltageClass
getMinimumVoltageClass()473     public int getMinimumVoltageClass() {
474         return mMinimumVoltageClass;
475     }
476 
isActive()477     public boolean isActive() {
478         return mActive;
479     }
480 
isRemovable()481     public boolean isRemovable() {
482         return mIsRemovable;
483     }
484 
485     /**
486      *  Returns the iccId specific to the port index.
487      *  Always use {@link com.android.internal.telephony.uicc.UiccPort#getIccId} to get the iccId.
488      *  Use this API to get the iccId of the inactive port only.
489      */
getIccId(int portIdx)490     public String getIccId(int portIdx) {
491         return mIccIds.get(portIdx);
492     }
493 
getEid()494     public String getEid() {
495         return mEid;
496     }
497 
isExtendedApduSupported()498     public boolean isExtendedApduSupported() {
499         return  (mAtr != null && mAtr.isExtendedApduSupported());
500     }
501 
502     @Override
finalize()503     protected void finalize() {
504         if (DBG) log("UiccSlot finalized");
505     }
506 
onIccSwap(boolean isAdded)507     private void onIccSwap(boolean isAdded) {
508 
509         boolean isHotSwapSupported = mContext.getResources().getBoolean(
510                 R.bool.config_hotswapCapable);
511 
512         if (isHotSwapSupported) {
513             log("onIccSwap: isHotSwapSupported is true, don't prompt for rebooting");
514             return;
515         }
516         // As this check is for shutdown status check, use any phoneId
517         Phone phone = PhoneFactory.getPhone(getAnyValidPhoneId());
518         if (phone != null && phone.isShuttingDown()) {
519             log("onIccSwap: already doing shutdown, no need to prompt");
520             return;
521         }
522 
523         log("onIccSwap: isHotSwapSupported is false, prompt for rebooting");
524 
525         promptForRestart(isAdded);
526     }
527 
promptForRestart(boolean isAdded)528     private void promptForRestart(boolean isAdded) {
529         synchronized (mLock) {
530             final Resources res = mContext.getResources();
531             final ComponentName dialogComponent = ComponentName.unflattenFromString(
532                     res.getString(R.string.config_iccHotswapPromptForRestartDialogComponent));
533             if (dialogComponent != null) {
534                 Intent intent = new Intent().setComponent(dialogComponent)
535                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
536                         .putExtra(EXTRA_ICC_CARD_ADDED, isAdded);
537                 try {
538                     mContext.startActivityAsUser(intent, UserHandle.CURRENT);
539                     return;
540                 } catch (ActivityNotFoundException e) {
541                     loge("Unable to find ICC hotswap prompt for restart activity: " + e);
542                 }
543             }
544 
545             // TODO: Here we assume the device can't handle SIM hot-swap
546             //      and has to reboot. We may want to add a property,
547             //      e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
548             //      hot-swap.
549             DialogInterface.OnClickListener listener = null;
550 
551 
552             // TODO: SimRecords is not reset while SIM ABSENT (only reset while
553             //       Radio_off_or_not_available). Have to reset in both both
554             //       added or removed situation.
555             listener = new DialogInterface.OnClickListener() {
556                 @Override
557                 public void onClick(DialogInterface dialog, int which) {
558                     synchronized (mLock) {
559                         if (which == DialogInterface.BUTTON_POSITIVE) {
560                             if (DBG) log("Reboot due to SIM swap");
561                             PowerManager pm = (PowerManager) mContext
562                                     .getSystemService(Context.POWER_SERVICE);
563                             pm.reboot("SIM is added.");
564                         }
565                     }
566                 }
567 
568             };
569 
570             Resources r = Resources.getSystem();
571 
572             String title = (isAdded) ? r.getString(R.string.sim_added_title) :
573                     r.getString(R.string.sim_removed_title);
574             String message = (isAdded) ? r.getString(R.string.sim_added_message) :
575                     r.getString(R.string.sim_removed_message);
576             String buttonTxt = r.getString(R.string.sim_restart_button);
577 
578             AlertDialog dialog = new AlertDialog.Builder(mContext)
579                     .setTitle(title)
580                     .setMessage(message)
581                     .setPositiveButton(buttonTxt, listener)
582                     .create();
583             dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
584             dialog.show();
585         }
586     }
587 
588     @Override
handleMessage(Message msg)589     public void handleMessage(Message msg) {
590         switch (msg.what) {
591             case EVENT_CARD_REMOVED:
592                 onIccSwap(false);
593                 break;
594             case EVENT_CARD_ADDED:
595                 onIccSwap(true);
596                 break;
597             default:
598                 loge("Unknown Event " + msg.what);
599         }
600     }
601 
602     /**
603      * Returns the state of the UiccCard in the slot.
604      * @return
605      */
getCardState()606     public CardState getCardState() {
607         synchronized (mLock) {
608             // CardState is not specific to any port index, use default port.
609             CardState cardState = mCardState.get(TelephonyManager.DEFAULT_PORT_INDEX);
610             return cardState == null ? CardState.CARDSTATE_ABSENT : cardState;
611         }
612     }
613 
614     /**
615      * Returns the UiccCard in the slot.
616      */
getUiccCard()617     public UiccCard getUiccCard() {
618         synchronized (mLock) {
619             return mUiccCard;
620         }
621     }
622 
623     /**
624      * Returns the supported MEP mode.
625      */
getSupportedMepMode()626     public MultipleEnabledProfilesMode getSupportedMepMode() {
627         synchronized (mLock) {
628             return mSupportedMepMode;
629         }
630     }
631     /**
632      * Processes radio state unavailable event
633      */
onRadioStateUnavailable(int phoneId)634     public void onRadioStateUnavailable(int phoneId) {
635         int portIndex = getPortIndexFromPhoneId(phoneId);
636         disposeUiccCardIfNeeded(true /* sim state is unknown */, portIndex);
637 
638         if (phoneId != INVALID_PHONE_ID) {
639             UiccController.getInstance().updateSimState(phoneId,
640                     IccCardConstants.State.UNKNOWN, null);
641         }
642         mLastRadioState.put(portIndex, TelephonyManager.RADIO_POWER_UNAVAILABLE);
643         // Reset CardState
644         mCardState.put(portIndex, null);
645     }
646 
log(String msg)647     private void log(String msg) {
648         Rlog.d(TAG, msg);
649     }
650 
loge(String msg)651     private void loge(String msg) {
652         Rlog.e(TAG, msg);
653     }
654 
getPrintableIccIds()655     private Map<Integer, String> getPrintableIccIds() {
656         Map<Integer, String> printableIccIds = mIccIds.entrySet().stream()
657                 .collect(Collectors.toMap(Map.Entry::getKey,
658                         e -> SubscriptionInfo.getPrintableId(e.getValue())));
659         return printableIccIds;
660     }
661 
662     /**
663      * Dump
664      */
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)665     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
666         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
667         pw.println("mActive=" + mActive);
668         pw.println("mIsEuicc=" + mIsEuicc);
669         pw.println("isEuiccSupportsMultipleEnabledProfiles=" + isMultipleEnabledProfileSupported());
670         pw.println("mIsRemovable=" + mIsRemovable);
671         pw.println("mLastRadioState=" + mLastRadioState);
672         pw.println("mIccIds=" + getPrintableIccIds());
673         pw.println("mPortIdxToPhoneId=" + mPortIdxToPhoneId);
674         pw.println("mEid=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid));
675         pw.println("mCardState=" + mCardState);
676         pw.println("mSupportedMepMode=" + mSupportedMepMode);
677         if (mUiccCard != null) {
678             pw.println("mUiccCard=");
679             mUiccCard.dump(fd, pw, args);
680         } else {
681             pw.println("mUiccCard=null");
682         }
683         pw.println();
684         pw.flush();
685     }
686 
687     @NonNull
688     @Override
toString()689     public String toString() {
690         return "[UiccSlot: mActive=" + mActive + ", mIccId=" + getPrintableIccIds() + ", mIsEuicc="
691                 + mIsEuicc + ", MEP=" + isMultipleEnabledProfileSupported() + ", mPortIdxToPhoneId="
692                 + mPortIdxToPhoneId + ", mEid=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid)
693                 + ", mCardState=" + mCardState + " mSupportedMepMode=" + mSupportedMepMode + "]";
694     }
695 }
696