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; 18 19 import android.app.FragmentManager; 20 import android.os.Bundle; 21 import android.telephony.SubscriptionInfo; 22 import android.telephony.SubscriptionManager; 23 import android.util.Log; 24 25 import com.android.settings.SidecarFragment; 26 import com.android.settings.network.telephony.EuiccOperationSidecar; 27 28 /** 29 * This sidecar is responsible for switching to the removable slot. It disables the active eSIM 30 * profile before switching if there is one. 31 */ 32 public class SwitchToRemovableSlotSidecar extends EuiccOperationSidecar 33 implements SidecarFragment.Listener { 34 private static final String TAG = "SwitchRemovableSidecar"; 35 private static final String ACTION_DISABLE_SUBSCRIPTION_AND_SWITCH_SLOT = 36 "disable_subscription_and_switch_slot_sidecar"; 37 38 // Stateless members. 39 private SwitchToEuiccSubscriptionSidecar mSwitchToSubscriptionSidecar; 40 private int mPhysicalSlotId; 41 private SubscriptionInfo mRemovedSubInfo; 42 43 /** Returns a SwitchToRemovableSlotSidecar sidecar instance. */ get(FragmentManager fm)44 public static SwitchToRemovableSlotSidecar get(FragmentManager fm) { 45 return SidecarFragment.get(fm, TAG, SwitchToRemovableSlotSidecar.class, null /* args */); 46 } 47 48 @Override onCreate(Bundle savedInstanceState)49 public void onCreate(Bundle savedInstanceState) { 50 super.onCreate(savedInstanceState); 51 mSwitchToSubscriptionSidecar = 52 SwitchToEuiccSubscriptionSidecar.get(getChildFragmentManager()); 53 } 54 55 @Override onResume()56 public void onResume() { 57 super.onResume(); 58 mSwitchToSubscriptionSidecar.addListener(this); 59 } 60 61 @Override onPause()62 public void onPause() { 63 mSwitchToSubscriptionSidecar.removeListener(this); 64 super.onPause(); 65 } 66 67 @Override getReceiverAction()68 protected String getReceiverAction() { 69 return ACTION_DISABLE_SUBSCRIPTION_AND_SWITCH_SLOT; 70 } 71 72 @Override onStateChange(SidecarFragment fragment)73 public void onStateChange(SidecarFragment fragment) { 74 if (fragment == mSwitchToSubscriptionSidecar) { 75 onSwitchToSubscriptionSidecarStateChange(); 76 } else if (fragment == mSwitchSlotSidecar) { 77 onSwitchSlotSidecarStateChange(); 78 } else { 79 Log.wtf(TAG, "Received state change from a sidecar not expected."); 80 } 81 } 82 83 /** 84 * Starts switching to the removable slot. 85 * 86 * @param physicalSlotId removable physical SIM slot ID. 87 * @param removedSubInfo if the all of slots have sims, it should remove the one of active sim. 88 * If the removedSubInfo is null, then use the default value. 89 * The default value is the removable physical SIM slot and portId 0. 90 */ run(int physicalSlotId, SubscriptionInfo removedSubInfo)91 public void run(int physicalSlotId, SubscriptionInfo removedSubInfo) { 92 mPhysicalSlotId = physicalSlotId; 93 mRemovedSubInfo = removedSubInfo; 94 SubscriptionManager subscriptionManager = 95 getContext().getSystemService(SubscriptionManager.class).createForAllUserProfiles(); 96 if (!mTelephonyManager.isMultiSimEnabled() 97 && SubscriptionUtil.getActiveSubscriptions(subscriptionManager).stream().anyMatch( 98 SubscriptionInfo::isEmbedded)) { 99 // In SS mode, the esim is active, then inactivate the esim. 100 Log.i(TAG, "There is an active eSIM profile. Disable the profile first."); 101 // Use INVALID_SUBSCRIPTION_ID to disable the only active profile. 102 mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 0, null); 103 } else if (mTelephonyManager.isMultiSimEnabled() && mRemovedSubInfo != null) { 104 // In DSDS mode+MEP, if the replaced esim is active, then it should disable that esim 105 // profile before changing SimSlotMapping process. 106 // Use INVALID_SUBSCRIPTION_ID to disable the esim profile. 107 mSwitchToSubscriptionSidecar.run(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 108 mRemovedSubInfo.getPortIndex(), null); 109 } else { 110 Log.i(TAG, "Start to switch to removable slot."); 111 mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo); 112 } 113 } 114 onSwitchToSubscriptionSidecarStateChange()115 private void onSwitchToSubscriptionSidecarStateChange() { 116 switch (mSwitchToSubscriptionSidecar.getState()) { 117 case State.SUCCESS: 118 mSwitchToSubscriptionSidecar.reset(); 119 Log.i(TAG, 120 "Successfully disabled eSIM profile. Start to switch to Removable slot."); 121 mSwitchSlotSidecar.runSwitchToRemovableSlot(mPhysicalSlotId, mRemovedSubInfo); 122 break; 123 case State.ERROR: 124 mSwitchToSubscriptionSidecar.reset(); 125 Log.i(TAG, "Failed to disable the active eSIM profile."); 126 setState(State.ERROR, Substate.UNUSED); 127 break; 128 } 129 } 130 onSwitchSlotSidecarStateChange()131 private void onSwitchSlotSidecarStateChange() { 132 switch (mSwitchSlotSidecar.getState()) { 133 case State.SUCCESS: 134 mSwitchSlotSidecar.reset(); 135 Log.i(TAG, "Successfully switched to removable slot."); 136 setState(State.SUCCESS, Substate.UNUSED); 137 break; 138 case State.ERROR: 139 mSwitchSlotSidecar.reset(); 140 Log.i(TAG, "Failed to switch to removable slot."); 141 setState(State.ERROR, Substate.UNUSED); 142 break; 143 } 144 } 145 } 146