• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.UiccCardInfo;
27 import android.text.TextUtils;
28 import android.util.Log;
29 import android.view.View;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.settings.R;
33 import com.android.settings.SidecarFragment;
34 import com.android.settings.network.EnableMultiSimSidecar;
35 import com.android.settings.network.SubscriptionUtil;
36 import com.android.settings.network.SwitchToEuiccSubscriptionSidecar;
37 import com.android.settings.network.SwitchToRemovableSlotSidecar;
38 import com.android.settings.network.UiccSlotUtil;
39 import com.android.settings.sim.SimActivationNotifier;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.stream.Collectors;
44 
45 /** This dialog activity handles both eSIM and pSIM subscriptions enabling and disabling. */
46 public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogActivity
47         implements SidecarFragment.Listener, ConfirmDialogFragment.OnConfirmListener {
48 
49     private static final String TAG = "ToggleSubscriptionDialogActivity";
50     // Arguments
51     @VisibleForTesting
52     public static final String ARG_enable = "enable";
53     // Dialog tags
54     private static final int DIALOG_TAG_DISABLE_SIM_CONFIRMATION = 1;
55     private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION = 2;
56     private static final int DIALOG_TAG_ENABLE_DSDS_CONFIRMATION = 3;
57     private static final int DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION = 4;
58     private static final int DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP = 5;
59 
60     // Number of SIMs for DSDS
61     private static final int NUM_OF_SIMS_FOR_DSDS = 2;
62     // Support RTL mode
63     private static final String LINE_BREAK = "\n";
64     private static final int LINE_BREAK_OFFSET_ONE = 1;
65     private static final int LINE_BREAK_OFFSET_TWO = 2;
66     private static final String RTL_MARK = "\u200F";
67 
68     /**
69      * Returns an intent of ToggleSubscriptionDialogActivity.
70      *
71      * @param context The context used to start the ToggleSubscriptionDialogActivity.
72      * @param subId The subscription ID of the subscription needs to be toggled.
73      * @param enable Whether the activity should enable or disable the subscription.
74      */
getIntent(Context context, int subId, boolean enable)75     public static Intent getIntent(Context context, int subId, boolean enable) {
76         Intent intent = new Intent(context, ToggleSubscriptionDialogActivity.class);
77         intent.putExtra(ARG_SUB_ID, subId);
78         intent.putExtra(ARG_enable, enable);
79         return intent;
80     }
81 
82     private SubscriptionInfo mSubInfo;
83     private SwitchToEuiccSubscriptionSidecar mSwitchToEuiccSubscriptionSidecar;
84     private SwitchToRemovableSlotSidecar mSwitchToRemovableSlotSidecar;
85     private EnableMultiSimSidecar mEnableMultiSimSidecar;
86     private boolean mEnable;
87     private boolean mIsEsimOperation;
88     private TelephonyManager mTelMgr;
89     private boolean isRtlMode;
90     private List<SubscriptionInfo> mActiveSubInfos;
91 
92     @Override
onCreate(Bundle savedInstanceState)93     protected void onCreate(Bundle savedInstanceState) {
94         super.onCreate(savedInstanceState);
95         Intent intent = getIntent();
96         int subId = intent.getIntExtra(ARG_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
97         mTelMgr = getSystemService(TelephonyManager.class);
98 
99         UserManager userManager = getSystemService(UserManager.class);
100         if (!userManager.isAdminUser()) {
101             Log.e(TAG, "It is not the admin user. Unable to toggle subscription.");
102             finish();
103             return;
104         }
105 
106         if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
107             Log.e(TAG, "The subscription id is not usable.");
108             finish();
109             return;
110         }
111 
112         mActiveSubInfos = SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
113         mSubInfo = SubscriptionUtil.getSubById(mSubscriptionManager, subId);
114         mIsEsimOperation = mSubInfo != null && mSubInfo.isEmbedded();
115         mSwitchToEuiccSubscriptionSidecar =
116                 SwitchToEuiccSubscriptionSidecar.get(getFragmentManager());
117         mSwitchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(getFragmentManager());
118         mEnableMultiSimSidecar = EnableMultiSimSidecar.get(getFragmentManager());
119         mEnable = intent.getBooleanExtra(ARG_enable, true);
120         isRtlMode = getResources().getConfiguration().getLayoutDirection()
121                 == View.LAYOUT_DIRECTION_RTL;
122         Log.i(TAG, "isMultipleEnabledProfilesSupported():" + isMultipleEnabledProfilesSupported());
123 
124         if (savedInstanceState == null) {
125             if (mEnable) {
126                 showEnableSubDialog();
127             } else {
128                 showDisableSimConfirmDialog();
129             }
130         }
131     }
132 
133     @Override
onResume()134     protected void onResume() {
135         super.onResume();
136         mSwitchToEuiccSubscriptionSidecar.addListener(this);
137         mSwitchToRemovableSlotSidecar.addListener(this);
138         mEnableMultiSimSidecar.addListener(this);
139     }
140 
141     @Override
onPause()142     protected void onPause() {
143         mEnableMultiSimSidecar.removeListener(this);
144         mSwitchToRemovableSlotSidecar.removeListener(this);
145         mSwitchToEuiccSubscriptionSidecar.removeListener(this);
146         super.onPause();
147     }
148 
149     @Override
onStateChange(SidecarFragment fragment)150     public void onStateChange(SidecarFragment fragment) {
151         if (fragment == mSwitchToEuiccSubscriptionSidecar) {
152             handleSwitchToEuiccSubscriptionSidecarStateChange();
153         } else if (fragment == mSwitchToRemovableSlotSidecar) {
154             handleSwitchToRemovableSlotSidecarStateChange();
155         } else if (fragment == mEnableMultiSimSidecar) {
156             handleEnableMultiSimSidecarStateChange();
157         }
158     }
159 
160     @Override
onConfirm(int tag, boolean confirmed, int itemPosition)161     public void onConfirm(int tag, boolean confirmed, int itemPosition) {
162         if (!confirmed
163                 && tag != DIALOG_TAG_ENABLE_DSDS_CONFIRMATION
164                 && tag != DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION) {
165             finish();
166             return;
167         }
168 
169         SubscriptionInfo removedSubInfo = null;
170         switch (tag) {
171             case DIALOG_TAG_DISABLE_SIM_CONFIRMATION:
172                 if (mIsEsimOperation) {
173                     Log.i(TAG, "Disabling the eSIM profile.");
174                     showProgressDialog(
175                             getString(R.string.privileged_action_disable_sub_dialog_progress));
176                     int port = mSubInfo != null ? mSubInfo.getPortIndex() : 0;
177                     mSwitchToEuiccSubscriptionSidecar.run(
178                             SubscriptionManager.INVALID_SUBSCRIPTION_ID, port, null);
179                     return;
180                 }
181                 Log.i(TAG, "Disabling the pSIM profile.");
182                 handleTogglePsimAction();
183                 break;
184             case DIALOG_TAG_ENABLE_DSDS_CONFIRMATION:
185                 if (!confirmed) {
186                     Log.i(TAG, "User cancel the dialog to enable DSDS.");
187                     showEnableSimConfirmDialog();
188                     return;
189                 }
190                 if (mTelMgr.doesSwitchMultiSimConfigTriggerReboot()) {
191                     Log.i(TAG, "Device does not support reboot free DSDS.");
192                     showRebootConfirmDialog();
193                     return;
194                 }
195                 Log.i(TAG, "Enabling DSDS without rebooting.");
196                 showProgressDialog(
197                         getString(R.string.sim_action_enabling_sim_without_carrier_name));
198                 mEnableMultiSimSidecar.run(NUM_OF_SIMS_FOR_DSDS);
199                 break;
200             case DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION:
201                 if (!confirmed) {
202                     Log.i(TAG, "User cancel the dialog to reboot to enable DSDS.");
203                     showEnableSimConfirmDialog();
204                     return;
205                 }
206                 Log.i(TAG, "User confirmed reboot to enable DSDS.");
207                 SimActivationNotifier.setShowSimSettingsNotification(this, true);
208                 mTelMgr.switchMultiSimConfig(NUM_OF_SIMS_FOR_DSDS);
209                 break;
210             case DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP:
211                 if (itemPosition != -1) {
212                     removedSubInfo = (mActiveSubInfos != null) ? mActiveSubInfos.get(itemPosition)
213                             : null;
214                 }
215             case DIALOG_TAG_ENABLE_SIM_CONFIRMATION:
216                 Log.i(TAG, "User confirmed to enable the subscription.");
217                 showProgressDialog(
218                         getString(
219                                 R.string.sim_action_switch_sub_dialog_progress,
220                                 SubscriptionUtil.getUniqueSubscriptionDisplayName(mSubInfo, this)),
221                         removedSubInfo != null ? true : false);
222                 if (mIsEsimOperation) {
223                     mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
224                             UiccSlotUtil.INVALID_PORT_ID,
225                             removedSubInfo);
226                     return;
227                 }
228                 mSwitchToRemovableSlotSidecar.run(UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID,
229                         removedSubInfo);
230                 break;
231             default:
232                 Log.e(TAG, "Unrecognized confirmation dialog tag: " + tag);
233                 break;
234         }
235     }
236 
handleSwitchToEuiccSubscriptionSidecarStateChange()237     private void handleSwitchToEuiccSubscriptionSidecarStateChange() {
238         switch (mSwitchToEuiccSubscriptionSidecar.getState()) {
239             case SidecarFragment.State.SUCCESS:
240                 Log.i(TAG,
241                         String.format(
242                                 "Successfully %s the eSIM profile.",
243                                 mEnable ? "enable" : "disable"));
244                 mSwitchToEuiccSubscriptionSidecar.reset();
245                 dismissProgressDialog();
246                 finish();
247                 break;
248             case SidecarFragment.State.ERROR:
249                 Log.i(TAG,
250                         String.format(
251                                 "Failed to %s the eSIM profile.", mEnable ? "enable" : "disable"));
252                 mSwitchToEuiccSubscriptionSidecar.reset();
253                 dismissProgressDialog();
254                 showErrorDialog(
255                         getString(R.string.privileged_action_disable_fail_title),
256                         getString(R.string.privileged_action_disable_fail_text));
257                 break;
258         }
259     }
260 
handleSwitchToRemovableSlotSidecarStateChange()261     private void handleSwitchToRemovableSlotSidecarStateChange() {
262         switch (mSwitchToRemovableSlotSidecar.getState()) {
263             case SidecarFragment.State.SUCCESS:
264                 Log.i(TAG, "Successfully switched to removable slot.");
265                 mSwitchToRemovableSlotSidecar.reset();
266                 handleTogglePsimAction();
267                 dismissProgressDialog();
268                 finish();
269                 break;
270             case SidecarFragment.State.ERROR:
271                 Log.e(TAG, "Failed switching to removable slot.");
272                 mSwitchToRemovableSlotSidecar.reset();
273                 dismissProgressDialog();
274                 showErrorDialog(
275                         getString(R.string.sim_action_enable_sim_fail_title),
276                         getString(R.string.sim_action_enable_sim_fail_text));
277                 break;
278         }
279     }
280 
handleEnableMultiSimSidecarStateChange()281     private void handleEnableMultiSimSidecarStateChange() {
282         switch (mEnableMultiSimSidecar.getState()) {
283             case SidecarFragment.State.SUCCESS:
284                 mEnableMultiSimSidecar.reset();
285                 Log.i(TAG, "Successfully switched to DSDS without reboot.");
286                 handleEnableSubscriptionAfterEnablingDsds();
287                 break;
288             case SidecarFragment.State.ERROR:
289                 mEnableMultiSimSidecar.reset();
290                 Log.i(TAG, "Failed to switch to DSDS without rebooting.");
291                 dismissProgressDialog();
292                 showErrorDialog(
293                         getString(R.string.dsds_activation_failure_title),
294                         getString(R.string.dsds_activation_failure_body_msg2));
295                 break;
296         }
297     }
298 
handleEnableSubscriptionAfterEnablingDsds()299     private void handleEnableSubscriptionAfterEnablingDsds() {
300         if (mIsEsimOperation) {
301             Log.i(TAG, "DSDS enabled, start to enable profile: " + mSubInfo.getSubscriptionId());
302             // For eSIM operations, we simply switch to the selected eSIM profile.
303             mSwitchToEuiccSubscriptionSidecar.run(mSubInfo.getSubscriptionId(),
304                     UiccSlotUtil.INVALID_PORT_ID, null);
305             return;
306         }
307 
308         Log.i(TAG, "DSDS enabled, start to enable pSIM profile.");
309         handleTogglePsimAction();
310         dismissProgressDialog();
311         finish();
312     }
313 
handleTogglePsimAction()314     private void handleTogglePsimAction() {
315         if (mSubscriptionManager.canDisablePhysicalSubscription() && mSubInfo != null) {
316             mSubscriptionManager.setUiccApplicationsEnabled(mSubInfo.getSubscriptionId(), mEnable);
317             finish();
318         } else {
319             Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
320                     + "enable the removable slot.");
321         }
322     }
323 
324     /* Handles the enabling SIM action. */
showEnableSubDialog()325     private void showEnableSubDialog() {
326         Log.d(TAG, "Handle subscription enabling.");
327         if (isDsdsConditionSatisfied()) {
328             showEnableDsdsConfirmDialog();
329             return;
330         }
331         if (!mIsEsimOperation && mTelMgr.isMultiSimEnabled()
332                 && isRemovableSimEnabled()) {
333             // This case is for switching on psim when device is not multiple enable profile
334             // supported.
335             Log.i(TAG, "Toggle on pSIM, no dialog displayed.");
336             handleTogglePsimAction();
337             finish();
338             return;
339         }
340         showEnableSimConfirmDialog();
341     }
342 
showEnableDsdsConfirmDialog()343     private void showEnableDsdsConfirmDialog() {
344         ConfirmDialogFragment.show(
345                 this,
346                 ConfirmDialogFragment.OnConfirmListener.class,
347                 DIALOG_TAG_ENABLE_DSDS_CONFIRMATION,
348                 getString(R.string.sim_action_enable_dsds_title),
349                 getString(R.string.sim_action_enable_dsds_text),
350                 getString(R.string.sim_action_yes),
351                 getString(R.string.sim_action_no_thanks));
352     }
353 
showRebootConfirmDialog()354     private void showRebootConfirmDialog() {
355         ConfirmDialogFragment.show(
356                 this,
357                 ConfirmDialogFragment.OnConfirmListener.class,
358                 DIALOG_TAG_ENABLE_DSDS_REBOOT_CONFIRMATION,
359                 getString(R.string.sim_action_restart_title),
360                 getString(R.string.sim_action_enable_dsds_text),
361                 getString(R.string.sim_action_reboot),
362                 getString(R.string.sim_action_cancel));
363     }
364 
365     /* Displays the SIM toggling confirmation dialog. */
showDisableSimConfirmDialog()366     private void showDisableSimConfirmDialog() {
367         final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
368                 mSubInfo, this);
369         String title =
370                 mSubInfo == null || TextUtils.isEmpty(displayName)
371                         ? getString(
372                                 R.string.privileged_action_disable_sub_dialog_title_without_carrier)
373                         : getString(
374                                 R.string.privileged_action_disable_sub_dialog_title, displayName);
375 
376         ConfirmDialogFragment.show(
377                 this,
378                 ConfirmDialogFragment.OnConfirmListener.class,
379                 DIALOG_TAG_DISABLE_SIM_CONFIRMATION,
380                 title,
381                 null,
382                 getString(R.string.yes),
383                 getString(R.string.sim_action_cancel));
384     }
385 
showEnableSimConfirmDialog()386     private void showEnableSimConfirmDialog() {
387         if (mActiveSubInfos == null || mActiveSubInfos.isEmpty()) {
388             Log.i(TAG, "No active subscriptions available.");
389             showNonSwitchSimConfirmDialog();
390             return;
391         }
392         Log.i(TAG, "mActiveSubInfos:" + mActiveSubInfos);
393 
394         boolean isSwitchingBetweenEsims = mIsEsimOperation
395                 && mActiveSubInfos.stream().anyMatch(activeSubInfo -> activeSubInfo.isEmbedded());
396         boolean isMultiSimEnabled = mTelMgr.isMultiSimEnabled();
397         if (isMultiSimEnabled
398                 && !isMultipleEnabledProfilesSupported()
399                 && !isSwitchingBetweenEsims) {
400             // Showing the "no switch dialog" for below cases.
401             // DSDS mode + no MEP +
402             //     (there is the active psim -> esim switch on => active (psim + esim))
403             showNonSwitchSimConfirmDialog();
404             return;
405         }
406 
407         if (isMultiSimEnabled && isMultipleEnabledProfilesSupported()) {
408             if (mActiveSubInfos.size() < NUM_OF_SIMS_FOR_DSDS) {
409                 // The sim can add into device directly, so showing the "no switch dialog".
410                 // DSDS + MEP + (active sim < NUM_OF_SIMS_FOR_DSDS)
411                 showNonSwitchSimConfirmDialog();
412             } else {
413                 // The all of slots have sim, it needs to show the "MEP switch dialog".
414                 // DSDS + MEP + two active sims
415                 showMepSwitchSimConfirmDialog();
416             }
417             return;
418         }
419 
420         // Showing the "switch dialog" for below cases.
421         // case1: SS mode + psim switch on from esim.
422         // case2: SS mode + esim switch from psim.
423         // case3: DSDS mode + No MEP + esim switch on from another esim.
424         SubscriptionInfo activeSub =
425                 (isMultiSimEnabled && isSwitchingBetweenEsims)
426                         ? mActiveSubInfos.stream()
427                                 .filter(activeSubInfo -> activeSubInfo.isEmbedded())
428                                 .findFirst().get()
429                         : mActiveSubInfos.get(0);
430         ConfirmDialogFragment.show(
431                 this,
432                 ConfirmDialogFragment.OnConfirmListener.class,
433                 DIALOG_TAG_ENABLE_SIM_CONFIRMATION,
434                 getSwitchSubscriptionTitle(),
435                 getSwitchDialogBodyMsg(activeSub, isSwitchingBetweenEsims),
436                 getSwitchDialogPosBtnText(),
437                 getString(R.string.sim_action_cancel));
438     }
439 
showNonSwitchSimConfirmDialog()440     private void showNonSwitchSimConfirmDialog() {
441         ConfirmDialogFragment.show(
442                 this,
443                 ConfirmDialogFragment.OnConfirmListener.class,
444                 DIALOG_TAG_ENABLE_SIM_CONFIRMATION,
445                 getEnableSubscriptionTitle(),
446                 null /* msg */,
447                 getString(R.string.yes),
448                 getString(R.string.sim_action_cancel));
449     }
450 
showMepSwitchSimConfirmDialog()451     private void showMepSwitchSimConfirmDialog() {
452         Log.d(TAG, "showMepSwitchSimConfirmDialog");
453         final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
454                 mSubInfo, this);
455         String title = getString(R.string.sim_action_switch_sub_dialog_mep_title, displayName);
456         final StringBuilder switchDialogMsg = new StringBuilder();
457         switchDialogMsg.append(
458                 getString(R.string.sim_action_switch_sub_dialog_mep_text, displayName));
459         if (isRtlMode) {
460             /* The RTL symbols must be added before and after each sentence.
461              * (Each message are all with two line break symbols)
462              */
463             switchDialogMsg.insert(0, RTL_MARK)
464                     .insert(switchDialogMsg.length(), RTL_MARK);
465         }
466         ConfirmDialogFragment.show(
467                 this,
468                 ConfirmDialogFragment.OnConfirmListener.class,
469                 DIALOG_TAG_ENABLE_SIM_CONFIRMATION_MEP,
470                 title,
471                 switchDialogMsg.toString(),
472                 null,
473                 null,
474                 getSwitchDialogBodyList());
475     }
476 
getSwitchDialogPosBtnText()477     private String getSwitchDialogPosBtnText() {
478         return mIsEsimOperation
479                 ? getString(
480                         R.string.sim_action_switch_sub_dialog_confirm,
481                         SubscriptionUtil.getUniqueSubscriptionDisplayName(mSubInfo, this))
482                 : getString(R.string.sim_switch_button);
483     }
484 
getEnableSubscriptionTitle()485     private String getEnableSubscriptionTitle() {
486         final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
487                 mSubInfo, this);
488         if (mSubInfo == null || TextUtils.isEmpty(displayName)) {
489             return getString(R.string.sim_action_enable_sub_dialog_title_without_carrier_name);
490         }
491         return getString(R.string.sim_action_enable_sub_dialog_title, displayName);
492     }
493 
getSwitchSubscriptionTitle()494     private String getSwitchSubscriptionTitle() {
495         if (mIsEsimOperation) {
496             return getString(
497                     R.string.sim_action_switch_sub_dialog_title,
498                     SubscriptionUtil.getUniqueSubscriptionDisplayName(mSubInfo, this));
499         }
500         return getString(R.string.sim_action_switch_psim_dialog_title);
501     }
502 
getSwitchDialogBodyMsg(SubscriptionInfo activeSub, boolean betweenEsim)503     private String getSwitchDialogBodyMsg(SubscriptionInfo activeSub, boolean betweenEsim) {
504         final CharSequence subInfoName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
505                 mSubInfo, this);
506         final CharSequence activeSubName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
507                 activeSub, this);
508         final StringBuilder switchDialogMsg = new StringBuilder();
509         if (betweenEsim && mIsEsimOperation) {
510             switchDialogMsg.append(getString(
511                     R.string.sim_action_switch_sub_dialog_text_downloaded,
512                     subInfoName,
513                     activeSubName));
514         } else if (mIsEsimOperation) {
515             switchDialogMsg.append(getString(
516                     R.string.sim_action_switch_sub_dialog_text,
517                     subInfoName,
518                     activeSubName));
519         } else {
520             switchDialogMsg.append(getString(
521                     R.string.sim_action_switch_sub_dialog_text_single_sim,
522                     activeSubName));
523         }
524         if (isRtlMode) {
525             /* There are two lines of message in the dialog, and the RTL symbols must be added
526              * before and after each sentence, so use the line break symbol to find the position.
527              * (Each message are all with two line break symbols)
528              */
529             switchDialogMsg.insert(0, RTL_MARK)
530                     .insert(switchDialogMsg.indexOf(LINE_BREAK) - LINE_BREAK_OFFSET_ONE, RTL_MARK)
531                     .insert(switchDialogMsg.indexOf(LINE_BREAK) + LINE_BREAK_OFFSET_TWO, RTL_MARK)
532                     .insert(switchDialogMsg.length(), RTL_MARK);
533         }
534         return switchDialogMsg.toString();
535     }
536 
getSwitchDialogBodyList()537     private ArrayList<String> getSwitchDialogBodyList() {
538         ArrayList<String> list = new ArrayList<String>(mActiveSubInfos.stream()
539                 .map(subInfo -> {
540                     CharSequence subInfoName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
541                             subInfo, this);
542                     return getString(
543                             R.string.sim_action_switch_sub_dialog_carrier_list_item_for_turning_off,
544                             subInfoName);
545                 })
546                 .collect(Collectors.toList()));
547         list.add(getString(R.string.sim_action_cancel));
548         return list;
549     }
550 
isDsdsConditionSatisfied()551     private boolean isDsdsConditionSatisfied() {
552         if (mTelMgr.isMultiSimEnabled()) {
553             Log.d(TAG, "DSDS is already enabled. Condition not satisfied.");
554             return false;
555         }
556         if (mTelMgr.isMultiSimSupported() != TelephonyManager.MULTISIM_ALLOWED) {
557             Log.d(TAG, "Hardware does not support DSDS.");
558             return false;
559         }
560         boolean isActiveSim = SubscriptionUtil.getActiveSubscriptions(
561                 mSubscriptionManager).size() > 0;
562         if (isMultipleEnabledProfilesSupported() && isActiveSim) {
563             Log.d(TAG,
564                     "Device supports MEP and eSIM operation and eSIM profile is enabled."
565                             + " DSDS condition satisfied.");
566             return true;
567         }
568         boolean isRemovableSimEnabled = isRemovableSimEnabled();
569         if (mIsEsimOperation && isRemovableSimEnabled) {
570             Log.d(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied.");
571             return true;
572         }
573         boolean isEsimProfileEnabled =
574                 SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream()
575                         .anyMatch(SubscriptionInfo::isEmbedded);
576         if (!mIsEsimOperation && isEsimProfileEnabled) {
577             Log.d(TAG, "Removable SIM operation and eSIM profile is enabled. DSDS condition"
578                     + " satisfied.");
579             return true;
580         }
581         Log.d(TAG, "DSDS condition not satisfied.");
582         return false;
583     }
584 
isRemovableSimEnabled()585     private boolean isRemovableSimEnabled() {
586         return UiccSlotUtil.isRemovableSimEnabled(mTelMgr);
587     }
588 
isMultipleEnabledProfilesSupported()589     private boolean isMultipleEnabledProfilesSupported() {
590         List<UiccCardInfo> cardInfos = mTelMgr.getUiccCardsInfo();
591         if (cardInfos == null) {
592             Log.w(TAG, "UICC cards info list is empty.");
593             return false;
594         }
595         return cardInfos.stream().anyMatch(
596                 cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
597     }
598 }
599