Home | History | Annotate | Download | only in uicc
      1 /*
      2  * Copyright (C) 2006, 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.internal.telephony.uicc;
     18 
     19 import static android.Manifest.permission.READ_PHONE_STATE;
     20 import android.app.ActivityManagerNative;
     21 import android.app.AlertDialog;
     22 import android.content.Context;
     23 import android.content.DialogInterface;
     24 import android.content.Intent;
     25 import android.content.res.Resources;
     26 import android.os.AsyncResult;
     27 import android.os.Handler;
     28 import android.os.Message;
     29 import android.os.PowerManager;
     30 import android.os.Registrant;
     31 import android.os.RegistrantList;
     32 import android.telephony.Rlog;
     33 import android.view.WindowManager;
     34 
     35 import com.android.internal.telephony.CommandsInterface;
     36 import com.android.internal.telephony.PhoneBase;
     37 import com.android.internal.telephony.CommandsInterface.RadioState;
     38 import com.android.internal.telephony.IccCardConstants.State;
     39 import com.android.internal.telephony.gsm.GSMPhone;
     40 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
     41 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
     42 import com.android.internal.telephony.uicc.IccCardStatus.PinState;
     43 import com.android.internal.telephony.cat.CatService;
     44 import com.android.internal.telephony.cdma.CDMALTEPhone;
     45 import com.android.internal.telephony.cdma.CDMAPhone;
     46 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
     47 
     48 import android.os.SystemProperties;
     49 
     50 import com.android.internal.R;
     51 
     52 import java.io.FileDescriptor;
     53 import java.io.PrintWriter;
     54 
     55 /**
     56  * {@hide}
     57  */
     58 public class UiccCard {
     59     protected static final String LOG_TAG = "UiccCard";
     60     protected static final boolean DBG = true;
     61 
     62     private final Object mLock = new Object();
     63     private CardState mCardState;
     64     private PinState mUniversalPinState;
     65     private int mGsmUmtsSubscriptionAppIndex;
     66     private int mCdmaSubscriptionAppIndex;
     67     private int mImsSubscriptionAppIndex;
     68     private UiccCardApplication[] mUiccApplications =
     69             new UiccCardApplication[IccCardStatus.CARD_MAX_APPS];
     70     private Context mContext;
     71     private CommandsInterface mCi;
     72     private CatService mCatService;
     73     private boolean mDestroyed = false; //set to true once this card is commanded to be disposed of.
     74     private RadioState mLastRadioState =  RadioState.RADIO_UNAVAILABLE;
     75 
     76     private RegistrantList mAbsentRegistrants = new RegistrantList();
     77 
     78     private static final int EVENT_CARD_REMOVED = 13;
     79     private static final int EVENT_CARD_ADDED = 14;
     80 
     81     public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
     82         if (DBG) log("Creating");
     83         mCardState = ics.mCardState;
     84         update(c, ci, ics);
     85     }
     86 
     87     public void dispose() {
     88         synchronized (mLock) {
     89             if (DBG) log("Disposing card");
     90             if (mCatService != null) mCatService.dispose();
     91             for (UiccCardApplication app : mUiccApplications) {
     92                 if (app != null) {
     93                     app.dispose();
     94                 }
     95             }
     96             mCatService = null;
     97             mUiccApplications = null;
     98         }
     99     }
    100 
    101     public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
    102         synchronized (mLock) {
    103             if (mDestroyed) {
    104                 loge("Updated after destroyed! Fix me!");
    105                 return;
    106             }
    107             CardState oldState = mCardState;
    108             mCardState = ics.mCardState;
    109             mUniversalPinState = ics.mUniversalPinState;
    110             mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
    111             mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
    112             mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
    113             mContext = c;
    114             mCi = ci;
    115             //update applications
    116             if (DBG) log(ics.mApplications.length + " applications");
    117             for ( int i = 0; i < mUiccApplications.length; i++) {
    118                 if (mUiccApplications[i] == null) {
    119                     //Create newly added Applications
    120                     if (i < ics.mApplications.length) {
    121                         mUiccApplications[i] = new UiccCardApplication(this,
    122                                 ics.mApplications[i], mContext, mCi);
    123                     }
    124                 } else if (i >= ics.mApplications.length) {
    125                     //Delete removed applications
    126                     mUiccApplications[i].dispose();
    127                     mUiccApplications[i] = null;
    128                 } else {
    129                     //Update the rest
    130                     mUiccApplications[i].update(ics.mApplications[i], mContext, mCi);
    131                 }
    132             }
    133 
    134             if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
    135                 // Initialize or Reinitialize CatService
    136                 mCatService = CatService.getInstance(mCi,
    137                                                      mContext,
    138                                                      this);
    139             } else {
    140                 if (mCatService != null) {
    141                     mCatService.dispose();
    142                 }
    143                 mCatService = null;
    144             }
    145 
    146             sanitizeApplicationIndexes();
    147 
    148             RadioState radioState = mCi.getRadioState();
    149             if (DBG) log("update: radioState=" + radioState + " mLastRadioState="
    150                     + mLastRadioState);
    151             // No notifications while radio is off or we just powering up
    152             if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
    153                 if (oldState != CardState.CARDSTATE_ABSENT &&
    154                         mCardState == CardState.CARDSTATE_ABSENT) {
    155                     if (DBG) log("update: notify card removed");
    156                     mAbsentRegistrants.notifyRegistrants();
    157                     mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
    158                 } else if (oldState == CardState.CARDSTATE_ABSENT &&
    159                         mCardState != CardState.CARDSTATE_ABSENT) {
    160                     if (DBG) log("update: notify card added");
    161                     mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
    162                 }
    163             }
    164             mLastRadioState = radioState;
    165         }
    166     }
    167 
    168     @Override
    169     protected void finalize() {
    170         if (DBG) log("UiccCard finalized");
    171     }
    172 
    173     /**
    174      * This function makes sure that application indexes are valid
    175      * and resets invalid indexes. (This should never happen, but in case
    176      * RIL misbehaves we need to manage situation gracefully)
    177      */
    178     private void sanitizeApplicationIndexes() {
    179         mGsmUmtsSubscriptionAppIndex =
    180                 checkIndex(mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM);
    181         mCdmaSubscriptionAppIndex =
    182                 checkIndex(mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM);
    183         mImsSubscriptionAppIndex =
    184                 checkIndex(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null);
    185     }
    186 
    187     private int checkIndex(int index, AppType expectedAppType, AppType altExpectedAppType) {
    188         if (mUiccApplications == null || index >= mUiccApplications.length) {
    189             loge("App index " + index + " is invalid since there are no applications");
    190             return -1;
    191         }
    192 
    193         if (index < 0) {
    194             // This is normal. (i.e. no application of this type)
    195             return -1;
    196         }
    197 
    198         if (mUiccApplications[index].getType() != expectedAppType &&
    199             mUiccApplications[index].getType() != altExpectedAppType) {
    200             loge("App index " + index + " is invalid since it's not " +
    201                     expectedAppType + " and not " + altExpectedAppType);
    202             return -1;
    203         }
    204 
    205         // Seems to be valid
    206         return index;
    207     }
    208 
    209     /**
    210      * Notifies handler of any transition into State.ABSENT
    211      */
    212     public void registerForAbsent(Handler h, int what, Object obj) {
    213         synchronized (mLock) {
    214             Registrant r = new Registrant (h, what, obj);
    215 
    216             mAbsentRegistrants.add(r);
    217 
    218             if (mCardState == CardState.CARDSTATE_ABSENT) {
    219                 r.notifyRegistrant();
    220             }
    221         }
    222     }
    223 
    224     public void unregisterForAbsent(Handler h) {
    225         synchronized (mLock) {
    226             mAbsentRegistrants.remove(h);
    227         }
    228     }
    229 
    230     private void onIccSwap(boolean isAdded) {
    231         synchronized (mLock) {
    232             // TODO: Here we assume the device can't handle SIM hot-swap
    233             //      and has to reboot. We may want to add a property,
    234             //      e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
    235             //      hot-swap.
    236             DialogInterface.OnClickListener listener = null;
    237 
    238 
    239             // TODO: SimRecords is not reset while SIM ABSENT (only reset while
    240             //       Radio_off_or_not_available). Have to reset in both both
    241             //       added or removed situation.
    242             listener = new DialogInterface.OnClickListener() {
    243                 @Override
    244                 public void onClick(DialogInterface dialog, int which) {
    245                     synchronized (mLock) {
    246                         if (which == DialogInterface.BUTTON_POSITIVE) {
    247                             if (DBG) log("Reboot due to SIM swap");
    248                             PowerManager pm = (PowerManager) mContext
    249                                     .getSystemService(Context.POWER_SERVICE);
    250                             pm.reboot("SIM is added.");
    251                         }
    252                     }
    253                 }
    254 
    255             };
    256 
    257             Resources r = Resources.getSystem();
    258 
    259             String title = (isAdded) ? r.getString(R.string.sim_added_title) :
    260                 r.getString(R.string.sim_removed_title);
    261             String message = (isAdded) ? r.getString(R.string.sim_added_message) :
    262                 r.getString(R.string.sim_removed_message);
    263             String buttonTxt = r.getString(R.string.sim_restart_button);
    264 
    265             AlertDialog dialog = new AlertDialog.Builder(mContext)
    266             .setTitle(title)
    267             .setMessage(message)
    268             .setPositiveButton(buttonTxt, listener)
    269             .create();
    270             dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
    271             dialog.show();
    272         }
    273     }
    274 
    275     protected Handler mHandler = new Handler() {
    276         @Override
    277         public void handleMessage(Message msg){
    278             if (mDestroyed) {
    279                 loge("Received message " + msg + "[" + msg.what
    280                         + "] while being destroyed. Ignoring.");
    281                 return;
    282             }
    283 
    284             switch (msg.what) {
    285                 case EVENT_CARD_REMOVED:
    286                     onIccSwap(false);
    287                     break;
    288                 case EVENT_CARD_ADDED:
    289                     onIccSwap(true);
    290                     break;
    291                 default:
    292                     loge("Unknown Event " + msg.what);
    293             }
    294         }
    295     };
    296 
    297     public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) {
    298         synchronized (mLock) {
    299             for (int i = 0 ; i < mUiccApplications.length; i++) {
    300                 if (mUiccApplications[i] != null && mUiccApplications[i].getType() == type) {
    301                     return true;
    302                 }
    303             }
    304             return false;
    305         }
    306     }
    307 
    308     public CardState getCardState() {
    309         synchronized (mLock) {
    310             return mCardState;
    311         }
    312     }
    313 
    314     public PinState getUniversalPinState() {
    315         synchronized (mLock) {
    316             return mUniversalPinState;
    317         }
    318     }
    319 
    320     public UiccCardApplication getApplication(int family) {
    321         synchronized (mLock) {
    322             int index = IccCardStatus.CARD_MAX_APPS;
    323             switch (family) {
    324                 case UiccController.APP_FAM_3GPP:
    325                     index = mGsmUmtsSubscriptionAppIndex;
    326                     break;
    327                 case UiccController.APP_FAM_3GPP2:
    328                     index = mCdmaSubscriptionAppIndex;
    329                     break;
    330                 case UiccController.APP_FAM_IMS:
    331                     index = mImsSubscriptionAppIndex;
    332                     break;
    333             }
    334             if (index >= 0 && index < mUiccApplications.length) {
    335                 return mUiccApplications[index];
    336             }
    337             return null;
    338         }
    339     }
    340 
    341     public UiccCardApplication getApplicationIndex(int index) {
    342         synchronized (mLock) {
    343             if (index >= 0 && index < mUiccApplications.length) {
    344                 return mUiccApplications[index];
    345             }
    346             return null;
    347         }
    348     }
    349 
    350     private void log(String msg) {
    351         Rlog.d(LOG_TAG, msg);
    352     }
    353 
    354     private void loge(String msg) {
    355         Rlog.e(LOG_TAG, msg);
    356     }
    357 
    358     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    359         pw.println("UiccCard:");
    360         pw.println(" mCi=" + mCi);
    361         pw.println(" mDestroyed=" + mDestroyed);
    362         pw.println(" mLastRadioState=" + mLastRadioState);
    363         pw.println(" mCatService=" + mCatService);
    364         pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size());
    365         for (int i = 0; i < mAbsentRegistrants.size(); i++) {
    366             pw.println("  mAbsentRegistrants[" + i + "]="
    367                     + ((Registrant)mAbsentRegistrants.get(i)).getHandler());
    368         }
    369         pw.println(" mCardState=" + mCardState);
    370         pw.println(" mUniversalPinState=" + mUniversalPinState);
    371         pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex);
    372         pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex);
    373         pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
    374         pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex);
    375         pw.println(" mUiccApplications: length=" + mUiccApplications.length);
    376         for (int i = 0; i < mUiccApplications.length; i++) {
    377             if (mUiccApplications[i] == null) {
    378                 pw.println("  mUiccApplications[" + i + "]=" + null);
    379             } else {
    380                 pw.println("  mUiccApplications[" + i + "]="
    381                         + mUiccApplications[i].getType() + " " + mUiccApplications[i]);
    382             }
    383         }
    384         pw.println();
    385         // Print details of all applications
    386         for (UiccCardApplication app : mUiccApplications) {
    387             if (app != null) {
    388                 app.dump(fd, pw, args);
    389                 pw.println();
    390             }
    391         }
    392         // Print details of all IccRecords
    393         for (UiccCardApplication app : mUiccApplications) {
    394             if (app != null) {
    395                 IccRecords ir = app.getIccRecords();
    396                 if (ir != null) {
    397                     ir.dump(fd, pw, args);
    398                     pw.println();
    399                 }
    400             }
    401         }
    402         pw.flush();
    403     }
    404 }
    405