1 /*
2  * Copyright (C) 2011-2012 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 static android.telephony.TelephonyManager.UNINITIALIZED_CARD_ID;
20 import static android.telephony.TelephonyManager.UNSUPPORTED_CARD_ID;
21 
22 import android.annotation.UnsupportedAppUsage;
23 import android.app.BroadcastOptions;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.SharedPreferences;
27 import android.content.pm.PackageManager;
28 import android.os.AsyncResult;
29 import android.os.Handler;
30 import android.os.Message;
31 import android.os.Registrant;
32 import android.os.RegistrantList;
33 import android.os.storage.StorageManager;
34 import android.preference.PreferenceManager;
35 import android.telephony.CarrierConfigManager;
36 import android.telephony.Rlog;
37 import android.telephony.TelephonyManager;
38 import android.telephony.UiccCardInfo;
39 import android.text.TextUtils;
40 import android.util.LocalLog;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.telephony.CommandException;
44 import com.android.internal.telephony.CommandsInterface;
45 import com.android.internal.telephony.IccCardConstants;
46 import com.android.internal.telephony.PhoneConstants;
47 import com.android.internal.telephony.PhoneFactory;
48 import com.android.internal.telephony.RadioConfig;
49 import com.android.internal.telephony.SubscriptionInfoUpdater;
50 import com.android.internal.telephony.uicc.euicc.EuiccCard;
51 
52 import java.io.FileDescriptor;
53 import java.io.PrintWriter;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.HashSet;
57 import java.util.Set;
58 
59 /**
60  * This class is responsible for keeping all knowledge about
61  * Universal Integrated Circuit Card (UICC), also know as SIM's,
62  * in the system. It is also used as API to get appropriate
63  * applications to pass them to phone and service trackers.
64  *
65  * UiccController is created with the call to make() function.
66  * UiccController is a singleton and make() must only be called once
67  * and throws an exception if called multiple times.
68  *
69  * Once created UiccController registers with RIL for "on" and "unsol_sim_status_changed"
70  * notifications. When such notification arrives UiccController will call
71  * getIccCardStatus (GET_SIM_STATUS). Based on the response of GET_SIM_STATUS
72  * request appropriate tree of uicc objects will be created.
73  *
74  * Following is class diagram for uicc classes:
75  *
76  *                       UiccController
77  *                            #
78  *                            |
79  *                        UiccSlot[]
80  *                            #
81  *                            |
82  *                        UiccCard
83  *                            #
84  *                            |
85  *                       UiccProfile
86  *                          #   #
87  *                          |   ------------------
88  *                    UiccCardApplication    CatService
89  *                      #            #
90  *                      |            |
91  *                 IccRecords    IccFileHandler
92  *                 ^ ^ ^           ^ ^ ^ ^ ^
93  *    SIMRecords---- | |           | | | | ---SIMFileHandler
94  *    RuimRecords----- |           | | | ----RuimFileHandler
95  *    IsimUiccRecords---           | | -----UsimFileHandler
96  *                                 | ------CsimFileHandler
97  *                                 ----IsimFileHandler
98  *
99  * Legend: # stands for Composition
100  *         ^ stands for Generalization
101  *
102  * See also {@link com.android.internal.telephony.IccCard}
103  */
104 public class UiccController extends Handler {
105     private static final boolean DBG = true;
106     private static final boolean VDBG = false; //STOPSHIP if true
107     private static final String LOG_TAG = "UiccController";
108 
109     public static final int INVALID_SLOT_ID = -1;
110 
111     public static final int APP_FAM_3GPP =  1;
112     public static final int APP_FAM_3GPP2 = 2;
113     public static final int APP_FAM_IMS   = 3;
114 
115     private static final int EVENT_ICC_STATUS_CHANGED = 1;
116     private static final int EVENT_SLOT_STATUS_CHANGED = 2;
117     private static final int EVENT_GET_ICC_STATUS_DONE = 3;
118     private static final int EVENT_GET_SLOT_STATUS_DONE = 4;
119     private static final int EVENT_RADIO_ON = 5;
120     private static final int EVENT_RADIO_AVAILABLE = 6;
121     private static final int EVENT_RADIO_UNAVAILABLE = 7;
122     private static final int EVENT_SIM_REFRESH = 8;
123     private static final int EVENT_EID_READY = 9;
124 
125     // this needs to be here, because on bootup we dont know which index maps to which UiccSlot
126     @UnsupportedAppUsage
127     private CommandsInterface[] mCis;
128     @VisibleForTesting
129     public UiccSlot[] mUiccSlots;
130     private int[] mPhoneIdToSlotId;
131     private boolean mIsSlotStatusSupported = true;
132 
133     // This maps the externally exposed card ID (int) to the internal card ID string (ICCID/EID).
134     // The array index is the card ID (int).
135     // This mapping exists to expose card-based functionality without exposing the EID, which is
136     // considered sensetive information.
137     // mCardStrings is populated using values from the IccSlotStatus and IccCardStatus. For
138     // HAL < 1.2, these do not contain the EID or the ICCID, so mCardStrings will be empty
139     private ArrayList<String> mCardStrings;
140 
141     // This is the card ID of the default eUICC. It starts as UNINITIALIZED_CARD_ID.
142     // When we load the EID (either with slot status or from the EuiccCard), we set it to the eUICC
143     // with the lowest slot index.
144     // If EID is not supported (e.g. on HAL version < 1.2), we set it to UNSUPPORTED_CARD_ID
145     private int mDefaultEuiccCardId;
146 
147     // Default Euicc Card ID used when the device is temporarily unable to read the EID (e.g. on HAL
148     // 1.2-1.3 if the eUICC is currently inactive). This value is only used within the
149     // UiccController and should be converted to UNSUPPORTED_CARD_ID when others ask.
150     // (This value is -3 because UNSUPPORTED_CARD_ID and UNINITIALIZED_CARD_ID are -1 and -2)
151     private static final int TEMPORARILY_UNSUPPORTED_CARD_ID = -3;
152 
153     // GSM SGP.02 section 2.2.2 states that the EID is always 32 digits long
154     private static final int EID_LENGTH = 32;
155 
156     // SharedPreference key for saving the known card strings (ICCIDs and EIDs) ordered by card ID
157     private static final String CARD_STRINGS = "card_strings";
158 
159     // SharedPreferences key for saving the default euicc card ID
160     private static final String DEFAULT_CARD = "default_card";
161 
162     @UnsupportedAppUsage
163     private static final Object mLock = new Object();
164     @UnsupportedAppUsage
165     private static UiccController mInstance;
166     private static ArrayList<IccSlotStatus> sLastSlotStatus;
167 
168     @UnsupportedAppUsage
169     @VisibleForTesting
170     public Context mContext;
171 
172     protected RegistrantList mIccChangedRegistrants = new RegistrantList();
173 
174     private UiccStateChangedLauncher mLauncher;
175     private RadioConfig mRadioConfig;
176 
177     // LocalLog buffer to hold important SIM related events for debugging
178     static LocalLog sLocalLog = new LocalLog(100);
179 
make(Context c, CommandsInterface[] ci)180     public static UiccController make(Context c, CommandsInterface[] ci) {
181         synchronized (mLock) {
182             if (mInstance != null) {
183                 throw new RuntimeException("UiccController.make() should only be called once");
184             }
185             mInstance = new UiccController(c, ci);
186             return mInstance;
187         }
188     }
189 
UiccController(Context c, CommandsInterface []ci)190     private UiccController(Context c, CommandsInterface []ci) {
191         if (DBG) log("Creating UiccController");
192         mContext = c;
193         mCis = ci;
194         if (DBG) {
195             String logStr = "config_num_physical_slots = " + c.getResources().getInteger(
196                     com.android.internal.R.integer.config_num_physical_slots);
197             log(logStr);
198             sLocalLog.log(logStr);
199         }
200         int numPhysicalSlots = c.getResources().getInteger(
201                 com.android.internal.R.integer.config_num_physical_slots);
202         // Minimum number of physical slot count should be equals to or greater than phone count,
203         // if it is less than phone count use phone count as physical slot count.
204         if (numPhysicalSlots < mCis.length) {
205             numPhysicalSlots = mCis.length;
206         }
207 
208         mUiccSlots = new UiccSlot[numPhysicalSlots];
209         mPhoneIdToSlotId = new int[ci.length];
210         Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);
211         if (VDBG) logPhoneIdToSlotIdMapping();
212         mRadioConfig = RadioConfig.getInstance(mContext);
213         mRadioConfig.registerForSimSlotStatusChanged(this, EVENT_SLOT_STATUS_CHANGED, null);
214         for (int i = 0; i < mCis.length; i++) {
215             mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, i);
216 
217             // TODO remove this once modem correctly notifies the unsols
218             // If the device is unencrypted or has been decrypted or FBE is supported,
219             // i.e. not in CryptKeeper bounce, read SIM when radio state is available.
220             // Else wait for radio to be on. This is needed for the scenario when SIM is locked --
221             // to avoid overlap of CryptKeeper and SIM unlock screen.
222             if (!StorageManager.inCryptKeeperBounce()) {
223                 mCis[i].registerForAvailable(this, EVENT_RADIO_AVAILABLE, i);
224             } else {
225                 mCis[i].registerForOn(this, EVENT_RADIO_ON, i);
226             }
227             mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, i);
228             mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, i);
229         }
230 
231         mLauncher = new UiccStateChangedLauncher(c, this);
232         mCardStrings = loadCardStrings();
233         mDefaultEuiccCardId = UNINITIALIZED_CARD_ID;
234     }
235 
236     /**
237      * Given the slot index, return the phone ID, or -1 if no phone is associated with the given
238      * slot.
239      * @param slotId the slot index to check
240      * @return the associated phone ID or -1
241      */
getPhoneIdFromSlotId(int slotId)242     public int getPhoneIdFromSlotId(int slotId) {
243         for (int i = 0; i < mPhoneIdToSlotId.length; i++) {
244             if (mPhoneIdToSlotId[i] == slotId) {
245                 return i;
246             }
247         }
248         return -1;
249     }
250 
251     /**
252      * Return the physical slot id associated with the given phoneId, or INVALID_SLOT_ID.
253      * @param phoneId the phoneId to check
254      */
getSlotIdFromPhoneId(int phoneId)255     public int getSlotIdFromPhoneId(int phoneId) {
256         try {
257             return mPhoneIdToSlotId[phoneId];
258         } catch (ArrayIndexOutOfBoundsException e) {
259             return INVALID_SLOT_ID;
260         }
261     }
262 
263     @UnsupportedAppUsage
getInstance()264     public static UiccController getInstance() {
265         synchronized (mLock) {
266             if (mInstance == null) {
267                 throw new RuntimeException(
268                         "UiccController.getInstance can't be called before make()");
269             }
270             return mInstance;
271         }
272     }
273 
274     @UnsupportedAppUsage
getUiccCard(int phoneId)275     public UiccCard getUiccCard(int phoneId) {
276         synchronized (mLock) {
277             return getUiccCardForPhone(phoneId);
278         }
279     }
280 
281     /**
282      * API to get UiccCard corresponding to given physical slot index
283      * @param slotId index of physical slot on the device
284      * @return UiccCard object corresponting to given physical slot index; null if card is
285      * absent
286      */
getUiccCardForSlot(int slotId)287     public UiccCard getUiccCardForSlot(int slotId) {
288         synchronized (mLock) {
289             UiccSlot uiccSlot = getUiccSlot(slotId);
290             if (uiccSlot != null) {
291                 return uiccSlot.getUiccCard();
292             }
293             return null;
294         }
295     }
296 
297     /**
298      * API to get UiccCard corresponding to given phone id
299      * @return UiccCard object corresponding to given phone id; null if there is no card present for
300      * the phone id
301      */
getUiccCardForPhone(int phoneId)302     public UiccCard getUiccCardForPhone(int phoneId) {
303         synchronized (mLock) {
304             if (isValidPhoneIndex(phoneId)) {
305                 UiccSlot uiccSlot = getUiccSlotForPhone(phoneId);
306                 if (uiccSlot != null) {
307                     return uiccSlot.getUiccCard();
308                 }
309             }
310             return null;
311         }
312     }
313 
314     /**
315      * API to get UiccProfile corresponding to given phone id
316      * @return UiccProfile object corresponding to given phone id; null if there is no card/profile
317      * present for the phone id
318      */
getUiccProfileForPhone(int phoneId)319     public UiccProfile getUiccProfileForPhone(int phoneId) {
320         synchronized (mLock) {
321             if (isValidPhoneIndex(phoneId)) {
322                 UiccCard uiccCard = getUiccCardForPhone(phoneId);
323                 return uiccCard != null ? uiccCard.getUiccProfile() : null;
324             }
325             return null;
326         }
327     }
328 
329     /**
330      * API to get all the UICC slots.
331      * @return UiccSlots array.
332      */
getUiccSlots()333     public UiccSlot[] getUiccSlots() {
334         synchronized (mLock) {
335             return mUiccSlots;
336         }
337     }
338 
339     /** Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. */
switchSlots(int[] physicalSlots, Message response)340     public void switchSlots(int[] physicalSlots, Message response) {
341         mRadioConfig.setSimSlotsMapping(physicalSlots, response);
342     }
343 
344     /**
345      * API to get UiccSlot object for a specific physical slot index on the device
346      * @return UiccSlot object for the given physical slot index
347      */
getUiccSlot(int slotId)348     public UiccSlot getUiccSlot(int slotId) {
349         synchronized (mLock) {
350             if (isValidSlotIndex(slotId)) {
351                 return mUiccSlots[slotId];
352             }
353             return null;
354         }
355     }
356 
357     /**
358      * API to get UiccSlot object for a given phone id
359      * @return UiccSlot object for the given phone id
360      */
getUiccSlotForPhone(int phoneId)361     public UiccSlot getUiccSlotForPhone(int phoneId) {
362         synchronized (mLock) {
363             if (isValidPhoneIndex(phoneId)) {
364                 int slotId = getSlotIdFromPhoneId(phoneId);
365                 if (isValidSlotIndex(slotId)) {
366                     return mUiccSlots[slotId];
367                 }
368             }
369             return null;
370         }
371     }
372 
373     /**
374      * API to get UiccSlot object for a given cardId
375      * @param cardId Identifier for a SIM. This can be an ICCID, or an EID in case of an eSIM.
376      * @return int Index of UiccSlot for the given cardId if one is found, {@link #INVALID_SLOT_ID}
377      * otherwise
378      */
getUiccSlotForCardId(String cardId)379     public int getUiccSlotForCardId(String cardId) {
380         synchronized (mLock) {
381             // first look up based on cardId
382             for (int idx = 0; idx < mUiccSlots.length; idx++) {
383                 if (mUiccSlots[idx] != null) {
384                     UiccCard uiccCard = mUiccSlots[idx].getUiccCard();
385                     if (uiccCard != null && cardId.equals(uiccCard.getCardId())) {
386                         return idx;
387                     }
388                 }
389             }
390             // if a match is not found, do a lookup based on ICCID
391             for (int idx = 0; idx < mUiccSlots.length; idx++) {
392                 if (mUiccSlots[idx] != null && cardId.equals(mUiccSlots[idx].getIccId())) {
393                     return idx;
394                 }
395             }
396             return INVALID_SLOT_ID;
397         }
398     }
399 
400     // Easy to use API
401     @UnsupportedAppUsage
getIccRecords(int phoneId, int family)402     public IccRecords getIccRecords(int phoneId, int family) {
403         synchronized (mLock) {
404             UiccCardApplication app = getUiccCardApplication(phoneId, family);
405             if (app != null) {
406                 return app.getIccRecords();
407             }
408             return null;
409         }
410     }
411 
412     // Easy to use API
413     @UnsupportedAppUsage
getIccFileHandler(int phoneId, int family)414     public IccFileHandler getIccFileHandler(int phoneId, int family) {
415         synchronized (mLock) {
416             UiccCardApplication app = getUiccCardApplication(phoneId, family);
417             if (app != null) {
418                 return app.getIccFileHandler();
419             }
420             return null;
421         }
422     }
423 
424 
425     //Notifies when card status changes
426     @UnsupportedAppUsage
registerForIccChanged(Handler h, int what, Object obj)427     public void registerForIccChanged(Handler h, int what, Object obj) {
428         synchronized (mLock) {
429             Registrant r = new Registrant (h, what, obj);
430             mIccChangedRegistrants.add(r);
431             //Notify registrant right after registering, so that it will get the latest ICC status,
432             //otherwise which may not happen until there is an actual change in ICC status.
433             r.notifyRegistrant();
434         }
435     }
436 
unregisterForIccChanged(Handler h)437     public void unregisterForIccChanged(Handler h) {
438         synchronized (mLock) {
439             mIccChangedRegistrants.remove(h);
440         }
441     }
442 
443     @Override
handleMessage(Message msg)444     public void handleMessage (Message msg) {
445         synchronized (mLock) {
446             Integer phoneId = getCiIndex(msg);
447 
448             if (phoneId < 0 || phoneId >= mCis.length) {
449                 Rlog.e(LOG_TAG, "Invalid phoneId : " + phoneId + " received with event "
450                         + msg.what);
451                 return;
452             }
453 
454             sLocalLog.log("handleMessage: Received " + msg.what + " for phoneId " + phoneId);
455 
456             AsyncResult ar = (AsyncResult)msg.obj;
457             switch (msg.what) {
458                 case EVENT_ICC_STATUS_CHANGED:
459                     if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
460                     mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
461                             phoneId));
462                     break;
463                 case EVENT_RADIO_AVAILABLE:
464                 case EVENT_RADIO_ON:
465                     if (DBG) {
466                         log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON, calling "
467                                 + "getIccCardStatus");
468                     }
469                     mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
470                             phoneId));
471                     // slot status should be the same on all RILs; request it only for phoneId 0
472                     if (phoneId == 0) {
473                         if (DBG) {
474                             log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON for phoneId 0, "
475                                     + "calling getIccSlotsStatus");
476                         }
477                         mRadioConfig.getSimSlotsStatus(obtainMessage(EVENT_GET_SLOT_STATUS_DONE,
478                                 phoneId));
479                     }
480                     break;
481                 case EVENT_GET_ICC_STATUS_DONE:
482                     if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
483                     onGetIccCardStatusDone(ar, phoneId);
484                     break;
485                 case EVENT_SLOT_STATUS_CHANGED:
486                 case EVENT_GET_SLOT_STATUS_DONE:
487                     if (DBG) {
488                         log("Received EVENT_SLOT_STATUS_CHANGED or EVENT_GET_SLOT_STATUS_DONE");
489                     }
490                     onGetSlotStatusDone(ar);
491                     break;
492                 case EVENT_RADIO_UNAVAILABLE:
493                     if (DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card");
494                     UiccSlot uiccSlot = getUiccSlotForPhone(phoneId);
495                     if (uiccSlot != null) {
496                         uiccSlot.onRadioStateUnavailable();
497                     }
498                     mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, phoneId, null));
499                     break;
500                 case EVENT_SIM_REFRESH:
501                     if (DBG) log("Received EVENT_SIM_REFRESH");
502                     onSimRefresh(ar, phoneId);
503                     break;
504                 case EVENT_EID_READY:
505                     if (DBG) log("Received EVENT_EID_READY");
506                     onEidReady(ar, phoneId);
507                     break;
508                 default:
509                     Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
510                     break;
511             }
512         }
513     }
514 
getCiIndex(Message msg)515     private Integer getCiIndex(Message msg) {
516         AsyncResult ar;
517         Integer index = new Integer(PhoneConstants.DEFAULT_CARD_INDEX);
518 
519         /*
520          * The events can be come in two ways. By explicitly sending it using
521          * sendMessage, in this case the user object passed is msg.obj and from
522          * the CommandsInterface, in this case the user object is msg.obj.userObj
523          */
524         if (msg != null) {
525             if (msg.obj != null && msg.obj instanceof Integer) {
526                 index = (Integer)msg.obj;
527             } else if(msg.obj != null && msg.obj instanceof AsyncResult) {
528                 ar = (AsyncResult)msg.obj;
529                 if (ar.userObj != null && ar.userObj instanceof Integer) {
530                     index = (Integer)ar.userObj;
531                 }
532             }
533         }
534         return index;
535     }
536 
537     // Easy to use API
538     @UnsupportedAppUsage
getUiccCardApplication(int phoneId, int family)539     public UiccCardApplication getUiccCardApplication(int phoneId, int family) {
540         synchronized (mLock) {
541             UiccCard uiccCard = getUiccCardForPhone(phoneId);
542             if (uiccCard != null) {
543                 return uiccCard.getApplication(family);
544             }
545             return null;
546         }
547     }
548 
getIccStateIntentString(IccCardConstants.State state)549     static String getIccStateIntentString(IccCardConstants.State state) {
550         switch (state) {
551             case ABSENT: return IccCardConstants.INTENT_VALUE_ICC_ABSENT;
552             case PIN_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
553             case PUK_REQUIRED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
554             case NETWORK_LOCKED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
555             case READY: return IccCardConstants.INTENT_VALUE_ICC_READY;
556             case NOT_READY: return IccCardConstants.INTENT_VALUE_ICC_NOT_READY;
557             case PERM_DISABLED: return IccCardConstants.INTENT_VALUE_ICC_LOCKED;
558             case CARD_IO_ERROR: return IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR;
559             case CARD_RESTRICTED: return IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED;
560             case LOADED: return IccCardConstants.INTENT_VALUE_ICC_LOADED;
561             default: return IccCardConstants.INTENT_VALUE_ICC_UNKNOWN;
562         }
563     }
564 
updateInternalIccState(Context context, IccCardConstants.State state, String reason, int phoneId)565     static void updateInternalIccState(Context context, IccCardConstants.State state, String reason,
566             int phoneId) {
567         updateInternalIccState(context, state, reason, phoneId, false);
568     }
569 
570     // absentAndInactive is a special case when we need to update subscriptions but don't want to
571     // broadcast a state change
updateInternalIccState(Context context, IccCardConstants.State state, String reason, int phoneId, boolean absentAndInactive)572     static void updateInternalIccState(Context context, IccCardConstants.State state, String reason,
573             int phoneId, boolean absentAndInactive) {
574         TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
575                 Context.TELEPHONY_SERVICE);
576         telephonyManager.setSimStateForPhone(phoneId, state.toString());
577 
578         SubscriptionInfoUpdater subInfoUpdator = PhoneFactory.getSubscriptionInfoUpdater();
579         if (subInfoUpdator != null) {
580             subInfoUpdator.updateInternalIccState(getIccStateIntentString(state),
581                     reason, phoneId, absentAndInactive);
582         } else {
583             Rlog.e(LOG_TAG, "subInfoUpdate is null.");
584         }
585     }
586 
onGetIccCardStatusDone(AsyncResult ar, Integer index)587     private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
588         if (ar.exception != null) {
589             Rlog.e(LOG_TAG,"Error getting ICC status. "
590                     + "RIL_REQUEST_GET_ICC_STATUS should "
591                     + "never return an error", ar.exception);
592             return;
593         }
594         if (!isValidPhoneIndex(index)) {
595             Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);
596             return;
597         }
598 
599         IccCardStatus status = (IccCardStatus)ar.result;
600 
601         sLocalLog.log("onGetIccCardStatusDone: phoneId " + index + " IccCardStatus: " + status);
602 
603         int slotId = status.physicalSlotIndex;
604         if (VDBG) log("onGetIccCardStatusDone: phoneId " + index + " physicalSlotIndex " + slotId);
605         if (slotId == INVALID_SLOT_ID) {
606             slotId = index;
607         }
608 
609         if (eidIsNotSupported(status)) {
610             // we will never get EID from the HAL, so set mDefaultEuiccCardId to UNSUPPORTED_CARD_ID
611             if (DBG) log("eid is not supported");
612             mDefaultEuiccCardId = UNSUPPORTED_CARD_ID;
613         }
614         mPhoneIdToSlotId[index] = slotId;
615 
616         if (VDBG) logPhoneIdToSlotIdMapping();
617 
618         if (mUiccSlots[slotId] == null) {
619             if (VDBG) {
620                 log("Creating mUiccSlots[" + slotId + "]; mUiccSlots.length = "
621                         + mUiccSlots.length);
622             }
623             mUiccSlots[slotId] = new UiccSlot(mContext, true);
624         }
625 
626         mUiccSlots[slotId].update(mCis[index], status, index, slotId);
627 
628         UiccCard card = mUiccSlots[slotId].getUiccCard();
629         if (card == null) {
630             if (DBG) log("mUiccSlots[" + slotId + "] has no card. Notifying IccChangedRegistrants");
631             mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
632             return;
633         }
634 
635         String cardString = null;
636         boolean isEuicc = mUiccSlots[slotId].isEuicc();
637         if (isEuicc) {
638             cardString = ((EuiccCard) card).getEid();
639         } else {
640             cardString = card.getIccId();
641         }
642 
643         // EID may be unpopulated if RadioConfig<1.2
644         // If so, just register for EID loaded and skip this stuff
645         if (isEuicc && cardString == null
646                 && mDefaultEuiccCardId != UNSUPPORTED_CARD_ID) {
647             ((EuiccCard) card).registerForEidReady(this, EVENT_EID_READY, index);
648         }
649 
650         if (cardString != null) {
651             addCardId(cardString);
652         }
653 
654         if (DBG) log("Notifying IccChangedRegistrants");
655         mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
656     }
657 
658     /**
659      * Returns true if EID is not supproted.
660      */
eidIsNotSupported(IccCardStatus status)661     private boolean eidIsNotSupported(IccCardStatus status) {
662         // if card status does not contain slot ID, we know we are on HAL < 1.2, so EID will never
663         // be available
664         return status.physicalSlotIndex == INVALID_SLOT_ID;
665     }
666 
667     /**
668      * Add a cardString to mCardStrings. If this is an ICCID, trailing Fs will be automatically
669      * stripped.
670      */
addCardId(String cardString)671     private void addCardId(String cardString) {
672         if (TextUtils.isEmpty(cardString)) {
673             return;
674         }
675         if (cardString.length() < EID_LENGTH) {
676             cardString = IccUtils.stripTrailingFs(cardString);
677         }
678         if (!mCardStrings.contains(cardString)) {
679             mCardStrings.add(cardString);
680             saveCardStrings();
681         }
682     }
683 
684     /**
685      * Converts the card string (the ICCID/EID, formerly named card ID) to the public int cardId.
686      * If the given cardString is an ICCID, trailing Fs will be automatically stripped before trying
687      * to match to a card ID.
688      *
689      * @return the matching cardId, or UNINITIALIZED_CARD_ID if the card string does not map to a
690      * currently loaded cardId, or UNSUPPORTED_CARD_ID if the device does not support card IDs
691      */
convertToPublicCardId(String cardString)692     public int convertToPublicCardId(String cardString) {
693         if (mDefaultEuiccCardId == UNSUPPORTED_CARD_ID) {
694             // even if cardString is not an EID, if EID is not supported (e.g. HAL < 1.2) we can't
695             // guarentee a working card ID implementation, so return UNSUPPORTED_CARD_ID
696             return UNSUPPORTED_CARD_ID;
697         }
698         if (TextUtils.isEmpty(cardString)) {
699             return UNINITIALIZED_CARD_ID;
700         }
701 
702         if (cardString.length() < EID_LENGTH) {
703             cardString = IccUtils.stripTrailingFs(cardString);
704         }
705         int id = mCardStrings.indexOf(cardString);
706         if (id == -1) {
707             return UNINITIALIZED_CARD_ID;
708         } else {
709             return id;
710         }
711     }
712 
713     /**
714      * Returns the UiccCardInfo of all currently inserted UICCs and embedded eUICCs.
715      */
getAllUiccCardInfos()716     public ArrayList<UiccCardInfo> getAllUiccCardInfos() {
717         ArrayList<UiccCardInfo> infos = new ArrayList<>();
718         for (int slotIndex = 0; slotIndex < mUiccSlots.length; slotIndex++) {
719             final UiccSlot slot = mUiccSlots[slotIndex];
720             if (slot == null) continue;
721             boolean isEuicc = slot.isEuicc();
722             String eid = null;
723             UiccCard card = slot.getUiccCard();
724             String iccid = null;
725             int cardId = UNINITIALIZED_CARD_ID;
726             boolean isRemovable = slot.isRemovable();
727 
728             // first we try to populate UiccCardInfo using the UiccCard, but if it doesn't exist
729             // (e.g. the slot is for an inactive eUICC) then we try using the UiccSlot.
730             if (card != null) {
731                 iccid = card.getIccId();
732                 if (isEuicc) {
733                     eid = ((EuiccCard) card).getEid();
734                     cardId = convertToPublicCardId(eid);
735                 } else {
736                     // leave eid null if the UICC is not embedded
737                     cardId = convertToPublicCardId(iccid);
738                 }
739             } else {
740                 iccid = slot.getIccId();
741                 // Fill in the fields we can
742                 if (!isEuicc && !TextUtils.isEmpty(iccid)) {
743                     cardId = convertToPublicCardId(iccid);
744                 }
745             }
746             UiccCardInfo info = new UiccCardInfo(isEuicc, cardId, eid,
747                     IccUtils.stripTrailingFs(iccid), slotIndex, isRemovable);
748             infos.add(info);
749         }
750         return infos;
751     }
752 
753     /**
754      * Get the card ID of the default eUICC.
755      */
getCardIdForDefaultEuicc()756     public int getCardIdForDefaultEuicc() {
757         if (mDefaultEuiccCardId == TEMPORARILY_UNSUPPORTED_CARD_ID) {
758             return UNSUPPORTED_CARD_ID;
759         }
760         return mDefaultEuiccCardId;
761     }
762 
loadCardStrings()763     private ArrayList<String> loadCardStrings() {
764         String cardStrings =
765                 PreferenceManager.getDefaultSharedPreferences(mContext).getString(CARD_STRINGS, "");
766         if (TextUtils.isEmpty(cardStrings)) {
767             // just return an empty list, since String.split would return the list { "" }
768             return new ArrayList<String>();
769         }
770         return new ArrayList<String>(Arrays.asList(cardStrings.split(",")));
771     }
772 
saveCardStrings()773     private void saveCardStrings() {
774         SharedPreferences.Editor editor =
775                 PreferenceManager.getDefaultSharedPreferences(mContext).edit();
776         editor.putString(CARD_STRINGS, TextUtils.join(",", mCardStrings));
777         editor.commit();
778     }
779 
onGetSlotStatusDone(AsyncResult ar)780     private synchronized void onGetSlotStatusDone(AsyncResult ar) {
781         if (!mIsSlotStatusSupported) {
782             if (VDBG) log("onGetSlotStatusDone: ignoring since mIsSlotStatusSupported is false");
783             return;
784         }
785         Throwable e = ar.exception;
786         if (e != null) {
787             String logStr;
788             if (!(e instanceof CommandException) || ((CommandException) e).getCommandError()
789                     != CommandException.Error.REQUEST_NOT_SUPPORTED) {
790                 // this is not expected; there should be no exception other than
791                 // REQUEST_NOT_SUPPORTED
792                 logStr = "Unexpected error getting slot status: " + ar.exception;
793                 Rlog.e(LOG_TAG, logStr);
794                 sLocalLog.log(logStr);
795             } else {
796                 // REQUEST_NOT_SUPPORTED
797                 logStr = "onGetSlotStatusDone: request not supported; marking "
798                         + "mIsSlotStatusSupported to false";
799                 log(logStr);
800                 sLocalLog.log(logStr);
801                 mIsSlotStatusSupported = false;
802             }
803             return;
804         }
805 
806         ArrayList<IccSlotStatus> status = (ArrayList<IccSlotStatus>) ar.result;
807 
808         if (!slotStatusChanged(status)) {
809             log("onGetSlotStatusDone: No change in slot status");
810             return;
811         }
812 
813         sLastSlotStatus = status;
814 
815         int numActiveSlots = 0;
816         boolean isDefaultEuiccCardIdSet = false;
817         boolean anyEuiccIsActive = false;
818         boolean hasEuicc = false;
819         for (int i = 0; i < status.size(); i++) {
820             IccSlotStatus iss = status.get(i);
821             boolean isActive = (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_ACTIVE);
822             if (isActive) {
823                 numActiveSlots++;
824 
825                 // sanity check: logicalSlotIndex should be valid for an active slot
826                 if (!isValidPhoneIndex(iss.logicalSlotIndex)) {
827                     Rlog.e(LOG_TAG, "Skipping slot " + i + " as phone " + iss.logicalSlotIndex
828                                + " is not available to communicate with this slot");
829 
830                 } else {
831                     mPhoneIdToSlotId[iss.logicalSlotIndex] = i;
832                 }
833             }
834 
835             if (mUiccSlots[i] == null) {
836                 if (VDBG) {
837                     log("Creating mUiccSlot[" + i + "]; mUiccSlots.length = " + mUiccSlots.length);
838                 }
839                 mUiccSlots[i] = new UiccSlot(mContext, isActive);
840             }
841 
842             if (!isValidPhoneIndex(iss.logicalSlotIndex)) {
843                 mUiccSlots[i].update(null, iss, i /* slotIndex */);
844             } else {
845                 mUiccSlots[i].update(isActive ? mCis[iss.logicalSlotIndex] : null, iss,
846                         i /* slotIndex */);
847             }
848 
849             if (mUiccSlots[i].isEuicc()) {
850                 hasEuicc = true;
851                 if (isActive) {
852                     anyEuiccIsActive = true;
853                 }
854                 String eid = iss.eid;
855                 if (TextUtils.isEmpty(eid)) {
856                     // iss.eid is not populated on HAL<1.4
857                     continue;
858                 }
859 
860                 addCardId(eid);
861 
862                 // whenever slot status is received, set default card to the eUICC with the
863                 // lowest slot index.
864                 if (!isDefaultEuiccCardIdSet) {
865                     isDefaultEuiccCardIdSet = true;
866                     // TODO(b/122738148) the default eUICC should not be removable
867                     mDefaultEuiccCardId = convertToPublicCardId(eid);
868                     log("Using eid=" + eid + " in slot=" + i + " to set mDefaultEuiccCardId="
869                             + mDefaultEuiccCardId);
870                 }
871             }
872         }
873 
874         if (hasEuicc && !anyEuiccIsActive && !isDefaultEuiccCardIdSet) {
875             log("onGetSlotStatusDone: setting TEMPORARILY_UNSUPPORTED_CARD_ID");
876             mDefaultEuiccCardId = TEMPORARILY_UNSUPPORTED_CARD_ID;
877         }
878 
879         if (VDBG) logPhoneIdToSlotIdMapping();
880 
881         // sanity check: number of active slots should be valid
882         if (numActiveSlots != mPhoneIdToSlotId.length) {
883             Rlog.e(LOG_TAG, "Number of active slots " + numActiveSlots
884                        + " does not match the number of Phones" + mPhoneIdToSlotId.length);
885         }
886 
887         // sanity check: slotIds should be unique in mPhoneIdToSlotId
888         Set<Integer> slotIds = new HashSet<>();
889         for (int slotId : mPhoneIdToSlotId) {
890             if (slotIds.contains(slotId)) {
891                 throw new RuntimeException("slotId " + slotId + " mapped to multiple phoneIds");
892             }
893             slotIds.add(slotId);
894         }
895 
896         // broadcast slot status changed
897         final BroadcastOptions options = BroadcastOptions.makeBasic();
898         options.setBackgroundActivityStartsAllowed(true);
899         Intent intent = new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED);
900         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
901         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
902         mContext.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
903                 options.toBundle());
904     }
905 
slotStatusChanged(ArrayList<IccSlotStatus> slotStatusList)906     private boolean slotStatusChanged(ArrayList<IccSlotStatus> slotStatusList) {
907         if (sLastSlotStatus == null || sLastSlotStatus.size() != slotStatusList.size()) {
908             return true;
909         }
910         for (IccSlotStatus iccSlotStatus : slotStatusList) {
911             if (!sLastSlotStatus.contains(iccSlotStatus)) {
912                 return true;
913             }
914         }
915         return false;
916     }
917 
logPhoneIdToSlotIdMapping()918     private void logPhoneIdToSlotIdMapping() {
919         log("mPhoneIdToSlotId mapping:");
920         for (int i = 0; i < mPhoneIdToSlotId.length; i++) {
921             log("    phoneId " + i + " slotId " + mPhoneIdToSlotId[i]);
922         }
923     }
924 
onSimRefresh(AsyncResult ar, Integer index)925     private void onSimRefresh(AsyncResult ar, Integer index) {
926         if (ar.exception != null) {
927             Rlog.e(LOG_TAG, "onSimRefresh: Sim REFRESH with exception: " + ar.exception);
928             return;
929         }
930 
931         if (!isValidPhoneIndex(index)) {
932             Rlog.e(LOG_TAG,"onSimRefresh: invalid index : " + index);
933             return;
934         }
935 
936         IccRefreshResponse resp = (IccRefreshResponse) ar.result;
937         log("onSimRefresh: " + resp);
938         sLocalLog.log("onSimRefresh: " + resp);
939 
940         if (resp == null) {
941             Rlog.e(LOG_TAG, "onSimRefresh: received without input");
942             return;
943         }
944 
945         UiccCard uiccCard = getUiccCardForPhone(index);
946         if (uiccCard == null) {
947             Rlog.e(LOG_TAG,"onSimRefresh: refresh on null card : " + index);
948             return;
949         }
950 
951         boolean changed = false;
952         switch(resp.refreshResult) {
953             // Reset the required apps when we know about the refresh so that
954             // anyone interested does not get stale state.
955             case IccRefreshResponse.REFRESH_RESULT_RESET:
956                 changed = uiccCard.resetAppWithAid(resp.aid, true /* reset */);
957                 break;
958             case IccRefreshResponse.REFRESH_RESULT_INIT:
959                 // don't dispose CatService on SIM REFRESH of type INIT
960                 changed = uiccCard.resetAppWithAid(resp.aid, false /* initialize */);
961                 break;
962             default:
963                 return;
964         }
965 
966         if (changed && resp.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
967             // If there is any change on RESET, reset carrier config as well. From carrier config
968             // perspective, this is treated the same as sim state unknown
969             CarrierConfigManager configManager = (CarrierConfigManager)
970                     mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
971             configManager.updateConfigForPhoneId(index, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
972 
973             boolean requirePowerOffOnSimRefreshReset = mContext.getResources().getBoolean(
974                     com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
975             if (requirePowerOffOnSimRefreshReset) {
976                 mCis[index].setRadioPower(false, null);
977             }
978         }
979 
980         // The card status could have changed. Get the latest state.
981         mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
982     }
983 
984     // for RadioConfig 1.2 or higher, the EID comes with the IccSlotStatus
985     // for RadioConfig<1.2 we register for EID ready set mCardStrings and mDefaultEuiccCardId here
onEidReady(AsyncResult ar, Integer index)986     private void onEidReady(AsyncResult ar, Integer index) {
987         if (ar.exception != null) {
988             Rlog.e(LOG_TAG, "onEidReady: exception: " + ar.exception);
989             return;
990         }
991 
992         if (!isValidPhoneIndex(index)) {
993             Rlog.e(LOG_TAG, "onEidReady: invalid index: " + index);
994             return;
995         }
996         int slotId = mPhoneIdToSlotId[index];
997         UiccCard card = mUiccSlots[slotId].getUiccCard();
998         if (card == null) {
999             Rlog.e(LOG_TAG, "onEidReady: UiccCard in slot " + slotId + " is null");
1000             return;
1001         }
1002 
1003         // set mCardStrings and the defaultEuiccCardId using the now available EID
1004         String eid = ((EuiccCard) card).getEid();
1005         addCardId(eid);
1006         if (mDefaultEuiccCardId == UNINITIALIZED_CARD_ID
1007                 || mDefaultEuiccCardId == TEMPORARILY_UNSUPPORTED_CARD_ID) {
1008             // TODO(b/122738148) the default eUICC should not be removable
1009             mDefaultEuiccCardId = convertToPublicCardId(eid);
1010             log("onEidReady: eid=" + eid + " slot=" + slotId + " mDefaultEuiccCardId="
1011                     + mDefaultEuiccCardId);
1012         }
1013         ((EuiccCard) card).unregisterForEidReady(this);
1014     }
1015 
1016     /**
1017      * static method to return whether CDMA is supported on the device
1018      * @param context object representative of the application that is calling this method
1019      * @return true if CDMA is supported by the device
1020      */
isCdmaSupported(Context context)1021     public static boolean isCdmaSupported(Context context) {
1022         PackageManager packageManager = context.getPackageManager();
1023         boolean isCdmaSupported =
1024                 packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA);
1025         return isCdmaSupported;
1026     }
1027 
isValidPhoneIndex(int index)1028     private boolean isValidPhoneIndex(int index) {
1029         return (index >= 0 && index < TelephonyManager.getDefault().getPhoneCount());
1030     }
1031 
isValidSlotIndex(int index)1032     private boolean isValidSlotIndex(int index) {
1033         return (index >= 0 && index < mUiccSlots.length);
1034     }
1035 
1036     @UnsupportedAppUsage
log(String string)1037     private void log(String string) {
1038         Rlog.d(LOG_TAG, string);
1039     }
1040 
addCardLog(String data)1041     public void addCardLog(String data) {
1042         sLocalLog.log(data);
1043     }
1044 
dump(FileDescriptor fd, PrintWriter pw, String[] args)1045     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1046         pw.println("UiccController: " + this);
1047         pw.println(" mContext=" + mContext);
1048         pw.println(" mInstance=" + mInstance);
1049         pw.println(" mIccChangedRegistrants: size=" + mIccChangedRegistrants.size());
1050         for (int i = 0; i < mIccChangedRegistrants.size(); i++) {
1051             pw.println("  mIccChangedRegistrants[" + i + "]="
1052                     + ((Registrant)mIccChangedRegistrants.get(i)).getHandler());
1053         }
1054         pw.println();
1055         pw.flush();
1056         pw.println(" mIsCdmaSupported=" + isCdmaSupported(mContext));
1057         pw.println(" mUiccSlots: size=" + mUiccSlots.length);
1058         pw.println(" mCardStrings=" + mCardStrings);
1059         pw.println(" mDefaultEuiccCardId=" + mDefaultEuiccCardId);
1060         for (int i = 0; i < mUiccSlots.length; i++) {
1061             if (mUiccSlots[i] == null) {
1062                 pw.println("  mUiccSlots[" + i + "]=null");
1063             } else {
1064                 pw.println("  mUiccSlots[" + i + "]=" + mUiccSlots[i]);
1065                 mUiccSlots[i].dump(fd, pw, args);
1066             }
1067         }
1068         pw.println(" sLocalLog= ");
1069         sLocalLog.dump(fd, pw, args);
1070     }
1071 }
1072