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