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