UiccCard.java revision 05ef3b65972826780859b9acbd1fa9580d099832
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 = "RIL_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 protected void finalize() { 169 if (DBG) log("UiccCard finalized"); 170 } 171 172 /** 173 * This function makes sure that application indexes are valid 174 * and resets invalid indexes. (This should never happen, but in case 175 * RIL misbehaves we need to manage situation gracefully) 176 */ 177 private void sanitizeApplicationIndexes() { 178 mGsmUmtsSubscriptionAppIndex = 179 checkIndex(mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM); 180 mCdmaSubscriptionAppIndex = 181 checkIndex(mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM); 182 mImsSubscriptionAppIndex = 183 checkIndex(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null); 184 } 185 186 private int checkIndex(int index, AppType expectedAppType, AppType altExpectedAppType) { 187 if (mUiccApplications == null || index >= mUiccApplications.length) { 188 loge("App index " + index + " is invalid since there are no applications"); 189 return -1; 190 } 191 192 if (index < 0) { 193 // This is normal. (i.e. no application of this type) 194 return -1; 195 } 196 197 if (mUiccApplications[index].getType() != expectedAppType && 198 mUiccApplications[index].getType() != altExpectedAppType) { 199 loge("App index " + index + " is invalid since it's not " + 200 expectedAppType + " and not " + altExpectedAppType); 201 return -1; 202 } 203 204 // Seems to be valid 205 return index; 206 } 207 208 /** 209 * Notifies handler of any transition into State.ABSENT 210 */ 211 public void registerForAbsent(Handler h, int what, Object obj) { 212 synchronized (mLock) { 213 Registrant r = new Registrant (h, what, obj); 214 215 mAbsentRegistrants.add(r); 216 217 if (mCardState == CardState.CARDSTATE_ABSENT) { 218 r.notifyRegistrant(); 219 } 220 } 221 } 222 223 public void unregisterForAbsent(Handler h) { 224 synchronized (mLock) { 225 mAbsentRegistrants.remove(h); 226 } 227 } 228 229 private void onIccSwap(boolean isAdded) { 230 synchronized (mLock) { 231 // TODO: Here we assume the device can't handle SIM hot-swap 232 // and has to reboot. We may want to add a property, 233 // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support 234 // hot-swap. 235 DialogInterface.OnClickListener listener = null; 236 237 238 // TODO: SimRecords is not reset while SIM ABSENT (only reset while 239 // Radio_off_or_not_available). Have to reset in both both 240 // added or removed situation. 241 listener = new DialogInterface.OnClickListener() { 242 @Override 243 public void onClick(DialogInterface dialog, int which) { 244 synchronized (mLock) { 245 if (which == DialogInterface.BUTTON_POSITIVE) { 246 if (DBG) log("Reboot due to SIM swap"); 247 PowerManager pm = (PowerManager) mContext 248 .getSystemService(Context.POWER_SERVICE); 249 pm.reboot("SIM is added."); 250 } 251 } 252 } 253 254 }; 255 256 Resources r = Resources.getSystem(); 257 258 String title = (isAdded) ? r.getString(R.string.sim_added_title) : 259 r.getString(R.string.sim_removed_title); 260 String message = (isAdded) ? r.getString(R.string.sim_added_message) : 261 r.getString(R.string.sim_removed_message); 262 String buttonTxt = r.getString(R.string.sim_restart_button); 263 264 AlertDialog dialog = new AlertDialog.Builder(mContext) 265 .setTitle(title) 266 .setMessage(message) 267 .setPositiveButton(buttonTxt, listener) 268 .create(); 269 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 270 dialog.show(); 271 } 272 } 273 274 protected Handler mHandler = new Handler() { 275 @Override 276 public void handleMessage(Message msg){ 277 if (mDestroyed) { 278 loge("Received message " + msg + "[" + msg.what 279 + "] while being destroyed. Ignoring."); 280 return; 281 } 282 283 switch (msg.what) { 284 case EVENT_CARD_REMOVED: 285 onIccSwap(false); 286 break; 287 case EVENT_CARD_ADDED: 288 onIccSwap(true); 289 break; 290 default: 291 loge("Unknown Event " + msg.what); 292 } 293 } 294 }; 295 296 public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) { 297 synchronized (mLock) { 298 for (int i = 0 ; i < mUiccApplications.length; i++) { 299 if (mUiccApplications[i] != null && mUiccApplications[i].getType() == type) { 300 return true; 301 } 302 } 303 return false; 304 } 305 } 306 307 public CardState getCardState() { 308 synchronized (mLock) { 309 return mCardState; 310 } 311 } 312 313 public PinState getUniversalPinState() { 314 synchronized (mLock) { 315 return mUniversalPinState; 316 } 317 } 318 319 public UiccCardApplication getApplication(int family) { 320 synchronized (mLock) { 321 int index = IccCardStatus.CARD_MAX_APPS; 322 switch (family) { 323 case UiccController.APP_FAM_3GPP: 324 index = mGsmUmtsSubscriptionAppIndex; 325 break; 326 case UiccController.APP_FAM_3GPP2: 327 index = mCdmaSubscriptionAppIndex; 328 break; 329 case UiccController.APP_FAM_IMS: 330 index = mImsSubscriptionAppIndex; 331 break; 332 } 333 if (index >= 0 && index < mUiccApplications.length) { 334 return mUiccApplications[index]; 335 } 336 return null; 337 } 338 } 339 340 public UiccCardApplication getApplicationIndex(int index) { 341 synchronized (mLock) { 342 if (index >= 0 && index < mUiccApplications.length) { 343 return mUiccApplications[index]; 344 } 345 return null; 346 } 347 } 348 349 private void log(String msg) { 350 Rlog.d(LOG_TAG, msg); 351 } 352 353 private void loge(String msg) { 354 Rlog.e(LOG_TAG, msg); 355 } 356 357 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 358 pw.println("UiccCard:"); 359 pw.println(" mCi=" + mCi); 360 pw.println(" mDestroyed=" + mDestroyed); 361 pw.println(" mLastRadioState=" + mLastRadioState); 362 pw.println(" mCatService=" + mCatService); 363 pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size()); 364 for (int i = 0; i < mAbsentRegistrants.size(); i++) { 365 pw.println(" mAbsentRegistrants[" + i + "]=" 366 + ((Registrant)mAbsentRegistrants.get(i)).getHandler()); 367 } 368 pw.println(" mCardState=" + mCardState); 369 pw.println(" mUniversalPinState=" + mUniversalPinState); 370 pw.println(" mGsmUmtsSubscriptionAppIndex=" + mGsmUmtsSubscriptionAppIndex); 371 pw.println(" mCdmaSubscriptionAppIndex=" + mCdmaSubscriptionAppIndex); 372 pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex); 373 pw.println(" mImsSubscriptionAppIndex=" + mImsSubscriptionAppIndex); 374 pw.println(" mUiccApplications: length=" + mUiccApplications.length); 375 for (int i = 0; i < mUiccApplications.length; i++) { 376 if (mUiccApplications[i] == null) { 377 pw.println(" mUiccApplications[" + i + "]=" + null); 378 } else { 379 pw.println(" mUiccApplications[" + i + "]=" 380 + mUiccApplications[i].getType() + " " + mUiccApplications[i]); 381 } 382 } 383 pw.println(); 384 // Print details of all applications 385 for (UiccCardApplication app : mUiccApplications) { 386 if (app != null) { 387 app.dump(fd, pw, args); 388 pw.println(); 389 } 390 } 391 // Print details of all IccRecords 392 for (UiccCardApplication app : mUiccApplications) { 393 if (app != null) { 394 IccRecords ir = app.getIccRecords(); 395 if (ir != null) { 396 ir.dump(fd, pw, args); 397 pw.println(); 398 } 399 } 400 } 401 pw.flush(); 402 } 403} 404