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.os.AsyncResult;
     21 import android.os.Handler;
     22 import android.os.Message;
     23 import android.os.Registrant;
     24 import android.os.RegistrantList;
     25 import android.os.SystemProperties;
     26 import android.telephony.TelephonyManager;
     27 import android.telephony.Rlog;
     28 import android.text.format.Time;
     29 
     30 import com.android.internal.telephony.CommandsInterface;
     31 import com.android.internal.telephony.PhoneConstants;
     32 import com.android.internal.telephony.SubscriptionController;
     33 
     34 import java.io.FileDescriptor;
     35 import java.io.PrintWriter;
     36 import java.util.LinkedList;
     37 
     38 /**
     39  * This class is responsible for keeping all knowledge about
     40  * Universal Integrated Circuit Card (UICC), also know as SIM's,
     41  * in the system. It is also used as API to get appropriate
     42  * applications to pass them to phone and service trackers.
     43  *
     44  * UiccController is created with the call to make() function.
     45  * UiccController is a singleton and make() must only be called once
     46  * and throws an exception if called multiple times.
     47  *
     48  * Once created UiccController registers with RIL for "on" and "unsol_sim_status_changed"
     49  * notifications. When such notification arrives UiccController will call
     50  * getIccCardStatus (GET_SIM_STATUS). Based on the response of GET_SIM_STATUS
     51  * request appropriate tree of uicc objects will be created.
     52  *
     53  * Following is class diagram for uicc classes:
     54  *
     55  *                       UiccController
     56  *                            #
     57  *                            |
     58  *                        UiccCard
     59  *                          #   #
     60  *                          |   ------------------
     61  *                    UiccCardApplication    CatService
     62  *                      #            #
     63  *                      |            |
     64  *                 IccRecords    IccFileHandler
     65  *                 ^ ^ ^           ^ ^ ^ ^ ^
     66  *    SIMRecords---- | |           | | | | ---SIMFileHandler
     67  *    RuimRecords----- |           | | | ----RuimFileHandler
     68  *    IsimUiccRecords---           | | -----UsimFileHandler
     69  *                                 | ------CsimFileHandler
     70  *                                 ----IsimFileHandler
     71  *
     72  * Legend: # stands for Composition
     73  *         ^ stands for Generalization
     74  *
     75  * See also {@link com.android.internal.telephony.IccCard}
     76  * and {@link com.android.internal.telephony.uicc.IccCardProxy}
     77  */
     78 public class UiccController extends Handler {
     79     private static final boolean DBG = true;
     80     private static final String LOG_TAG = "UiccController";
     81 
     82     public static final int APP_FAM_3GPP =  1;
     83     public static final int APP_FAM_3GPP2 = 2;
     84     public static final int APP_FAM_IMS   = 3;
     85 
     86     private static final int EVENT_ICC_STATUS_CHANGED = 1;
     87     private static final int EVENT_GET_ICC_STATUS_DONE = 2;
     88     private static final int EVENT_RADIO_UNAVAILABLE = 3;
     89     private static final int EVENT_SIM_REFRESH = 4;
     90 
     91     private static final String DECRYPT_STATE = "trigger_restart_framework";
     92 
     93     private CommandsInterface[] mCis;
     94     private UiccCard[] mUiccCards = new UiccCard[TelephonyManager.getDefault().getPhoneCount()];
     95 
     96     private static final Object mLock = new Object();
     97     private static UiccController mInstance;
     98 
     99     private Context mContext;
    100 
    101     protected RegistrantList mIccChangedRegistrants = new RegistrantList();
    102 
    103     // Logging for dumpsys. Useful in cases when the cards run into errors.
    104     private static final int MAX_PROACTIVE_COMMANDS_TO_LOG = 20;
    105     private LinkedList<String> mCardLogs = new LinkedList<String>();
    106 
    107     public static UiccController make(Context c, CommandsInterface[] ci) {
    108         synchronized (mLock) {
    109             if (mInstance != null) {
    110                 throw new RuntimeException("MSimUiccController.make() should only be called once");
    111             }
    112             mInstance = new UiccController(c, ci);
    113             return (UiccController)mInstance;
    114         }
    115     }
    116 
    117     private UiccController(Context c, CommandsInterface []ci) {
    118         if (DBG) log("Creating UiccController");
    119         mContext = c;
    120         mCis = ci;
    121         for (int i = 0; i < mCis.length; i++) {
    122             Integer index = new Integer(i);
    123             mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
    124             // TODO remove this once modem correctly notifies the unsols
    125             if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt"))) {
    126                 mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
    127             } else {
    128                 mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
    129             }
    130             mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index);
    131             mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, index);
    132         }
    133     }
    134 
    135     public static UiccController getInstance() {
    136         synchronized (mLock) {
    137             if (mInstance == null) {
    138                 throw new RuntimeException(
    139                         "UiccController.getInstance can't be called before make()");
    140             }
    141             return mInstance;
    142         }
    143     }
    144 
    145     public UiccCard getUiccCard(int phoneId) {
    146         synchronized (mLock) {
    147             if (isValidCardIndex(phoneId)) {
    148                 return mUiccCards[phoneId];
    149             }
    150             return null;
    151         }
    152     }
    153 
    154     public UiccCard[] getUiccCards() {
    155         // Return cloned array since we don't want to give out reference
    156         // to internal data structure.
    157         synchronized (mLock) {
    158             return mUiccCards.clone();
    159         }
    160     }
    161 
    162     // Easy to use API
    163     public IccRecords getIccRecords(int phoneId, int family) {
    164         synchronized (mLock) {
    165             UiccCardApplication app = getUiccCardApplication(phoneId, family);
    166             if (app != null) {
    167                 return app.getIccRecords();
    168             }
    169             return null;
    170         }
    171     }
    172 
    173     // Easy to use API
    174     public IccFileHandler getIccFileHandler(int phoneId, int family) {
    175         synchronized (mLock) {
    176             UiccCardApplication app = getUiccCardApplication(phoneId, family);
    177             if (app != null) {
    178                 return app.getIccFileHandler();
    179             }
    180             return null;
    181         }
    182     }
    183 
    184 
    185     //Notifies when card status changes
    186     public void registerForIccChanged(Handler h, int what, Object obj) {
    187         synchronized (mLock) {
    188             Registrant r = new Registrant (h, what, obj);
    189             mIccChangedRegistrants.add(r);
    190             //Notify registrant right after registering, so that it will get the latest ICC status,
    191             //otherwise which may not happen until there is an actual change in ICC status.
    192             r.notifyRegistrant();
    193         }
    194     }
    195 
    196     public void unregisterForIccChanged(Handler h) {
    197         synchronized (mLock) {
    198             mIccChangedRegistrants.remove(h);
    199         }
    200     }
    201 
    202     @Override
    203     public void handleMessage (Message msg) {
    204         synchronized (mLock) {
    205             Integer index = getCiIndex(msg);
    206 
    207             if (index < 0 || index >= mCis.length) {
    208                 Rlog.e(LOG_TAG, "Invalid index : " + index + " received with event " + msg.what);
    209                 return;
    210             }
    211 
    212             AsyncResult ar = (AsyncResult)msg.obj;
    213             switch (msg.what) {
    214                 case EVENT_ICC_STATUS_CHANGED:
    215                     if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus");
    216                     mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
    217                     break;
    218                 case EVENT_GET_ICC_STATUS_DONE:
    219                     if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE");
    220                     onGetIccCardStatusDone(ar, index);
    221                     break;
    222                 case EVENT_RADIO_UNAVAILABLE:
    223                     if (DBG) log("EVENT_RADIO_UNAVAILABLE, dispose card");
    224                     if (mUiccCards[index] != null) {
    225                         mUiccCards[index].dispose();
    226                     }
    227                     mUiccCards[index] = null;
    228                     mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
    229                     break;
    230                 case EVENT_SIM_REFRESH:
    231                     if (DBG) log("Received EVENT_SIM_REFRESH");
    232                     onSimRefresh(ar, index);
    233                     break;
    234                 default:
    235                     Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
    236             }
    237         }
    238     }
    239 
    240     private Integer getCiIndex(Message msg) {
    241         AsyncResult ar;
    242         Integer index = new Integer(PhoneConstants.DEFAULT_CARD_INDEX);
    243 
    244         /*
    245          * The events can be come in two ways. By explicitly sending it using
    246          * sendMessage, in this case the user object passed is msg.obj and from
    247          * the CommandsInterface, in this case the user object is msg.obj.userObj
    248          */
    249         if (msg != null) {
    250             if (msg.obj != null && msg.obj instanceof Integer) {
    251                 index = (Integer)msg.obj;
    252             } else if(msg.obj != null && msg.obj instanceof AsyncResult) {
    253                 ar = (AsyncResult)msg.obj;
    254                 if (ar.userObj != null && ar.userObj instanceof Integer) {
    255                     index = (Integer)ar.userObj;
    256                 }
    257             }
    258         }
    259         return index;
    260     }
    261 
    262     // Easy to use API
    263     public UiccCardApplication getUiccCardApplication(int phoneId, int family) {
    264         synchronized (mLock) {
    265             if (isValidCardIndex(phoneId)) {
    266                 UiccCard c = mUiccCards[phoneId];
    267                 if (c != null) {
    268                     return mUiccCards[phoneId].getApplication(family);
    269                 }
    270             }
    271             return null;
    272         }
    273     }
    274 
    275     private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {
    276         if (ar.exception != null) {
    277             Rlog.e(LOG_TAG,"Error getting ICC status. "
    278                     + "RIL_REQUEST_GET_ICC_STATUS should "
    279                     + "never return an error", ar.exception);
    280             return;
    281         }
    282         if (!isValidCardIndex(index)) {
    283             Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);
    284             return;
    285         }
    286 
    287         IccCardStatus status = (IccCardStatus)ar.result;
    288 
    289         if (mUiccCards[index] == null) {
    290             //Create new card
    291             mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index);
    292         } else {
    293             //Update already existing card
    294             mUiccCards[index].update(mContext, mCis[index] , status);
    295         }
    296 
    297         if (DBG) log("Notifying IccChangedRegistrants");
    298         mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
    299 
    300     }
    301 
    302     private void onSimRefresh(AsyncResult ar, Integer index) {
    303         if (ar.exception != null) {
    304             Rlog.e(LOG_TAG, "Sim REFRESH with exception: " + ar.exception);
    305             return;
    306         }
    307 
    308         if (!isValidCardIndex(index)) {
    309             Rlog.e(LOG_TAG,"onSimRefresh: invalid index : " + index);
    310             return;
    311         }
    312 
    313         IccRefreshResponse resp = (IccRefreshResponse) ar.result;
    314         Rlog.d(LOG_TAG, "onSimRefresh: " + resp);
    315 
    316         if (mUiccCards[index] == null) {
    317             Rlog.e(LOG_TAG,"onSimRefresh: refresh on null card : " + index);
    318             return;
    319         }
    320 
    321         if (resp.refreshResult != IccRefreshResponse.REFRESH_RESULT_RESET) {
    322           Rlog.d(LOG_TAG, "Ignoring non reset refresh: " + resp);
    323           return;
    324         }
    325 
    326         Rlog.d(LOG_TAG, "Handling refresh reset: " + resp);
    327 
    328         boolean changed = mUiccCards[index].resetAppWithAid(resp.aid);
    329         if (changed) {
    330             boolean requirePowerOffOnSimRefreshReset = mContext.getResources().getBoolean(
    331                 com.android.internal.R.bool.config_requireRadioPowerOffOnSimRefreshReset);
    332             if (requirePowerOffOnSimRefreshReset) {
    333                 mCis[index].setRadioPower(false, null);
    334             } else {
    335                 mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
    336             }
    337             mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
    338         }
    339     }
    340 
    341     private boolean isValidCardIndex(int index) {
    342         return (index >= 0 && index < mUiccCards.length);
    343     }
    344 
    345     private void log(String string) {
    346         Rlog.d(LOG_TAG, string);
    347     }
    348 
    349     // TODO: This is hacky. We need a better way of saving the logs.
    350     public void addCardLog(String data) {
    351         Time t = new Time();
    352         t.setToNow();
    353         mCardLogs.addLast(t.format("%m-%d %H:%M:%S") + " " + data);
    354         if (mCardLogs.size() > MAX_PROACTIVE_COMMANDS_TO_LOG) {
    355             mCardLogs.removeFirst();
    356         }
    357     }
    358 
    359     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    360         pw.println("UiccController: " + this);
    361         pw.println(" mContext=" + mContext);
    362         pw.println(" mInstance=" + mInstance);
    363         pw.println(" mIccChangedRegistrants: size=" + mIccChangedRegistrants.size());
    364         for (int i = 0; i < mIccChangedRegistrants.size(); i++) {
    365             pw.println("  mIccChangedRegistrants[" + i + "]="
    366                     + ((Registrant)mIccChangedRegistrants.get(i)).getHandler());
    367         }
    368         pw.println();
    369         pw.flush();
    370         pw.println(" mUiccCards: size=" + mUiccCards.length);
    371         for (int i = 0; i < mUiccCards.length; i++) {
    372             if (mUiccCards[i] == null) {
    373                 pw.println("  mUiccCards[" + i + "]=null");
    374             } else {
    375                 pw.println("  mUiccCards[" + i + "]=" + mUiccCards[i]);
    376                 mUiccCards[i].dump(fd, pw, args);
    377             }
    378         }
    379         pw.println("mCardLogs: ");
    380         for (int i = 0; i < mCardLogs.size(); ++i) {
    381             pw.println("  " + mCardLogs.get(i));
    382         }
    383     }
    384 }
    385