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.settings.network.telephony;
18 
19 import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
20 
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.database.Cursor;
27 import android.graphics.Color;
28 import android.graphics.drawable.ColorDrawable;
29 import android.graphics.drawable.Drawable;
30 import android.graphics.drawable.LayerDrawable;
31 import android.os.PersistableBundle;
32 import android.os.SystemProperties;
33 import android.provider.Settings;
34 import android.telecom.PhoneAccountHandle;
35 import android.telecom.TelecomManager;
36 import android.telephony.CarrierConfigManager;
37 import android.telephony.SubscriptionInfo;
38 import android.telephony.SubscriptionManager;
39 import android.telephony.TelephonyManager;
40 import android.telephony.euicc.EuiccManager;
41 import android.telephony.ims.ImsRcsManager;
42 import android.telephony.ims.ProvisioningManager;
43 import android.telephony.ims.RcsUceAdapter;
44 import android.telephony.ims.feature.ImsFeature;
45 import android.telephony.ims.feature.MmTelFeature;
46 import android.telephony.ims.stub.ImsRegistrationImplBase;
47 import android.text.TextUtils;
48 import android.util.Log;
49 import android.view.Gravity;
50 
51 import androidx.annotation.VisibleForTesting;
52 
53 import com.android.ims.ImsException;
54 import com.android.ims.ImsManager;
55 import com.android.internal.telephony.Phone;
56 import com.android.internal.telephony.PhoneConstants;
57 import com.android.internal.util.ArrayUtils;
58 import com.android.settings.R;
59 import com.android.settings.Utils;
60 import com.android.settings.core.BasePreferenceController;
61 import com.android.settings.network.telephony.TelephonyConstants.TelephonyManagerConstants;
62 import com.android.settingslib.development.DevelopmentSettingsEnabler;
63 import com.android.settingslib.graph.SignalDrawable;
64 
65 import java.util.Arrays;
66 import java.util.List;
67 
68 public class MobileNetworkUtils {
69 
70     private static final String TAG = "MobileNetworkUtils";
71 
72     // CID of the device.
73     private static final String KEY_CID = "ro.boot.cid";
74     // CIDs of devices which should not show anything related to eSIM.
75     private static final String KEY_ESIM_CID_IGNORE = "ro.setupwizard.esim_cid_ignore";
76     // System Property which is used to decide whether the default eSIM UI will be shown,
77     // the default value is false.
78     private static final String KEY_ENABLE_ESIM_UI_BY_DEFAULT =
79             "esim.enable_esim_system_ui_by_default";
80     private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
81             "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
82 
83     // The following constants are used to draw signal icon.
84     public static final int NO_CELL_DATA_TYPE_ICON = 0;
85     public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
86 
87     /**
88      * Returns if DPC APNs are enforced.
89      */
isDpcApnEnforced(Context context)90     public static boolean isDpcApnEnforced(Context context) {
91         try (Cursor enforceCursor = context.getContentResolver().query(ENFORCE_MANAGED_URI,
92                 null, null, null, null)) {
93             if (enforceCursor == null || enforceCursor.getCount() != 1) {
94                 return false;
95             }
96             enforceCursor.moveToFirst();
97             return enforceCursor.getInt(0) > 0;
98         }
99     }
100 
101     /**
102      * Returns true if Wifi calling is enabled for at least one subscription.
103      */
isWifiCallingEnabled(Context context)104     public static boolean isWifiCallingEnabled(Context context) {
105         final int[] subIds = getActiveSubscriptionIdList(context);
106         if (ArrayUtils.isEmpty(subIds)) {
107             Log.d(TAG, "isWifiCallingEnabled: subIds is empty");
108             return false;
109         }
110         for (int subId : subIds) {
111             if (isWifiCallingEnabled(context, subId)) {
112                 return true;
113             }
114         }
115         return false;
116     }
117 
118     /**
119      * Returns true if Wifi calling is provisioned for the specific subscription with id
120      * {@code subId}.
121      */
122     @VisibleForTesting
isWfcProvisionedOnDevice(int subId)123     public static boolean isWfcProvisionedOnDevice(int subId) {
124         final ProvisioningManager provisioningMgr =
125                 ProvisioningManager.createForSubscriptionId(subId);
126         if (provisioningMgr == null) {
127             return true;
128         }
129         return provisioningMgr.getProvisioningStatusForCapability(
130                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
131                 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
132     }
133 
134     /**
135      * Returns true if Wifi calling is enabled for the specific subscription with id {@code subId}.
136      */
isWifiCallingEnabled(Context context, int subId)137     public static boolean isWifiCallingEnabled(Context context, int subId) {
138         final PhoneAccountHandle simCallManager =
139                 context.getSystemService(TelecomManager.class)
140                        .getSimCallManagerForSubscription(subId);
141         final int phoneId = SubscriptionManager.getSlotIndex(subId);
142 
143         boolean isWifiCallingEnabled;
144         if (simCallManager != null) {
145             final Intent intent = buildPhoneAccountConfigureIntent(
146                     context, simCallManager);
147 
148             isWifiCallingEnabled = intent != null;
149         } else {
150             final ImsManager imsMgr = ImsManager.getInstance(context, phoneId);
151             isWifiCallingEnabled = imsMgr != null
152                     && imsMgr.isWfcEnabledByPlatform()
153                     && isWfcProvisionedOnDevice(subId)
154                     && isImsServiceStateReady(imsMgr);
155         }
156 
157         return isWifiCallingEnabled;
158     }
159 
160     /**
161      * @return The current user setting for whether or not contact discovery is enabled for the
162      * subscription id specified.
163      * @see RcsUceAdapter#isUceSettingEnabled()
164      */
isContactDiscoveryEnabled(Context context, int subId)165     public static boolean isContactDiscoveryEnabled(Context context, int subId) {
166         android.telephony.ims.ImsManager imsManager =
167                 context.getSystemService(android.telephony.ims.ImsManager.class);
168         return isContactDiscoveryEnabled(imsManager, subId);
169     }
170 
171     /**
172      * @return The current user setting for whether or not contact discovery is enabled for the
173      * subscription id specified.
174      * @see RcsUceAdapter#isUceSettingEnabled()
175      */
isContactDiscoveryEnabled(android.telephony.ims.ImsManager imsManager, int subId)176     public static boolean isContactDiscoveryEnabled(android.telephony.ims.ImsManager imsManager,
177             int subId) {
178         ImsRcsManager manager = getImsRcsManager(imsManager, subId);
179         if (manager == null) return false;
180         RcsUceAdapter adapter = manager.getUceAdapter();
181         try {
182             return adapter.isUceSettingEnabled();
183         } catch (android.telephony.ims.ImsException e) {
184             Log.w(TAG, "UCE service is not available: " + e.getMessage());
185         }
186         return false;
187     }
188 
189     /**
190      * Set the new user setting to enable or disable contact discovery through RCS UCE.
191      * @see RcsUceAdapter#setUceSettingEnabled(boolean)
192      */
setContactDiscoveryEnabled(android.telephony.ims.ImsManager imsManager, int subId, boolean isEnabled)193     public static void setContactDiscoveryEnabled(android.telephony.ims.ImsManager imsManager,
194             int subId, boolean isEnabled) {
195         ImsRcsManager manager = getImsRcsManager(imsManager, subId);
196         if (manager == null) return;
197         RcsUceAdapter adapter = manager.getUceAdapter();
198         try {
199             adapter.setUceSettingEnabled(isEnabled);
200         } catch (android.telephony.ims.ImsException e) {
201             Log.w(TAG, "UCE service is not available: " + e.getMessage());
202         }
203     }
204 
205     /**
206      * @return The ImsRcsManager associated with the subscription specified.
207      */
getImsRcsManager(android.telephony.ims.ImsManager imsManager, int subId)208     private static ImsRcsManager getImsRcsManager(android.telephony.ims.ImsManager imsManager,
209             int subId) {
210         if (imsManager == null) return null;
211         try {
212             return imsManager.getImsRcsManager(subId);
213         } catch (Exception e) {
214             Log.w(TAG, "Could not resolve ImsRcsManager: " + e.getMessage());
215         }
216         return null;
217     }
218 
219     /**
220      * @return true if contact discovery is available for the subscription specified and the option
221      * should be shown to the user, false if the option should be hidden.
222      */
isContactDiscoveryVisible(Context context, int subId)223     public static boolean isContactDiscoveryVisible(Context context, int subId) {
224         CarrierConfigManager carrierConfigManager = context.getSystemService(
225                 CarrierConfigManager.class);
226         if (carrierConfigManager == null) {
227             Log.w(TAG, "isContactDiscoveryVisible: Could not resolve carrier config");
228             return false;
229         }
230         PersistableBundle bundle = carrierConfigManager.getConfigForSubId(subId);
231         return bundle.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false /*default*/);
232     }
233 
234     @VisibleForTesting
buildPhoneAccountConfigureIntent( Context context, PhoneAccountHandle accountHandle)235     static Intent buildPhoneAccountConfigureIntent(
236             Context context, PhoneAccountHandle accountHandle) {
237         Intent intent = buildConfigureIntent(
238                 context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT);
239 
240         if (intent == null) {
241             // If the new configuration didn't work, try the old configuration intent.
242             intent = buildConfigureIntent(context, accountHandle,
243                     LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT);
244         }
245         return intent;
246     }
247 
buildConfigureIntent( Context context, PhoneAccountHandle accountHandle, String actionStr)248     private static Intent buildConfigureIntent(
249             Context context, PhoneAccountHandle accountHandle, String actionStr) {
250         if (accountHandle == null || accountHandle.getComponentName() == null
251                 || TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) {
252             return null;
253         }
254 
255         // Build the settings intent.
256         Intent intent = new Intent(actionStr);
257         intent.setPackage(accountHandle.getComponentName().getPackageName());
258         intent.addCategory(Intent.CATEGORY_DEFAULT);
259         intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
260 
261         // Check to see that the phone account package can handle the setting intent.
262         final PackageManager pm = context.getPackageManager();
263         final List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
264         if (resolutions.size() == 0) {
265             intent = null;  // set no intent if the package cannot handle it.
266         }
267 
268         return intent;
269     }
270 
isImsServiceStateReady(ImsManager imsMgr)271     public static boolean isImsServiceStateReady(ImsManager imsMgr) {
272         boolean isImsServiceStateReady = false;
273 
274         try {
275             if (imsMgr != null && imsMgr.getImsServiceState() == ImsFeature.STATE_READY) {
276                 isImsServiceStateReady = true;
277             }
278         } catch (ImsException ex) {
279             Log.e(TAG, "Exception when trying to get ImsServiceStatus: " + ex);
280         }
281 
282         Log.d(TAG, "isImsServiceStateReady=" + isImsServiceStateReady);
283         return isImsServiceStateReady;
284     }
285 
286     /**
287      * Whether to show the entry point to eUICC settings.
288      *
289      * <p>We show the entry point on any device which supports eUICC as long as either the eUICC
290      * was ever provisioned (that is, at least one profile was ever downloaded onto it), or if
291      * the user has enabled development mode.
292      */
showEuiccSettings(Context context)293     public static boolean showEuiccSettings(Context context) {
294         final EuiccManager euiccManager =
295                 (EuiccManager) context.getSystemService(EuiccManager.class);
296         if (!euiccManager.isEnabled()) {
297             return false;
298         }
299 
300         final ContentResolver cr = context.getContentResolver();
301 
302         final TelephonyManager tm =
303                 (TelephonyManager) context.getSystemService(TelephonyManager.class);
304         final String currentCountry = tm.getNetworkCountryIso().toLowerCase();
305         final String supportedCountries =
306                 Settings.Global.getString(cr, Settings.Global.EUICC_SUPPORTED_COUNTRIES);
307         final String unsupportedCountries =
308                 Settings.Global.getString(cr, Settings.Global.EUICC_UNSUPPORTED_COUNTRIES);
309 
310         boolean inEsimSupportedCountries = false;
311 
312         if (TextUtils.isEmpty(supportedCountries)) {
313             // White list is empty, use blacklist.
314             Log.d(TAG, "Using blacklist unsupportedCountries=" + unsupportedCountries);
315             inEsimSupportedCountries = !isEsimUnsupportedCountry(currentCountry,
316                     unsupportedCountries);
317         } else {
318             Log.d(TAG, "Using whitelist supportedCountries=" + supportedCountries);
319             inEsimSupportedCountries = isEsimSupportedCountry(currentCountry, supportedCountries);
320         }
321 
322         Log.d(TAG, "inEsimSupportedCountries=" + inEsimSupportedCountries);
323 
324         final boolean esimIgnoredDevice =
325                 Arrays.asList(TextUtils.split(SystemProperties.get(KEY_ESIM_CID_IGNORE, ""), ","))
326                         .contains(SystemProperties.get(KEY_CID, null));
327         final boolean enabledEsimUiByDefault =
328                 SystemProperties.getBoolean(KEY_ENABLE_ESIM_UI_BY_DEFAULT, true);
329         final boolean euiccProvisioned =
330                 Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
331         final boolean inDeveloperMode =
332                 DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
333 
334         return (inDeveloperMode || euiccProvisioned
335                 || (!esimIgnoredDevice && enabledEsimUiByDefault && inEsimSupportedCountries));
336     }
337 
338     /**
339      * Set whether to enable data for {@code subId}, also whether to disable data for other
340      * subscription
341      */
setMobileDataEnabled(Context context, int subId, boolean enabled, boolean disableOtherSubscriptions)342     public static void setMobileDataEnabled(Context context, int subId, boolean enabled,
343             boolean disableOtherSubscriptions) {
344         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
345                 .createForSubscriptionId(subId);
346         final SubscriptionManager subscriptionManager = context.getSystemService(
347                 SubscriptionManager.class);
348         telephonyManager.setDataEnabled(enabled);
349 
350         if (disableOtherSubscriptions) {
351             final List<SubscriptionInfo> subInfoList =
352                     subscriptionManager.getActiveSubscriptionInfoList();
353             if (subInfoList != null) {
354                 for (SubscriptionInfo subInfo : subInfoList) {
355                     // We never disable mobile data for opportunistic subscriptions.
356                     if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
357                         context.getSystemService(TelephonyManager.class).createForSubscriptionId(
358                                 subInfo.getSubscriptionId()).setDataEnabled(false);
359                     }
360                 }
361             }
362         }
363     }
364 
365     /**
366      * Return {@code true} if show CDMA category
367      */
isCdmaOptions(Context context, int subId)368     public static boolean isCdmaOptions(Context context, int subId) {
369         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
370             return false;
371         }
372         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
373                 .createForSubscriptionId(subId);
374         final PersistableBundle carrierConfig = context.getSystemService(
375                 CarrierConfigManager.class).getConfigForSubId(subId);
376 
377 
378         if (telephonyManager.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
379             return true;
380         } else if (carrierConfig != null
381                 && !carrierConfig.getBoolean(
382                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
383                 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
384             return true;
385         }
386 
387         if (isWorldMode(context, subId)) {
388             final int settingsNetworkMode = android.provider.Settings.Global.getInt(
389                     context.getContentResolver(),
390                     android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
391                     Phone.PREFERRED_NT_MODE);
392             if (settingsNetworkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA
393                     || settingsNetworkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO) {
394                 return true;
395             }
396 
397             if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
398                 return true;
399             }
400         }
401 
402         return false;
403     }
404 
405     /**
406      * return {@code true} if we need show Gsm related settings
407      */
isGsmOptions(Context context, int subId)408     public static boolean isGsmOptions(Context context, int subId) {
409         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
410             return false;
411         }
412         if (isGsmBasicOptions(context, subId)) {
413             return true;
414         }
415         final int networkMode = android.provider.Settings.Global.getInt(
416                 context.getContentResolver(),
417                 android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
418                 Phone.PREFERRED_NT_MODE);
419         if (isWorldMode(context, subId)) {
420             if (networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO
421                     || networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA) {
422                 return true;
423             } else if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
424                 return true;
425             }
426         }
427 
428         return false;
429     }
430 
isGsmBasicOptions(Context context, int subId)431     private static boolean isGsmBasicOptions(Context context, int subId) {
432         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
433                 .createForSubscriptionId(subId);
434         final PersistableBundle carrierConfig = context.getSystemService(
435                 CarrierConfigManager.class).getConfigForSubId(subId);
436 
437         if (telephonyManager.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
438             return true;
439         } else if (carrierConfig != null
440                 && !carrierConfig.getBoolean(
441                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
442                 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
443             return true;
444         }
445 
446         return false;
447     }
448 
449     /**
450      * Return {@code true} if it is world mode, and we may show advanced options in telephony
451      * settings
452      */
isWorldMode(Context context, int subId)453     public static boolean isWorldMode(Context context, int subId) {
454         final PersistableBundle carrierConfig = context.getSystemService(
455                 CarrierConfigManager.class).getConfigForSubId(subId);
456         return carrierConfig == null
457                 ? false
458                 : carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL);
459     }
460 
461     /**
462      * Return {@code true} if we need show settings for network selection(i.e. Verizon)
463      */
shouldDisplayNetworkSelectOptions(Context context, int subId)464     public static boolean shouldDisplayNetworkSelectOptions(Context context, int subId) {
465         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
466                 .createForSubscriptionId(subId);
467         final PersistableBundle carrierConfig = context.getSystemService(
468                 CarrierConfigManager.class).getConfigForSubId(subId);
469         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
470                 || carrierConfig == null
471                 || !carrierConfig.getBoolean(
472                 CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL)
473                 || carrierConfig.getBoolean(
474                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
475                 || (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL)
476                 && !telephonyManager.isManualNetworkSelectionAllowed())) {
477             return false;
478         }
479 
480         final int networkMode = android.provider.Settings.Global.getInt(
481                 context.getContentResolver(),
482                 android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
483                 Phone.PREFERRED_NT_MODE);
484         if (networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO
485                 && isWorldMode(context, subId)) {
486             return false;
487         }
488         if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
489             return false;
490         }
491 
492         if (isGsmBasicOptions(context, subId)) {
493             return true;
494         }
495 
496         if (isWorldMode(context, subId)) {
497             if (networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_GSM_WCDMA) {
498                 return true;
499             }
500         }
501 
502         return false;
503     }
504 
505     /**
506      * Return {@code true} if Tdscdma is supported in current subscription
507      */
isTdscdmaSupported(Context context, int subId)508     public static boolean isTdscdmaSupported(Context context, int subId) {
509         return isTdscdmaSupported(context,
510                 context.getSystemService(TelephonyManager.class).createForSubscriptionId(subId));
511     }
512 
513     //TODO(b/117651939): move it to telephony
isTdscdmaSupported(Context context, TelephonyManager telephonyManager)514     private static boolean isTdscdmaSupported(Context context, TelephonyManager telephonyManager) {
515         final PersistableBundle carrierConfig = context.getSystemService(
516                 CarrierConfigManager.class).getConfig();
517 
518         if (carrierConfig == null) {
519             return false;
520         }
521 
522         if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL)) {
523             return true;
524         }
525 
526         final String operatorNumeric = telephonyManager.getServiceState().getOperatorNumeric();
527         final String[] numericArray = carrierConfig.getStringArray(
528                 CarrierConfigManager.KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY);
529         if (numericArray == null || operatorNumeric == null) {
530             return false;
531         }
532         for (String numeric : numericArray) {
533             if (operatorNumeric.equals(numeric)) {
534                 return true;
535             }
536         }
537         return false;
538     }
539 
540     /**
541      * Return subId that supported by search. If there are more than one, return first one,
542      * otherwise return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
543      */
getSearchableSubscriptionId(Context context)544     public static int getSearchableSubscriptionId(Context context) {
545         final int[] subIds = getActiveSubscriptionIdList(context);
546 
547         return subIds.length >= 1 ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
548     }
549 
550     /**
551      * Return availability for a default subscription id. If subId already been set, use it to
552      * check, otherwise traverse all active subIds on device to check.
553      * @param context context
554      * @param defSubId Default subId get from telephony preference controller
555      * @param callback Callback to check availability for a specific subId
556      * @return Availability
557      *
558      * @see BasePreferenceController#getAvailabilityStatus()
559      */
getAvailability(Context context, int defSubId, TelephonyAvailabilityCallback callback)560     public static int getAvailability(Context context, int defSubId,
561             TelephonyAvailabilityCallback callback) {
562         if (defSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
563             // If subId has been set, return the corresponding status
564             return callback.getAvailabilityStatus(defSubId);
565         } else {
566             // Otherwise, search whether there is one subId in device that support this preference
567             final int[] subIds = getActiveSubscriptionIdList(context);
568             if (ArrayUtils.isEmpty(subIds)) {
569                 return callback.getAvailabilityStatus(
570                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
571             } else {
572                 for (final int subId : subIds) {
573                     final int status = callback.getAvailabilityStatus(subId);
574                     if (status == BasePreferenceController.AVAILABLE) {
575                         return status;
576                     }
577                 }
578                 return callback.getAvailabilityStatus(subIds[0]);
579             }
580         }
581     }
582 
583     /**
584      * This method is migrated from {@link com.android.phone.MobileNetworkSettings} and we should
585      * use it carefully. This code snippet doesn't have very clear meaning however we should
586      * update GSM or CDMA differently based on what it returns.
587      *
588      * 1. For all CDMA settings, make them visible if it return {@code true}
589      * 2. For GSM settings, make them visible if it return {@code true} unless 3
590      * 3. For network select settings, make it invisible if it return {@code true}
591      */
592     @VisibleForTesting
shouldSpeciallyUpdateGsmCdma(Context context, int subId)593     static boolean shouldSpeciallyUpdateGsmCdma(Context context, int subId) {
594         final int networkMode = android.provider.Settings.Global.getInt(
595                 context.getContentResolver(),
596                 android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
597                 Phone.PREFERRED_NT_MODE);
598         if (networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_TDSCDMA_GSM
599                 || networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA
600                 || networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_TDSCDMA
601                 || networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA
602                 || networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
603                 || networkMode == TelephonyManagerConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) {
604             if (!isTdscdmaSupported(context, subId) && isWorldMode(context, subId)) {
605                 return true;
606             }
607         }
608 
609         return false;
610     }
611 
getSignalStrengthIcon(Context context, int level, int numLevels, int iconType, boolean cutOut)612     public static Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
613             int iconType, boolean cutOut) {
614         final SignalDrawable signalDrawable = new SignalDrawable(context);
615         signalDrawable.setLevel(
616                 SignalDrawable.getState(level, numLevels, cutOut));
617 
618         // Make the network type drawable
619         final Drawable networkDrawable =
620                 iconType == NO_CELL_DATA_TYPE_ICON
621                         ? EMPTY_DRAWABLE
622                         : context
623                                 .getResources().getDrawable(iconType, context.getTheme());
624 
625         // Overlay the two drawables
626         final Drawable[] layers = {networkDrawable, signalDrawable};
627         final int iconSize =
628                 context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
629 
630         final LayerDrawable icons = new LayerDrawable(layers);
631         // Set the network type icon at the top left
632         icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
633         // Set the signal strength icon at the bottom right
634         icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
635         icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
636         icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
637         return icons;
638     }
639 
640     /**
641      * This method is migrated from
642      * {@link android.telephony.TelephonyManager.getNetworkOperatorName}. Which provides
643      *
644      * 1. Better support under multi-SIM environment.
645      * 2. Similar design which aligned with operator name displayed in status bar
646      */
getCurrentCarrierNameForDisplay(Context context, int subId)647     public static CharSequence getCurrentCarrierNameForDisplay(Context context, int subId) {
648         final SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
649         if (sm != null) {
650             final SubscriptionInfo subInfo = getSubscriptionInfo(sm, subId);
651             if (subInfo != null) {
652                 return subInfo.getCarrierName();
653             }
654         }
655         return getOperatorNameFromTelephonyManager(context);
656     }
657 
getCurrentCarrierNameForDisplay(Context context)658     public static CharSequence getCurrentCarrierNameForDisplay(Context context) {
659         final SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
660         if (sm != null) {
661             final int subId = sm.getDefaultSubscriptionId();
662             final SubscriptionInfo subInfo = getSubscriptionInfo(sm, subId);
663             if (subInfo != null) {
664                 return subInfo.getCarrierName();
665             }
666         }
667         return getOperatorNameFromTelephonyManager(context);
668     }
669 
getSubscriptionInfo(SubscriptionManager subManager, int subId)670     private static SubscriptionInfo getSubscriptionInfo(SubscriptionManager subManager,
671             int subId) {
672         List<SubscriptionInfo> subInfos = subManager.getAccessibleSubscriptionInfoList();
673         if (subInfos == null) {
674             subInfos = subManager.getActiveSubscriptionInfoList();
675         }
676         if (subInfos == null) {
677             return null;
678         }
679         for (SubscriptionInfo subInfo : subInfos) {
680             if (subInfo.getSubscriptionId() == subId) {
681                 return subInfo;
682             }
683         }
684         return null;
685     }
686 
getOperatorNameFromTelephonyManager(Context context)687     private static String getOperatorNameFromTelephonyManager(Context context) {
688         final TelephonyManager tm =
689                 (TelephonyManager) context.getSystemService(TelephonyManager.class);
690         if (tm == null) {
691             return null;
692         }
693         return tm.getNetworkOperatorName();
694     }
695 
isEsimSupportedCountry(String country, String countriesListString)696     private static boolean isEsimSupportedCountry(String country, String countriesListString) {
697         if (TextUtils.isEmpty(country)) {
698             return true;
699         } else if (TextUtils.isEmpty(countriesListString)) {
700             return false;
701         }
702         final List<String> supportedCountries =
703                 Arrays.asList(TextUtils.split(countriesListString.toLowerCase(), ","));
704         return supportedCountries.contains(country);
705     }
706 
isEsimUnsupportedCountry(String country, String countriesListString)707     private static boolean isEsimUnsupportedCountry(String country, String countriesListString) {
708         if (TextUtils.isEmpty(country) || TextUtils.isEmpty(countriesListString)) {
709             return false;
710         }
711         final List<String> unsupportedCountries =
712                 Arrays.asList(TextUtils.split(countriesListString.toLowerCase(), ","));
713         return unsupportedCountries.contains(country);
714     }
715 
getActiveSubscriptionIdList(Context context)716     private static int[] getActiveSubscriptionIdList(Context context) {
717         final SubscriptionManager subscriptionManager = context.getSystemService(
718                 SubscriptionManager.class);
719         final List<SubscriptionInfo> subInfoList =
720                 subscriptionManager.getActiveSubscriptionInfoList();
721         if (subInfoList == null) {
722             return new int[0];
723         }
724         int[] activeSubIds = new int[subInfoList.size()];
725         int i = 0;
726         for (SubscriptionInfo subInfo : subInfoList) {
727             activeSubIds[i] = subInfo.getSubscriptionId();
728             i++;
729         }
730         return activeSubIds;
731     }
732 }
733