UiccCard.java revision a8467dd0c524787104b1ccdddc5e8af10ba729ed
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 17package com.android.internal.telephony.uicc; 18 19import static android.Manifest.permission.READ_PHONE_STATE; 20import android.app.ActivityManagerNative; 21import android.app.AlertDialog; 22import android.content.Context; 23import android.content.DialogInterface; 24import android.content.Intent; 25import android.content.res.Resources; 26import android.os.AsyncResult; 27import android.os.Handler; 28import android.os.Message; 29import android.os.PowerManager; 30import android.os.Registrant; 31import android.os.RegistrantList; 32import android.telephony.Rlog; 33import android.view.WindowManager; 34 35import com.android.internal.telephony.CommandsInterface; 36import com.android.internal.telephony.PhoneBase; 37import com.android.internal.telephony.CommandsInterface.RadioState; 38import com.android.internal.telephony.IccCardConstants.State; 39import com.android.internal.telephony.gsm.GSMPhone; 40import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType; 41import com.android.internal.telephony.uicc.IccCardStatus.CardState; 42import com.android.internal.telephony.uicc.IccCardStatus.PinState; 43import com.android.internal.telephony.cat.CatService; 44import com.android.internal.telephony.cdma.CDMALTEPhone; 45import com.android.internal.telephony.cdma.CDMAPhone; 46import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; 47 48import android.os.SystemProperties; 49 50import com.android.internal.R; 51 52import java.io.FileDescriptor; 53import java.io.PrintWriter; 54 55/** 56 * {@hide} 57 */ 58public 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 private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 15; 81 private static final int EVENT_CLOSE_LOGICAL_CHANNEL_DONE = 16; 82 private static final int EVENT_TRANSMIT_APDU_DONE = 17; 83 84 int mSlotId; 85 86 public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) { 87 if (DBG) log("Creating"); 88 mCardState = ics.mCardState; 89 update(c, ci, ics); 90 } 91 92 public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int slotId) { 93 mCardState = ics.mCardState; 94 mSlotId = slotId; 95 update(c, ci, ics); 96 } 97 98 protected UiccCard() { 99 } 100 101 public void dispose() { 102 synchronized (mLock) { 103 if (DBG) log("Disposing card"); 104 if (mCatService != null) mCatService.dispose(); 105 for (UiccCardApplication app : mUiccApplications) { 106 if (app != null) { 107 app.dispose(); 108 } 109 } 110 mCatService = null; 111 mUiccApplications = null; 112 } 113 } 114 115 public void update(Context c, CommandsInterface ci, IccCardStatus ics) { 116 synchronized (mLock) { 117 if (mDestroyed) { 118 loge("Updated after destroyed! Fix me!"); 119 return; 120 } 121 CardState oldState = mCardState; 122 mCardState = ics.mCardState; 123 mUniversalPinState = ics.mUniversalPinState; 124 mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex; 125 mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex; 126 mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex; 127 mContext = c; 128 mCi = ci; 129 //update applications 130 if (DBG) log(ics.mApplications.length + " applications"); 131 for ( int i = 0; i < mUiccApplications.length; i++) { 132 if (mUiccApplications[i] == null) { 133 //Create newly added Applications 134 if (i < ics.mApplications.length) { 135 mUiccApplications[i] = new UiccCardApplication(this, 136 ics.mApplications[i], mContext, mCi); 137 } 138 } else if (i >= ics.mApplications.length) { 139 //Delete removed applications 140 mUiccApplications[i].dispose(); 141 mUiccApplications[i] = null; 142 } else { 143 //Update the rest 144 mUiccApplications[i].update(ics.mApplications[i], mContext, mCi); 145 } 146 } 147 148 createAndUpdateCatService(); 149 sanitizeApplicationIndexes(); 150 151 RadioState radioState = mCi.getRadioState(); 152 if (DBG) log("update: radioState=" + radioState + " mLastRadioState=" 153 + mLastRadioState); 154 // No notifications while radio is off or we just powering up 155 if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) { 156 if (oldState != CardState.CARDSTATE_ABSENT && 157 mCardState == CardState.CARDSTATE_ABSENT) { 158 if (DBG) log("update: notify card removed"); 159 mAbsentRegistrants.notifyRegistrants(); 160 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null)); 161 } else if (oldState == CardState.CARDSTATE_ABSENT && 162 mCardState != CardState.CARDSTATE_ABSENT) { 163 if (DBG) log("update: notify card added"); 164 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null)); 165 } 166 } 167 mLastRadioState = radioState; 168 } 169 } 170 171 protected void createAndUpdateCatService() { 172 if (mUiccApplications.length > 0 && mUiccApplications[0] != null) { 173 // Initialize or Reinitialize CatService 174 if (mCatService == null) { 175 mCatService = CatService.getInstance(mCi, mContext, this, mSlotId); 176 } else { 177 ((CatService)mCatService).update(mCi, mContext, this); 178 } 179 } else { 180 if (mCatService != null) { 181 mCatService.dispose(); 182 } 183 mCatService = null; 184 } 185 } 186 187 public CatService getCatService() { 188 return mCatService; 189 } 190 191 @Override 192 protected void finalize() { 193 if (DBG) log("UiccCard finalized"); 194 } 195 196 /** 197 * This function makes sure that application indexes are valid 198 * and resets invalid indexes. (This should never happen, but in case 199 * RIL misbehaves we need to manage situation gracefully) 200 */ 201 private void sanitizeApplicationIndexes() { 202 mGsmUmtsSubscriptionAppIndex = 203 checkIndex(mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM); 204 mCdmaSubscriptionAppIndex = 205 checkIndex(mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM); 206 mImsSubscriptionAppIndex = 207 checkIndex(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null); 208 } 209 210 private int checkIndex(int index, AppType expectedAppType, AppType altExpectedAppType) { 211 if (mUiccApplications == null || index >= mUiccApplications.length) { 212 loge("App index " + index + " is invalid since there are no applications"); 213 return -1; 214 } 215 216 if (index < 0) { 217 // This is normal. (i.e. no application of this type) 218 return -1; 219 } 220 221 if (mUiccApplications[index].getType() != expectedAppType && 222 mUiccApplications[index].getType() != altExpectedAppType) { 223 loge("App index " + index + " is invalid since it's not " + 224 expectedAppType + " and not " + altExpectedAppType); 225 return -1; 226 } 227 228 // Seems to be valid 229 return index; 230 } 231 232 /** 233 * Notifies handler of any transition into State.ABSENT 234 */ 235 public void registerForAbsent(Handler h, int what, Object obj) { 236 synchronized (mLock) { 237 Registrant r = new Registrant (h, what, obj); 238 239 mAbsentRegistrants.add(r); 240 241 if (mCardState == CardState.CARDSTATE_ABSENT) { 242 r.notifyRegistrant(); 243 } 244 } 245 } 246 247 public void unregisterForAbsent(Handler h) { 248 synchronized (mLock) { 249 mAbsentRegistrants.remove(h); 250 } 251 } 252 253 private void onIccSwap(boolean isAdded) { 254 synchronized (mLock) { 255 // TODO: Here we assume the device can't handle SIM hot-swap 256 // and has to reboot. We may want to add a property, 257 // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support 258 // hot-swap. 259 DialogInterface.OnClickListener listener = null; 260 261 262 // TODO: SimRecords is not reset while SIM ABSENT (only reset while 263 // Radio_off_or_not_available). Have to reset in both both 264 // added or removed situation. 265 listener = new DialogInterface.OnClickListener() { 266 @Override 267 public void onClick(DialogInterface dialog, int which) { 268 synchronized (mLock) { 269 if (which == DialogInterface.BUTTON_POSITIVE) { 270 if (DBG) log("Reboot due to SIM swap"); 271 PowerManager pm = (PowerManager) mContext 272 .getSystemService(Context.POWER_SERVICE); 273 pm.reboot("SIM is added."); 274 } 275 } 276 } 277 278 }; 279 280 Resources r = Resources.getSystem(); 281 282 String title = (isAdded) ? r.getString(R.string.sim_added_title) : 283 r.getString(R.string.sim_removed_title); 284 String message = (isAdded) ? r.getString(R.string.sim_added_message) : 285 r.getString(R.string.sim_removed_message); 286 String buttonTxt = r.getString(R.string.sim_restart_button); 287 288 AlertDialog dialog = new AlertDialog.Builder(mContext) 289 .setTitle(title) 290 .setMessage(message) 291 .setPositiveButton(buttonTxt, listener) 292 .create(); 293 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 294 dialog.show(); 295 } 296 } 297 298 protected Handler mHandler = new Handler() { 299 @Override 300 public void handleMessage(Message msg){ 301 if (mDestroyed) { 302 loge("Received message " + msg + "[" + msg.what 303 + "] while being destroyed. Ignoring."); 304 return; 305 } 306 307 switch (msg.what) { 308 case EVENT_CARD_REMOVED: 309 onIccSwap(false); 310 break; 311 case EVENT_CARD_ADDED: 312 onIccSwap(true); 313 break; 314 case EVENT_OPEN_LOGICAL_CHANNEL_DONE: 315 case EVENT_CLOSE_LOGICAL_CHANNEL_DONE: 316 case EVENT_TRANSMIT_APDU_DONE: 317 AsyncResult ar = (AsyncResult)msg.obj; 318 if (ar.exception != null) { 319 if (DBG) 320 log("Error in SIM access with exception" + ar.exception); 321 } 322 AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception); 323 ((Message)ar.userObj).sendToTarget(); 324 break; 325 default: 326 loge("Unknown Event " + msg.what); 327 } 328 } 329 }; 330 331 public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) { 332 synchronized (mLock) { 333 for (int i = 0 ; i < mUiccApplications.length; i++) { 334 if (mUiccApplications[i] != null && mUiccApplications[i].getType() == type) { 335 return true; 336 } 337 } 338 return false; 339 } 340 } 341 342 public CardState getCardState() { 343 synchronized (mLock) { 344 return mCardState; 345 } 346 } 347 348 public PinState getUniversalPinState() { 349 synchronized (mLock) { 350 return mUniversalPinState; 351 } 352 } 353 354 public UiccCardApplication getApplication(int family) { 355 synchronized (mLock) { 356 int index = IccCardStatus.CARD_MAX_APPS; 357 switch (family) { 358 case UiccController.APP_FAM_3GPP: 359 index = mGsmUmtsSubscriptionAppIndex; 360 break; 361 case UiccController.APP_FAM_3GPP2: 362 index = mCdmaSubscriptionAppIndex; 363 break; 364 case UiccController.APP_FAM_IMS: 365 index = mImsSubscriptionAppIndex; 366 break; 367 } 368 if (index >= 0 && index < mUiccApplications.length) { 369 return mUiccApplications[index]; 370 } 371 return null; 372 } 373 } 374 375 public UiccCardApplication getApplicationIndex(int index) { 376 synchronized (mLock) { 377 if (index >= 0 && index < mUiccApplications.length) { 378 return mUiccApplications[index]; 379 } 380 return null; 381 } 382 } 383 384 /** 385 * Returns the SIM application of the specified type. 386 * 387 * @param type ICC application type (@see com.android.internal.telephony.PhoneConstants#APPTYPE_xxx) 388 * @return application corresponding to type or a null if no match found 389 */ 390 public UiccCardApplication getApplicationByType(int type) { 391 synchronized (mLock) { 392 for (int i = 0 ; i < mUiccApplications.length; i++) { 393 if (mUiccApplications[i] != null && 394 mUiccApplications[i].getType().ordinal() == type) { 395 return mUiccApplications[i]; 396 } 397 } 398 return null; 399 } 400 } 401 402 /** 403 * Exposes {@link CommandsInterface.iccOpenLogicalChannel} 404 */ 405 public void iccOpenLogicalChannel(String AID, Message response) { 406 mCi.iccOpenLogicalChannel(AID, 407 mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response)); 408 } 409 410 /** 411 * Exposes {@link CommandsInterface.iccCloseLogicalChannel} 412 */ 413 public void iccCloseLogicalChannel(int channel, Message response) { 414 mCi.iccCloseLogicalChannel(channel, 415 mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response)); 416 } 417 418 /** 419 * Exposes {@link CommandsInterface.iccTransmitApduLogicalChannel} 420 */ 421 public void iccTransmitApduLogicalChannel(int channel, int cla, int command, 422 int p1, int p2, int p3, String data, Message response) { 423 mCi.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3, 424 data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_DONE, response)); 425 } 426 427 /** 428 * Exposes {@link CommandsInterface.sendEnvelopeWithStatus} 429 */ 430 public void sendEnvelopeWithStatus(String contents, Message response) { 431 mCi.sendEnvelopeWithStatus(contents, response); 432 } 433 434 /* Returns number of applications on this card */ 435 public int getNumApplications() { 436 int count = 0; 437 for (UiccCardApplication a : mUiccApplications) { 438 if (a != null) { 439 count++; 440 } 441 } 442 return count; 443 } 444 445 private void log(String msg) { 446 Rlog.d(LOG_TAG, msg); 447 } 448 449 private void loge(String msg) { 450 Rlog.e(LOG_TAG, msg); 451 } 452 453 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 454 pw.println("UiccCard:"); 455 pw.println(" mCi=" + mCi); 456 pw.println(" mDestroyed=" + mDestroyed); 457 pw.println(" mLastRadioState=" + mLastRadioState); 458 pw.println(" mCatService=" + mCatService); 459 pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size()); 460 for (int i = 0; i < mAbsentRegistrants.size(); i++) { 461 pw.println(" mAbsentRegistrants[" + i + "]=" 462 + ((Registrant)mAbsentRegistrants.get(i)).getHandler()); 463 } 464 pw.println(" mCardState=" + mCardState); 465 pw.println(" mUniversalPinState=" + mUniversalPinState); 466 pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex); 467 pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex); 468 pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex); 469 pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex); 470 pw.println(" mUiccApplications: length=" + mUiccApplications.length); 471 for (int i = 0; i < mUiccApplications.length; i++) { 472 if (mUiccApplications[i] == null) { 473 pw.println(" mUiccApplications[" + i + "]=" + null); 474 } else { 475 pw.println(" mUiccApplications[" + i + "]=" 476 + mUiccApplications[i].getType() + " " + mUiccApplications[i]); 477 } 478 } 479 pw.println(); 480 // Print details of all applications 481 for (UiccCardApplication app : mUiccApplications) { 482 if (app != null) { 483 app.dump(fd, pw, args); 484 pw.println(); 485 } 486 } 487 // Print details of all IccRecords 488 for (UiccCardApplication app : mUiccApplications) { 489 if (app != null) { 490 IccRecords ir = app.getIccRecords(); 491 if (ir != null) { 492 ir.dump(fd, pw, args); 493 pw.println(); 494 } 495 } 496 } 497 pw.flush(); 498 } 499} 500