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 17 package com.android.internal.telephony.uicc; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.app.AlertDialog; 22 import android.content.ActivityNotFoundException; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.content.res.Resources; 28 import android.os.Handler; 29 import android.os.Message; 30 import android.os.PowerManager; 31 import android.os.UserHandle; 32 import android.telephony.SubscriptionInfo; 33 import android.telephony.SubscriptionManager; 34 import android.telephony.TelephonyManager; 35 import android.text.TextUtils; 36 import android.util.IndentingPrintWriter; 37 import android.util.Log; 38 import android.view.WindowManager; 39 40 import com.android.internal.R; 41 import com.android.internal.telephony.CommandsInterface; 42 import com.android.internal.telephony.IccCardConstants; 43 import com.android.internal.telephony.Phone; 44 import com.android.internal.telephony.PhoneFactory; 45 import com.android.internal.telephony.uicc.IccCardStatus.CardState; 46 import com.android.internal.telephony.uicc.IccSlotStatus.MultipleEnabledProfilesMode; 47 import com.android.internal.telephony.uicc.euicc.EuiccCard; 48 import com.android.internal.telephony.util.ArrayUtils; 49 import com.android.internal.telephony.util.TelephonyUtils; 50 import com.android.telephony.Rlog; 51 52 import java.io.FileDescriptor; 53 import java.io.PrintWriter; 54 import java.lang.annotation.Retention; 55 import java.lang.annotation.RetentionPolicy; 56 import java.util.HashMap; 57 import java.util.List; 58 import java.util.Map; 59 import java.util.stream.Collectors; 60 61 /** 62 * This class represents a physical slot on the device. 63 */ 64 public class UiccSlot extends Handler { 65 private static final String TAG = "UiccSlot"; 66 private static final boolean DBG = true; 67 68 public static final String EXTRA_ICC_CARD_ADDED = 69 "com.android.internal.telephony.uicc.ICC_CARD_ADDED"; 70 public static final int INVALID_PHONE_ID = -1; 71 72 @Retention(RetentionPolicy.SOURCE) 73 @IntDef( 74 prefix = {"VOLTAGE_CLASS_"}, 75 value = {VOLTAGE_CLASS_UNKNOWN, VOLTAGE_CLASS_A, VOLTAGE_CLASS_B, VOLTAGE_CLASS_C}) 76 public @interface VoltageClass {} 77 78 public static final int VOLTAGE_CLASS_UNKNOWN = 0; 79 public static final int VOLTAGE_CLASS_A = 1; 80 public static final int VOLTAGE_CLASS_B = 2; 81 public static final int VOLTAGE_CLASS_C = 3; 82 83 private final Object mLock = new Object(); 84 private boolean mActive; 85 private boolean mStateIsUnknown = true; 86 private Context mContext; 87 private UiccCard mUiccCard; 88 private boolean mIsEuicc; 89 private @VoltageClass int mMinimumVoltageClass; 90 private String mEid; 91 private AnswerToReset mAtr; 92 private boolean mIsRemovable; 93 private MultipleEnabledProfilesMode mSupportedMepMode; 94 95 // Map each available portIdx to phoneId 96 private HashMap<Integer, Integer> mPortIdxToPhoneId = new HashMap<>(); 97 //Map each available portIdx with old radio state for state checking 98 private HashMap<Integer, Integer> mLastRadioState = new HashMap<>(); 99 // Store iccId of each port. 100 private HashMap<Integer, String> mIccIds = new HashMap<>(); 101 // IccCardStatus and IccSlotStatus events order is not guaranteed. Inorder to handle MEP mode, 102 // map each available portIdx with CardState for card state checking 103 private HashMap<Integer, CardState> mCardState = new HashMap<>(); 104 105 private static final int EVENT_CARD_REMOVED = 13; 106 private static final int EVENT_CARD_ADDED = 14; 107 UiccSlot(Context c, boolean isActive)108 public UiccSlot(Context c, boolean isActive) { 109 if (DBG) log("Creating"); 110 mContext = c; 111 mActive = isActive; 112 mSupportedMepMode = MultipleEnabledProfilesMode.NONE; 113 } 114 115 /** 116 * Update slot. The main trigger for this is a change in the ICC Card status. 117 */ update(CommandsInterface ci, IccCardStatus ics, int phoneId, int slotIndex)118 public void update(CommandsInterface ci, IccCardStatus ics, int phoneId, int slotIndex) { 119 synchronized (mLock) { 120 mPortIdxToPhoneId.put(ics.mSlotPortMapping.mPortIndex, phoneId); 121 CardState oldState = mCardState.get(ics.mSlotPortMapping.mPortIndex); 122 mCardState.put(ics.mSlotPortMapping.mPortIndex, ics.mCardState); 123 mIccIds.put(ics.mSlotPortMapping.mPortIndex, ics.iccid); 124 parseAtr(ics.atr); 125 mIsRemovable = isSlotRemovable(slotIndex); 126 // Update supported MEP mode in IccCardStatus if the CardState is present. 127 if (ics.mCardState.isCardPresent()) { 128 updateSupportedMepMode(ics.mSupportedMepMode); 129 } 130 131 int radioState = ci.getRadioState(); 132 if (DBG) { 133 log("update: radioState=" + radioState + " mLastRadioState=" + mLastRadioState); 134 } 135 136 if (absentStateUpdateNeeded(oldState, ics.mSlotPortMapping.mPortIndex)) { 137 updateCardStateAbsent(ci.getRadioState(), phoneId, 138 ics.mSlotPortMapping.mPortIndex); 139 // Because mUiccCard may be updated in both IccCardStatus and IccSlotStatus, we need to 140 // create a new UiccCard instance in two scenarios: 141 // 1. mCardState is changing from ABSENT to non ABSENT. 142 // 2. The latest mCardState is not ABSENT, but there is no UiccCard instance. 143 } else if ((oldState == null || oldState == CardState.CARDSTATE_ABSENT 144 || mUiccCard == null) && mCardState.get(ics.mSlotPortMapping.mPortIndex) 145 != CardState.CARDSTATE_ABSENT) { 146 // No notification while we are just powering up 147 if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE 148 && mLastRadioState.getOrDefault(ics.mSlotPortMapping.mPortIndex, 149 TelephonyManager.RADIO_POWER_UNAVAILABLE) 150 != TelephonyManager.RADIO_POWER_UNAVAILABLE) { 151 if (DBG) log("update: notify card added"); 152 sendMessage(obtainMessage(EVENT_CARD_ADDED, null)); 153 } 154 155 // card is present in the slot now; create new mUiccCard 156 if (mUiccCard != null && (!mIsEuicc 157 || ArrayUtils.isEmpty(mUiccCard.getUiccPortList()))) { 158 loge("update: mUiccCard != null when card was present; disposing it now"); 159 mUiccCard.dispose(); 160 mUiccCard = null; 161 } 162 163 if (!mIsEuicc) { 164 // Uicc does not support MEP, passing false by default. 165 mUiccCard = new UiccCard(mContext, ci, ics, phoneId, mLock, 166 MultipleEnabledProfilesMode.NONE); 167 } else { 168 // The EID should be reported with the card status, but in case it's not we want 169 // to catch that here 170 if (TextUtils.isEmpty(ics.eid)) { 171 loge("update: eid is missing. ics.eid=" 172 + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, ics.eid)); 173 } 174 if (mUiccCard == null) { 175 mUiccCard = new EuiccCard(mContext, ci, ics, phoneId, mLock, 176 getSupportedMepMode()); 177 } else { 178 // In MEP case, UiccCard instance is already created, just call update API. 179 // UiccPort initialization is handled inside UiccCard. 180 mUiccCard.update(mContext, ci, ics, phoneId); 181 } 182 } 183 } else { 184 if (mUiccCard != null) { 185 mUiccCard.update(mContext, ci, ics, phoneId); 186 } 187 } 188 mLastRadioState.put(ics.mSlotPortMapping.mPortIndex, radioState); 189 } 190 } 191 192 /** 193 * Update slot based on IccSlotStatus. 194 */ update(CommandsInterface[] ci, IccSlotStatus iss, int slotIndex)195 public void update(CommandsInterface[] ci, IccSlotStatus iss, int slotIndex) { 196 synchronized (mLock) { 197 IccSimPortInfo[] simPortInfos = iss.mSimPortInfos; 198 parseAtr(iss.atr); 199 mEid = iss.eid; 200 mIsRemovable = isSlotRemovable(slotIndex); 201 202 for (int i = 0; i < simPortInfos.length; i++) { 203 int phoneId = iss.mSimPortInfos[i].mLogicalSlotIndex; 204 CardState oldState = mCardState.get(i); 205 mCardState.put(i, iss.cardState); 206 mIccIds.put(i, simPortInfos[i].mIccId); 207 if (!iss.mSimPortInfos[i].mPortActive) { 208 // TODO: (b/79432584) evaluate whether should broadcast card state change 209 // even if it's inactive. 210 UiccController.getInstance().updateSimStateForInactivePort( 211 mPortIdxToPhoneId.getOrDefault(i, INVALID_PHONE_ID), 212 iss.mSimPortInfos[i].mIccId); 213 mLastRadioState.put(i, TelephonyManager.RADIO_POWER_UNAVAILABLE); 214 if (mUiccCard != null) { 215 // Dispose the port 216 mUiccCard.disposePort(i); 217 } 218 } else { 219 if (absentStateUpdateNeeded(oldState, i)) { 220 int radioState = SubscriptionManager.isValidPhoneId(phoneId) ? 221 ci[phoneId].getRadioState() : 222 TelephonyManager.RADIO_POWER_UNAVAILABLE; 223 updateCardStateAbsent(radioState, phoneId, i); 224 } 225 // TODO: (b/79432584) Create UiccCard or EuiccCard object here. 226 // Right now It's OK not creating it because Card status update will do it. 227 // But we should really make them symmetric. 228 } 229 } 230 // From MEP, Card can have multiple ports. So dispose UiccCard only when all the 231 // ports are inactive. 232 if (!hasActivePort(simPortInfos)) { 233 if (mActive) { 234 mActive = false; 235 nullifyUiccCard(true /* sim state is unknown */); 236 } 237 } else { 238 mActive = true; 239 } 240 mPortIdxToPhoneId.clear(); 241 for (int i = 0; i < simPortInfos.length; i++) { 242 // If port is not active, update with invalid phone id(i.e. -1) 243 mPortIdxToPhoneId.put(i, simPortInfos[i].mPortActive ? 244 simPortInfos[i].mLogicalSlotIndex : INVALID_PHONE_ID); 245 } 246 updateSupportedMepMode(iss.mSupportedMepMode); 247 // Since the MEP capability is related to supported MEP mode, thus need to 248 // update the flag after UiccCard creation. 249 if (mUiccCard != null) { 250 mUiccCard.updateSupportedMepMode(getSupportedMepMode()); 251 } 252 } 253 } 254 updateSupportedMepMode(MultipleEnabledProfilesMode mode)255 private void updateSupportedMepMode(MultipleEnabledProfilesMode mode) { 256 mSupportedMepMode = mode; 257 // If SupportedMepMode is MultipleEnabledProfilesMode.NONE, validate ATR and 258 // num of ports to handle backward compatibility for < RADIO_HAL_VERSION_2_1. 259 if (mode == MultipleEnabledProfilesMode.NONE) { 260 // Even ATR suggest UICC supports multiple enabled profiles, MEP can be disabled per 261 // carrier restrictions, so checking the real number of ports reported from modem is 262 // necessary. 263 if (mPortIdxToPhoneId.size() > 1 264 && mAtr != null && mAtr.isMultipleEnabledProfilesSupported()) { 265 // Set MEP-B mode in case if modem sends wrong mode even though supports MEP. 266 Log.i(TAG, "Modem does not send proper supported MEP mode or older HAL version"); 267 mSupportedMepMode = MultipleEnabledProfilesMode.MEP_B; 268 } 269 } 270 } 271 hasActivePort(IccSimPortInfo[] simPortInfos)272 private boolean hasActivePort(IccSimPortInfo[] simPortInfos) { 273 for (IccSimPortInfo simPortInfo : simPortInfos) { 274 if (simPortInfo.mPortActive) { 275 return true; 276 } 277 } 278 return false; 279 } 280 281 /* Return valid phoneId if possible from the portIdx mapping*/ getAnyValidPhoneId()282 private int getAnyValidPhoneId() { 283 for (int phoneId : mPortIdxToPhoneId.values()) { 284 if (SubscriptionManager.isValidPhoneId(phoneId)) { 285 return phoneId; 286 } 287 } 288 return INVALID_PHONE_ID; 289 } 290 291 @NonNull getPortList()292 public int[] getPortList() { 293 synchronized (mLock) { 294 return mPortIdxToPhoneId.keySet().stream().mapToInt(Integer::valueOf).toArray(); 295 } 296 } 297 298 /** Return whether the passing portIndex belong to this physical slot */ isValidPortIndex(int portIndex)299 public boolean isValidPortIndex(int portIndex) { 300 return mPortIdxToPhoneId.containsKey(portIndex); 301 } 302 getPortIndexFromPhoneId(int phoneId)303 public int getPortIndexFromPhoneId(int phoneId) { 304 synchronized (mLock) { 305 for (Map.Entry<Integer, Integer> entry : mPortIdxToPhoneId.entrySet()) { 306 if (entry.getValue() == phoneId) { 307 return entry.getKey(); 308 } 309 } 310 return TelephonyManager.DEFAULT_PORT_INDEX; 311 } 312 } 313 getPortIndexFromIccId(String iccId)314 public int getPortIndexFromIccId(String iccId) { 315 synchronized (mLock) { 316 for (Map.Entry<Integer, String> entry : mIccIds.entrySet()) { 317 if (IccUtils.compareIgnoreTrailingFs(entry.getValue(), iccId)) { 318 return entry.getKey(); 319 } 320 } 321 // If iccId is not found, return invalid port index. 322 return TelephonyManager.INVALID_PORT_INDEX; 323 } 324 } 325 getPhoneIdFromPortIndex(int portIndex)326 public int getPhoneIdFromPortIndex(int portIndex) { 327 synchronized (mLock) { 328 return mPortIdxToPhoneId.getOrDefault(portIndex, INVALID_PHONE_ID); 329 } 330 } 331 isPortActive(int portIdx)332 public boolean isPortActive(int portIdx) { 333 synchronized (mLock) { 334 return SubscriptionManager.isValidPhoneId( 335 mPortIdxToPhoneId.getOrDefault(portIdx, INVALID_PHONE_ID)); 336 } 337 } 338 339 /* Returns true if multiple enabled profiles are supported */ isMultipleEnabledProfileSupported()340 public boolean isMultipleEnabledProfileSupported() { 341 synchronized (mLock) { 342 return mSupportedMepMode.isMepMode(); 343 } 344 } 345 absentStateUpdateNeeded(CardState oldState, int portIndex)346 private boolean absentStateUpdateNeeded(CardState oldState, int portIndex) { 347 return (oldState != CardState.CARDSTATE_ABSENT || mUiccCard != null) 348 && mCardState.get(portIndex) == CardState.CARDSTATE_ABSENT; 349 } 350 updateCardStateAbsent(int radioState, int phoneId, int portIndex)351 private void updateCardStateAbsent(int radioState, int phoneId, int portIndex) { 352 // No notification while we are just powering up 353 if (radioState != TelephonyManager.RADIO_POWER_UNAVAILABLE 354 && mLastRadioState.getOrDefault( 355 portIndex, TelephonyManager.RADIO_POWER_UNAVAILABLE) 356 != TelephonyManager.RADIO_POWER_UNAVAILABLE) { 357 if (DBG) log("update: notify card removed"); 358 sendMessage(obtainMessage(EVENT_CARD_REMOVED, null)); 359 } 360 361 UiccController.getInstance().updateSimState(phoneId, IccCardConstants.State.ABSENT, null); 362 // no card present in the slot now; dispose port and then card if needed. 363 disposeUiccCardIfNeeded(false /* sim state is not unknown */, portIndex); 364 // If SLOT_STATUS is the last event, wrong subscription is getting invalidate during 365 // slot switch event. To avoid it, reset the phoneId corresponding to the portIndex. 366 mPortIdxToPhoneId.put(portIndex, INVALID_PHONE_ID); 367 mLastRadioState.put(portIndex, TelephonyManager.RADIO_POWER_UNAVAILABLE); 368 } 369 370 // whenever we set mUiccCard to null, we lose the ability to differentiate between absent and 371 // unknown states. To mitigate this, we will us mStateIsUnknown to keep track. The sim is only 372 // unknown if we haven't heard from the radio or if the radio has become unavailable. nullifyUiccCard(boolean stateUnknown)373 private void nullifyUiccCard(boolean stateUnknown) { 374 if (mUiccCard != null) { 375 mUiccCard.dispose(); 376 } 377 mStateIsUnknown = stateUnknown; 378 mUiccCard = null; 379 } 380 disposeUiccCardIfNeeded(boolean isStateUnknown, int portIndex)381 private void disposeUiccCardIfNeeded(boolean isStateUnknown, int portIndex) { 382 if (mUiccCard != null) { 383 // First dispose UiccPort corresponding to the portIndex 384 mUiccCard.disposePort(portIndex); 385 if (ArrayUtils.isEmpty(mUiccCard.getUiccPortList())) { 386 // No UiccPort objects are found, safe to dispose the card 387 nullifyUiccCard(isStateUnknown); 388 } 389 } else { 390 mStateIsUnknown = isStateUnknown; 391 } 392 } 393 isStateUnknown()394 public boolean isStateUnknown() { 395 // CardState is not specific to any port index, use default port. 396 CardState cardState = mCardState.get(TelephonyManager.DEFAULT_PORT_INDEX); 397 if (cardState == null || cardState == CardState.CARDSTATE_ABSENT) { 398 // mStateIsUnknown is valid only in this scenario. 399 return mStateIsUnknown; 400 } 401 // if mUiccCard is null, assume the state to be UNKNOWN for now. 402 // The state may be known but since the actual card object is not available, 403 // it is safer to return UNKNOWN. 404 return mUiccCard == null; 405 } 406 407 // Return true if a slot index is for removable UICCs or eUICCs isSlotRemovable(int slotIndex)408 private boolean isSlotRemovable(int slotIndex) { 409 int[] euiccSlots = mContext.getResources() 410 .getIntArray(com.android.internal.R.array.non_removable_euicc_slots); 411 if (euiccSlots == null) { 412 return true; 413 } 414 for (int euiccSlot : euiccSlots) { 415 if (euiccSlot == slotIndex) { 416 return false; 417 } 418 } 419 420 return true; 421 } 422 checkIsEuiccSupported()423 private void checkIsEuiccSupported() { 424 if (mAtr == null) { 425 mIsEuicc = false; 426 return; 427 } 428 mIsEuicc = mAtr.isEuiccSupported(); 429 log(" checkIsEuiccSupported : " + mIsEuicc); 430 } 431 checkMinimumVoltageClass()432 private void checkMinimumVoltageClass() { 433 mMinimumVoltageClass = VOLTAGE_CLASS_UNKNOWN; 434 if (mAtr == null) { 435 return; 436 } 437 // Supported voltage classes are stored in the 5 least significant bits of the TA byte for 438 // global interface. 439 List<AnswerToReset.InterfaceByte> interfaceBytes = mAtr.getInterfaceBytes(); 440 for (int i = 0; i < interfaceBytes.size() - 1; i++) { 441 if (interfaceBytes.get(i).getTD() != null 442 && (interfaceBytes.get(i).getTD() & AnswerToReset.T_MASK) 443 == AnswerToReset.T_VALUE_FOR_GLOBAL_INTERFACE 444 && interfaceBytes.get(i + 1).getTA() != null) { 445 byte ta = interfaceBytes.get(i + 1).getTA(); 446 if ((ta & 0x01) != 0) { 447 mMinimumVoltageClass = VOLTAGE_CLASS_A; 448 } 449 if ((ta & 0x02) != 0) { 450 mMinimumVoltageClass = VOLTAGE_CLASS_B; 451 } 452 if ((ta & 0x04) != 0) { 453 mMinimumVoltageClass = VOLTAGE_CLASS_C; 454 } 455 return; 456 } 457 } 458 // Use default value - only class A 459 mMinimumVoltageClass = VOLTAGE_CLASS_A; 460 } 461 parseAtr(String atr)462 private void parseAtr(String atr) { 463 mAtr = AnswerToReset.parseAtr(atr); 464 checkIsEuiccSupported(); 465 checkMinimumVoltageClass(); 466 } 467 isEuicc()468 public boolean isEuicc() { 469 return mIsEuicc; 470 } 471 472 @VoltageClass getMinimumVoltageClass()473 public int getMinimumVoltageClass() { 474 return mMinimumVoltageClass; 475 } 476 isActive()477 public boolean isActive() { 478 return mActive; 479 } 480 isRemovable()481 public boolean isRemovable() { 482 return mIsRemovable; 483 } 484 485 /** 486 * Returns the iccId specific to the port index. 487 * Always use {@link com.android.internal.telephony.uicc.UiccPort#getIccId} to get the iccId. 488 * Use this API to get the iccId of the inactive port only. 489 */ getIccId(int portIdx)490 public String getIccId(int portIdx) { 491 return mIccIds.get(portIdx); 492 } 493 getEid()494 public String getEid() { 495 return mEid; 496 } 497 isExtendedApduSupported()498 public boolean isExtendedApduSupported() { 499 return (mAtr != null && mAtr.isExtendedApduSupported()); 500 } 501 502 @Override finalize()503 protected void finalize() { 504 if (DBG) log("UiccSlot finalized"); 505 } 506 onIccSwap(boolean isAdded)507 private void onIccSwap(boolean isAdded) { 508 509 boolean isHotSwapSupported = mContext.getResources().getBoolean( 510 R.bool.config_hotswapCapable); 511 512 if (isHotSwapSupported) { 513 log("onIccSwap: isHotSwapSupported is true, don't prompt for rebooting"); 514 return; 515 } 516 // As this check is for shutdown status check, use any phoneId 517 Phone phone = PhoneFactory.getPhone(getAnyValidPhoneId()); 518 if (phone != null && phone.isShuttingDown()) { 519 log("onIccSwap: already doing shutdown, no need to prompt"); 520 return; 521 } 522 523 log("onIccSwap: isHotSwapSupported is false, prompt for rebooting"); 524 525 promptForRestart(isAdded); 526 } 527 promptForRestart(boolean isAdded)528 private void promptForRestart(boolean isAdded) { 529 synchronized (mLock) { 530 final Resources res = mContext.getResources(); 531 final ComponentName dialogComponent = ComponentName.unflattenFromString( 532 res.getString(R.string.config_iccHotswapPromptForRestartDialogComponent)); 533 if (dialogComponent != null) { 534 Intent intent = new Intent().setComponent(dialogComponent) 535 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 536 .putExtra(EXTRA_ICC_CARD_ADDED, isAdded); 537 try { 538 mContext.startActivityAsUser(intent, UserHandle.CURRENT); 539 return; 540 } catch (ActivityNotFoundException e) { 541 loge("Unable to find ICC hotswap prompt for restart activity: " + e); 542 } 543 } 544 545 // TODO: Here we assume the device can't handle SIM hot-swap 546 // and has to reboot. We may want to add a property, 547 // e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support 548 // hot-swap. 549 DialogInterface.OnClickListener listener = null; 550 551 552 // TODO: SimRecords is not reset while SIM ABSENT (only reset while 553 // Radio_off_or_not_available). Have to reset in both both 554 // added or removed situation. 555 listener = new DialogInterface.OnClickListener() { 556 @Override 557 public void onClick(DialogInterface dialog, int which) { 558 synchronized (mLock) { 559 if (which == DialogInterface.BUTTON_POSITIVE) { 560 if (DBG) log("Reboot due to SIM swap"); 561 PowerManager pm = (PowerManager) mContext 562 .getSystemService(Context.POWER_SERVICE); 563 pm.reboot("SIM is added."); 564 } 565 } 566 } 567 568 }; 569 570 Resources r = Resources.getSystem(); 571 572 String title = (isAdded) ? r.getString(R.string.sim_added_title) : 573 r.getString(R.string.sim_removed_title); 574 String message = (isAdded) ? r.getString(R.string.sim_added_message) : 575 r.getString(R.string.sim_removed_message); 576 String buttonTxt = r.getString(R.string.sim_restart_button); 577 578 AlertDialog dialog = new AlertDialog.Builder(mContext) 579 .setTitle(title) 580 .setMessage(message) 581 .setPositiveButton(buttonTxt, listener) 582 .create(); 583 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 584 dialog.show(); 585 } 586 } 587 588 @Override handleMessage(Message msg)589 public void handleMessage(Message msg) { 590 switch (msg.what) { 591 case EVENT_CARD_REMOVED: 592 onIccSwap(false); 593 break; 594 case EVENT_CARD_ADDED: 595 onIccSwap(true); 596 break; 597 default: 598 loge("Unknown Event " + msg.what); 599 } 600 } 601 602 /** 603 * Returns the state of the UiccCard in the slot. 604 * @return 605 */ getCardState()606 public CardState getCardState() { 607 synchronized (mLock) { 608 // CardState is not specific to any port index, use default port. 609 CardState cardState = mCardState.get(TelephonyManager.DEFAULT_PORT_INDEX); 610 return cardState == null ? CardState.CARDSTATE_ABSENT : cardState; 611 } 612 } 613 614 /** 615 * Returns the UiccCard in the slot. 616 */ getUiccCard()617 public UiccCard getUiccCard() { 618 synchronized (mLock) { 619 return mUiccCard; 620 } 621 } 622 623 /** 624 * Returns the supported MEP mode. 625 */ getSupportedMepMode()626 public MultipleEnabledProfilesMode getSupportedMepMode() { 627 synchronized (mLock) { 628 return mSupportedMepMode; 629 } 630 } 631 /** 632 * Processes radio state unavailable event 633 */ onRadioStateUnavailable(int phoneId)634 public void onRadioStateUnavailable(int phoneId) { 635 int portIndex = getPortIndexFromPhoneId(phoneId); 636 disposeUiccCardIfNeeded(true /* sim state is unknown */, portIndex); 637 638 if (phoneId != INVALID_PHONE_ID) { 639 UiccController.getInstance().updateSimState(phoneId, 640 IccCardConstants.State.UNKNOWN, null); 641 } 642 mLastRadioState.put(portIndex, TelephonyManager.RADIO_POWER_UNAVAILABLE); 643 // Reset CardState 644 mCardState.put(portIndex, null); 645 } 646 log(String msg)647 private void log(String msg) { 648 Rlog.d(TAG, msg); 649 } 650 loge(String msg)651 private void loge(String msg) { 652 Rlog.e(TAG, msg); 653 } 654 getPrintableIccIds()655 private Map<Integer, String> getPrintableIccIds() { 656 Map<Integer, String> printableIccIds = mIccIds.entrySet().stream() 657 .collect(Collectors.toMap(Map.Entry::getKey, 658 e -> SubscriptionInfo.getPrintableId(e.getValue()))); 659 return printableIccIds; 660 } 661 662 /** 663 * Dump 664 */ dump(FileDescriptor fd, PrintWriter printWriter, String[] args)665 public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) { 666 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 667 pw.println("mActive=" + mActive); 668 pw.println("mIsEuicc=" + mIsEuicc); 669 pw.println("isEuiccSupportsMultipleEnabledProfiles=" + isMultipleEnabledProfileSupported()); 670 pw.println("mIsRemovable=" + mIsRemovable); 671 pw.println("mLastRadioState=" + mLastRadioState); 672 pw.println("mIccIds=" + getPrintableIccIds()); 673 pw.println("mPortIdxToPhoneId=" + mPortIdxToPhoneId); 674 pw.println("mEid=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid)); 675 pw.println("mCardState=" + mCardState); 676 pw.println("mSupportedMepMode=" + mSupportedMepMode); 677 if (mUiccCard != null) { 678 pw.println("mUiccCard="); 679 mUiccCard.dump(fd, pw, args); 680 } else { 681 pw.println("mUiccCard=null"); 682 } 683 pw.println(); 684 pw.flush(); 685 } 686 687 @NonNull 688 @Override toString()689 public String toString() { 690 return "[UiccSlot: mActive=" + mActive + ", mIccId=" + getPrintableIccIds() + ", mIsEuicc=" 691 + mIsEuicc + ", MEP=" + isMultipleEnabledProfileSupported() + ", mPortIdxToPhoneId=" 692 + mPortIdxToPhoneId + ", mEid=" + Rlog.pii(TelephonyUtils.IS_DEBUGGABLE, mEid) 693 + ", mCardState=" + mCardState + " mSupportedMepMode=" + mSupportedMepMode + "]"; 694 } 695 } 696