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