Home | History | Annotate | Download | only in uicc
      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 
    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 
    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 
    186     private int getSlotIdFromPhoneId(int phoneId) {
    187         return mPhoneIdToSlotId[phoneId];
    188     }
    189 
    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 
    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      */
    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      */
    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      */
    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      */
    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. */
    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      */
    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      */
    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      */
    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
    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
    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
    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 
    359     public void unregisterForIccChanged(Handler h) {
    360         synchronized (mLock) {
    361             mIccChangedRegistrants.remove(h);
    362         }
    363     }
    364 
    365     @Override
    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 
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    673     private boolean isValidPhoneIndex(int index) {
    674         return (index >= 0 && index < TelephonyManager.getDefault().getPhoneCount());
    675     }
    676 
    677     private boolean isValidSlotIndex(int index) {
    678         return (index >= 0 && index < mUiccSlots.length);
    679     }
    680 
    681     private void log(String string) {
    682         Rlog.d(LOG_TAG, string);
    683     }
    684 
    685     public void addCardLog(String data) {
    686         sLocalLog.log(data);
    687     }
    688 
    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