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