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