UiccCard.java revision d720945f2be5ea5fe0faf67e67d9ea0e184eba67
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 52/** 53 * {@hide} 54 */ 55public class UiccCard { 56 protected static final String LOG_TAG = "RIL_UiccCard"; 57 protected static final boolean DBG = true; 58 59 private final Object mLock = new Object(); 60 private CardState mCardState; 61 private PinState mUniversalPinState; 62 private int mGsmUmtsSubscriptionAppIndex; 63 private int mCdmaSubscriptionAppIndex; 64 private int mImsSubscriptionAppIndex; 65 private UiccCardApplication[] mUiccApplications = 66 new UiccCardApplication[IccCardStatus.CARD_MAX_APPS]; 67 private Context mContext; 68 private CommandsInterface mCi; 69 private CatService mCatService; 70 private boolean mDestroyed = false; //set to true once this card is commanded to be disposed of. 71 private RadioState mLastRadioState = RadioState.RADIO_UNAVAILABLE; 72 73 private RegistrantList mAbsentRegistrants = new RegistrantList(); 74 75 private static final int EVENT_CARD_REMOVED = 13; 76 private static final int EVENT_CARD_ADDED = 14; 77 78 public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) { 79 if (DBG) log("Creating"); 80 mCardState = ics.mCardState; 81 update(c, ci, ics); 82 } 83 84 public void dispose() { 85 synchronized (mLock) { 86 if (DBG) log("Disposing card"); 87 if (mCatService != null) mCatService.dispose(); 88 for (UiccCardApplication app : mUiccApplications) { 89 if (app != null) { 90 app.dispose(); 91 } 92 } 93 mCatService = null; 94 mUiccApplications = null; 95 } 96 } 97 98 public void update(Context c, CommandsInterface ci, IccCardStatus ics) { 99 synchronized (mLock) { 100 if (mDestroyed) { 101 loge("Updated after destroyed! Fix me!"); 102 return; 103 } 104 CardState oldState = mCardState; 105 mCardState = ics.mCardState; 106 mUniversalPinState = ics.mUniversalPinState; 107 mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex; 108 mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex; 109 mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex; 110 mContext = c; 111 mCi = ci; 112 //update applications 113 if (DBG) log(ics.mApplications.length + " applications"); 114 for ( int i = 0; i < mUiccApplications.length; i++) { 115 if (mUiccApplications[i] == null) { 116 //Create newly added Applications 117 if (i < ics.mApplications.length) { 118 mUiccApplications[i] = new UiccCardApplication(this, 119 ics.mApplications[i], mContext, mCi); 120 } 121 } else if (i >= ics.mApplications.length) { 122 //Delete removed applications 123 mUiccApplications[i].dispose(); 124 mUiccApplications[i] = null; 125 } else { 126 //Update the rest 127 mUiccApplications[i].update(ics.mApplications[i], mContext, mCi); 128 } 129 } 130 131 if (mUiccApplications.length > 0 && mUiccApplications[0] != null) { 132 // Initialize or Reinitialize CatService 133 mCatService = CatService.getInstance(mCi, 134 mContext, 135 this); 136 } else { 137 if (mCatService != null) { 138 mCatService.dispose(); 139 } 140 mCatService = null; 141 } 142 143 sanitizeApplicationIndexes(); 144 145 RadioState radioState = mCi.getRadioState(); 146 if (DBG) log("update: radioState=" + radioState + " mLastRadioState=" 147 + mLastRadioState); 148 // No notifications while radio is off or we just powering up 149 if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) { 150 if (oldState != CardState.CARDSTATE_ABSENT && 151 mCardState == CardState.CARDSTATE_ABSENT) { 152 if (DBG) log("update: notify card removed"); 153 mAbsentRegistrants.notifyRegistrants(); 154 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null)); 155 } else if (oldState == CardState.CARDSTATE_ABSENT && 156 mCardState != CardState.CARDSTATE_ABSENT) { 157 if (DBG) log("update: notify card added"); 158 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null)); 159 } 160 } 161 mLastRadioState = radioState; 162 } 163 } 164 165 protected void finalize() { 166 if (DBG) log("UiccCard finalized"); 167 } 168 169 /** 170 * This function makes sure that application indexes are valid 171 * and resets invalid indexes. (This should never happen, but in case 172 * RIL misbehaves we need to manage situation gracefully) 173 */ 174 private void sanitizeApplicationIndexes() { 175 mGsmUmtsSubscriptionAppIndex = 176 checkIndex(mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM); 177 mCdmaSubscriptionAppIndex = 178 checkIndex(mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM); 179 mImsSubscriptionAppIndex = 180 checkIndex(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null); 181 } 182 183 private int checkIndex(int index, AppType expectedAppType, AppType altExpectedAppType) { 184 if (mUiccApplications == null || index >= mUiccApplications.length) { 185 loge("App index " + index + " is invalid since there are no applications"); 186 return -1; 187 } 188 189 if (index < 0) { 190 // This is normal. (i.e. no application of this type) 191 return -1; 192 } 193 194 if (mUiccApplications[index].getType() != expectedAppType && 195 mUiccApplications[index].getType() != altExpectedAppType) { 196 loge("App index " + index + " is invalid since it's not " + 197 expectedAppType + " and not " + altExpectedAppType); 198 return -1; 199 } 200 201 // Seems to be valid 202 return index; 203 } 204 205 /** 206 * Notifies handler of any transition into State.ABSENT 207 */ 208 public void registerForAbsent(Handler h, int what, Object obj) { 209 synchronized (mLock) { 210 Registrant r = new Registrant (h, what, obj); 211 212 mAbsentRegistrants.add(r); 213 214 if (mCardState == CardState.CARDSTATE_ABSENT) { 215 r.notifyRegistrant(); 216 } 217 } 218 } 219 220 public void unregisterForAbsent(Handler h) { 221 synchronized (mLock) { 222 mAbsentRegistrants.remove(h); 223 } 224 } 225 226 private void onIccSwap(boolean isAdded) { 227 synchronized (mLock) { 228 // TODO: Here we assume the device can't handle SIM hot-swap 229 // and has to reboot. We may want to add a property, 230 // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support 231 // hot-swap. 232 DialogInterface.OnClickListener listener = null; 233 234 235 // TODO: SimRecords is not reset while SIM ABSENT (only reset while 236 // Radio_off_or_not_available). Have to reset in both both 237 // added or removed situation. 238 listener = new DialogInterface.OnClickListener() { 239 @Override 240 public void onClick(DialogInterface dialog, int which) { 241 synchronized (mLock) { 242 if (which == DialogInterface.BUTTON_POSITIVE) { 243 if (DBG) log("Reboot due to SIM swap"); 244 PowerManager pm = (PowerManager) mContext 245 .getSystemService(Context.POWER_SERVICE); 246 pm.reboot("SIM is added."); 247 } 248 } 249 } 250 251 }; 252 253 Resources r = Resources.getSystem(); 254 255 String title = (isAdded) ? r.getString(R.string.sim_added_title) : 256 r.getString(R.string.sim_removed_title); 257 String message = (isAdded) ? r.getString(R.string.sim_added_message) : 258 r.getString(R.string.sim_removed_message); 259 String buttonTxt = r.getString(R.string.sim_restart_button); 260 261 AlertDialog dialog = new AlertDialog.Builder(mContext) 262 .setTitle(title) 263 .setMessage(message) 264 .setPositiveButton(buttonTxt, listener) 265 .create(); 266 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 267 dialog.show(); 268 } 269 } 270 271 protected Handler mHandler = new Handler() { 272 @Override 273 public void handleMessage(Message msg){ 274 if (mDestroyed) { 275 loge("Received message " + msg + "[" + msg.what 276 + "] while being destroyed. Ignoring."); 277 return; 278 } 279 280 switch (msg.what) { 281 case EVENT_CARD_REMOVED: 282 onIccSwap(false); 283 break; 284 case EVENT_CARD_ADDED: 285 onIccSwap(true); 286 break; 287 default: 288 loge("Unknown Event " + msg.what); 289 } 290 } 291 }; 292 293 public boolean isApplicationOnIcc(IccCardApplicationStatus.AppType type) { 294 synchronized (mLock) { 295 for (int i = 0 ; i < mUiccApplications.length; i++) { 296 if (mUiccApplications[i] != null && mUiccApplications[i].getType() == type) { 297 return true; 298 } 299 } 300 return false; 301 } 302 } 303 304 public CardState getCardState() { 305 synchronized (mLock) { 306 return mCardState; 307 } 308 } 309 310 public PinState getUniversalPinState() { 311 synchronized (mLock) { 312 return mUniversalPinState; 313 } 314 } 315 316 public UiccCardApplication getApplication(int family) { 317 synchronized (mLock) { 318 int index = IccCardStatus.CARD_MAX_APPS; 319 switch (family) { 320 case UiccController.APP_FAM_3GPP: 321 index = mGsmUmtsSubscriptionAppIndex; 322 break; 323 case UiccController.APP_FAM_3GPP2: 324 index = mCdmaSubscriptionAppIndex; 325 break; 326 case UiccController.APP_FAM_IMS: 327 index = mImsSubscriptionAppIndex; 328 break; 329 } 330 if (index >= 0 && index < mUiccApplications.length) { 331 return mUiccApplications[index]; 332 } 333 return null; 334 } 335 } 336 337 public UiccCardApplication getApplicationIndex(int index) { 338 synchronized (mLock) { 339 if (index >= 0 && index < mUiccApplications.length) { 340 return mUiccApplications[index]; 341 } 342 return null; 343 } 344 } 345 346 private void log(String msg) { 347 Rlog.d(LOG_TAG, msg); 348 } 349 350 private void loge(String msg) { 351 Rlog.e(LOG_TAG, msg); 352 } 353} 354