1 /* 2 * Copyright (C) 2020 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.settings.network.telephony; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.os.Bundle; 22 import android.os.UserManager; 23 import android.telephony.SubscriptionInfo; 24 import android.telephony.SubscriptionManager; 25 import android.telephony.TelephonyManager; 26 import android.telephony.UiccSlotInfo; 27 import android.text.TextUtils; 28 import android.util.Log; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.settings.R; 32 import com.android.settings.SidecarFragment; 33 import com.android.settings.network.EnableMultiSimSidecar; 34 import com.android.settings.network.SubscriptionUtil; 35 import com.android.settings.network.SwitchToEuiccSubscriptionSidecar; 36 import com.android.settings.network.SwitchToRemovableSlotSidecar; 37 import com.android.settings.network.UiccSlotUtil; 38 import com.android.settings.sim.SimActivationNotifier; 39 40 import com.google.common.collect.ImmutableList; 41 42 import java.util.List; 43 44 /** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */ 45 public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity 46 implements SidecarFragment.Listener, ConfirmDialogFragment.OnConfirmListener { 47 48 private static final String TAG = "ToggleSubscriptionDialogActivity"; 49 // Arguments 50 @VisibleForTesting 51 public static final String ARG_enable = "enable"; 52 // Dialog tags 53 private static final int DIALOG_TAG_DISABLE_SIM_CONFIRMATION = 1; 54 private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION = 2; 55 private static final int DIALOG_TAG_ENABLE_DSDS_CONFIRMATION = 3; 56 private static final int DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION = 4; 57 // Number of SIMs for DSDS 58 private static final int NUM_OF_SIMS_FOR_DSDS = 2; 59 60 /** 61 * Returns an intent of ToggleSubscriptionDialogActivity. 62 * 63 * @param context The context used to start the ToggleSubscriptionDialogActivity. 64 * @param subId The subscription ID of the subscription needs to be toggled. 65 * @param enable Whether the activity should enable or disable the subscription. 66 */ getIntent(Context context, int subId, boolean enable)67 public static Intent getIntent(Context context, int subId, boolean enable) { 68 Intent intent = new Intent(context, ToggleSubscriptionDialogActivity.class); 69 intent.putExtra(ARG_SUB_ID, subId); 70 intent.putExtra(ARG_enable, enable); 71 return intent; 72 } 73 74 private SubscriptionInfo mSubInfo; 75 private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar; 76 private SwitchToRemovableSlotSidecar mSwitchToRemovableSlotSidecar; 77 private EnableMultiSimSidecar mEnableMultiSimSidecar; 78 private boolean mEnable; 79 private boolean mIsEsimOperation; 80 private TelephonyManager mTelMgr; 81 82 @Override onCreate(Bundle savedInstanceState)83 protected void onCreate(Bundle savedInstanceState) { 84 super.onCreate(savedInstanceState); 85 86 Intent intent = getIntent(); 87 int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 88 mTelMgr = getSystemService(TelephonyManager.class); 89 90 UserManager userManager = getSystemService(UserManager.class); 91 if (!userManager.isAdminUser()) { 92 Log.e(TAG, "It is not the admin user. Unable to toggle subscription."); 93 finish(); 94 return; 95 } 96 97 if (!SubscriptionManager.isUsableSubscriptionId(subId)) { 98 Log.e(TAG, "The subscription id is not usable."); 99 finish(); 100 return; 101 } 102 103 mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId); 104 mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded(); 105 mSwitchToEuiccSubscriptionSidecar = 106 SwitchToEuiccSubscriptionSidecar.get(getFragmentManager()); 107 mSwitchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(getFragmentManager()); 108 mEnableMultiSimSidecar = EnableMultiSimSidecar.get(getFragmentManager()); 109 mEnable = intent.getBooleanExtra(ARG_enable, true); 110 111 if (savedInstanceState == null) { 112 if (mEnable) { 113 showEnableSubDialog(); 114 } else { 115 showDisableSimConfirmDialog(); 116 } 117 } 118 } 119 120 @Override onResume()121 protected void onResume() { 122 super.onResume(); 123 mSwitchToEuiccSubscriptionSidecar.addListener(this); 124 mSwitchToRemovableSlotSidecar.addListener(this); 125 mEnableMultiSimSidecar.addListener(this); 126 } 127 128 @Override onPause()129 protected void onPause() { 130 mEnableMultiSimSidecar.removeListener(this); 131 mSwitchToRemovableSlotSidecar.removeListener(this); 132 mSwitchToEuiccSubscriptionSidecar.removeListener(this); 133 super.onPause(); 134 } 135 136 @Override onStateChange(SidecarFragment fragment)137 public void onStateChange(SidecarFragment fragment) { 138 if (fragment == mSwitchToEuiccSubscriptionSidecar) { 139 handleSwitchToEuiccSubscriptionSidecarStateChange(); 140 } else if (fragment == mSwitchToRemovableSlotSidecar) { 141 handleSwitchToRemovableSlotSidecarStateChange(); 142 } else if (fragment == mEnableMultiSimSidecar) { 143 handleEnableMultiSimSidecarStateChange(); 144 } 145 } 146 147 @Override onConfirm(int tag, boolean confirmed)148 public void onConfirm(int tag, boolean confirmed) { 149 if (!confirmed 150 && tag != DIALOG_TAG_ENABLE_DSDS_CONFIRMATION 151 && tag != DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION) { 152 finish(); 153 return; 154 } 155 156 switch (tag) { 157 case DIALOG_TAG_DISABLE_SIM_CONFIRMATION: 158 if (mIsEsimOperation) { 159 Log.i(TAG, "Disabling the eSIM profile."); 160 showProgressDialog( 161 getString(R.string.privileged_action_disable_sub_dialog_progress)); 162 mSwitchToEuiccSubscriptionSidecar.run( 163 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 164 return; 165 } 166 Log.i(TAG, "Disabling the pSIM profile."); 167 handleTogglePsimAction(); 168 break; 169 case DIALOG_TAG_ENABLE_DSDS_CONFIRMATION: 170 if (!confirmed) { 171 Log.i(TAG, "User cancel the dialog to enable DSDS."); 172 showEnableSimConfirmDialog(); 173 return; 174 } 175 if (mTelMgr.doesSwitchMultiSimConfigTriggerReboot()) { 176 Log.i(TAG, "Device does not support reboot free DSDS."); 177 showRebootConfirmDialog(); 178 return; 179 } 180 Log.i(TAG, "Enabling DSDS without rebooting."); 181 showProgressDialog( 182 getString(R.string.sim_action_enabling_sim_without_carrier_name)); 183 mEnableMultiSimSidecar.run(NUM_OF_SIMS_FOR_DSDS); 184 break; 185 case DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION: 186 if (!confirmed) { 187 Log.i(TAG, "User cancel the dialog to reboot to enable DSDS."); 188 showEnableSimConfirmDialog(); 189 return; 190 } 191 Log.i(TAG, "User confirmed reboot to enable DSDS."); 192 SimActivationNotifier.setShowSimSettingsNotification(this, true); 193 mTelMgr.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS); 194 break; 195 case DIALOG_TAG_ENABLE_SIM_CONFIRMATION: 196 Log.i(TAG, "User confirmed to enable the subscription."); 197 if (mIsEsimOperation) { 198 showProgressDialog( 199 getString( 200 R.string.sim_action_switch_sub_dialog_progress, 201 SubscriptionUtil.getUniqueSubscriptionDisplayName( 202 mSubInfo, this))); 203 mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId()); 204 return; 205 } 206 showProgressDialog( 207 getString(R.string.sim_action_enabling_sim_without_carrier_name)); 208 mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID); 209 break; 210 default: 211 Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag); 212 break; 213 } 214 } 215 handleSwitchToEuiccSubscriptionSidecarStateChange()216 private void handleSwitchToEuiccSubscriptionSidecarStateChange() { 217 switch (mSwitchToEuiccSubscriptionSidecar.getState()) { 218 case SidecarFragment.State.SUCCESS: 219 Log.i( 220 TAG, 221 String.format( 222 "Successfully %s the eSIM profile.", 223 mEnable ? "enable" : "disable")); 224 mSwitchToEuiccSubscriptionSidecar.reset(); 225 dismissProgressDialog(); 226 finish(); 227 break; 228 case SidecarFragment.State.ERROR: 229 Log.i( 230 TAG, 231 String.format( 232 "Failed to %s the eSIM profile.", mEnable ? "enable" : "disable")); 233 mSwitchToEuiccSubscriptionSidecar.reset(); 234 dismissProgressDialog(); 235 showErrorDialog( 236 getString(R.string.privileged_action_disable_fail_title), 237 getString(R.string.privileged_action_disable_fail_text)); 238 break; 239 } 240 } 241 handleSwitchToRemovableSlotSidecarStateChange()242 private void handleSwitchToRemovableSlotSidecarStateChange() { 243 switch (mSwitchToRemovableSlotSidecar.getState()) { 244 case SidecarFragment.State.SUCCESS: 245 Log.i(TAG, "Successfully switched to removable slot."); 246 mSwitchToRemovableSlotSidecar.reset(); 247 handleTogglePsimAction(); 248 dismissProgressDialog(); 249 finish(); 250 break; 251 case SidecarFragment.State.ERROR: 252 Log.e(TAG, "Failed switching to removable slot."); 253 mSwitchToRemovableSlotSidecar.reset(); 254 dismissProgressDialog(); 255 showErrorDialog( 256 getString(R.string.sim_action_enable_sim_fail_title), 257 getString(R.string.sim_action_enable_sim_fail_text)); 258 break; 259 } 260 } 261 handleEnableMultiSimSidecarStateChange()262 private void handleEnableMultiSimSidecarStateChange() { 263 switch (mEnableMultiSimSidecar.getState()) { 264 case SidecarFragment.State.SUCCESS: 265 mEnableMultiSimSidecar.reset(); 266 Log.i(TAG, "Successfully switched to DSDS without reboot."); 267 handleEnableSubscriptionAfterEnablingDsds(); 268 break; 269 case SidecarFragment.State.ERROR: 270 mEnableMultiSimSidecar.reset(); 271 Log.i(TAG, "Failed to switch to DSDS without rebooting."); 272 dismissProgressDialog(); 273 showErrorDialog( 274 getString(R.string.dsds_activation_failure_title), 275 getString(R.string.dsds_activation_failure_body_msg2)); 276 break; 277 } 278 } 279 handleEnableSubscriptionAfterEnablingDsds()280 private void handleEnableSubscriptionAfterEnablingDsds() { 281 if (mIsEsimOperation) { 282 Log.i(TAG, "DSDS enabled, start to enable profile: " + mSubInfo.getSubscriptionId()); 283 // For eSIM operations, we simply switch to the selected eSIM profile. 284 mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId()); 285 return; 286 } 287 288 Log.i(TAG, "DSDS enabled, start to enable pSIM profile."); 289 handleTogglePsimAction(); 290 dismissProgressDialog(); 291 finish(); 292 } 293 handleTogglePsimAction()294 private void handleTogglePsimAction() { 295 if (mSubscriptionManager.canDisablePhysicalSubscription() && mSubInfo != null) { 296 mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable); 297 finish(); 298 } else { 299 Log.i( 300 TAG, 301 "The device does not support toggling pSIM. It is enough to just " 302 + "enable the removable slot."); 303 } 304 } 305 306 /* Handles the enabling SIM action. */ showEnableSubDialog()307 private void showEnableSubDialog() { 308 Log.i(TAG, "Handle subscription enabling."); 309 if (isDsdsConditionSatisfied()) { 310 showEnableDsdsConfirmDialog(); 311 return; 312 } 313 if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()) { 314 Log.i(TAG, "Toggle on pSIM, no dialog displayed."); 315 handleTogglePsimAction(); 316 finish(); 317 return; 318 } 319 showEnableSimConfirmDialog(); 320 } 321 showEnableDsdsConfirmDialog()322 private void showEnableDsdsConfirmDialog() { 323 ConfirmDialogFragment.show( 324 this, 325 ConfirmDialogFragment.OnConfirmListener.class, 326 DIALOG_TAG_ENABLE_DSDS_CONFIRMATION, 327 getString(R.string.sim_action_enable_dsds_title), 328 getString(R.string.sim_action_enable_dsds_text), 329 getString(R.string.sim_action_yes), 330 getString(R.string.sim_action_no_thanks)); 331 } 332 showRebootConfirmDialog()333 private void showRebootConfirmDialog() { 334 ConfirmDialogFragment.show( 335 this, 336 ConfirmDialogFragment.OnConfirmListener.class, 337 DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION, 338 getString(R.string.sim_action_restart_title), 339 getString(R.string.sim_action_enable_dsds_text), 340 getString(R.string.sim_action_reboot), 341 getString(R.string.cancel)); 342 } 343 344 /* Displays the SIM toggling confirmation dialog. */ showDisableSimConfirmDialog()345 private void showDisableSimConfirmDialog() { 346 final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName( 347 mSubInfo, this); 348 String title = 349 mSubInfo == null || TextUtils.isEmpty(displayName) 350 ? getString( 351 R.string.privileged_action_disable_sub_dialog_title_without_carrier) 352 : getString( 353 R.string.privileged_action_disable_sub_dialog_title, displayName); 354 355 ConfirmDialogFragment.show( 356 this, 357 ConfirmDialogFragment.OnConfirmListener.class, 358 DIALOG_TAG_DISABLE_SIM_CONFIRMATION, 359 title, 360 null, 361 getString(R.string.yes), 362 getString(R.string.cancel)); 363 } 364 showEnableSimConfirmDialog()365 private void showEnableSimConfirmDialog() { 366 List<SubscriptionInfo> activeSubs = 367 SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager); 368 SubscriptionInfo activeSub = activeSubs.isEmpty() ? null : activeSubs.get(0); 369 if (activeSub == null) { 370 Log.i(TAG, "No active subscriptions available."); 371 showNonSwitchSimConfirmDialog(); 372 return; 373 } 374 Log.i(TAG, "Found active subscription."); 375 boolean isBetweenEsim = mIsEsimOperation && activeSub.isEmbedded(); 376 if (mTelMgr.isMultiSimEnabled() && !isBetweenEsim) { 377 showNonSwitchSimConfirmDialog(); 378 return; 379 } 380 ConfirmDialogFragment.show( 381 this, 382 ConfirmDialogFragment.OnConfirmListener.class, 383 DIALOG_TAG_ENABLE_SIM_CONFIRMATION, 384 getSwitchSubscriptionTitle(), 385 getSwitchDialogBodyMsg(activeSub, isBetweenEsim), 386 getSwitchDialogPosBtnText(), 387 getString(android.R.string.cancel)); 388 } 389 showNonSwitchSimConfirmDialog()390 private void showNonSwitchSimConfirmDialog() { 391 ConfirmDialogFragment.show( 392 this, 393 ConfirmDialogFragment.OnConfirmListener.class, 394 DIALOG_TAG_ENABLE_SIM_CONFIRMATION, 395 getEnableSubscriptionTitle(), 396 null /* msg */, 397 getString(R.string.yes), 398 getString(android.R.string.cancel)); 399 } 400 getSwitchDialogPosBtnText()401 private String getSwitchDialogPosBtnText() { 402 return mIsEsimOperation 403 ? getString( 404 R.string.sim_action_switch_sub_dialog_confirm, 405 SubscriptionUtil.getUniqueSubscriptionDisplayName(mSubInfo, this)) 406 : getString(R.string.sim_switch_button); 407 } 408 getEnableSubscriptionTitle()409 private String getEnableSubscriptionTitle() { 410 final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName( 411 mSubInfo, this); 412 if (mSubInfo == null || TextUtils.isEmpty(displayName)) { 413 return getString(R.string.sim_action_enable_sub_dialog_title_without_carrier_name); 414 } 415 return getString(R.string.sim_action_enable_sub_dialog_title, displayName); 416 } 417 getSwitchSubscriptionTitle()418 private String getSwitchSubscriptionTitle() { 419 if (mIsEsimOperation) { 420 return getString( 421 R.string.sim_action_switch_sub_dialog_title, 422 SubscriptionUtil.getUniqueSubscriptionDisplayName(mSubInfo, this)); 423 } 424 return getString(R.string.sim_action_switch_psim_dialog_title); 425 } 426 getSwitchDialogBodyMsg(SubscriptionInfo activeSub, boolean betweenEsim)427 private String getSwitchDialogBodyMsg(SubscriptionInfo activeSub, boolean betweenEsim) { 428 final CharSequence subInfoName = SubscriptionUtil.getUniqueSubscriptionDisplayName( 429 mSubInfo, this); 430 final CharSequence activeSubName = SubscriptionUtil.getUniqueSubscriptionDisplayName( 431 activeSub, this); 432 if (betweenEsim && mIsEsimOperation) { 433 return getString( 434 R.string.sim_action_switch_sub_dialog_text_downloaded, 435 subInfoName, 436 activeSubName); 437 } else if (mIsEsimOperation) { 438 return getString( 439 R.string.sim_action_switch_sub_dialog_text, 440 subInfoName, 441 activeSubName); 442 } else { 443 return getString( 444 R.string.sim_action_switch_sub_dialog_text_single_sim, 445 activeSubName); 446 } 447 } 448 isDsdsConditionSatisfied()449 private boolean isDsdsConditionSatisfied() { 450 if (mTelMgr.isMultiSimEnabled()) { 451 Log.i(TAG, "DSDS is already enabled. Condition not satisfied."); 452 return false; 453 } 454 if (mTelMgr.isMultiSimSupported() != TelephonyManager.MULTISIM_ALLOWED) { 455 Log.i(TAG, "Hardware does not support DSDS."); 456 return false; 457 } 458 ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(mTelMgr); 459 boolean isRemovableSimEnabled = 460 slotInfos.stream() 461 .anyMatch( 462 slot -> 463 slot != null 464 && slot.isRemovable() 465 && slot.getIsActive() 466 && slot.getCardStateInfo() 467 == UiccSlotInfo.CARD_STATE_INFO_PRESENT); 468 if (mIsEsimOperation && isRemovableSimEnabled) { 469 Log.i(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied."); 470 return true; 471 } 472 boolean isEsimProfileEnabled = 473 SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream() 474 .anyMatch(SubscriptionInfo::isEmbedded); 475 if (!mIsEsimOperation && isEsimProfileEnabled) { 476 Log.i( 477 TAG, 478 "Removable SIM operation and eSIM profile is enabled. DSDS condition" 479 + " satisfied."); 480 return true; 481 } 482 Log.i(TAG, "DSDS condition not satisfied."); 483 return false; 484 } 485 } 486