UiccSlot.java revision de72f77373b6b38cd650133c89f6cf72c55ac06b
1/* 2 * Copyright 2017 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 android.app.AlertDialog; 20import android.content.ActivityNotFoundException; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.DialogInterface; 24import android.content.Intent; 25import android.content.res.Resources; 26import android.os.Handler; 27import android.os.Message; 28import android.os.PowerManager; 29import android.telephony.Rlog; 30import android.view.WindowManager; 31 32import com.android.internal.R; 33import com.android.internal.telephony.CommandsInterface; 34import com.android.internal.telephony.CommandsInterface.RadioState; 35import com.android.internal.telephony.IccCardConstants; 36import com.android.internal.telephony.uicc.IccCardStatus.CardState; 37import com.android.internal.telephony.uicc.euicc.EuiccCard; 38 39import java.io.FileDescriptor; 40import java.io.PrintWriter; 41 42/** 43 * This class represents a physical slot on the device. 44 */ 45public class UiccSlot extends Handler { 46 private static final String TAG = "UiccSlot"; 47 private static final boolean DBG = true; 48 49 public static final String EXTRA_ICC_CARD_ADDED = 50 "com.android.internal.telephony.uicc.ICC_CARD_ADDED"; 51 public static final int INVALID_PHONE_ID = -1; 52 53 private final Object mLock = new Object(); 54 private boolean mActive; 55 private CardState mCardState; 56 private Context mContext; 57 private CommandsInterface mCi; 58 private UiccCard mUiccCard; 59 private RadioState mLastRadioState = RadioState.RADIO_UNAVAILABLE; 60 private boolean mIsEuicc; 61 private String mIccId; 62 private AnswerToReset mAtr; 63 private int mPhoneId = INVALID_PHONE_ID; 64 65 private static final int EVENT_CARD_REMOVED = 13; 66 private static final int EVENT_CARD_ADDED = 14; 67 68 public UiccSlot(Context c, boolean isActive) { 69 if (DBG) log("Creating"); 70 mContext = c; 71 mActive = isActive; 72 mCardState = null; 73 } 74 75 /** 76 * Update slot. The main trigger for this is a change in the ICC Card status. 77 */ 78 public void update(CommandsInterface ci, IccCardStatus ics, int phoneId) { 79 if (DBG) log("cardStatus update: " + ics.toString()); 80 synchronized (mLock) { 81 CardState oldState = mCardState; 82 mCardState = ics.mCardState; 83 mIccId = ics.iccid; 84 mPhoneId = phoneId; 85 parseAtr(ics.atr); 86 mCi = ci; 87 88 RadioState radioState = mCi.getRadioState(); 89 if (DBG) { 90 log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState); 91 } 92 93 if (oldState != CardState.CARDSTATE_ABSENT 94 && mCardState == CardState.CARDSTATE_ABSENT) { 95 // No notifications while radio is off or we just powering up 96 if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) { 97 if (DBG) log("update: notify card removed"); 98 sendMessage(obtainMessage(EVENT_CARD_REMOVED, null)); 99 } 100 101 UiccController.updateInternalIccState( 102 IccCardConstants.INTENT_VALUE_ICC_ABSENT, null, mPhoneId); 103 104 // no card present in the slot now; dispose card and make mUiccCard null 105 if (mUiccCard != null) { 106 mUiccCard.dispose(); 107 mUiccCard = null; 108 } 109 // Because mUiccCard may be updated in both IccCardStatus and IccSlotStatus, we need to 110 // create a new UiccCard instance in two scenarios: 111 // 1. mCardState is changing from ABSENT to non ABSENT. 112 // 2. The latest mCardState is not ABSENT, but there is no UiccCard instance. 113 } else if ((oldState == null || oldState == CardState.CARDSTATE_ABSENT 114 || mUiccCard == null) && mCardState != CardState.CARDSTATE_ABSENT) { 115 // No notifications while radio is off or we just powering up 116 if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) { 117 if (DBG) log("update: notify card added"); 118 sendMessage(obtainMessage(EVENT_CARD_ADDED, null)); 119 } 120 121 // card is present in the slot now; create new mUiccCard 122 if (mUiccCard != null) { 123 loge("update: mUiccCard != null when card was present; disposing it now"); 124 mUiccCard.dispose(); 125 } 126 127 if (!mIsEuicc) { 128 mUiccCard = new UiccCard(mContext, mCi, ics, mPhoneId); 129 } else { 130 mUiccCard = new EuiccCard(mContext, mCi, ics, phoneId); 131 } 132 } else { 133 if (mUiccCard != null) { 134 mUiccCard.update(mContext, mCi, ics); 135 } 136 } 137 mLastRadioState = radioState; 138 } 139 } 140 141 /** 142 * Update slot based on IccSlotStatus. 143 */ 144 public void update(CommandsInterface ci, IccSlotStatus iss) { 145 if (DBG) log("slotStatus update: " + iss.toString()); 146 synchronized (mLock) { 147 mCi = ci; 148 if (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_INACTIVE) { 149 if (mActive) { 150 mActive = false; 151 mLastRadioState = RadioState.RADIO_UNAVAILABLE; 152 mPhoneId = INVALID_PHONE_ID; 153 if (mUiccCard != null) mUiccCard.dispose(); 154 mUiccCard = null; 155 } 156 parseAtr(iss.atr); 157 mCardState = iss.cardState; 158 mIccId = iss.iccid; 159 } else if (!mActive && iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_ACTIVE) { 160 mActive = true; 161 parseAtr(iss.atr); 162 // todo - ignoring these fields for now; relying on sim state changed to update 163 // these 164 // iss.cardState; 165 // iss.iccid; 166 // iss.logicalSlotIndex; 167 } 168 } 169 } 170 171 private void checkIsEuiccSupported() { 172 if (mAtr != null && mAtr.isEuiccSupported()) { 173 mIsEuicc = true; 174 } else { 175 mIsEuicc = false; 176 } 177 } 178 179 private void parseAtr(String atr) { 180 mAtr = AnswerToReset.parseAtr(atr); 181 if (mAtr == null) { 182 return; 183 } 184 checkIsEuiccSupported(); 185 } 186 187 public boolean isEuicc() { 188 return mIsEuicc; 189 } 190 191 public boolean isActive() { 192 return mActive; 193 } 194 195 public int getPhoneId() { 196 return mPhoneId; 197 } 198 199 public String getIccId() { 200 if (mIccId != null) { 201 return mIccId; 202 } else if (mUiccCard != null) { 203 return mUiccCard.getIccId(); 204 } else { 205 return null; 206 } 207 } 208 209 public boolean isExtendedApduSupported() { 210 return (mAtr != null && mAtr.isExtendedApduSupported()); 211 } 212 213 @Override 214 protected void finalize() { 215 if (DBG) log("UiccSlot finalized"); 216 } 217 218 private void onIccSwap(boolean isAdded) { 219 220 boolean isHotSwapSupported = mContext.getResources().getBoolean( 221 R.bool.config_hotswapCapable); 222 223 if (isHotSwapSupported) { 224 log("onIccSwap: isHotSwapSupported is true, don't prompt for rebooting"); 225 return; 226 } 227 log("onIccSwap: isHotSwapSupported is false, prompt for rebooting"); 228 229 promptForRestart(isAdded); 230 } 231 232 private void promptForRestart(boolean isAdded) { 233 synchronized (mLock) { 234 final Resources res = mContext.getResources(); 235 final String dialogComponent = res.getString( 236 R.string.config_iccHotswapPromptForRestartDialogComponent); 237 if (dialogComponent != null) { 238 Intent intent = new Intent().setComponent(ComponentName.unflattenFromString( 239 dialogComponent)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 240 .putExtra(EXTRA_ICC_CARD_ADDED, isAdded); 241 try { 242 mContext.startActivity(intent); 243 return; 244 } catch (ActivityNotFoundException e) { 245 loge("Unable to find ICC hotswap prompt for restart activity: " + e); 246 } 247 } 248 249 // TODO: Here we assume the device can't handle SIM hot-swap 250 // and has to reboot. We may want to add a property, 251 // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support 252 // hot-swap. 253 DialogInterface.OnClickListener listener = null; 254 255 256 // TODO: SimRecords is not reset while SIM ABSENT (only reset while 257 // Radio_off_or_not_available). Have to reset in both both 258 // added or removed situation. 259 listener = new DialogInterface.OnClickListener() { 260 @Override 261 public void onClick(DialogInterface dialog, int which) { 262 synchronized (mLock) { 263 if (which == DialogInterface.BUTTON_POSITIVE) { 264 if (DBG) log("Reboot due to SIM swap"); 265 PowerManager pm = (PowerManager) mContext 266 .getSystemService(Context.POWER_SERVICE); 267 pm.reboot("SIM is added."); 268 } 269 } 270 } 271 272 }; 273 274 Resources r = Resources.getSystem(); 275 276 String title = (isAdded) ? r.getString(R.string.sim_added_title) : 277 r.getString(R.string.sim_removed_title); 278 String message = (isAdded) ? r.getString(R.string.sim_added_message) : 279 r.getString(R.string.sim_removed_message); 280 String buttonTxt = r.getString(R.string.sim_restart_button); 281 282 AlertDialog dialog = new AlertDialog.Builder(mContext) 283 .setTitle(title) 284 .setMessage(message) 285 .setPositiveButton(buttonTxt, listener) 286 .create(); 287 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 288 dialog.show(); 289 } 290 } 291 292 @Override 293 public void handleMessage(Message msg) { 294 switch (msg.what) { 295 case EVENT_CARD_REMOVED: 296 onIccSwap(false); 297 break; 298 case EVENT_CARD_ADDED: 299 onIccSwap(true); 300 break; 301 default: 302 loge("Unknown Event " + msg.what); 303 } 304 } 305 306 /** 307 * Returns the state of the UiccCard in the slot. 308 * @return 309 */ 310 public CardState getCardState() { 311 synchronized (mLock) { 312 if (mCardState == null) { 313 return CardState.CARDSTATE_ABSENT; 314 } else { 315 return mCardState; 316 } 317 } 318 } 319 320 /** 321 * Returns the UiccCard in the slot. 322 */ 323 public UiccCard getUiccCard() { 324 synchronized (mLock) { 325 return mUiccCard; 326 } 327 } 328 329 /** 330 * Processes radio state unavailable event 331 */ 332 public void onRadioStateUnavailable() { 333 if (mUiccCard != null) { 334 mUiccCard.dispose(); 335 } 336 mUiccCard = null; 337 338 if (mPhoneId != INVALID_PHONE_ID) { 339 UiccController.updateInternalIccState( 340 IccCardConstants.INTENT_VALUE_ICC_UNKNOWN, null, mPhoneId); 341 } 342 343 mCardState = CardState.CARDSTATE_ABSENT; 344 mLastRadioState = RadioState.RADIO_UNAVAILABLE; 345 } 346 347 private void log(String msg) { 348 Rlog.d(TAG, msg); 349 } 350 351 private void loge(String msg) { 352 Rlog.e(TAG, msg); 353 } 354 355 /** 356 * Dump 357 */ 358 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 359 pw.println("UiccSlot:"); 360 pw.println(" mCi=" + mCi); 361 pw.println(" mActive=" + mActive); 362 pw.println(" mLastRadioState=" + mLastRadioState); 363 pw.println(" mCardState=" + mCardState); 364 if (mUiccCard != null) { 365 pw.println(" mUiccCard=" + mUiccCard); 366 mUiccCard.dump(fd, pw, args); 367 } else { 368 pw.println(" mUiccCard=null"); 369 } 370 pw.println(); 371 pw.flush(); 372 pw.flush(); 373 } 374} 375