• 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.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