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.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.provider.Settings;
25 import android.telephony.CarrierConfigManager;
26 import android.telephony.TelephonyManager;
27 import android.telephony.UiccSlotInfo;
28 import android.util.ArraySet;
29 import android.util.Log;
30 
31 import com.android.settings.AsyncTaskSidecar;
32 import com.android.settings.SidecarFragment;
33 
34 import java.util.Collections;
35 import java.util.Set;
36 import java.util.concurrent.CountDownLatch;
37 import java.util.concurrent.TimeUnit;
38 
39 /**
40  * {@code EnableMultiSimSidecar} enables multi SIM on the device. It should only be called for
41  * Android R+. After {@code run} is called, it sets the configuration on modem side to enable
42  * multiple SIMs. Once the configuration is set successfully, it will listen to UICC card changes
43  * until {@code TelMan.EXTRA_ACTIVE_SIM_SUPPORTED_COUNT} matches {@code mNumOfActiveSim} or timeout.
44  */
45 public class EnableMultiSimSidecar extends AsyncTaskSidecar<Void, Boolean> {
46 
47     // Tags
48     private static final String TAG = "EnableMultiSimSidecar";
49 
50     private static final long DEFAULT_ENABLE_MULTI_SIM_TIMEOUT_MILLS = 40 * 1000L;
51 
get(FragmentManager fm)52     public static EnableMultiSimSidecar get(FragmentManager fm) {
53         return SidecarFragment.get(fm, TAG, EnableMultiSimSidecar.class, null /* args */);
54     }
55 
56     final CountDownLatch mSimCardStateChangedLatch = new CountDownLatch(1);
57     private TelephonyManager mTelephonyManager;
58     private int mNumOfActiveSim = 0;
59 
60     private final BroadcastReceiver mCarrierConfigChangeReceiver =
61             new BroadcastReceiver() {
62                 @Override
63                 public void onReceive(Context context, Intent intent) {
64                     int readySimsCount = getReadySimsCount();
65                     int activeSlotsCount = getActiveSlotsCount();
66                     // If the number of ready SIM count and active slots equal to the number of SIMs
67                     // need to be activated, the device is successfully switched to multiple active
68                     // SIM mode.
69                     if (readySimsCount == mNumOfActiveSim && activeSlotsCount == mNumOfActiveSim) {
70                         Log.i(
71                                 TAG,
72                                 String.format("%d slots are active and ready.", mNumOfActiveSim));
73                         mSimCardStateChangedLatch.countDown();
74                         return;
75                     }
76                     Log.i(
77                             TAG,
78                             String.format(
79                                     "%d slots are active and %d SIMs are ready. Keep waiting until"
80                                             + " timeout.",
81                                     activeSlotsCount, readySimsCount));
82                 }
83             };
84 
85     @Override
doInBackground(Void aVoid)86     protected Boolean doInBackground(Void aVoid) {
87         return updateMultiSimConfig();
88     }
89 
90     @Override
onPostExecute(Boolean isDsdsEnabled)91     protected void onPostExecute(Boolean isDsdsEnabled) {
92         if (isDsdsEnabled) {
93             setState(State.SUCCESS, Substate.UNUSED);
94         } else {
95             setState(State.ERROR, Substate.UNUSED);
96         }
97     }
98 
run(int numberOfSimToActivate)99     public void run(int numberOfSimToActivate) {
100         mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
101         mNumOfActiveSim = numberOfSimToActivate;
102 
103         if (mNumOfActiveSim > mTelephonyManager.getSupportedModemCount()) {
104             Log.e(TAG, "Requested number of active SIM is greater than supported modem count.");
105             setState(State.ERROR, Substate.UNUSED);
106             return;
107         }
108         if (mTelephonyManager.doesSwitchMultiSimConfigTriggerReboot()) {
109             Log.e(TAG, "The device does not support reboot free DSDS.");
110             setState(State.ERROR, Substate.UNUSED);
111             return;
112         }
113         super.run(null /* param */);
114     }
115 
116     // This method registers a ACTION_SIM_CARD_STATE_CHANGED broadcast receiver and wait for slot
117     // changes. If multi SIMs have been successfully enabled, it returns true. Otherwise, returns
118     // false.
updateMultiSimConfig()119     private boolean updateMultiSimConfig() {
120         try {
121             getContext()
122                     .registerReceiver(
123                             mCarrierConfigChangeReceiver,
124                             new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
125             mTelephonyManager.switchMultiSimConfig(mNumOfActiveSim);
126             long waitingTimeMillis =
127                     Settings.Global.getLong(
128                             getContext().getContentResolver(),
129                             Settings.Global.ENABLE_MULTI_SLOT_TIMEOUT_MILLIS,
130                             DEFAULT_ENABLE_MULTI_SIM_TIMEOUT_MILLS);
131             if (mSimCardStateChangedLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) {
132                 Log.i(TAG, "Multi SIM were successfully enabled.");
133                 return true;
134             } else {
135                 Log.e(TAG, "Timeout for waiting SIM status.");
136                 return false;
137             }
138         } catch (InterruptedException e) {
139             Log.e(TAG, "Failed to enable multiple SIM due to InterruptedException", e);
140             return false;
141         } finally {
142             getContext().unregisterReceiver(mCarrierConfigChangeReceiver);
143         }
144     }
145 
146     // Returns how many SIMs have SIM ready state, not ready state, or removable slot with absent
147     // SIM state.
getReadySimsCount()148     private int getReadySimsCount() {
149         int readyCardsCount = 0;
150         int activeSlotCount = mTelephonyManager.getActiveModemCount();
151         Set<Integer> activeRemovableLogicalSlots = getActiveRemovableLogicalSlotIds();
152         for (int logicalSlotId = 0; logicalSlotId < activeSlotCount; logicalSlotId++) {
153             int simState = mTelephonyManager.getSimState(logicalSlotId);
154             if (simState == TelephonyManager.SIM_STATE_READY
155                     || simState == TelephonyManager.SIM_STATE_NOT_READY
156                     || simState == TelephonyManager.SIM_STATE_LOADED
157                     || (simState == TelephonyManager.SIM_STATE_ABSENT
158                             && activeRemovableLogicalSlots.contains(logicalSlotId))) {
159                 readyCardsCount++;
160             }
161         }
162         return readyCardsCount;
163     }
164 
165     // Get active slots count from {@code TelephonyManager#getUiccSlotsInfo}.
getActiveSlotsCount()166     private int getActiveSlotsCount() {
167         UiccSlotInfo[] slotsInfo = mTelephonyManager.getUiccSlotsInfo();
168         if (slotsInfo == null) {
169             return 0;
170         }
171         int activeSlots = 0;
172         for (UiccSlotInfo slotInfo : slotsInfo) {
173             if (slotInfo != null && slotInfo.getIsActive()) {
174                 activeSlots++;
175             }
176         }
177         return activeSlots;
178     }
179 
180     /** Returns a list of active removable logical slot ids. */
getActiveRemovableLogicalSlotIds()181     public Set<Integer> getActiveRemovableLogicalSlotIds() {
182         UiccSlotInfo[] infos = mTelephonyManager.getUiccSlotsInfo();
183         if (infos == null) {
184             return Collections.emptySet();
185         }
186         Set<Integer> activeRemovableLogicalSlotIds = new ArraySet<>();
187         for (UiccSlotInfo info : infos) {
188             if (info != null && info.getIsActive() && info.isRemovable()) {
189                 activeRemovableLogicalSlotIds.add(info.getLogicalSlotIdx());
190             }
191         }
192         return activeRemovableLogicalSlotIds;
193     }
194 }
195