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