UiccCard.java revision 49abb46bedc84a79e5057caab9baf6a5452a8ab7
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 public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) { 85 if (DBG) log("Creating"); 86 mCardState = ics.mCardState; 87 update(c, ci, ics); 88 } 89 90 public void dispose() { 91 synchronized (mLock) { 92 if (DBG) log("Disposing card"); 93 if (mCatService != null) mCatService.dispose(); 94 for (UiccCardApplication app : mUiccApplications) { 95 if (app != null) { 96 app.dispose(); 97 } 98 } 99 mCatService = null; 100 mUiccApplications = null; 101 } 102 } 103 104 public void update(Context c, CommandsInterface ci, IccCardStatus ics) { 105 synchronized (mLock) { 106 if (mDestroyed) { 107 loge("Updated after destroyed! Fix me!"); 108 return; 109 } 110 CardState oldState = mCardState; 111 mCardState = ics.mCardState; 112 mUniversalPinState = ics.mUniversalPinState; 113 mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex; 114 mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex; 115 mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex; 116 mContext = c; 117 mCi = ci; 118 //update applications 119 if (DBG) log(ics.mApplications.length + " applications"); 120 for ( int i = 0; i < mUiccApplications.length; i++) { 121 if (mUiccApplications[i] == null) { 122 //Create newly added Applications 123 if (i < ics.mApplications.length) { 124 mUiccApplications[i] = new UiccCardApplication(this, 125 ics.mApplications[i], mContext, mCi); 126 } 127 } else if (i >= ics.mApplications.length) { 128 //Delete removed applications 129 mUiccApplications[i].dispose(); 130 mUiccApplications[i] = null; 131 } else { 132 //Update the rest 133 mUiccApplications[i].update(ics.mApplications[i], mContext, mCi); 134 } 135 } 136 137 if (mUiccApplications.length > 0 && mUiccApplications[0] != null) { 138 // Initialize or Reinitialize CatService 139 mCatService = CatService.getInstance(mCi, 140 mContext, 141 this); 142 } else { 143 if (mCatService != null) { 144 mCatService.dispose(); 145 } 146 mCatService = null; 147 } 148 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 @Override 172 protected void finalize() { 173 if (DBG) log("UiccCard finalized"); 174 } 175 176 /** 177 * This function makes sure that application indexes are valid 178 * and resets invalid indexes. (This should never happen, but in case 179 * RIL misbehaves we need to manage situation gracefully) 180 */ 181 private void sanitizeApplicationIndexes() { 182 mGsmUmtsSubscriptionAppIndex = 183 checkIndex(mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM); 184 mCdmaSubscriptionAppIndex = 185 checkIndex(mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM); 186 mImsSubscriptionAppIndex = 187 checkIndex(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null); 188 } 189 190 private int checkIndex(int index, AppType expectedAppType, AppType altExpectedAppType) { 191 if (mUiccApplications == null || index >= mUiccApplications.length) { 192 loge("App index " + index + " is invalid since there are no applications"); 193 return -1; 194 } 195 196 if (index < 0) { 197 // This is normal. (i.e. no application of this type) 198 return -1; 199 } 200 201 if (mUiccApplications[index].getType() != expectedAppType && 202 mUiccApplications[index].getType() != altExpectedAppType) { 203 loge("App index " + index + " is invalid since it's not " + 204 expectedAppType + " and not " + altExpectedAppType); 205 return -1; 206 } 207 208 // Seems to be valid 209 return index; 210 } 211 212 /** 213 * Notifies handler of any transition into State.ABSENT 214 */ 215 public void registerForAbsent(Handler h, int what, Object obj) { 216 synchronized (mLock) { 217 Registrant r = new Registrant (h, what, obj); 218 219 mAbsentRegistrants.add(r); 220 221 if (mCardState == CardState.CARDSTATE_ABSENT) { 222 r.notifyRegistrant(); 223 } 224 } 225 } 226 227 public void unregisterForAbsent(Handler h) { 228 synchronized (mLock) { 229 mAbsentRegistrants.remove(h); 230 } 231 } 232 233 private void onIccSwap(boolean isAdded) { 234 synchronized (mLock) { 235 // TODO: Here we assume the device can't handle SIM hot-swap 236 // and has to reboot. We may want to add a property, 237 // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support 238 // hot-swap. 239 DialogInterface.OnClickListener listener = null; 240 241 242 // TODO: SimRecords is not reset while SIM ABSENT (only reset while 243 // Radio_off_or_not_available). Have to reset in both both 244 // added or removed situation. 245 listener = new DialogInterface.OnClickListener() { 246 @Override 247 public void onClick(DialogInterface dialog, int which) { 248 synchronized (mLock) { 249 if (which == DialogInterface.BUTTON_POSITIVE) { 250 if (DBG) log("Reboot due to SIM swap"); 251 PowerManager pm = (PowerManager) mContext 252 .getSystemService(Context.POWER_SERVICE); 253 pm.reboot("SIM is added."); 254 } 255 } 256 } 257 258 }; 259 260 Resources r = Resources.getSystem(); 261 262 String title = (isAdded) ? r.getString(R.string.sim_added_title) : 263 r.getString(R.string.sim_removed_title); 264 String message = (isAdded) ? r.getString(R.string.sim_added_message) : 265 r.getString(R.string.sim_removed_message); 266 String buttonTxt = r.getString(R.string.sim_restart_button); 267 268 AlertDialog dialog = new AlertDialog.Builder(mContext) 269 .setTitle(title) 270 .setMessage(message) 271 .setPositiveButton(buttonTxt, listener) 272 .create(); 273 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 274 dialog.show(); 275 } 276 } 277 278 protected Handler mHandler = new Handler() { 279 @Override 280 public void handleMessage(Message msg){ 281 if (mDestroyed) { 282 loge("Received message " + msg + "[" + msg.what 283 + "] while being destroyed. Ignoring."); 284 return; 285 } 286 287 switch (msg.what) { 288 case EVENT_CARD_REMOVED: 289 onIccSwap(false); 290 break; 291 case EVENT_CARD_ADDED: 292 onIccSwap(true); 293 break; 294 case EVENT_OPEN_LOGICAL_CHANNEL_DONE: 295 case EVENT_CLOSE_LOGICAL_CHANNEL_DONE: 296 case EVENT_TRANSMIT_APDU_DONE: 297 AsyncResult ar = (AsyncResult)msg.obj; 298 if (ar.exception != null) { 299 if (DBG) 300 log("Error in SIM access with exception" + ar.exception); 301 } 302 AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception); 303 ((Message)ar.userObj).sendToTarget(); 304 break; 305 default: 306 loge("Unknown Event " + msg.what); 307 } 308 } 309 }; 310 311 public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) { 312 synchronized (mLock) { 313 for (int i = 0 ; i < mUiccApplications.length; i++) { 314 if (mUiccApplications[i] != null && mUiccApplications[i].getType() == type) { 315 return true; 316 } 317 } 318 return false; 319 } 320 } 321 322 public CardState getCardState() { 323 synchronized (mLock) { 324 return mCardState; 325 } 326 } 327 328 public PinState getUniversalPinState() { 329 synchronized (mLock) { 330 return mUniversalPinState; 331 } 332 } 333 334 public UiccCardApplication getApplication(int family) { 335 synchronized (mLock) { 336 int index = IccCardStatus.CARD_MAX_APPS; 337 switch (family) { 338 case UiccController.APP_FAM_3GPP: 339 index = mGsmUmtsSubscriptionAppIndex; 340 break; 341 case UiccController.APP_FAM_3GPP2: 342 index = mCdmaSubscriptionAppIndex; 343 break; 344 case UiccController.APP_FAM_IMS: 345 index = mImsSubscriptionAppIndex; 346 break; 347 } 348 if (index >= 0 && index < mUiccApplications.length) { 349 return mUiccApplications[index]; 350 } 351 return null; 352 } 353 } 354 355 public UiccCardApplication getApplicationIndex(int index) { 356 synchronized (mLock) { 357 if (index >= 0 && index < mUiccApplications.length) { 358 return mUiccApplications[index]; 359 } 360 return null; 361 } 362 } 363 364 /** 365 * Exposes {@link CommandsInterface.iccOpenLogicalChannel} 366 */ 367 public void iccOpenLogicalChannel(String AID, Message response) { 368 mCi.iccOpenLogicalChannel(AID, 369 mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response)); 370 } 371 372 /** 373 * Exposes {@link CommandsInterface.iccCloseLogicalChannel} 374 */ 375 public void iccCloseLogicalChannel(int channel, Message response) { 376 mCi.iccCloseLogicalChannel(channel, 377 mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response)); 378 } 379 380 /** 381 * Exposes {@link CommandsInterface.iccTransmitApduLogicalChannel} 382 */ 383 public void iccTransmitApduLogicalChannel(int channel, int cla, int command, 384 int p1, int p2, int p3, String data, Message response) { 385 mCi.iccTransmitApduLogicalChannel(channel, cla, command, p1, p2, p3, 386 data, mHandler.obtainMessage(EVENT_TRANSMIT_APDU_DONE, response)); 387 } 388 389 private void log(String msg) { 390 Rlog.d(LOG_TAG, msg); 391 } 392 393 private void loge(String msg) { 394 Rlog.e(LOG_TAG, msg); 395 } 396 397 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 398 pw.println("UiccCard:"); 399 pw.println(" mCi=" + mCi); 400 pw.println(" mDestroyed=" + mDestroyed); 401 pw.println(" mLastRadioState=" + mLastRadioState); 402 pw.println(" mCatService=" + mCatService); 403 pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size()); 404 for (int i = 0; i < mAbsentRegistrants.size(); i++) { 405 pw.println(" mAbsentRegistrants[" + i + "]=" 406 + ((Registrant)mAbsentRegistrants.get(i)).getHandler()); 407 } 408 pw.println(" mCardState=" + mCardState); 409 pw.println(" mUniversalPinState=" + mUniversalPinState); 410 pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex); 411 pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex); 412 pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex); 413 pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex); 414 pw.println(" mUiccApplications: length=" + mUiccApplications.length); 415 for (int i = 0; i < mUiccApplications.length; i++) { 416 if (mUiccApplications[i] == null) { 417 pw.println(" mUiccApplications[" + i + "]=" + null); 418 } else { 419 pw.println(" mUiccApplications[" + i + "]=" 420 + mUiccApplications[i].getType() + " " + mUiccApplications[i]); 421 } 422 } 423 pw.println(); 424 // Print details of all applications 425 for (UiccCardApplication app : mUiccApplications) { 426 if (app != null) { 427 app.dump(fd, pw, args); 428 pw.println(); 429 } 430 } 431 // Print details of all IccRecords 432 for (UiccCardApplication app : mUiccApplications) { 433 if (app != null) { 434 IccRecords ir = app.getIccRecords(); 435 if (ir != null) { 436 ir.dump(fd, pw, args); 437 pw.println(); 438 } 439 } 440 } 441 pw.flush(); 442 } 443} 444