• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.internal.telephony;
18 
19 import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
20 import static android.telephony.TelephonyManager.EXTRA_ACTIVE_SIM_SUPPORTED_COUNT;
21 
22 import android.annotation.NonNull;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.os.AsyncResult;
26 import android.os.Build;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.os.PowerManager;
30 import android.os.RegistrantList;
31 import android.os.SystemProperties;
32 import android.os.UserHandle;
33 import android.provider.DeviceConfig;
34 import android.sysprop.TelephonyProperties;
35 import android.telephony.PhoneCapability;
36 import android.telephony.SubscriptionManager;
37 import android.telephony.TelephonyManager;
38 import android.telephony.TelephonyRegistryManager;
39 import android.text.TextUtils;
40 import android.util.Log;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.telephony.flags.FeatureFlags;
44 import com.android.internal.telephony.subscription.SubscriptionManagerService;
45 import com.android.telephony.Rlog;
46 
47 import java.util.HashMap;
48 import java.util.HashSet;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.NoSuchElementException;
52 import java.util.Optional;
53 import java.util.Set;
54 import java.util.concurrent.CopyOnWriteArraySet;
55 import java.util.function.Consumer;
56 import java.util.stream.Collectors;
57 
58 /**
59  * This class manages phone's configuration which defines the potential capability (static) of the
60  * phone and its current activated capability (current).
61  * It gets and monitors static and current phone capability from the modem; send broadcast
62  * if they change, and sends commands to modem to enable or disable phones.
63  */
64 public class PhoneConfigurationManager {
65     public static final String DSDA = "dsda";
66     public static final String DSDS = "dsds";
67     public static final String TSTS = "tsts";
68     public static final String SSSS = "";
69     /** DeviceConfig key for whether Virtual DSDA is enabled. */
70     private static final String KEY_ENABLE_VIRTUAL_DSDA = "enable_virtual_dsda";
71     private static final String LOG_TAG = "PhoneCfgMgr";
72     private static final int EVENT_SWITCH_DSDS_CONFIG_DONE = 100;
73     private static final int EVENT_GET_MODEM_STATUS = 101;
74     private static final int EVENT_GET_MODEM_STATUS_DONE = 102;
75     private static final int EVENT_GET_PHONE_CAPABILITY_DONE = 103;
76     private static final int EVENT_DEVICE_CONFIG_CHANGED = 104;
77     private static final int EVENT_GET_SIMULTANEOUS_CALLING_SUPPORT_DONE = 105;
78     private static final int EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED = 106;
79 
80     /**
81      * Listener interface for events related to the {@link PhoneConfigurationManager} which should
82      * be reported to the {@link SimultaneousCallingTracker}.
83      */
84     public interface Listener {
onPhoneCapabilityChanged()85         public void onPhoneCapabilityChanged();
onDeviceConfigChanged()86         public void onDeviceConfigChanged();
87     }
88 
89     /**
90      * Base listener implementation.
91      */
92     public abstract static class ListenerBase implements Listener {
93         @Override
onPhoneCapabilityChanged()94         public void onPhoneCapabilityChanged() {}
95         @Override
onDeviceConfigChanged()96         public void onDeviceConfigChanged() {}
97     }
98 
99 
100     private static PhoneConfigurationManager sInstance = null;
101     private final Context mContext;
102     // Static capability retrieved from the modem - may be null in the case where no info has been
103     // retrieved yet.
104     private PhoneCapability mStaticCapability = null;
105     private final Set<Integer> mSlotsSupportingSimultaneousCellularCalls = new HashSet<>(3);
106     private final Set<Integer> mSubIdsSupportingSimultaneousCellularCalls = new HashSet<>(3);
107     private final HashSet<Consumer<Set<Integer>>> mSimultaneousCellularCallingListeners =
108             new HashSet<>(1);
109     private final RadioConfig mRadioConfig;
110     private final Handler mHandler;
111     // mPhones is obtained from PhoneFactory and can have phones corresponding to inactive modems as
112     // well. That is, the array size can be 2 even if num of active modems is 1.
113     private Phone[] mPhones;
114     private final Map<Integer, Boolean> mPhoneStatusMap;
115     private MockableInterface mMi = new MockableInterface();
116     private TelephonyManager mTelephonyManager;
117 
118     /** Feature flags */
119     @NonNull
120     private final FeatureFlags mFeatureFlags;
121     private final DefaultPhoneNotifier mNotifier;
122     public Set<Listener> mListeners = new CopyOnWriteArraySet<>();
123 
124     /**
125      * True if 'Virtual DSDA' i.e., in-call IMS connectivity on both subs with only single logical
126      * modem, is enabled.
127      */
128     private boolean mVirtualDsdaEnabled;
129     private static final RegistrantList sMultiSimConfigChangeRegistrants = new RegistrantList();
130     private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
131     private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
132     private static final boolean DEBUG = !"user".equals(Build.TYPE);
133     /**
134      * Init method to instantiate the object
135      * Should only be called once.
136      */
init(Context context, @NonNull FeatureFlags featureFlags)137     public static PhoneConfigurationManager init(Context context,
138             @NonNull FeatureFlags featureFlags) {
139         synchronized (PhoneConfigurationManager.class) {
140             if (sInstance == null) {
141                 sInstance = new PhoneConfigurationManager(context, featureFlags);
142             } else {
143                 Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
144             }
145             return sInstance;
146         }
147     }
148 
149     /**
150      * Constructor.
151      * @param context context needed to send broadcast.
152      */
PhoneConfigurationManager(Context context, @NonNull FeatureFlags featureFlags)153     private PhoneConfigurationManager(Context context, @NonNull FeatureFlags featureFlags) {
154         mContext = context;
155         mFeatureFlags = featureFlags;
156         // TODO: send commands to modem once interface is ready.
157         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
158         mRadioConfig = RadioConfig.getInstance();
159         mHandler = new ConfigManagerHandler();
160         mPhoneStatusMap = new HashMap<>();
161         mVirtualDsdaEnabled = DeviceConfig.getBoolean(
162                 DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_VIRTUAL_DSDA, false);
163         mNotifier = new DefaultPhoneNotifier(mContext, mFeatureFlags);
164         DeviceConfig.addOnPropertiesChangedListener(
165                 DeviceConfig.NAMESPACE_TELEPHONY, Runnable::run,
166                 properties -> {
167                     if (TextUtils.equals(DeviceConfig.NAMESPACE_TELEPHONY,
168                             properties.getNamespace())) {
169                         mHandler.sendEmptyMessage(EVENT_DEVICE_CONFIG_CHANGED);
170                     }
171                 });
172 
173         notifyCapabilityChanged();
174 
175         mPhones = PhoneFactory.getPhones();
176 
177         for (Phone phone : mPhones) {
178             registerForRadioState(phone);
179         }
180     }
181 
182     /**
183      * Assign a listener to be notified of state changes.
184      *
185      * @param listener A listener.
186      */
addListener(Listener listener)187     public void addListener(Listener listener) {
188         mListeners.add(listener);
189     }
190 
191     /**
192      * Removes a listener.
193      *
194      * @param listener A listener.
195      */
removeListener(Listener listener)196     public final void removeListener(Listener listener) {
197         mListeners.remove(listener);
198     }
199 
200     /**
201      * Updates the mapping between the slot IDs that support simultaneous calling and the
202      * associated sub IDs as well as notifies listeners.
203      */
updateSimultaneousSubIdsFromPhoneIdMappingAndNotify()204     private void updateSimultaneousSubIdsFromPhoneIdMappingAndNotify() {
205         if (!mFeatureFlags.simultaneousCallingIndications()) return;
206         Set<Integer> slotCandidates = mSlotsSupportingSimultaneousCellularCalls.stream()
207                 .map(i -> mPhones[i].getSubId())
208                 .filter(i ->i > SubscriptionManager.INVALID_SUBSCRIPTION_ID)
209                         .collect(Collectors.toSet());
210         if (mSubIdsSupportingSimultaneousCellularCalls.equals(slotCandidates))  return;
211         log("updateSimultaneousSubIdsFromPhoneIdMapping update: "
212                 + mSubIdsSupportingSimultaneousCellularCalls + " -> " + slotCandidates);
213         mSubIdsSupportingSimultaneousCellularCalls.clear();
214         mSubIdsSupportingSimultaneousCellularCalls.addAll(slotCandidates);
215         mNotifier.notifySimultaneousCellularCallingSubscriptionsChanged(
216                 mSubIdsSupportingSimultaneousCellularCalls);
217     }
218 
registerForRadioState(Phone phone)219     private void registerForRadioState(Phone phone) {
220         phone.mCi.registerForAvailable(mHandler, Phone.EVENT_RADIO_AVAILABLE, phone);
221     }
222 
getDefaultCapability()223     private PhoneCapability getDefaultCapability() {
224         if (getPhoneCount() > 1) {
225             return PhoneCapability.DEFAULT_DSDS_CAPABILITY;
226         } else {
227             return PhoneCapability.DEFAULT_SSSS_CAPABILITY;
228         }
229     }
230 
231     /**
232      * Listener for listening to events in the {@link android.telephony.TelephonyRegistryManager}
233      */
234     private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener =
235             new SubscriptionManager.OnSubscriptionsChangedListener() {
236                 @Override
237                 public void onSubscriptionsChanged() {
238                     updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
239                 }
240             };
241 
242     /**
243      * If virtual DSDA is enabled for this UE, then increase maxActiveVoiceSubscriptions to 2.
244      */
maybeOverrideMaxActiveVoiceSubscriptions( final PhoneCapability staticCapability)245     private PhoneCapability maybeOverrideMaxActiveVoiceSubscriptions(
246             final PhoneCapability staticCapability) {
247         boolean isVDsdaEnabled = staticCapability.getLogicalModemList().size() > 1
248                 && mVirtualDsdaEnabled;
249         boolean isBkwdCompatDsdaEnabled = mFeatureFlags.simultaneousCallingIndications()
250                 && mMi.getMultiSimProperty().orElse(SSSS).equals(DSDA);
251         if (isVDsdaEnabled || isBkwdCompatDsdaEnabled) {
252             // Since we already initialized maxActiveVoiceSubscriptions to the count the
253             // modem is capable of, we are only able to increase that count via this method. We do
254             // not allow a decrease of maxActiveVoiceSubscriptions:
255             int updatedMaxActiveVoiceSubscriptions =
256                     Math.max(staticCapability.getMaxActiveVoiceSubscriptions(), 2);
257             return new PhoneCapability.Builder(staticCapability)
258                     .setMaxActiveVoiceSubscriptions(updatedMaxActiveVoiceSubscriptions)
259                     .build();
260         } else {
261             return staticCapability;
262         }
263     }
264 
maybeEnableCellularDSDASupport()265     private void maybeEnableCellularDSDASupport() {
266         boolean bkwdsCompatDsda = mFeatureFlags.simultaneousCallingIndications()
267                 && getPhoneCount() > 1
268                 && mMi.getMultiSimProperty().orElse(SSSS).equals(DSDA);
269         boolean halSupportSimulCalling = mRadioConfig != null
270                 && mRadioConfig.getRadioConfigProxy(null).getVersion().greaterOrEqual(
271                         RIL.RADIO_HAL_VERSION_2_2)
272                 && getPhoneCount() > 1
273                 && getCellularStaticPhoneCapability().getMaxActiveVoiceSubscriptions() > 1;
274         // Register for simultaneous calling support changes in the modem if the HAL supports it
275         if (halSupportSimulCalling) {
276             updateSimultaneousCallingSupport();
277             mRadioConfig.registerForSimultaneousCallingSupportStatusChanged(mHandler,
278                     EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED, null);
279         } else if (bkwdsCompatDsda) {
280             // For older devices that only declare that they support DSDA via modem config,
281             // set DSDA as capable now statically.
282             log("DSDA modem config detected - setting DSDA enabled");
283             for (Phone p : mPhones) {
284                 mSlotsSupportingSimultaneousCellularCalls.add(p.getPhoneId());
285             }
286             updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
287             notifySimultaneousCellularCallingSlotsChanged();
288         }
289         // Register for subId updates to notify listeners when simultaneous calling is configured
290         if (mFeatureFlags.simultaneousCallingIndications()
291                 && (bkwdsCompatDsda || halSupportSimulCalling)) {
292             Log.d(LOG_TAG, "maybeEnableCellularDSDASupport: registering "
293                             + "mSubscriptionsChangedListener");
294             mContext.getSystemService(TelephonyRegistryManager.class)
295                     .addOnSubscriptionsChangedListener(
296                             mSubscriptionsChangedListener, mHandler::post);
297         }
298     }
299 
300     /**
301      * Static method to get instance.
302      */
getInstance()303     public static PhoneConfigurationManager getInstance() {
304         if (sInstance == null) {
305             Log.wtf(LOG_TAG, "getInstance null");
306         }
307 
308         return sInstance;
309     }
310 
311     /**
312      * Handler class to handle callbacks
313      */
314     private final class ConfigManagerHandler extends Handler {
315         @Override
handleMessage(Message msg)316         public void handleMessage(Message msg) {
317             AsyncResult ar;
318             Phone phone = null;
319             switch (msg.what) {
320                 case Phone.EVENT_RADIO_AVAILABLE:
321                 case Phone.EVENT_RADIO_ON:
322                     log("Received EVENT_RADIO_AVAILABLE/EVENT_RADIO_ON");
323                     ar = (AsyncResult) msg.obj;
324                     if (ar.userObj != null && ar.userObj instanceof Phone) {
325                         phone = (Phone) ar.userObj;
326                         updatePhoneStatus(phone);
327                     } else {
328                         // phone is null
329                         log("Unable to add phoneStatus to cache. "
330                                 + "No phone object provided for event " + msg.what);
331                     }
332                     updateRadioCapability();
333                     break;
334                 case EVENT_SWITCH_DSDS_CONFIG_DONE:
335                     ar = (AsyncResult) msg.obj;
336                     if (ar != null && ar.exception == null) {
337                         int numOfLiveModems = msg.arg1;
338                         onMultiSimConfigChanged(numOfLiveModems);
339                     } else {
340                         log(msg.what + " failure. Not switching multi-sim config." + ar.exception);
341                     }
342                     break;
343                 case EVENT_GET_MODEM_STATUS_DONE:
344                     ar = (AsyncResult) msg.obj;
345                     if (ar != null && ar.exception == null) {
346                         int phoneId = msg.arg1;
347                         boolean enabled = (boolean) ar.result;
348                         // update the cache each time getModemStatus is requested
349                         addToPhoneStatusCache(phoneId, enabled);
350                     } else {
351                         log(msg.what + " failure. Not updating modem status." + ar.exception);
352                     }
353                     break;
354                 case EVENT_GET_PHONE_CAPABILITY_DONE:
355                     ar = (AsyncResult) msg.obj;
356                     if (ar != null && ar.exception == null) {
357                         setStaticPhoneCapability((PhoneCapability) ar.result);
358                         notifyCapabilityChanged();
359                         for (Listener l : mListeners) {
360                             l.onPhoneCapabilityChanged();
361                         }
362                         maybeEnableCellularDSDASupport();
363                     } else {
364                         log(msg.what + " failure. Not getting phone capability." + ar.exception);
365                     }
366                     break;
367                 case EVENT_DEVICE_CONFIG_CHANGED:
368                     boolean isVirtualDsdaEnabled = DeviceConfig.getBoolean(
369                             DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_VIRTUAL_DSDA, false);
370                     if (isVirtualDsdaEnabled != mVirtualDsdaEnabled) {
371                         log("EVENT_DEVICE_CONFIG_CHANGED: from " + mVirtualDsdaEnabled + " to "
372                                 + isVirtualDsdaEnabled);
373                         mVirtualDsdaEnabled = isVirtualDsdaEnabled;
374                         for (Listener l : mListeners) {
375                             l.onDeviceConfigChanged();
376                         }
377                     }
378                     break;
379                 case EVENT_SIMULTANEOUS_CALLING_SUPPORT_CHANGED:
380                 case EVENT_GET_SIMULTANEOUS_CALLING_SUPPORT_DONE:
381                     log("Received EVENT_SLOTS_SUPPORTING_SIMULTANEOUS_CALL_CHANGED/DONE");
382                     if (getPhoneCount() < 2) {
383                         if (!mSlotsSupportingSimultaneousCellularCalls.isEmpty()) {
384                             mSlotsSupportingSimultaneousCellularCalls.clear();
385                         }
386                         break;
387                     }
388                     ar = (AsyncResult) msg.obj;
389                     if (ar != null && ar.exception == null) {
390                         List<Integer> returnedArrayList = (List<Integer>) ar.result;
391                         if (!mSlotsSupportingSimultaneousCellularCalls.isEmpty()) {
392                             mSlotsSupportingSimultaneousCellularCalls.clear();
393                         }
394                         int maxValidPhoneSlot = getPhoneCount() - 1;
395                         for (int i : returnedArrayList) {
396                             if (i < 0 || i > maxValidPhoneSlot) {
397                                 loge("Invalid slot supporting DSDA =" + i + ". Disabling DSDA.");
398                                 mSlotsSupportingSimultaneousCellularCalls.clear();
399                                 break;
400                             }
401                             mSlotsSupportingSimultaneousCellularCalls.add(i);
402                         }
403                         // Ensure the number of slots supporting cellular DSDA is valid:
404                         if (mSlotsSupportingSimultaneousCellularCalls.size() > getPhoneCount() ||
405                                 mSlotsSupportingSimultaneousCellularCalls.size() < 2) {
406                             loge("Invalid size of DSDA slots. Disabling cellular DSDA. Size of "
407                                     + "mSlotsSupportingSimultaneousCellularCalls=" +
408                                     mSlotsSupportingSimultaneousCellularCalls.size());
409                             mSlotsSupportingSimultaneousCellularCalls.clear();
410                         }
411                     } else {
412                         log(msg.what + " failure. Not getting logical slots that support "
413                                 + "simultaneous calling." + ar.exception);
414                         mSlotsSupportingSimultaneousCellularCalls.clear();
415                     }
416                     if (mFeatureFlags.simultaneousCallingIndications()) {
417                         updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
418                         notifySimultaneousCellularCallingSlotsChanged();
419                     }
420                     break;
421                 default:
422                     log("Unknown event: " + msg.what);
423             }
424         }
425     }
426 
427     /**
428      * Enable or disable phone
429      *
430      * @param phone which phone to operate on
431      * @param enable true or false
432      * @param result the message to sent back when it's done.
433      */
enablePhone(Phone phone, boolean enable, Message result)434     public void enablePhone(Phone phone, boolean enable, Message result) {
435         if (phone == null) {
436             log("enablePhone failed phone is null");
437             return;
438         }
439         phone.mCi.enableModem(enable, result);
440     }
441 
442     /**
443      * Get phone status (enabled/disabled)
444      * first query cache, if the status is not in cache,
445      * add it to cache and return a default value true (non-blocking).
446      *
447      * @param phone which phone to operate on
448      */
getPhoneStatus(Phone phone)449     public boolean getPhoneStatus(Phone phone) {
450         if (phone == null) {
451             log("getPhoneStatus failed phone is null");
452             return false;
453         }
454 
455         int phoneId = phone.getPhoneId();
456 
457         //use cache if the status has already been updated/queried
458         try {
459             return getPhoneStatusFromCache(phoneId);
460         } catch (NoSuchElementException ex) {
461             // Return true if modem status cannot be retrieved. For most cases, modem status
462             // is on. And for older version modems, GET_MODEM_STATUS and disable modem are not
463             // supported. Modem is always on.
464             //TODO: this should be fixed in R to support a third status UNKNOWN b/131631629
465             return true;
466         } finally {
467             //in either case send an asynchronous request to retrieve the phone status
468             updatePhoneStatus(phone);
469         }
470     }
471 
472     /**
473      * Get phone status (enabled/disabled) directly from modem, and use a result Message object
474      * Note: the caller of this method is reponsible to call this in a blocking fashion as well
475      * as read the results and handle the error case.
476      * (In order to be consistent, in error case, we should return default value of true; refer
477      *  to #getPhoneStatus method)
478      *
479      * @param phone which phone to operate on
480      * @param result message that will be updated with result
481      */
getPhoneStatusFromModem(Phone phone, Message result)482     public void getPhoneStatusFromModem(Phone phone, Message result) {
483         if (phone == null) {
484             log("getPhoneStatus failed phone is null");
485         }
486         phone.mCi.getModemStatus(result);
487     }
488 
489     /**
490      * return modem status from cache, NoSuchElementException if phoneId not in cache
491      * @param phoneId
492      */
getPhoneStatusFromCache(int phoneId)493     public boolean getPhoneStatusFromCache(int phoneId) throws NoSuchElementException {
494         if (mPhoneStatusMap.containsKey(phoneId)) {
495             return mPhoneStatusMap.get(phoneId);
496         } else {
497             throw new NoSuchElementException("phoneId not found: " + phoneId);
498         }
499     }
500 
501     /**
502      * method to call RIL getModemStatus
503      */
updatePhoneStatus(Phone phone)504     private void updatePhoneStatus(Phone phone) {
505         Message result = Message.obtain(
506                 mHandler, EVENT_GET_MODEM_STATUS_DONE, phone.getPhoneId(), 0 /**dummy arg*/);
507         phone.mCi.getModemStatus(result);
508     }
509 
510     /**
511      * Add status of the phone to the status HashMap
512      * @param phoneId
513      * @param status
514      */
addToPhoneStatusCache(int phoneId, boolean status)515     public void addToPhoneStatusCache(int phoneId, boolean status) {
516         mPhoneStatusMap.put(phoneId, status);
517     }
518 
519     /**
520      * Returns how many phone objects the device supports.
521      */
getPhoneCount()522     public int getPhoneCount() {
523         return mTelephonyManager.getActiveModemCount();
524     }
525 
526     /**
527      * @return The updated list of logical slots that support simultaneous cellular calling from the
528      * modem based on current network conditions.
529      */
getSlotsSupportingSimultaneousCellularCalls()530     public Set<Integer> getSlotsSupportingSimultaneousCellularCalls() {
531         return mSlotsSupportingSimultaneousCellularCalls;
532     }
533 
534     /**
535      * Get the current the list of logical slots supporting simultaneous cellular calling from the
536      * modem based on current network conditions.
537      */
538     @VisibleForTesting
updateSimultaneousCallingSupport()539     public void updateSimultaneousCallingSupport() {
540         log("updateSimultaneousCallingSupport: sending the request for "
541                 + "getting the list of logical slots supporting simultaneous cellular calling");
542         Message callback = Message.obtain(
543                 mHandler, EVENT_GET_SIMULTANEOUS_CALLING_SUPPORT_DONE);
544         mRadioConfig.updateSimultaneousCallingSupport(callback);
545         log("updateSimultaneousCallingSupport: "
546                 + "mSlotsSupportingSimultaneousCellularCalls = " +
547                 mSlotsSupportingSimultaneousCellularCalls);
548     }
549 
550     /**
551      * @return static overall phone capabilities for all phones, including voice overrides.
552      */
getStaticPhoneCapability()553     public synchronized PhoneCapability getStaticPhoneCapability() {
554         boolean isDefault = mStaticCapability == null;
555         PhoneCapability caps = isDefault ? getDefaultCapability() : mStaticCapability;
556         caps = maybeOverrideMaxActiveVoiceSubscriptions(caps);
557         log("getStaticPhoneCapability: isDefault=" + isDefault + ", caps=" + caps);
558         return caps;
559     }
560 
561     /**
562      * @return untouched capabilities returned from the modem
563      */
getCellularStaticPhoneCapability()564     private synchronized PhoneCapability getCellularStaticPhoneCapability() {
565         log("getCellularStaticPhoneCapability: mStaticCapability " + mStaticCapability);
566         return mStaticCapability;
567     }
568 
569     /**
570      * Caches the static PhoneCapability returned by the modem
571      */
setStaticPhoneCapability(PhoneCapability capability)572     public synchronized void setStaticPhoneCapability(PhoneCapability capability) {
573         log("setStaticPhoneCapability: mStaticCapability " + capability);
574         mStaticCapability = capability;
575     }
576 
577     /**
578      * Query the modem to return its static PhoneCapability and cache it
579      */
580     @VisibleForTesting
updateRadioCapability()581     public void updateRadioCapability() {
582         log("updateRadioCapability: sending the request for getting PhoneCapability");
583         Message callback = Message.obtain(mHandler, EVENT_GET_PHONE_CAPABILITY_DONE);
584         mRadioConfig.getPhoneCapability(callback);
585     }
586 
587     /**
588      * get configuration related status of each phone.
589      */
getCurrentPhoneCapability()590     public PhoneCapability getCurrentPhoneCapability() {
591         return getStaticPhoneCapability();
592     }
593 
getNumberOfModemsWithSimultaneousDataConnections()594     public int getNumberOfModemsWithSimultaneousDataConnections() {
595         return getStaticPhoneCapability().getMaxActiveDataSubscriptions();
596     }
597 
getNumberOfModemsWithSimultaneousVoiceConnections()598     public int getNumberOfModemsWithSimultaneousVoiceConnections() {
599         return getStaticPhoneCapability().getMaxActiveVoiceSubscriptions();
600     }
601 
isVirtualDsdaEnabled()602     public boolean isVirtualDsdaEnabled() {
603         return mVirtualDsdaEnabled;
604     }
605 
606     /**
607      * Register to listen to changes in the Phone slots that support simultaneous calling.
608      * @param consumer A consumer that will be used to consume the new slots supporting simultaneous
609      *                 cellular calling when it changes.
610      */
registerForSimultaneousCellularCallingSlotsChanged( Consumer<Set<Integer>> consumer)611     public void registerForSimultaneousCellularCallingSlotsChanged(
612             Consumer<Set<Integer>> consumer) {
613         mSimultaneousCellularCallingListeners.add(consumer);
614     }
615 
notifySimultaneousCellularCallingSlotsChanged()616     private void notifySimultaneousCellularCallingSlotsChanged() {
617         log("notifying listeners of changes to simultaneous cellular calling - new state:"
618                 + mSlotsSupportingSimultaneousCellularCalls);
619         for (Consumer<Set<Integer>> consumer : mSimultaneousCellularCallingListeners) {
620             try {
621                 consumer.accept(new HashSet<>(mSlotsSupportingSimultaneousCellularCalls));
622             } catch (Exception e) {
623                 log("Unexpected Exception encountered when notifying listener: " + e);
624             }
625         }
626     }
627 
notifyCapabilityChanged()628     private void notifyCapabilityChanged() {
629         mNotifier.notifyPhoneCapabilityChanged(getStaticPhoneCapability());
630     }
631 
632     /**
633      * Switch configs to enable multi-sim or switch back to single-sim
634      * @param numOfSims number of active sims we want to switch to
635      */
switchMultiSimConfig(int numOfSims)636     public void switchMultiSimConfig(int numOfSims) {
637         log("switchMultiSimConfig: with numOfSims = " + numOfSims);
638         if (getStaticPhoneCapability().getLogicalModemList().size() < numOfSims) {
639             log("switchMultiSimConfig: Phone is not capable of enabling "
640                     + numOfSims + " sims, exiting!");
641             return;
642         }
643         if (getPhoneCount() != numOfSims) {
644             log("switchMultiSimConfig: sending the request for switching");
645             Message callback = Message.obtain(
646                     mHandler, EVENT_SWITCH_DSDS_CONFIG_DONE, numOfSims, 0 /**dummy arg*/);
647             mRadioConfig.setNumOfLiveModems(numOfSims, callback);
648         } else {
649             log("switchMultiSimConfig: No need to switch. getNumOfActiveSims is already "
650                     + numOfSims);
651         }
652     }
653 
654     /**
655      * Get whether reboot is required or not after making changes to modem configurations.
656      * Return value defaults to true
657      */
isRebootRequiredForModemConfigChange()658     public boolean isRebootRequiredForModemConfigChange() {
659         return mMi.isRebootRequiredForModemConfigChange();
660     }
661 
onMultiSimConfigChanged(int numOfActiveModems)662     private void onMultiSimConfigChanged(int numOfActiveModems) {
663         int oldNumOfActiveModems = getPhoneCount();
664         setMultiSimProperties(numOfActiveModems);
665 
666         if (isRebootRequiredForModemConfigChange()) {
667             log("onMultiSimConfigChanged: Rebooting.");
668             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
669             pm.reboot("Multi-SIM config changed.");
670         } else {
671             log("onMultiSimConfigChanged: Rebooting is not required.");
672             mMi.notifyPhoneFactoryOnMultiSimConfigChanged(mContext, numOfActiveModems);
673             broadcastMultiSimConfigChange(numOfActiveModems);
674             boolean subInfoCleared = false;
675             // if numOfActiveModems is decreasing, deregister old RILs
676             // eg if we are going from 2 phones to 1 phone, we need to deregister RIL for the
677             // second phone. This loop does nothing if numOfActiveModems is increasing.
678             for (int phoneId = numOfActiveModems; phoneId < oldNumOfActiveModems; phoneId++) {
679                 SubscriptionManagerService.getInstance().markSubscriptionsInactive(phoneId);
680                 subInfoCleared = true;
681                 mPhones[phoneId].mCi.onSlotActiveStatusChange(
682                         SubscriptionManager.isValidPhoneId(phoneId));
683             }
684             if (subInfoCleared) {
685                 // This triggers update of default subs. This should be done asap after
686                 // setMultiSimProperties() to avoid (minimize) duration for which default sub can be
687                 // invalid and can map to a non-existent phone.
688                 // If forexample someone calls a TelephonyManager API on default sub after
689                 // setMultiSimProperties() and before onSubscriptionsChanged() below -- they can be
690                 // using an invalid sub, which can map to a non-existent phone and can cause an
691                 // exception (see b/163582235).
692                 MultiSimSettingController.getInstance().onPhoneRemoved();
693             }
694             // old phone objects are not needed now; mPhones can be updated
695             mPhones = PhoneFactory.getPhones();
696             // if numOfActiveModems is increasing, register new RILs
697             // eg if we are going from 1 phone to 2 phones, we need to register RIL for the second
698             // phone. This loop does nothing if numOfActiveModems is decreasing.
699             for (int phoneId = oldNumOfActiveModems; phoneId < numOfActiveModems; phoneId++) {
700                 Phone phone = mPhones[phoneId];
701                 registerForRadioState(phone);
702                 phone.mCi.onSlotActiveStatusChange(SubscriptionManager.isValidPhoneId(phoneId));
703             }
704 
705             if (numOfActiveModems > 1) {
706                 // Check if cellular DSDA is supported. If it is, then send a request to the
707                 // modem to refresh the list of SIM slots that currently support DSDA based on
708                 // current network conditions
709                 maybeEnableCellularDSDASupport();
710             } else {
711                 // The number of active modems is 0 or 1, disable cellular DSDA:
712                 mSlotsSupportingSimultaneousCellularCalls.clear();
713                 if (mFeatureFlags.simultaneousCallingIndications()) {
714                     updateSimultaneousSubIdsFromPhoneIdMappingAndNotify();
715                     notifySimultaneousCellularCallingSlotsChanged();
716                 }
717             }
718 
719             // When the user enables DSDS mode, the default VOICE and SMS subId should be switched
720             // to "No Preference".  Doing so will sync the network/sim settings and telephony.
721             // (see b/198123192)
722             if (numOfActiveModems > oldNumOfActiveModems && numOfActiveModems == 2) {
723                 Log.i(LOG_TAG, " onMultiSimConfigChanged: DSDS mode enabled; "
724                         + "setting VOICE & SMS subId to -1 (No Preference)");
725 
726                 //Set the default VOICE subId to -1 ("No Preference")
727                 SubscriptionManagerService.getInstance().setDefaultVoiceSubId(
728                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
729 
730                 //TODO:: Set the default SMS sub to "No Preference". Tracking this bug (b/227386042)
731             } else {
732                 Log.i(LOG_TAG,
733                         "onMultiSimConfigChanged: DSDS mode NOT detected.  NOT setting the "
734                                 + "default VOICE and SMS subId to -1 (No Preference)");
735             }
736         }
737     }
738 
739     /**
740      * Helper method to set system properties for setting multi sim configs,
741      * as well as doing the phone reboot
742      * NOTE: In order to support more than 3 sims, we need to change this method.
743      * @param numOfActiveModems number of active sims
744      */
setMultiSimProperties(int numOfActiveModems)745     private void setMultiSimProperties(int numOfActiveModems) {
746         mMi.setMultiSimProperties(numOfActiveModems);
747     }
748 
749     @VisibleForTesting
notifyMultiSimConfigChange(int numOfActiveModems)750     public static void notifyMultiSimConfigChange(int numOfActiveModems) {
751         sMultiSimConfigChangeRegistrants.notifyResult(numOfActiveModems);
752     }
753 
754     /**
755      * Register for multi-SIM configuration change, for example if the devices switched from single
756      * SIM to dual-SIM mode.
757      *
758      * It doesn't trigger callback upon registration as multi-SIM config change is in-frequent.
759      */
registerForMultiSimConfigChange(Handler h, int what, Object obj)760     public static void registerForMultiSimConfigChange(Handler h, int what, Object obj) {
761         sMultiSimConfigChangeRegistrants.addUnique(h, what, obj);
762     }
763 
764     /**
765      * Unregister for multi-SIM configuration change.
766      */
unregisterForMultiSimConfigChange(Handler h)767     public static void unregisterForMultiSimConfigChange(Handler h) {
768         sMultiSimConfigChangeRegistrants.remove(h);
769     }
770 
771     /**
772      * Unregister for all multi-SIM configuration change events.
773      */
unregisterAllMultiSimConfigChangeRegistrants()774     public static void unregisterAllMultiSimConfigChangeRegistrants() {
775         sMultiSimConfigChangeRegistrants.removeAll();
776     }
777 
broadcastMultiSimConfigChange(int numOfActiveModems)778     private void broadcastMultiSimConfigChange(int numOfActiveModems) {
779         log("broadcastSimSlotNumChange numOfActiveModems" + numOfActiveModems);
780         // Notify internal registrants first.
781         notifyMultiSimConfigChange(numOfActiveModems);
782 
783         Intent intent = new Intent(ACTION_MULTI_SIM_CONFIG_CHANGED);
784         intent.putExtra(EXTRA_ACTIVE_SIM_SUPPORTED_COUNT, numOfActiveModems);
785         if (mFeatureFlags.hsumBroadcast()) {
786             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
787         } else {
788             mContext.sendBroadcast(intent);
789         }
790     }
791     /**
792      * This is invoked from shell commands during CTS testing only.
793      * @return true if the modem service is set successfully, false otherwise.
794      */
setModemService(String serviceName)795     public boolean setModemService(String serviceName) {
796         log("setModemService: " + serviceName);
797         boolean statusRadioConfig = false;
798         boolean statusRil = false;
799         final boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false);
800         final boolean isAllowedForBoot =
801                 SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false);
802 
803         // Check for ALLOW_MOCK_MODEM_PROPERTY and BOOT_ALLOW_MOCK_MODEM_PROPERTY on user builds
804         if (isAllowed || isAllowedForBoot || DEBUG) {
805             if (mRadioConfig != null) {
806                 statusRadioConfig = mRadioConfig.setModemService(serviceName);
807             }
808 
809             if (!statusRadioConfig) {
810                 loge("setModemService: switching modem service for radioconfig fail");
811                 return false;
812             }
813 
814             for (int i = 0; i < getPhoneCount(); i++) {
815                 if (mPhones[i] != null) {
816                     statusRil = mPhones[i].mCi.setModemService(serviceName);
817                 }
818 
819                 if (!statusRil) {
820                     loge("setModemService: switch modem for radio " + i + " fail");
821 
822                     // Disconnect the switched service
823                     mRadioConfig.setModemService(null);
824                     for (int t = 0; t < i; t++) {
825                         mPhones[t].mCi.setModemService(null);
826                     }
827                     return false;
828                 }
829             }
830         } else {
831             loge("setModemService is not allowed");
832             return false;
833         }
834 
835         return true;
836     }
837 
838      /**
839      * This is invoked from shell commands to query during CTS testing only.
840      * @return the service name of the connected service.
841      */
getModemService()842     public String getModemService() {
843         if (mPhones[0] == null) {
844             return "";
845         }
846 
847         return mPhones[0].mCi.getModemService();
848     }
849 
850     /**
851      * A wrapper class that wraps some methods so that they can be replaced or mocked in unit-tests.
852      *
853      * For example, setting or reading system property are static native methods that can't be
854      * directly mocked. We can mock it by replacing MockableInterface object with a mock instance
855      * in unittest.
856      */
857     @VisibleForTesting
858     public static class MockableInterface {
859         /**
860          * Wrapper function to decide whether reboot is required for modem config change.
861          */
862         @VisibleForTesting
isRebootRequiredForModemConfigChange()863         public boolean isRebootRequiredForModemConfigChange() {
864             boolean rebootRequired = TelephonyProperties.reboot_on_modem_change().orElse(false);
865             log("isRebootRequiredForModemConfigChange: isRebootRequired = " + rebootRequired);
866             return rebootRequired;
867         }
868 
869         /**
870          * Wrapper function to call setMultiSimProperties.
871          */
872         @VisibleForTesting
setMultiSimProperties(int numOfActiveModems)873         public void setMultiSimProperties(int numOfActiveModems) {
874             String multiSimConfig;
875             switch(numOfActiveModems) {
876                 case 3:
877                     multiSimConfig = TSTS;
878                     break;
879                 case 2:
880                     multiSimConfig = DSDS;
881                     break;
882                 default:
883                     multiSimConfig = SSSS;
884             }
885 
886             log("setMultiSimProperties to " + multiSimConfig);
887             TelephonyProperties.multi_sim_config(multiSimConfig);
888         }
889 
890         /**
891          * Wrapper function to call PhoneFactory.onMultiSimConfigChanged.
892          */
893         @VisibleForTesting
notifyPhoneFactoryOnMultiSimConfigChanged( Context context, int numOfActiveModems)894         public void notifyPhoneFactoryOnMultiSimConfigChanged(
895                 Context context, int numOfActiveModems) {
896             PhoneFactory.onMultiSimConfigChanged(context, numOfActiveModems);
897         }
898 
899         /**
900          * Wrapper function to query the sysprop for multi_sim_config
901          */
getMultiSimProperty()902         public Optional<String> getMultiSimProperty() {
903             return TelephonyProperties.multi_sim_config();
904         }
905     }
906 
log(String s)907     private static void log(String s) {
908         Rlog.d(LOG_TAG, s);
909     }
910 
loge(String s)911     private static void loge(String s) {
912         Rlog.e(LOG_TAG, s);
913     }
914 
loge(String s, Exception ex)915     private static void loge(String s, Exception ex) {
916         Rlog.e(LOG_TAG, s, ex);
917     }
918 }
919