• 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.settings.network.telephony;
18 
19 import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
20 
21 import android.app.KeyguardManager;
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.hardware.biometrics.BiometricPrompt;
32 import android.net.ConnectivityManager;
33 import android.net.Network;
34 import android.net.NetworkCapabilities;
35 import android.os.Bundle;
36 import android.os.CancellationSignal;
37 import android.os.Handler;
38 import android.os.Looper;
39 import android.os.PersistableBundle;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.provider.Settings;
43 import android.telecom.PhoneAccountHandle;
44 import android.telecom.TelecomManager;
45 import android.telephony.CarrierConfigManager;
46 import android.telephony.RadioAccessFamily;
47 import android.telephony.ServiceState;
48 import android.telephony.SubscriptionInfo;
49 import android.telephony.SubscriptionManager;
50 import android.telephony.TelephonyManager;
51 import android.telephony.ims.ImsManager;
52 import android.telephony.ims.ImsRcsManager;
53 import android.telephony.ims.ProvisioningManager;
54 import android.telephony.ims.RcsUceAdapter;
55 import android.telephony.ims.feature.MmTelFeature;
56 import android.telephony.ims.stub.ImsRegistrationImplBase;
57 import android.text.TextUtils;
58 import android.util.Log;
59 import android.view.Gravity;
60 
61 import androidx.annotation.NonNull;
62 import androidx.annotation.Nullable;
63 import androidx.annotation.VisibleForTesting;
64 
65 import com.android.internal.util.ArrayUtils;
66 import com.android.settings.R;
67 import com.android.settings.Utils;
68 import com.android.settings.core.BasePreferenceController;
69 import com.android.settings.core.SubSettingLauncher;
70 import com.android.settings.network.CarrierConfigCache;
71 import com.android.settings.network.SubscriptionUtil;
72 import com.android.settings.network.ims.WifiCallingQueryImsState;
73 import com.android.settings.network.telephony.wificalling.WifiCallingRepository;
74 import com.android.settingslib.core.instrumentation.Instrumentable;
75 import com.android.settingslib.graph.SignalDrawable;
76 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
77 
78 import java.util.List;
79 
80 public class MobileNetworkUtils {
81 
82     private static final String TAG = "MobileNetworkUtils";
83 
84     private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
85             "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
86     private static final String RTL_MARK = "\u200F";
87 
88     // The following constants are used to draw signal icon.
89     public static final int NO_CELL_DATA_TYPE_ICON = 0;
90     public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
91 
92     /**
93      * Return true if current user limited by UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS.
94      *
95      * Note: Guest user should have this restriction through
96      *       GuestTelephonyPreferenceController.java.
97      *       However, it's not help with those devices upgraded their software.
98      */
isMobileNetworkUserRestricted(Context context)99     public static boolean isMobileNetworkUserRestricted(Context context) {
100         UserManager um = context.getSystemService(UserManager.class);
101         boolean disallow = false;
102         if (um != null) {
103             disallow = um.isGuestUser() || um.hasUserRestriction(
104                     UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
105         }
106         return disallow;
107     }
108 
109     /**
110      * Returns if DPC APNs are enforced.
111      */
isDpcApnEnforced(Context context)112     public static boolean isDpcApnEnforced(Context context) {
113         try (Cursor enforceCursor = context.getContentResolver().query(ENFORCE_MANAGED_URI,
114                 null, null, null, null)) {
115             if (enforceCursor == null || enforceCursor.getCount() != 1) {
116                 return false;
117             }
118             enforceCursor.moveToFirst();
119             return enforceCursor.getInt(0) > 0;
120         }
121     }
122 
123     /**
124      * Returns true if Wifi calling is provisioned for the specific subscription with id
125      * {@code subId}.
126      */
127     @VisibleForTesting
isWfcProvisionedOnDevice(int subId)128     public static boolean isWfcProvisionedOnDevice(int subId) {
129         final ProvisioningManager provisioningMgr =
130                 ProvisioningManager.createForSubscriptionId(subId);
131         if (provisioningMgr == null) {
132             return true;
133         }
134         return provisioningMgr.getProvisioningStatusForCapability(
135                 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
136                 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
137     }
138 
139     /**
140      * @return The current user setting for whether or not contact discovery is enabled for the
141      * subscription id specified.
142      * @see RcsUceAdapter#isUceSettingEnabled()
143      */
isContactDiscoveryEnabled(Context context, int subId)144     public static boolean isContactDiscoveryEnabled(Context context, int subId) {
145         ImsManager imsManager =
146                 context.getSystemService(ImsManager.class);
147         return isContactDiscoveryEnabled(imsManager, subId);
148     }
149 
150     /**
151      * @return The current user setting for whether or not contact discovery is enabled for the
152      * subscription id specified.
153      * @see RcsUceAdapter#isUceSettingEnabled()
154      */
isContactDiscoveryEnabled(ImsManager imsManager, int subId)155     public static boolean isContactDiscoveryEnabled(ImsManager imsManager,
156             int subId) {
157         ImsRcsManager manager = getImsRcsManager(imsManager, subId);
158         if (manager == null) return false;
159         RcsUceAdapter adapter = manager.getUceAdapter();
160         try {
161             return adapter.isUceSettingEnabled();
162         } catch (android.telephony.ims.ImsException e) {
163             Log.w(TAG, "UCE service is not available: " + e.getMessage());
164         }
165         return false;
166     }
167 
168     /**
169      * Set the new user setting to enable or disable contact discovery through RCS UCE.
170      * @see RcsUceAdapter#setUceSettingEnabled(boolean)
171      */
setContactDiscoveryEnabled(ImsManager imsManager, int subId, boolean isEnabled)172     public static void setContactDiscoveryEnabled(ImsManager imsManager,
173             int subId, boolean isEnabled) {
174         ImsRcsManager manager = getImsRcsManager(imsManager, subId);
175         if (manager == null) return;
176         RcsUceAdapter adapter = manager.getUceAdapter();
177         try {
178             adapter.setUceSettingEnabled(isEnabled);
179         } catch (android.telephony.ims.ImsException e) {
180             Log.w(TAG, "UCE service is not available: " + e.getMessage());
181         }
182     }
183 
184     /**
185      * @return The ImsRcsManager associated with the subscription specified.
186      */
getImsRcsManager(ImsManager imsManager, int subId)187     private static ImsRcsManager getImsRcsManager(ImsManager imsManager,
188             int subId) {
189         if (imsManager == null) return null;
190         try {
191             return imsManager.getImsRcsManager(subId);
192         } catch (Exception e) {
193             Log.w(TAG, "Could not resolve ImsRcsManager: " + e.getMessage());
194         }
195         return null;
196     }
197 
198     /**
199      * @return true if contact discovery is available for the subscription specified and the option
200      * should be shown to the user, false if the option should be hidden.
201      */
isContactDiscoveryVisible(Context context, int subId)202     public static boolean isContactDiscoveryVisible(Context context, int subId) {
203         CarrierConfigCache carrierConfigCache = CarrierConfigCache.getInstance(context);
204         if (!carrierConfigCache.hasCarrierConfigManager()) {
205             Log.w(TAG, "isContactDiscoveryVisible: Could not resolve carrier config");
206             return false;
207         }
208         PersistableBundle bundle = carrierConfigCache.getConfigForSubId(subId);
209         return bundle == null ? false : bundle.getBoolean(
210                 CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL, false /*default*/)
211                 || bundle.getBoolean(CarrierConfigManager.Ims.KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL,
212                 false /*default*/);
213     }
214 
buildPhoneAccountConfigureIntent( Context context, PhoneAccountHandle accountHandle)215     public static Intent buildPhoneAccountConfigureIntent(
216             Context context, PhoneAccountHandle accountHandle) {
217         Intent intent = buildConfigureIntent(
218                 context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT);
219 
220         if (intent == null) {
221             // If the new configuration didn't work, try the old configuration intent.
222             intent = buildConfigureIntent(context, accountHandle,
223                     LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT);
224         }
225         return intent;
226     }
227 
buildConfigureIntent( Context context, PhoneAccountHandle accountHandle, String actionStr)228     private static Intent buildConfigureIntent(
229             Context context, PhoneAccountHandle accountHandle, String actionStr) {
230         if (accountHandle == null || accountHandle.getComponentName() == null
231                 || TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) {
232             return null;
233         }
234 
235         // Build the settings intent.
236         Intent intent = new Intent(actionStr);
237         intent.setPackage(accountHandle.getComponentName().getPackageName());
238         intent.addCategory(Intent.CATEGORY_DEFAULT);
239         intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
240 
241         // Check to see that the phone account package can handle the setting intent.
242         final PackageManager pm = context.getPackageManager();
243         final List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
244         if (resolutions.size() == 0) {
245             intent = null;  // set no intent if the package cannot handle it.
246         }
247 
248         return intent;
249     }
250 
251     /**
252      * Return {@code true} if mobile data is enabled
253      */
isMobileDataEnabled(Context context)254     public static boolean isMobileDataEnabled(Context context) {
255         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
256         if (!telephonyManager.isDataEnabled()) {
257             // Check if the data is enabled on the second SIM in the case of dual SIM.
258             final TelephonyManager tmDefaultData = telephonyManager.createForSubscriptionId(
259                     SubscriptionManager.getDefaultDataSubscriptionId());
260             if (tmDefaultData == null || !tmDefaultData.isDataEnabled()) {
261                 return false;
262             }
263         }
264         return true;
265     }
266 
267     /**
268      * Set whether to enable data for {@code subId}, also whether to disable data for other
269      * subscription
270      */
setMobileDataEnabled(Context context, int subId, boolean enabled, boolean disableOtherSubscriptions)271     public static void setMobileDataEnabled(Context context, int subId, boolean enabled,
272             boolean disableOtherSubscriptions) {
273         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
274                 .createForSubscriptionId(subId);
275         final SubscriptionManager subscriptionManager = context.getSystemService(
276                 SubscriptionManager.class).createForAllUserProfiles();
277         Log.d(TAG, "setDataEnabledForReason: " + enabled);
278         telephonyManager.setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER,
279                 enabled);
280 
281         if (disableOtherSubscriptions) {
282             final List<SubscriptionInfo> subInfoList =
283                     subscriptionManager.getActiveSubscriptionInfoList();
284             if (subInfoList != null) {
285                 for (SubscriptionInfo subInfo : subInfoList) {
286                     // We never disable mobile data for opportunistic subscriptions.
287                     if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
288                         context.getSystemService(TelephonyManager.class)
289                                 .createForSubscriptionId(subInfo.getSubscriptionId())
290                                 .setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER,
291                                         false);
292                     }
293                 }
294             }
295         }
296     }
297 
298     /**
299      * Return {@code true} if show CDMA category
300      */
isCdmaOptions(Context context, int subId)301     public static boolean isCdmaOptions(Context context, int subId) {
302         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
303             return false;
304         }
305         final PersistableBundle carrierConfig =
306                 CarrierConfigCache.getInstance(context).getConfigForSubId(subId);
307         if (carrierConfig != null
308                 && !carrierConfig.getBoolean(
309                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
310                 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
311             return true;
312         }
313 
314         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
315                 .createForSubscriptionId(subId);
316         if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
317             return true;
318         }
319 
320         if (isWorldMode(context, subId)) {
321             final int settingsNetworkMode = RadioAccessFamily.getNetworkTypeFromRaf(
322                     (int) telephonyManager.getAllowedNetworkTypesForReason(
323                             TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
324 
325             if (settingsNetworkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA
326                     || settingsNetworkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO
327                     || settingsNetworkMode == TelephonyManager.NETWORK_MODE_NR_LTE_GSM_WCDMA
328                     || settingsNetworkMode == TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO) {
329                 return true;
330             }
331 
332             if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
333                 return true;
334             }
335         }
336 
337         return false;
338     }
339 
340     /**
341      * return {@code true} if we need show Gsm related settings
342      */
isGsmOptions(Context context, int subId)343     public static boolean isGsmOptions(Context context, int subId) {
344         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
345             return false;
346         }
347         if (isGsmBasicOptions(context, subId)) {
348             return true;
349         }
350         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
351                 .createForSubscriptionId(subId);
352         final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
353                 (int) telephonyManager.getAllowedNetworkTypesForReason(
354                         TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
355         if (isWorldMode(context, subId)) {
356             if (networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO
357                     || networkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA
358                     || networkMode == TelephonyManager.NETWORK_MODE_NR_LTE_CDMA_EVDO
359                     || networkMode == TelephonyManager.NETWORK_MODE_NR_LTE_GSM_WCDMA) {
360                 return true;
361             } else if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
362                 return true;
363             }
364         }
365 
366         return false;
367     }
368 
isGsmBasicOptions(Context context, int subId)369     private static boolean isGsmBasicOptions(Context context, int subId) {
370         final PersistableBundle carrierConfig =
371                 CarrierConfigCache.getInstance(context).getConfigForSubId(subId);
372         if (carrierConfig != null
373                 && !carrierConfig.getBoolean(
374                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
375                 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
376             return true;
377         }
378 
379         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
380                 .createForSubscriptionId(subId);
381         if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
382             return true;
383         }
384 
385         return false;
386     }
387 
388     /**
389      * Return {@code true} if it is world mode, and we may show advanced options in telephony
390      * settings
391      */
isWorldMode(Context context, int subId)392     public static boolean isWorldMode(Context context, int subId) {
393         final PersistableBundle carrierConfig =
394                 CarrierConfigCache.getInstance(context).getConfigForSubId(subId);
395         return carrierConfig == null
396                 ? false
397                 : carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL);
398     }
399 
400     /**
401      * Return {@code true} if we need show settings for network selection(i.e. Verizon)
402      */
shouldDisplayNetworkSelectOptions(Context context, int subId)403     public static boolean shouldDisplayNetworkSelectOptions(Context context, int subId) {
404         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
405                 .createForSubscriptionId(subId);
406         final PersistableBundle carrierConfig =
407                 CarrierConfigCache.getInstance(context).getConfigForSubId(subId);
408         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
409                 || carrierConfig == null
410                 || !carrierConfig.getBoolean(
411                 CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL)
412                 || carrierConfig.getBoolean(
413                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
414                 || (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL)
415                 && !telephonyManager.isManualNetworkSelectionAllowed())) {
416             return false;
417         }
418 
419         if (isWorldMode(context, subId)) {
420             final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
421                     (int) telephonyManager.getAllowedNetworkTypesForReason(
422                             TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
423             if (networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO) {
424                 return false;
425             }
426             if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
427                 return false;
428             }
429 
430             if (networkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA) {
431                 return true;
432             }
433         }
434 
435         return isGsmBasicOptions(context, subId);
436     }
437 
438     /**
439      * Return {@code true} if Tdscdma is supported in current subscription
440      */
isTdscdmaSupported(Context context, int subId)441     public static boolean isTdscdmaSupported(Context context, int subId) {
442         return isTdscdmaSupported(context,
443                 context.getSystemService(TelephonyManager.class).createForSubscriptionId(subId));
444     }
445 
446     //TODO(b/117651939): move it to telephony
isTdscdmaSupported(Context context, TelephonyManager telephonyManager)447     private static boolean isTdscdmaSupported(Context context, TelephonyManager telephonyManager) {
448         final PersistableBundle carrierConfig = CarrierConfigCache.getInstance(context).getConfig();
449 
450         if (carrierConfig == null) {
451             return false;
452         }
453 
454         if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL)) {
455             return true;
456         }
457         final String[] numericArray = carrierConfig.getStringArray(
458                 CarrierConfigManager.KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY);
459         if (numericArray == null) {
460             return false;
461         }
462         final ServiceState serviceState = telephonyManager.getServiceState();
463         final String operatorNumeric =
464                 (serviceState != null) ? serviceState.getOperatorNumeric() : null;
465         if (operatorNumeric == null) {
466             return false;
467         }
468         for (String numeric : numericArray) {
469             if (operatorNumeric.equals(numeric)) {
470                 return true;
471             }
472         }
473         return false;
474     }
475 
476     /**
477      * Return subId that supported by search. If there are more than one, return first one,
478      * otherwise return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
479      */
getSearchableSubscriptionId(Context context)480     public static int getSearchableSubscriptionId(Context context) {
481         final int[] subIds = getActiveSubscriptionIdList(context);
482 
483         return subIds.length >= 1 ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
484     }
485 
486     /**
487      * Return availability for a default subscription id. If subId already been set, use it to
488      * check, otherwise traverse all active subIds on device to check.
489      * @param context context
490      * @param defSubId Default subId get from telephony preference controller
491      * @param callback Callback to check availability for a specific subId
492      * @return Availability
493      *
494      * @see BasePreferenceController#getAvailabilityStatus()
495      */
getAvailability(Context context, int defSubId, TelephonyAvailabilityCallback callback)496     public static int getAvailability(Context context, int defSubId,
497             TelephonyAvailabilityCallback callback) {
498         if (defSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
499             // If subId has been set, return the corresponding status
500             return callback.getAvailabilityStatus(defSubId);
501         } else {
502             // Otherwise, search whether there is one subId in device that support this preference
503             final int[] subIds = getActiveSubscriptionIdList(context);
504             if (ArrayUtils.isEmpty(subIds)) {
505                 return callback.getAvailabilityStatus(
506                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
507             } else {
508                 for (final int subId : subIds) {
509                     final int status = callback.getAvailabilityStatus(subId);
510                     if (status == BasePreferenceController.AVAILABLE) {
511                         return status;
512                     }
513                 }
514                 return callback.getAvailabilityStatus(subIds[0]);
515             }
516         }
517     }
518 
519     /**
520      * This method is migrated from {@link com.android.phone.MobileNetworkSettings} and we should
521      * use it carefully. This code snippet doesn't have very clear meaning however we should
522      * update GSM or CDMA differently based on what it returns.
523      *
524      * 1. For all CDMA settings, make them visible if it return {@code true}
525      * 2. For GSM settings, make them visible if it return {@code true} unless 3
526      * 3. For network select settings, make it invisible if it return {@code true}
527      */
528     @VisibleForTesting
shouldSpeciallyUpdateGsmCdma(Context context, int subId)529     static boolean shouldSpeciallyUpdateGsmCdma(Context context, int subId) {
530         if (!isWorldMode(context, subId)) {
531             return false;
532         }
533         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
534                 .createForSubscriptionId(subId);
535         final int networkMode = RadioAccessFamily.getNetworkTypeFromRaf(
536                 (int) telephonyManager.getAllowedNetworkTypesForReason(
537                         TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));
538         if (networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM
539                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA
540                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA
541                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_WCDMA
542                 || networkMode
543                 == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
544                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) {
545             if (!isTdscdmaSupported(context, subId)) {
546                 return true;
547             }
548         }
549 
550         return false;
551     }
552 
getSignalStrengthIcon(Context context, int level, int numLevels, int iconType, boolean cutOut, boolean carrierNetworkChanged)553     public static Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
554             int iconType, boolean cutOut, boolean carrierNetworkChanged) {
555         final SignalDrawable signalDrawable = new SignalDrawable(context);
556         signalDrawable.setLevel(
557                 carrierNetworkChanged ? SignalDrawable.getCarrierChangeState(numLevels)
558                         : SignalDrawable.getState(level, numLevels, cutOut));
559 
560         // Make the network type drawable
561         final Drawable networkDrawable =
562                 iconType == NO_CELL_DATA_TYPE_ICON
563                         ? EMPTY_DRAWABLE
564                         : context.getResources().getDrawable(iconType, context.getTheme());
565 
566         // Overlay the two drawables
567         final Drawable[] layers = {networkDrawable, signalDrawable};
568         final int iconSize =
569                 context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
570 
571         final LayerDrawable icons = new LayerDrawable(layers);
572         // Set the network type icon at the top left
573         icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
574         // Set the signal strength icon at the bottom right
575         icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
576         icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
577         icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
578         return icons;
579     }
580 
581     /**
582      * This method is migrated from
583      * {@link android.telephony.TelephonyManager.getNetworkOperatorName}. Which provides
584      *
585      * 1. Better support under multi-SIM environment.
586      * 2. Similar design which aligned with operator name displayed in status bar
587      */
getCurrentCarrierNameForDisplay(Context context, int subId)588     public static CharSequence getCurrentCarrierNameForDisplay(Context context, int subId) {
589         final SubscriptionInfo subInfo = getSubscriptionInfo(context, subId);
590         if (subInfo != null) {
591             return subInfo.getCarrierName();
592         }
593         return getOperatorNameFromTelephonyManager(context);
594     }
595 
getCurrentCarrierNameForDisplay(Context context)596     public static CharSequence getCurrentCarrierNameForDisplay(Context context) {
597         return getCurrentCarrierNameForDisplay(context,
598                 SubscriptionManager.getDefaultSubscriptionId());
599     }
600 
getSubscriptionInfo(Context context, int subId)601     private static @Nullable SubscriptionInfo getSubscriptionInfo(Context context, int subId) {
602         SubscriptionManager sm = context.getSystemService(SubscriptionManager.class);
603         if (sm == null) return null;
604         return sm.createForAllUserProfiles().getActiveSubscriptionInfo(subId);
605     }
606 
getOperatorNameFromTelephonyManager(Context context)607     private static String getOperatorNameFromTelephonyManager(Context context) {
608         final TelephonyManager tm =
609                 (TelephonyManager) context.getSystemService(TelephonyManager.class);
610         if (tm == null) {
611             return null;
612         }
613         return tm.getNetworkOperatorName();
614     }
615 
616     @VisibleForTesting
getActiveSubscriptionIdList(Context context)617     static int[] getActiveSubscriptionIdList(Context context) {
618         final SubscriptionManager subscriptionManager = context.getSystemService(
619                 SubscriptionManager.class).createForAllUserProfiles();
620         final List<SubscriptionInfo> subInfoList =
621                 SubscriptionUtil.getActiveSubscriptions(subscriptionManager);
622         if (subInfoList == null || subInfoList.isEmpty()) {
623             return new int[0];
624         }
625         int[] activeSubIds = new int[subInfoList.size()];
626         int i = 0;
627         for (SubscriptionInfo subInfo : subInfoList) {
628             activeSubIds[i] = subInfo.getSubscriptionId();
629             i++;
630         }
631         return activeSubIds;
632     }
633 
634     /**
635      * Copied from SubscriptionsPreferenceController#activeNetworkIsCellular()
636      */
activeNetworkIsCellular(Context context)637     public static boolean activeNetworkIsCellular(Context context) {
638         final ConnectivityManager connectivityManager =
639                 context.getSystemService(ConnectivityManager.class);
640         final Network activeNetwork = connectivityManager.getActiveNetwork();
641         if (activeNetwork == null) {
642             return false;
643         }
644         final NetworkCapabilities networkCapabilities =
645                 connectivityManager.getNetworkCapabilities(activeNetwork);
646         if (networkCapabilities == null) {
647             return false;
648         }
649         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
650     }
651 
652     /**
653      * Copied from WifiCallingPreferenceController#isWifiCallingEnabled()
654      *
655      * @deprecated Use {@link WifiCallingRepository#wifiCallingReadyFlow()} instead.
656      */
657     @Deprecated
isWifiCallingEnabled(Context context, int subId, @Nullable WifiCallingQueryImsState queryImsState)658     public static boolean isWifiCallingEnabled(Context context, int subId,
659             @Nullable WifiCallingQueryImsState queryImsState) {
660         if (queryImsState == null) {
661             queryImsState = new WifiCallingQueryImsState(context, subId);
662         }
663         return queryImsState.isReadyToWifiCalling();
664     }
665 
666     /**
667      * Returns preferred status of Calls & SMS separately when Provider Model is enabled.
668      */
getPreferredStatus(boolean isRtlMode, Context context, boolean isPreferredCallStatus, List<SubscriptionInfoEntity> entityList)669     public static CharSequence getPreferredStatus(boolean isRtlMode, Context context,
670             boolean isPreferredCallStatus, List<SubscriptionInfoEntity> entityList) {
671         if (entityList != null && !entityList.isEmpty()) {
672             final StringBuilder summary = new StringBuilder();
673             for (SubscriptionInfoEntity subInfo : entityList) {
674                 int subsSize = entityList.size();
675                 final CharSequence displayName = subInfo.uniqueName;
676 
677                 // Set displayName as summary if there is only one valid SIM.
678                 if (subsSize == 1 && subInfo.isValidSubscription) {
679                     return displayName;
680                 }
681 
682                 CharSequence status = isPreferredCallStatus
683                         ? getPreferredCallStatus(context, subInfo)
684                         : getPreferredSmsStatus(context, subInfo);
685                 if (status.toString().isEmpty()) {
686                     // If there are 2 or more SIMs and one of these has no preferred status,
687                     // set only its displayName as summary.
688                     summary.append(displayName);
689                 } else {
690                     summary.append(displayName)
691                             .append(" (")
692                             .append(status)
693                             .append(")");
694                 }
695                 // Do not add ", " for the last subscription.
696                 if (subInfo != entityList.get(entityList.size() - 1)) {
697                     summary.append(", ");
698                 }
699 
700                 if (isRtlMode) {
701                     summary.insert(0, RTL_MARK).insert(summary.length(), RTL_MARK);
702                 }
703             }
704             return summary;
705         } else {
706             return "";
707         }
708     }
709 
getPreferredCallStatus(Context context, SubscriptionInfoEntity subInfo)710     private static CharSequence getPreferredCallStatus(Context context,
711             SubscriptionInfoEntity subInfo) {
712         String status = "";
713         if (subInfo.getSubId() == SubscriptionManager.getDefaultVoiceSubscriptionId()) {
714             status = setSummaryResId(context, R.string.calls_sms_preferred);
715         }
716 
717         return status;
718     }
719 
getPreferredSmsStatus(Context context, SubscriptionInfoEntity subInfo)720     private static CharSequence getPreferredSmsStatus(Context context,
721             SubscriptionInfoEntity subInfo) {
722         String status = "";
723         if (subInfo.getSubId() == SubscriptionManager.getDefaultSmsSubscriptionId()) {
724             status = setSummaryResId(context, R.string.calls_sms_preferred);
725         }
726 
727         return status;
728     }
729 
setSummaryResId(Context context, int resId)730     private static String setSummaryResId(Context context, int resId) {
731         return context.getResources().getString(resId);
732     }
733 
launchMobileNetworkSettings(Context context, SubscriptionInfo info)734     public static void launchMobileNetworkSettings(Context context, SubscriptionInfo info) {
735         if (!SubscriptionUtil.isSimHardwareVisible(context)) {
736             Log.e(TAG, "launchMobileNetworkSettings fail, device without such UI.");
737             return;
738         }
739         final int subId = info.getSubscriptionId();
740         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
741             Log.d(TAG, "launchMobileNetworkSettings fail, subId is invalid.");
742             return;
743         }
744 
745         Log.d(TAG, "launchMobileNetworkSettings for subId: " + subId);
746         final Bundle extra = new Bundle();
747         extra.putInt(Settings.EXTRA_SUB_ID, subId);
748         new SubSettingLauncher(context)
749                 .setTitleText(SubscriptionUtil.getUniqueSubscriptionDisplayName(info, context))
750                 .setDestination(MobileNetworkSettings.class.getCanonicalName())
751                 .setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN)
752                 .setArguments(extra)
753                 .launch();
754     }
755 
launchMobileNetworkSettings(Context context, SubscriptionInfoEntity info)756     public static void launchMobileNetworkSettings(Context context, SubscriptionInfoEntity info) {
757         final int subId = Integer.valueOf(info.subId);
758         if (!info.isValidSubscription) {
759             Log.d(TAG, "launchMobileNetworkSettings fail, subId is invalid.");
760             return;
761         }
762 
763         Log.d(TAG, "launchMobileNetworkSettings for SubscriptionInfoEntity subId: " + subId);
764         final Bundle extra = new Bundle();
765         extra.putInt(Settings.EXTRA_SUB_ID, subId);
766         new SubSettingLauncher(context)
767                 .setTitleText(info.uniqueName)
768                 .setDestination(MobileNetworkSettings.class.getCanonicalName())
769                 .setSourceMetricsCategory(Instrumentable.METRICS_CATEGORY_UNKNOWN)
770                 .setArguments(extra)
771                 .launch();
772     }
773 
774     /**
775      * Shows authentication screen to confirm credentials (pin/pattern/password) for the current
776      * user of the device.
777      *
778      * <p>Similar to WifiDppUtils.showLockScreen(), but doesn't check for the existence of
779      * SIM PIN lock, only screen PIN lock.
780      *
781      * @param context The {@code Context} used to get {@link KeyguardManager} service
782      * @param onSuccess The {@code Runnable} which will be executed if the user does not setup
783      *                  device security or if lock screen is unlocked
784      */
showLockScreen(@onNull Context context, @NonNull Runnable onSuccess)785     public static void showLockScreen(@NonNull Context context, @NonNull Runnable onSuccess) {
786         final KeyguardManager keyguardManager =
787                 context.getSystemService(KeyguardManager.class);
788 
789         if (keyguardManager.isDeviceSecure()) {
790             final BiometricPrompt.AuthenticationCallback authenticationCallback =
791                     new BiometricPrompt.AuthenticationCallback() {
792                         @Override
793                         public void onAuthenticationSucceeded(
794                                     BiometricPrompt.AuthenticationResult result) {
795                             onSuccess.run();
796                         }
797 
798                         @Override
799                         public void onAuthenticationError(int errorCode, CharSequence errString) {
800                             // Do nothing
801                         }
802             };
803 
804             final int userId = UserHandle.myUserId();
805             final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context)
806                     .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title))
807                     .setDeviceCredentialAllowed(true)
808                     .setTextForDeviceCredential(
809                         /* title= */ null,
810                         Utils.getConfirmCredentialStringForUser(
811                                 context, userId, Utils.getCredentialType(context, userId)),
812                         /* description= */ null)
813                     .build();
814             final Handler handler = new Handler(Looper.getMainLooper());
815             biometricPrompt.authenticate(
816                     new CancellationSignal(),
817                     handler::post,
818                     authenticationCallback);
819         } else {
820             onSuccess.run();
821         }
822     }
823 }
824