• 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.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.feature.ImsFeature;
42 import android.text.TextUtils;
43 import android.util.Log;
44 import android.view.Gravity;
45 
46 import androidx.annotation.VisibleForTesting;
47 
48 import com.android.ims.ImsException;
49 import com.android.ims.ImsManager;
50 import com.android.internal.telephony.Phone;
51 import com.android.internal.telephony.PhoneConstants;
52 import com.android.internal.util.ArrayUtils;
53 import com.android.settings.R;
54 import com.android.settings.Utils;
55 import com.android.settings.core.BasePreferenceController;
56 import com.android.settingslib.graph.SignalDrawable;
57 
58 import java.util.Arrays;
59 import java.util.List;
60 
61 public class MobileNetworkUtils {
62 
63     private static final String TAG = "MobileNetworkUtils";
64 
65     // CID of the device.
66     private static final String KEY_CID = "ro.boot.cid";
67     // CIDs of devices which should not show anything related to eSIM.
68     private static final String KEY_ESIM_CID_IGNORE = "ro.setupwizard.esim_cid_ignore";
69     // System Property which is used to decide whether the default eSIM UI will be shown,
70     // the default value is false.
71     private static final String KEY_ENABLE_ESIM_UI_BY_DEFAULT =
72             "esim.enable_esim_system_ui_by_default";
73     private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
74             "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
75 
76     // The following constants are used to draw signal icon.
77     public static final int NO_CELL_DATA_TYPE_ICON = 0;
78     public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
79 
80     /**
81      * Returns if DPC APNs are enforced.
82      */
isDpcApnEnforced(Context context)83     public static boolean isDpcApnEnforced(Context context) {
84         try (Cursor enforceCursor = context.getContentResolver().query(ENFORCE_MANAGED_URI,
85                 null, null, null, null)) {
86             if (enforceCursor == null || enforceCursor.getCount() != 1) {
87                 return false;
88             }
89             enforceCursor.moveToFirst();
90             return enforceCursor.getInt(0) > 0;
91         }
92     }
93 
94     /**
95      * Returns true if Wifi calling is enabled for at least one subscription.
96      */
isWifiCallingEnabled(Context context)97     public static boolean isWifiCallingEnabled(Context context) {
98         SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
99         if (subManager == null) {
100             Log.e(TAG, "isWifiCallingEnabled: couldn't get system service.");
101             return false;
102         }
103         for (int subId : subManager.getActiveSubscriptionIdList()) {
104             if (isWifiCallingEnabled(context, subId)) {
105                 return true;
106             }
107         }
108         return false;
109     }
110 
111     /**
112      * Returns true if Wifi calling is enabled for the specific subscription with id {@code subId}.
113      */
isWifiCallingEnabled(Context context, int subId)114     public static boolean isWifiCallingEnabled(Context context, int subId) {
115         final PhoneAccountHandle simCallManager =
116                 TelecomManager.from(context).getSimCallManagerForSubscription(subId);
117         final int phoneId = SubscriptionManager.getSlotIndex(subId);
118 
119         boolean isWifiCallingEnabled;
120         if (simCallManager != null) {
121             Intent intent = buildPhoneAccountConfigureIntent(
122                     context, simCallManager);
123 
124             isWifiCallingEnabled = intent != null;
125         } else {
126             ImsManager imsMgr = ImsManager.getInstance(context, phoneId);
127             isWifiCallingEnabled = imsMgr != null
128                     && imsMgr.isWfcEnabledByPlatform()
129                     && imsMgr.isWfcProvisionedOnDevice()
130                     && isImsServiceStateReady(imsMgr);
131         }
132 
133         return isWifiCallingEnabled;
134     }
135 
136     @VisibleForTesting
buildPhoneAccountConfigureIntent( Context context, PhoneAccountHandle accountHandle)137     static Intent buildPhoneAccountConfigureIntent(
138             Context context, PhoneAccountHandle accountHandle) {
139         Intent intent = buildConfigureIntent(
140                 context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT);
141 
142         if (intent == null) {
143             // If the new configuration didn't work, try the old configuration intent.
144             intent = buildConfigureIntent(context, accountHandle,
145                     LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT);
146         }
147         return intent;
148     }
149 
buildConfigureIntent( Context context, PhoneAccountHandle accountHandle, String actionStr)150     private static Intent buildConfigureIntent(
151             Context context, PhoneAccountHandle accountHandle, String actionStr) {
152         if (accountHandle == null || accountHandle.getComponentName() == null
153                 || TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) {
154             return null;
155         }
156 
157         // Build the settings intent.
158         Intent intent = new Intent(actionStr);
159         intent.setPackage(accountHandle.getComponentName().getPackageName());
160         intent.addCategory(Intent.CATEGORY_DEFAULT);
161         intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
162 
163         // Check to see that the phone account package can handle the setting intent.
164         PackageManager pm = context.getPackageManager();
165         List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
166         if (resolutions.size() == 0) {
167             intent = null;  // set no intent if the package cannot handle it.
168         }
169 
170         return intent;
171     }
172 
isImsServiceStateReady(ImsManager imsMgr)173     public static boolean isImsServiceStateReady(ImsManager imsMgr) {
174         boolean isImsServiceStateReady = false;
175 
176         try {
177             if (imsMgr != null && imsMgr.getImsServiceState() == ImsFeature.STATE_READY) {
178                 isImsServiceStateReady = true;
179             }
180         } catch (ImsException ex) {
181             Log.e(TAG, "Exception when trying to get ImsServiceStatus: " + ex);
182         }
183 
184         Log.d(TAG, "isImsServiceStateReady=" + isImsServiceStateReady);
185         return isImsServiceStateReady;
186     }
187 
188     /**
189      * Whether to show the entry point to eUICC settings.
190      *
191      * <p>We show the entry point on any device which supports eUICC as long as either the eUICC
192      * was ever provisioned (that is, at least one profile was ever downloaded onto it), or if
193      * the user has enabled development mode.
194      */
showEuiccSettings(Context context)195     public static boolean showEuiccSettings(Context context) {
196         EuiccManager euiccManager =
197                 (EuiccManager) context.getSystemService(EuiccManager.class);
198         if (!euiccManager.isEnabled()) {
199             return false;
200         }
201 
202         final ContentResolver cr = context.getContentResolver();
203 
204         TelephonyManager tm =
205                 (TelephonyManager) context.getSystemService(TelephonyManager.class);
206         String currentCountry = tm.getNetworkCountryIso().toLowerCase();
207         String supportedCountries =
208                 Settings.Global.getString(cr, Settings.Global.EUICC_SUPPORTED_COUNTRIES);
209         boolean inEsimSupportedCountries = false;
210         if (TextUtils.isEmpty(currentCountry)) {
211             inEsimSupportedCountries = true;
212         } else if (!TextUtils.isEmpty(supportedCountries)) {
213             List<String> supportedCountryList =
214                     Arrays.asList(TextUtils.split(supportedCountries.toLowerCase(), ","));
215             if (supportedCountryList.contains(currentCountry)) {
216                 inEsimSupportedCountries = true;
217             }
218         }
219         final boolean esimIgnoredDevice =
220                 Arrays.asList(TextUtils.split(SystemProperties.get(KEY_ESIM_CID_IGNORE, ""), ","))
221                         .contains(SystemProperties.get(KEY_CID, null));
222         final boolean enabledEsimUiByDefault =
223                 SystemProperties.getBoolean(KEY_ENABLE_ESIM_UI_BY_DEFAULT, true);
224         final boolean euiccProvisioned =
225                 Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
226         final boolean inDeveloperMode =
227                 Settings.Global.getInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
228 
229         return (inDeveloperMode || euiccProvisioned
230                 || (!esimIgnoredDevice && enabledEsimUiByDefault && inEsimSupportedCountries));
231     }
232 
233     /**
234      * Set whether to enable data for {@code subId}, also whether to disable data for other
235      * subscription
236      */
setMobileDataEnabled(Context context, int subId, boolean enabled, boolean disableOtherSubscriptions)237     public static void setMobileDataEnabled(Context context, int subId, boolean enabled,
238             boolean disableOtherSubscriptions) {
239         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
240                 .createForSubscriptionId(subId);
241         final SubscriptionManager subscriptionManager = context.getSystemService(
242                 SubscriptionManager.class);
243         telephonyManager.setDataEnabled(enabled);
244 
245         if (disableOtherSubscriptions) {
246             List<SubscriptionInfo> subInfoList =
247                     subscriptionManager.getActiveSubscriptionInfoList(true);
248             if (subInfoList != null) {
249                 for (SubscriptionInfo subInfo : subInfoList) {
250                     // We never disable mobile data for opportunistic subscriptions.
251                     if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
252                         context.getSystemService(TelephonyManager.class).createForSubscriptionId(
253                                 subInfo.getSubscriptionId()).setDataEnabled(false);
254                     }
255                 }
256             }
257         }
258     }
259 
260     /**
261      * Return {@code true} if show CDMA category
262      */
isCdmaOptions(Context context, int subId)263     public static boolean isCdmaOptions(Context context, int subId) {
264         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
265             return false;
266         }
267         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
268                 .createForSubscriptionId(subId);
269         final PersistableBundle carrierConfig = context.getSystemService(
270                 CarrierConfigManager.class).getConfigForSubId(subId);
271 
272 
273         if (telephonyManager.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
274             return true;
275         } else if (carrierConfig != null
276                 && !carrierConfig.getBoolean(
277                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
278                 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
279             return true;
280         }
281 
282         if (isWorldMode(context, subId)) {
283             final int settingsNetworkMode = android.provider.Settings.Global.getInt(
284                     context.getContentResolver(),
285                     android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
286                     Phone.PREFERRED_NT_MODE);
287             if (settingsNetworkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA
288                     || settingsNetworkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO) {
289                 return true;
290             }
291 
292             if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
293                 return true;
294             }
295         }
296 
297         return false;
298     }
299 
300     /**
301      * return {@code true} if we need show Gsm related settings
302      */
isGsmOptions(Context context, int subId)303     public static boolean isGsmOptions(Context context, int subId) {
304         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
305             return false;
306         }
307         if (isGsmBasicOptions(context, subId)) {
308             return true;
309         }
310         final int networkMode = android.provider.Settings.Global.getInt(
311                 context.getContentResolver(),
312                 android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
313                 Phone.PREFERRED_NT_MODE);
314         if (isWorldMode(context, subId)) {
315             if (networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO
316                     || networkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA) {
317                 return true;
318             } else if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
319                 return true;
320             }
321         }
322 
323         return false;
324     }
325 
isGsmBasicOptions(Context context, int subId)326     private static boolean isGsmBasicOptions(Context context, int subId) {
327         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
328                 .createForSubscriptionId(subId);
329         final PersistableBundle carrierConfig = context.getSystemService(
330                 CarrierConfigManager.class).getConfigForSubId(subId);
331 
332         if (telephonyManager.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
333             return true;
334         } else if (carrierConfig != null
335                 && !carrierConfig.getBoolean(
336                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
337                 && carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
338             return true;
339         }
340 
341         return false;
342     }
343 
344     /**
345      * Return {@code true} if it is world mode, and we may show advanced options in telephony
346      * settings
347      */
isWorldMode(Context context, int subId)348     public static boolean isWorldMode(Context context, int subId) {
349         final PersistableBundle carrierConfig = context.getSystemService(
350                 CarrierConfigManager.class).getConfigForSubId(subId);
351         return carrierConfig == null
352                 ? false
353                 : carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_MODE_ENABLED_BOOL);
354     }
355 
356     /**
357      * Return {@code true} if we need show settings for network selection(i.e. Verizon)
358      */
shouldDisplayNetworkSelectOptions(Context context, int subId)359     public static boolean shouldDisplayNetworkSelectOptions(Context context, int subId) {
360         final TelephonyManager telephonyManager = TelephonyManager.from(context)
361                 .createForSubscriptionId(subId);
362         final PersistableBundle carrierConfig = context.getSystemService(
363                 CarrierConfigManager.class).getConfigForSubId(subId);
364         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
365                 || carrierConfig == null
366                 || !carrierConfig.getBoolean(
367                 CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL)
368                 || carrierConfig.getBoolean(
369                 CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)
370                 || (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL)
371                 && !telephonyManager.isManualNetworkSelectionAllowed())) {
372             return false;
373         }
374 
375         final int networkMode = android.provider.Settings.Global.getInt(
376                 context.getContentResolver(),
377                 android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
378                 Phone.PREFERRED_NT_MODE);
379         if (networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO
380                 && isWorldMode(context, subId)) {
381             return false;
382         }
383         if (shouldSpeciallyUpdateGsmCdma(context, subId)) {
384             return false;
385         }
386 
387         if (isGsmBasicOptions(context, subId)) {
388             return true;
389         }
390 
391         if (isWorldMode(context, subId)) {
392             if (networkMode == TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA) {
393                 return true;
394             }
395         }
396 
397         return false;
398     }
399 
400     /**
401      * Return {@code true} if Tdscdma is supported in current subscription
402      */
isTdscdmaSupported(Context context, int subId)403     public static boolean isTdscdmaSupported(Context context, int subId) {
404         return isTdscdmaSupported(context,
405                 context.getSystemService(TelephonyManager.class).createForSubscriptionId(subId));
406     }
407 
408     //TODO(b/117651939): move it to telephony
isTdscdmaSupported(Context context, TelephonyManager telephonyManager)409     private static boolean isTdscdmaSupported(Context context, TelephonyManager telephonyManager) {
410         final PersistableBundle carrierConfig = context.getSystemService(
411                 CarrierConfigManager.class).getConfig();
412 
413         if (carrierConfig == null) {
414             return false;
415         }
416 
417         if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SUPPORT_TDSCDMA_BOOL)) {
418             return true;
419         }
420 
421         String operatorNumeric = telephonyManager.getServiceState().getOperatorNumeric();
422         String[] numericArray = carrierConfig.getStringArray(
423                 CarrierConfigManager.KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY);
424         if (numericArray == null || operatorNumeric == null) {
425             return false;
426         }
427         for (String numeric : numericArray) {
428             if (operatorNumeric.equals(numeric)) {
429                 return true;
430             }
431         }
432         return false;
433     }
434 
435     /**
436      * Return subId that supported by search. If there are more than one, return first one,
437      * otherwise return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}
438      */
getSearchableSubscriptionId(Context context)439     public static int getSearchableSubscriptionId(Context context) {
440         final SubscriptionManager subscriptionManager = context.getSystemService(
441                 SubscriptionManager.class);
442         final int subIds[] = subscriptionManager.getActiveSubscriptionIdList();
443 
444         return subIds.length >= 1 ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
445     }
446 
447     /**
448      * Return availability for a default subscription id. If subId already been set, use it to
449      * check, otherwise traverse all active subIds on device to check.
450      * @param context context
451      * @param defSubId Default subId get from telephony preference controller
452      * @param callback Callback to check availability for a specific subId
453      * @return Availability
454      *
455      * @see BasePreferenceController#getAvailabilityStatus()
456      */
getAvailability(Context context, int defSubId, TelephonyAvailabilityCallback callback)457     public static int getAvailability(Context context, int defSubId,
458             TelephonyAvailabilityCallback callback) {
459         final SubscriptionManager subscriptionManager = context.getSystemService(
460                 SubscriptionManager.class);
461         if (defSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
462             // If subId has been set, return the corresponding status
463             return callback.getAvailabilityStatus(defSubId);
464         } else {
465             // Otherwise, search whether there is one subId in device that support this preference
466             final int[] subIds = subscriptionManager.getActiveSubscriptionIdList();
467             if (ArrayUtils.isEmpty(subIds)) {
468                 return callback.getAvailabilityStatus(
469                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
470             } else {
471                 for (final int subId : subIds) {
472                     final int status = callback.getAvailabilityStatus(subId);
473                     if (status == BasePreferenceController.AVAILABLE) {
474                         return status;
475                     }
476                 }
477                 return callback.getAvailabilityStatus(subIds[0]);
478             }
479         }
480     }
481 
482     /**
483      * This method is migrated from {@link com.android.phone.MobileNetworkSettings} and we should
484      * use it carefully. This code snippet doesn't have very clear meaning however we should
485      * update GSM or CDMA differently based on what it returns.
486      *
487      * 1. For all CDMA settings, make them visible if it return {@code true}
488      * 2. For GSM settings, make them visible if it return {@code true} unless 3
489      * 3. For network select settings, make it invisible if it return {@code true}
490      */
491     @VisibleForTesting
shouldSpeciallyUpdateGsmCdma(Context context, int subId)492     static boolean shouldSpeciallyUpdateGsmCdma(Context context, int subId) {
493         final int networkMode = android.provider.Settings.Global.getInt(
494                 context.getContentResolver(),
495                 android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
496                 Phone.PREFERRED_NT_MODE);
497         if (networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM
498                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA
499                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA
500                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_WCDMA
501                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
502                 || networkMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) {
503             if (!isTdscdmaSupported(context, subId) && isWorldMode(context, subId)) {
504                 return true;
505             }
506         }
507 
508         return false;
509     }
510 
getSignalStrengthIcon(Context context, int level, int numLevels, int iconType, boolean cutOut)511     public static Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
512             int iconType, boolean cutOut) {
513         SignalDrawable signalDrawable = new SignalDrawable(context);
514         signalDrawable.setLevel(
515                 SignalDrawable.getState(level, numLevels, cutOut));
516 
517         // Make the network type drawable
518         Drawable networkDrawable =
519                 iconType == NO_CELL_DATA_TYPE_ICON
520                         ? EMPTY_DRAWABLE
521                         : context
522                                 .getResources().getDrawable(iconType, context.getTheme());
523 
524         // Overlay the two drawables
525         final Drawable[] layers = {networkDrawable, signalDrawable};
526         final int iconSize =
527                 context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
528 
529         LayerDrawable icons = new LayerDrawable(layers);
530         // Set the network type icon at the top left
531         icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
532         // Set the signal strength icon at the bottom right
533         icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
534         icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
535         icons.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
536         return icons;
537     }
538 }
539