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 android.content.Context;
20 import android.content.Intent;
21 import android.os.AsyncResult;
22 import android.os.Handler;
23 import android.os.Message;
24 import android.os.Registrant;
25 import android.os.RegistrantList;
26 import android.os.storage.StorageManager;
27 import android.telephony.CarrierConfigManager;
28 import android.telephony.Rlog;
29 import android.telephony.TelephonyManager;
30 import android.util.LocalLog;
31 
32 import com.android.internal.telephony.CommandException;
33 import com.android.internal.telephony.CommandsInterface;
34 import com.android.internal.telephony.IccCardConstants;
35 import com.android.internal.telephony.PhoneConstants;
36 import com.android.internal.telephony.PhoneFactory;
37 import com.android.internal.telephony.RadioConfig;
38 import com.android.internal.telephony.SubscriptionInfoUpdater;
39 
40 import java.io.FileDescriptor;
41 import java.io.PrintWriter;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.HashSet;
45 import java.util.Set;
46 
47 /**
48  * This class is responsible for keeping all knowledge about
49  * Universal Integrated Circuit Card (UICC), also know as SIM's,
50  * in the system. It is also used as API to get appropriate
51  * applications to pass them to phone and service trackers.
52  *
53  * UiccController is created with the call to make() function.
54  * UiccController is a singleton and make() must only be called once
55  * and throws an exception if called multiple times.
56  *
57  * Once created UiccController registers with RIL for "on" and "unsol_sim_status_changed"
58  * notifications. When such notification arrives UiccController will call
59  * getIccCardStatus (GET_SIM_STATUS). Based on the response of GET_SIM_STATUS
60  * request appropriate tree of uicc objects will be created.
61  *
62  * Following is class diagram for uicc classes:
63  *
64  *                       UiccController
65  *                            #
66  *                            |
67  *                        UiccSlot[]
68  *                            #
69  *                            |
70  *                        UiccCard
71  *                            #
72  *                            |
73  *                       UiccProfile
74  *                          #   #
75  *                          |   ------------------
76  *                    UiccCardApplication    CatService
77  *                      #            #
78  *                      |            |
79  *                 IccRecords    IccFileHandler
80  *                 ^ ^ ^           ^ ^ ^ ^ ^
81  *    SIMRecords---- | |           | | | | ---SIMFileHandler
82  *    RuimRecords----- |           | | | ----RuimFileHandler
83  *    IsimUiccRecords---           | | -----UsimFileHandler
84  *                                 | ------CsimFileHandler
85  *                                 ----IsimFileHandler
86  *
87  * Legend: # stands for Composition
88  *         ^ stands for Generalization
89  *
90  * See also {@link com.android.internal.telephony.IccCard}
91  */
92 public class UiccController extends Handler {
93     private static final boolean DBG = true;
94     private static final boolean VDBG = false; //STOPSHIP if true
95     private static final String LOG_TAG = "UiccController";
96 
97     public static final int INVALID_SLOT_ID = -1;
98 
99     public static final int APP_FAM_3GPP =  1;
100     public static final int APP_FAM_3GPP2 = 2;
101     public static final int APP_FAM_IMS   = 3;
102 
103     private static final int EVENT_ICC_STATUS_CHANGED = 1;
104     private static final int EVENT_SLOT_STATUS_CHANGED = 2;
105     private static final int EVENT_GET_ICC_STATUS_DONE = 3;
106     private static final int EVENT_GET_SLOT_STATUS_DONE = 4;
107     private static final int EVENT_RADIO_ON = 5;
108     private static final int EVENT_RADIO_AVAILABLE = 6;
109     private static final int EVENT_RADIO_UNAVAILABLE = 7;
110     private static final int EVENT_SIM_REFRESH = 8;
111 
112     // this needs to be here, because on bootup we dont know which index maps to which UiccSlot
113     private CommandsInterface[] mCis;
114     private UiccSlot[] mUiccSlots;
115     private int[] mPhoneIdToSlotId;
116     private boolean mIsSlotStatusSupported = true;
117 
118     private static final Object mLock = new Object();
119     private static UiccController mInstance;
120     private static ArrayList<IccSlotStatus> sLastSlotStatus;
121 
122     private Context mContext;
123 
124     protected RegistrantList mIccChangedRegistrants = new RegistrantList();
125 
126     private UiccStateChangedLauncher mLauncher;
127     private RadioConfig mRadioConfig;
128 
129     // LocalLog buffer to hold important SIM related events for debugging
130     static LocalLog sLocalLog = new LocalLog(100);
131 
make(Context c, CommandsInterface[] ci)132     public static UiccController make(Context c, CommandsInterface[] ci) {
133         synchronized (mLock) {
134             if (mInstance != null) {
135                 throw new RuntimeException("UiccController.make() should only be called once");
136             }
137             mInstance = new UiccController(c, ci);
138             return mInstance;
139         }
140     }
141 
UiccController(Context c, CommandsInterface []ci)142     private UiccController(Context c, CommandsInterface []ci) {
143         if (DBG) log("Creating UiccController");
144         mContext = c;
145         mCis = ci;
146         if (DBG) {
147             String logStr = "config_num_physical_slots = " + c.getResources().getInteger(
148                     com.android.internal.R.integer.config_num_physical_slots);
149             log(logStr);
150             sLocalLog.log(logStr);
151         }
152         int numPhysicalSlots = c.getResources().getInteger(
153                 com.android.internal.R.integer.config_num_physical_slots);
154         // Minimum number of physical slot count should be equals to or greater than phone count,
155         // if it is less than phone count use phone count as physical slot count.
156         if (numPhysicalSlots < mCis.length) {
157             numPhysicalSlots = mCis.length;
158         }
159 
160         mUiccSlots = new UiccSlot[numPhysicalSlots];
161         mPhoneIdToSlotId = new int[ci.length];
162         Arrays.fill(mPhoneIdToSlotId, INVALID_SLOT_ID);
163         if (VDBG) logPhoneIdToSlotIdMapping();
164         mRadioConfig = RadioConfig.getInstance(mContext);
165         mRadioConfig.registerForSimSlotStatusChanged(this, EVENT_SLOT_STATUS_CHANGED, null);
166         for (int i = 0; i < mCis.length; i++) {
167             mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, i);
168 
169             // TODO remove this once modem correctly notifies the unsols
170             // If the device is unencrypted or has been decrypted or FBE is supported,
171             // i.e. not in CryptKeeper bounce, read SIM when radio state is available.
172             // Else wait for radio to be on. This is needed for the scenario when SIM is locked --
173             // to avoid overlap of CryptKeeper and SIM unlock screen.
174             if (!StorageManager.inCryptKeeperBounce()) {
175                 mCis[i].registerForAvailable(this, EVENT_RADIO_AVAILABLE, i);
176             } else {
177                 mCis[i].registerForOn(this, EVENT_RADIO_ON, i);
178             }
179             mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, i);
180             mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, i);
181         }
182 
183         mLauncher = new UiccStateChangedLauncher(c, this);
184     }
185 
getSlotIdFromPhoneId(int phoneId)186     private int getSlotIdFromPhoneId(int phoneId) {
187         return mPhoneIdToSlotId[phoneId];
188     }
189 
getInstance()190     public static UiccController getInstance() {
191         synchronized (mLock) {
192             if (mInstance == null) {
193                 throw new RuntimeException(
194                         "UiccController.getInstance can't be called before make()");
195             }
196             return mInstance;
197         }
198     }
199 
getUiccCard(int phoneId)200     public UiccCard getUiccCard(int phoneId) {
201         synchronized (mLock) {
202             return getUiccCardForPhone(phoneId);
203         }
204     }
205 
206     /**
207      * API to get UiccCard corresponding to given physical slot index
208      * @param slotId index of physical slot on the device
209      * @return UiccCard object corresponting to given physical slot index; null if card is
210      * absent
211      */
getUiccCardForSlot(int slotId)212     public UiccCard getUiccCardForSlot(int slotId) {
213         synchronized (mLock) {
214             UiccSlot uiccSlot = getUiccSlot(slotId);
215             if (uiccSlot != null) {
216                 return uiccSlot.getUiccCard();
217             }
218             return null;
219         }
220     }
221 
222     /**
223      * API to get UiccCard corresponding to given phone id
224      * @return UiccCard object corresponding to given phone id; null if there is no card present for
225      * the phone id
226      */
getUiccCardForPhone(int phoneId)227     public UiccCard getUiccCardForPhone(int phoneId) {
228         synchronized (mLock) {
229             if (isValidPhoneIndex(phoneId)) {
230                 UiccSlot uiccSlot = getUiccSlotForPhone(phoneId);
231                 if (uiccSlot != null) {
232                     return uiccSlot.getUiccCard();
233                 }
234             }
235             return null;
236         }
237     }
238 
239     /**
240      * API to get UiccProfile corresponding to given phone id
241      * @return UiccProfile object corresponding to given phone id; null if there is no card/profile
242      * present for the phone id
243      */
getUiccProfileForPhone(int phoneId)244     public UiccProfile getUiccProfileForPhone(int phoneId) {
245         synchronized (mLock) {
246             if (isValidPhoneIndex(phoneId)) {
247                 UiccCard uiccCard = getUiccCardForPhone(phoneId);
248                 return uiccCard != null ? uiccCard.getUiccProfile() : null;
249             }
250             return null;
251         }
252     }
253 
254     /**
255      * API to get all the UICC slots.
256      * @return UiccSlots array.
257      */
getUiccSlots()258     public UiccSlot[] getUiccSlots() {
259         synchronized (mLock) {
260             return mUiccSlots;
261         }
262     }
263 
264     /** Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. */
switchSlots(int[] physicalSlots, Message response)265     public void switchSlots(int[] physicalSlots, Message response) {
266         mRadioConfig.setSimSlotsMapping(physicalSlots, response);
267     }
268 
269     /**
270      * API to get UiccSlot object for a specific physical slot index on the device
271      * @return UiccSlot object for the given physical slot index
272      */
getUiccSlot(int slotId)273     public UiccSlot getUiccSlot(int slotId) {
274         synchronized (mLock) {
275             if (isValidSlotIndex(slotId)) {
276                 return mUiccSlots[slotId];
277             }
278             return null;
279         }
280     }
281 
282     /**
283      * API to get UiccSlot object for a given phone id
284      * @return UiccSlot object for the given phone id
285      */
getUiccSlotForPhone(int phoneId)286     public UiccSlot getUiccSlotForPhone(int phoneId) {
287         synchronized (mLock) {
288             if (isValidPhoneIndex(phoneId)) {
289                 int slotId = getSlotIdFromPhoneId(phoneId);
290                 if (isValidSlotIndex(slotId)) {
291                     return mUiccSlots[slotId];
292                 }
293             }
294             return null;
295         }
296     }
297 
298     /**
299      * API to get UiccSlot object for a given cardId
300      * @param cardId Identifier for a SIM. This can be an ICCID, or an EID in case of an eSIM.
301      * @return int Index of UiccSlot for the given cardId if one is found, {@link #INVALID_SLOT_ID}
302      * otherwise
303      */
getUiccSlotForCardId(String cardId)304     public int getUiccSlotForCardId(String cardId) {
305         synchronized (mLock) {
306             // first look up based on cardId
307             for (int idx = 0; idx < mUiccSlots.length; idx++) {
308                 if (mUiccSlots[idx] != null) {
309                     UiccCard uiccCard = mUiccSlots[idx].getUiccCard();
310                     if (uiccCard != null && cardId.equals(uiccCard.getCardId())) {
311                         return idx;
312                     }
313                 }
314             }
315             // if a match is not found, do a lookup based on ICCID
316             for (int idx = 0; idx < mUiccSlots.length; idx++) {
317                 if (mUiccSlots[idx] != null && cardId.equals(mUiccSlots[idx].getIccId())) {
318                     return idx;
319                 }
320             }
321             return INVALID_SLOT_ID;
322         }
323     }
324 
325     // Easy to use API
getIccRecords(int phoneId, int family)326     public IccRecords getIccRecords(int phoneId, int family) {
327         synchronized (mLock) {
328             UiccCardApplication app = getUiccCardApplication(phoneId, family);
329             if (app != null) {
330                 return app.getIccRecords();
331             }
332             return null;
333         }
334     }
335 
336     // Easy to use API
getIccFileHandler(int phoneId, int family)337     public IccFileHandler getIccFileHandler(int phoneId, int family) {
338         synchronized (mLock) {
339             UiccCardApplication app = getUiccCardApplication(phoneId, family);
340             if (app != null) {
341                 return app.getIccFileHandler();
342             }
343             return null;
344         }
345     }
346 
347 
348     //Notifies when card status changes
registerForIccChanged(Handler h, int what, Object obj)349     public void registerForIccChanged(Handler h, int what, Object obj) {
350         synchronized (mLock) {
351             Registrant r = new Registrant (h, what, obj);
352             mIccChangedRegistrants.add(r);
353             //Notify registrant right after registering, so that it will get the latest ICC status,
354             //otherwise which may not happen until there is an actual change in ICC status.
355             r.notifyRegistrant();
356         }
357     }
358 
unregisterForIccChanged(Handler h)359     public void unregisterForIccChanged(Handler h) {
360         synchronized (mLock) {
361             mIccChangedRegistrants.remove(h);
362         }
363     }
364 
365     @Override
handleMessage(Message msg)366     public void handleMessage (Message msg) {
367         synchronized (mLock) {
368             Integer phoneId = getCiIndex(msg);
369 
370             if (phoneId < 0 || phoneId >= mCis.length) {
371                 Rlog.e(LOG_TAG, "Invalid phoneId : " + phoneId + " received with event "
372                         + msg.what);
373                 return;
374             }
375 
376             sLocalLog.log("handleMessage: Received " + msg.what + " for phoneId " + phoneId);
377 
378             AsyncResult ar = (AsyncResult)msg.obj;
379             switch (msg.what) {
380                 case EVENT_ICC_STATUS_CHANGED:
381                     if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
382                     mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
383                             phoneId));
384                     break;
385                 case EVENT_RADIO_AVAILABLE:
386                 case EVENT_RADIO_ON:
387                     if (DBG) {
388                         log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON, calling "
389                                 + "getIccCardStatus");
390                     }
391                     mCis[phoneId].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE,
392                             phoneId));
393                     // slot status should be the same on all RILs; request it only for phoneId 0
394                     if (phoneId == 0) {
395                         if (DBG) {
396                             log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON for phoneId 0, "
397                                     + "calling getIccSlotsStatus");
398                         }
399                         mRadioConfig.getSimSlotsStatus(obtainMessage(EVENT_GET_SLOT_STATUS_DONE,
400                                 phoneId));
401                     }
402                     break;
403                 case EVENT_GET_ICC_STATUS_DONE:
404                     if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
405                     onGetIccCardStatusDone(ar, phoneId);
406                     break;
407                 case EVENT_SLOT_STATUS_CHANGED:
408                 case EVENT_GET_SLOT_STATUS_DONE:
409                     if (DBG) {
410                         log("Received EVENT_SLOT_STATUS_CHANGED or EVENT_GET_SLOT_STATUS_DONE");
411                     }
412                     onGetSlotStatusDone(ar);
413                     break;
414                 case EVENT_RADIO_UNAVAILABLE:
415                     if (DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card");
416                     UiccSlot uiccSlot = getUiccSlotForPhone(phoneId);
417                     if (uiccSlot != null) {
418                         uiccSlot.onRadioStateUnavailable();
419                     }
420                     mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, phoneId, null));
421                     break;
422                 case EVENT_SIM_REFRESH:
423                     if (DBG) log("Received EVENT_SIM_REFRESH");
424                     onSimRefresh(ar, phoneId);
425                     break;
426                 default:
427                     Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
428                     break;
429             }
430         }
431     }
432 
getCiIndex(Message msg)433     private Integer getCiIndex(Message msg) {
434         AsyncResult ar;
435         Integer index = new Integer(PhoneConstants.DEFAULT_CARD_INDEX);
436 
437         /*
438          * The events can be come in two ways. By explicitly sending it using
439          * sendMessage, in this case the user object passed is msg.obj and from
440          * the CommandsInterface, in this case the user object is msg.obj.userObj
441          */
442         if (msg != null) {
443             if (msg.obj != null && msg.obj instanceof Integer) {
444                 index = (Integer)msg.obj;
445             } else if(msg.obj != null && msg.obj instanceof AsyncResult) {
446                 ar = (AsyncResult)msg.obj;
447                 if (ar.userObj != null && ar.userObj instanceof Integer) {
448                     index = (Integer)ar.userObj;
449                 }
450             }
451         }
452         return index;
453     }
454 
455     // Easy to use API
getUiccCardApplication(int phoneId, int family)456     public UiccCardApplication getUiccCardApplication(int phoneId, int family) {
457         synchronized (mLock) {
458             UiccCard uiccCard = getUiccCardForPhone(phoneId);
459             if (uiccCard != null) {
460                 return uiccCard.getApplication(family);
461             }
462             return null;
463         }
464     }
465 
updateInternalIccState(String value, String reason, int phoneId)466     static void updateInternalIccState(String value, String reason, int phoneId) {
467         SubscriptionInfoUpdater subInfoUpdator = PhoneFactory.getSubscriptionInfoUpdater();
468         if (subInfoUpdator != null) {
469             subInfoUpdator.updateInternalIccState(value, reason, phoneId);
470         } else {
471             Rlog.e(LOG_TAG, "subInfoUpdate is null.");
472         }
473     }
474 
onGetIccCardStatusDone(AsyncResult ar, Integer index)475     private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
476         if (ar.exception != null) {
477             Rlog.e(LOG_TAG,"Error getting ICC status. "
478                     + "RIL_REQUEST_GET_ICC_STATUS should "
479                     + "never return an error", ar.exception);
480             return;
481         }
482         if (!isValidPhoneIndex(index)) {
483             Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);
484             return;
485         }
486 
487         IccCardStatus status = (IccCardStatus)ar.result;
488 
489         sLocalLog.log("onGetIccCardStatusDone: phoneId " + index + " IccCardStatus: " + status);
490 
491         int slotId = status.physicalSlotIndex;
492         if (VDBG) log("onGetIccCardStatusDone: phoneId " + index + " physicalSlotIndex " + slotId);
493         if (slotId == INVALID_SLOT_ID) {
494             slotId = index;
495         }
496         mPhoneIdToSlotId[index] = slotId;
497 
498         if (VDBG) logPhoneIdToSlotIdMapping();
499 
500         if (mUiccSlots[slotId] == null) {
501             if (VDBG) {
502                 log("Creating mUiccSlots[" + slotId + "]; mUiccSlots.length = "
503                         + mUiccSlots.length);
504             }
505             mUiccSlots[slotId] = new UiccSlot(mContext, true);
506         }
507 
508         mUiccSlots[slotId].update(mCis[index], status, index);
509 
510         if (DBG) log("Notifying IccChangedRegistrants");
511         mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
512     }
513 
onGetSlotStatusDone(AsyncResult ar)514     private synchronized void onGetSlotStatusDone(AsyncResult ar) {
515         if (!mIsSlotStatusSupported) {
516             if (VDBG) log("onGetSlotStatusDone: ignoring since mIsSlotStatusSupported is false");
517             return;
518         }
519         Throwable e = ar.exception;
520         if (e != null) {
521             String logStr;
522             if (!(e instanceof CommandException) || ((CommandException) e).getCommandError()
523                     != CommandException.Error.REQUEST_NOT_SUPPORTED) {
524                 // this is not expected; there should be no exception other than
525                 // REQUEST_NOT_SUPPORTED
526                 logStr = "Unexpected error getting slot status: " + ar.exception;
527                 Rlog.e(LOG_TAG, logStr);
528                 sLocalLog.log(logStr);
529             } else {
530                 // REQUEST_NOT_SUPPORTED
531                 logStr = "onGetSlotStatusDone: request not supported; marking "
532                         + "mIsSlotStatusSupported to false";
533                 log(logStr);
534                 sLocalLog.log(logStr);
535                 mIsSlotStatusSupported = false;
536             }
537             return;
538         }
539 
540         ArrayList<IccSlotStatus> status = (ArrayList<IccSlotStatus>) ar.result;
541 
542         if (!slotStatusChanged(status)) {
543             log("onGetSlotStatusDone: No change in slot status");
544             return;
545         }
546 
547         sLastSlotStatus = status;
548 
549         int numActiveSlots = 0;
550         for (int i = 0; i < status.size(); i++) {
551             IccSlotStatus iss = status.get(i);
552             boolean isActive = (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_ACTIVE);
553             if (isActive) {
554                 numActiveSlots++;
555 
556                 // sanity check: logicalSlotIndex should be valid for an active slot
557                 if (!isValidPhoneIndex(iss.logicalSlotIndex)) {
558                     throw new RuntimeException("Logical slot index " + iss.logicalSlotIndex
559                             + " invalid for physical slot " + i);
560                 }
561                 mPhoneIdToSlotId[iss.logicalSlotIndex] = i;
562             }
563 
564             if (mUiccSlots[i] == null) {
565                 if (VDBG) {
566                     log("Creating mUiccSlot[" + i + "]; mUiccSlots.length = " + mUiccSlots.length);
567                 }
568                 mUiccSlots[i] = new UiccSlot(mContext, isActive);
569             }
570 
571             mUiccSlots[i].update(isActive ? mCis[iss.logicalSlotIndex] : null, iss);
572         }
573 
574         if (VDBG) logPhoneIdToSlotIdMapping();
575 
576         // sanity check: number of active slots should be valid
577         if (numActiveSlots != mPhoneIdToSlotId.length) {
578             throw new RuntimeException("Number of active slots " + numActiveSlots
579                     + " does not match the expected value " + mPhoneIdToSlotId.length);
580         }
581 
582         // sanity check: slotIds should be unique in mPhoneIdToSlotId
583         Set<Integer> slotIds = new HashSet<>();
584         for (int slotId : mPhoneIdToSlotId) {
585             if (slotIds.contains(slotId)) {
586                 throw new RuntimeException("slotId " + slotId + " mapped to multiple phoneIds");
587             }
588             slotIds.add(slotId);
589         }
590 
591         // broadcast slot status changed
592         Intent intent = new Intent(TelephonyManager.ACTION_SIM_SLOT_STATUS_CHANGED);
593         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
594         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
595         mContext.sendBroadcast(intent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
596     }
597 
slotStatusChanged(ArrayList<IccSlotStatus> slotStatusList)598     private boolean slotStatusChanged(ArrayList<IccSlotStatus> slotStatusList) {
599         if (sLastSlotStatus == null || sLastSlotStatus.size() != slotStatusList.size()) {
600             return true;
601         }
602         for (IccSlotStatus iccSlotStatus : slotStatusList) {
603             if (!sLastSlotStatus.contains(iccSlotStatus)) {
604                 return true;
605             }
606         }
607         return false;
608     }
609 
logPhoneIdToSlotIdMapping()610     private void logPhoneIdToSlotIdMapping() {
611         log("mPhoneIdToSlotId mapping:");
612         for (int i = 0; i < mPhoneIdToSlotId.length; i++) {
613             log("    phoneId " + i + " slotId " + mPhoneIdToSlotId[i]);
614         }
615     }
616 
onSimRefresh(AsyncResult ar, Integer index)617     private void onSimRefresh(AsyncResult ar, Integer index) {
618         if (ar.exception != null) {
619             Rlog.e(LOG_TAG, "onSimRefresh: Sim REFRESH with exception: " + ar.exception);
620             return;
621         }
622 
623         if (!isValidPhoneIndex(index)) {
624             Rlog.e(LOG_TAG,"onSimRefresh: invalid index : " + index);
625             return;
626         }
627 
628         IccRefreshResponse resp = (IccRefreshResponse) ar.result;
629         log("onSimRefresh: " + resp);
630         sLocalLog.log("onSimRefresh: " + resp);
631 
632         if (resp == null) {
633             Rlog.e(LOG_TAG, "onSimRefresh: received without input");
634             return;
635         }
636 
637         UiccCard uiccCard = getUiccCardForPhone(index);
638         if (uiccCard == null) {
639             Rlog.e(LOG_TAG,"onSimRefresh: refresh on null card : " + index);
640             return;
641         }
642 
643         boolean changed = false;
644         switch(resp.refreshResult) {
645             case IccRefreshResponse.REFRESH_RESULT_RESET:
646             case IccRefreshResponse.REFRESH_RESULT_INIT:
647                  // Reset the required apps when we know about the refresh so that
648                  // anyone interested does not get stale state.
649                  changed = uiccCard.resetAppWithAid(resp.aid);
650                  break;
651             default:
652                  return;
653         }
654 
655         if (changed && resp.refreshResult == IccRefreshResponse.REFRESH_RESULT_RESET) {
656             // If there is any change on RESET, reset carrier config as well. From carrier config
657             // perspective, this is treated the same as sim state unknown
658             CarrierConfigManager configManager = (CarrierConfigManager)
659                     mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
660             configManager.updateConfigForPhoneId(index, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
661 
662             boolean requirePowerOffOnSimRefreshReset = mContext.getResources().getBoolean(
663                     com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
664             if (requirePowerOffOnSimRefreshReset) {
665                 mCis[index].setRadioPower(false, null);
666             }
667         }
668 
669         // The card status could have changed. Get the latest state.
670         mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
671     }
672 
isValidPhoneIndex(int index)673     private boolean isValidPhoneIndex(int index) {
674         return (index >= 0 && index < TelephonyManager.getDefault().getPhoneCount());
675     }
676 
isValidSlotIndex(int index)677     private boolean isValidSlotIndex(int index) {
678         return (index >= 0 && index < mUiccSlots.length);
679     }
680 
log(String string)681     private void log(String string) {
682         Rlog.d(LOG_TAG, string);
683     }
684 
addCardLog(String data)685     public void addCardLog(String data) {
686         sLocalLog.log(data);
687     }
688 
dump(FileDescriptor fd, PrintWriter pw, String[] args)689     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
690         pw.println("UiccController: " + this);
691         pw.println(" mContext=" + mContext);
692         pw.println(" mInstance=" + mInstance);
693         pw.println(" mIccChangedRegistrants: size=" + mIccChangedRegistrants.size());
694         for (int i = 0; i < mIccChangedRegistrants.size(); i++) {
695             pw.println("  mIccChangedRegistrants[" + i + "]="
696                     + ((Registrant)mIccChangedRegistrants.get(i)).getHandler());
697         }
698         pw.println();
699         pw.flush();
700         pw.println(" mUiccSlots: size=" + mUiccSlots.length);
701         for (int i = 0; i < mUiccSlots.length; i++) {
702             if (mUiccSlots[i] == null) {
703                 pw.println("  mUiccSlots[" + i + "]=null");
704             } else {
705                 pw.println("  mUiccSlots[" + i + "]=" + mUiccSlots[i]);
706                 mUiccSlots[i].dump(fd, pw, args);
707             }
708         }
709         pw.println(" sLocalLog= ");
710         sLocalLog.dump(fd, pw, args);
711     }
712 }
713