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;
18 
19 import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
20 import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
21 
22 import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON;
23 import static com.android.settingslib.mobile.MobileMappings.getIconKey;
24 import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
25 
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.graphics.drawable.Drawable;
31 import android.net.wifi.WifiManager;
32 import android.os.UserManager;
33 import android.provider.Settings;
34 import android.telephony.AccessNetworkConstants;
35 import android.telephony.NetworkRegistrationInfo;
36 import android.telephony.ServiceState;
37 import android.telephony.SignalStrength;
38 import android.telephony.SubscriptionInfo;
39 import android.telephony.SubscriptionManager;
40 import android.telephony.TelephonyDisplayInfo;
41 import android.telephony.TelephonyManager;
42 import android.text.Html;
43 import android.util.ArraySet;
44 import android.util.Log;
45 
46 import androidx.annotation.VisibleForTesting;
47 import androidx.collection.ArrayMap;
48 import androidx.lifecycle.Lifecycle;
49 import androidx.lifecycle.LifecycleObserver;
50 import androidx.lifecycle.OnLifecycleEvent;
51 import androidx.preference.Preference;
52 import androidx.preference.PreferenceGroup;
53 import androidx.preference.PreferenceScreen;
54 
55 import com.android.settings.R;
56 import com.android.settings.Utils;
57 import com.android.settings.network.telephony.DataConnectivityListener;
58 import com.android.settings.network.telephony.MobileNetworkActivity;
59 import com.android.settings.network.telephony.MobileNetworkUtils;
60 import com.android.settings.network.telephony.SignalStrengthListener;
61 import com.android.settings.network.telephony.TelephonyDisplayInfoListener;
62 import com.android.settings.widget.MutableGearPreference;
63 import com.android.settings.wifi.WifiPickerTrackerHelper;
64 import com.android.settingslib.SignalIcon.MobileIconGroup;
65 import com.android.settingslib.core.AbstractPreferenceController;
66 import com.android.settingslib.mobile.MobileMappings;
67 import com.android.settingslib.mobile.MobileMappings.Config;
68 import com.android.settingslib.mobile.TelephonyIcons;
69 import com.android.settingslib.net.SignalStrengthUtil;
70 
71 import java.util.Collections;
72 import java.util.List;
73 import java.util.Map;
74 import java.util.Set;
75 
76 /**
77  * This controller manages preference with data subscription information and make its state
78  * display on preference.
79  */
80 public class SubscriptionsPreferenceController extends AbstractPreferenceController implements
81         LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient,
82         MobileDataEnabledListener.Client, DataConnectivityListener.Client,
83         SignalStrengthListener.Callback, TelephonyDisplayInfoListener.Callback {
84     private static final String TAG = "SubscriptionsPrefCntrlr";
85 
86     private UpdateListener mUpdateListener;
87     private String mPreferenceGroupKey;
88     private PreferenceGroup mPreferenceGroup;
89     private TelephonyManager mTelephonyManager;
90     private SubscriptionManager mSubscriptionManager;
91     private SubscriptionsChangeListener mSubscriptionsListener;
92     private MobileDataEnabledListener mDataEnabledListener;
93     private DataConnectivityListener mConnectivityListener;
94     private SignalStrengthListener mSignalStrengthListener;
95     private TelephonyDisplayInfoListener mTelephonyDisplayInfoListener;
96     private WifiPickerTrackerHelper mWifiPickerTrackerHelper;
97     private final WifiManager mWifiManager;
98 
99     @VisibleForTesting
100     final BroadcastReceiver mConnectionChangeReceiver = new BroadcastReceiver() {
101         @Override
102         public void onReceive(Context context, Intent intent) {
103             final String action = intent.getAction();
104             if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
105                 mConfig = mSubsPrefCtrlInjector.getConfig(mContext);
106                 update();
107             } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
108                 update();
109             }
110         }
111     };
112 
113     // Map of subscription id to Preference
114     private Map<Integer, Preference> mSubscriptionPreferences;
115     private int mStartOrder;
116     private MutableGearPreference mSubsGearPref;
117     private Config mConfig = null;
118     private SubsPrefCtrlInjector mSubsPrefCtrlInjector;
119     private TelephonyDisplayInfo mTelephonyDisplayInfo =
120             new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
121                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
122 
123     /**
124      * This interface lets a parent of this class know that some change happened - this could
125      * either be because overall availability changed, or because we've added/removed/updated some
126      * preferences.
127      */
128     public interface UpdateListener {
onChildrenUpdated()129         void onChildrenUpdated();
130     }
131 
132     /**
133      * @param context            the context for the UI where we're placing these preferences
134      * @param lifecycle          for listening to lifecycle events for the UI
135      * @param updateListener     called to let our parent controller know that our availability has
136      *                           changed, or that one or more of the preferences we've placed in the
137      *                           PreferenceGroup has changed
138      * @param preferenceGroupKey the key used to lookup the PreferenceGroup where Preferences will
139      *                           be placed
140      * @param startOrder         the order that should be given to the first Preference placed into
141      *                           the PreferenceGroup; the second will use startOrder+1, third will
142      *                           use startOrder+2, etc. - this is useful for when the parent wants
143      *                           to have other preferences in the same PreferenceGroup and wants
144      *                           a specific ordering relative to this controller's prefs.
145      */
SubscriptionsPreferenceController(Context context, Lifecycle lifecycle, UpdateListener updateListener, String preferenceGroupKey, int startOrder)146     public SubscriptionsPreferenceController(Context context, Lifecycle lifecycle,
147             UpdateListener updateListener, String preferenceGroupKey, int startOrder) {
148         super(context);
149         mUpdateListener = updateListener;
150         mPreferenceGroupKey = preferenceGroupKey;
151         mStartOrder = startOrder;
152         mTelephonyManager = context.getSystemService(TelephonyManager.class);
153         mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
154         mWifiManager = context.getSystemService(WifiManager.class);
155         mSubscriptionPreferences = new ArrayMap<>();
156         mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
157         mDataEnabledListener = new MobileDataEnabledListener(context, this);
158         mConnectivityListener = new DataConnectivityListener(context, this);
159         mSignalStrengthListener = new SignalStrengthListener(context, this);
160         mTelephonyDisplayInfoListener = new TelephonyDisplayInfoListener(context, this);
161         lifecycle.addObserver(this);
162         mSubsPrefCtrlInjector = createSubsPrefCtrlInjector();
163         mConfig = mSubsPrefCtrlInjector.getConfig(mContext);
164     }
165 
registerReceiver()166     private void registerReceiver() {
167         IntentFilter filter = new IntentFilter();
168         filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
169         filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
170         mContext.registerReceiver(mConnectionChangeReceiver, filter);
171     }
172 
unRegisterReceiver()173     private void unRegisterReceiver() {
174         if (mConnectionChangeReceiver != null) {
175             mContext.unregisterReceiver(mConnectionChangeReceiver);
176         }
177     }
178 
179     @OnLifecycleEvent(ON_RESUME)
onResume()180     public void onResume() {
181         mSubscriptionsListener.start();
182         mDataEnabledListener.start(mSubsPrefCtrlInjector.getDefaultDataSubscriptionId());
183         mConnectivityListener.start();
184         mSignalStrengthListener.resume();
185         mTelephonyDisplayInfoListener.resume();
186         registerReceiver();
187         update();
188     }
189 
190     @OnLifecycleEvent(ON_PAUSE)
onPause()191     public void onPause() {
192         mSubscriptionsListener.stop();
193         mDataEnabledListener.stop();
194         mConnectivityListener.stop();
195         mSignalStrengthListener.pause();
196         mTelephonyDisplayInfoListener.pause();
197         unRegisterReceiver();
198         resetProviderPreferenceSummary();
199     }
200 
201     @Override
displayPreference(PreferenceScreen screen)202     public void displayPreference(PreferenceScreen screen) {
203         mPreferenceGroup = screen.findPreference(mPreferenceGroupKey);
204         update();
205     }
206 
update()207     private void update() {
208         if (mPreferenceGroup == null) {
209             return;
210         }
211         if (!isAvailable()) {
212             if (mSubsGearPref != null) {
213                 mPreferenceGroup.removePreference(mSubsGearPref);
214             }
215             for (Preference pref : mSubscriptionPreferences.values()) {
216                 mPreferenceGroup.removePreference(pref);
217             }
218 
219             mSubscriptionPreferences.clear();
220             mSignalStrengthListener.updateSubscriptionIds(Collections.emptySet());
221             mTelephonyDisplayInfoListener.updateSubscriptionIds(Collections.emptySet());
222             mUpdateListener.onChildrenUpdated();
223             return;
224         }
225 
226         SubscriptionInfo subInfo = mSubscriptionManager.getDefaultDataSubscriptionInfo();
227         if (subInfo == null) {
228             mPreferenceGroup.removeAll();
229             return;
230         }
231         if (mSubsGearPref == null) {
232             mPreferenceGroup.removeAll();
233             mSubsGearPref = new MutableGearPreference(mContext, null);
234             mSubsGearPref.setOnPreferenceClickListener(preference -> {
235                 connectCarrierNetwork();
236                 return true;
237             });
238 
239             mSubsGearPref.setOnGearClickListener(p ->
240                     startMobileNetworkActivity(mContext, subInfo.getSubscriptionId()));
241         }
242 
243         if (!(mContext.getSystemService(UserManager.class)).isAdminUser()) {
244             mSubsGearPref.setGearEnabled(false);
245         }
246 
247         mSubsGearPref.setTitle(SubscriptionUtil.getUniqueSubscriptionDisplayName(
248                 subInfo, mContext));
249         mSubsGearPref.setOrder(mStartOrder);
250         mSubsGearPref.setSummary(getMobilePreferenceSummary(subInfo.getSubscriptionId()));
251         mSubsGearPref.setIcon(getIcon(subInfo.getSubscriptionId()));
252         mPreferenceGroup.addPreference(mSubsGearPref);
253 
254         final Set<Integer> activeDataSubIds = new ArraySet<>();
255         activeDataSubIds.add(subInfo.getSubscriptionId());
256         mSignalStrengthListener.updateSubscriptionIds(activeDataSubIds);
257         mTelephonyDisplayInfoListener.updateSubscriptionIds(activeDataSubIds);
258         mUpdateListener.onChildrenUpdated();
259     }
260 
getMobilePreferenceSummary(int subId)261     private CharSequence getMobilePreferenceSummary(int subId) {
262         final TelephonyManager tmForSubId = mTelephonyManager.createForSubscriptionId(subId);
263         if (!tmForSubId.isDataEnabled()) {
264             return mContext.getString(R.string.mobile_data_off_summary);
265         }
266         final ServiceState serviceState = tmForSubId.getServiceState();
267         final NetworkRegistrationInfo regInfo = (serviceState == null)
268                 ? null
269                 : serviceState.getNetworkRegistrationInfo(
270                         NetworkRegistrationInfo.DOMAIN_PS,
271                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
272 
273         final boolean isDataInService = (regInfo == null)
274                 ? false
275                 : regInfo.isRegistered();
276         final boolean isCarrierNetworkActive = isCarrierNetworkActive();
277         String result = mSubsPrefCtrlInjector.getNetworkType(
278                 mContext, mConfig, mTelephonyDisplayInfo, subId, isCarrierNetworkActive);
279         if (mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext) || isCarrierNetworkActive) {
280             Log.i(TAG, "Active cellular network or active carrier network.");
281             result = mContext.getString(R.string.preference_summary_default_combination,
282                     mContext.getString(R.string.mobile_data_connection_active), result);
283         } else if (!isDataInService) {
284             result = mContext.getString(R.string.mobile_data_no_connection);
285         }
286         return Html.fromHtml(result, Html.FROM_HTML_MODE_LEGACY);
287     }
288 
getIcon(int subId)289     private Drawable getIcon(int subId) {
290         final TelephonyManager tmForSubId = mTelephonyManager.createForSubscriptionId(subId);
291         final SignalStrength strength = tmForSubId.getSignalStrength();
292         int level = (strength == null) ? 0 : strength.getLevel();
293         int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
294         boolean isCarrierNetworkActive = isCarrierNetworkActive();
295         if (shouldInflateSignalStrength(subId) || isCarrierNetworkActive) {
296             level = isCarrierNetworkActive
297                     ? SignalStrength.NUM_SIGNAL_STRENGTH_BINS
298                     : (level + 1);
299             numLevels += 1;
300         }
301 
302         Drawable icon = mContext.getDrawable(R.drawable.ic_signal_strength_zero_bar_no_internet);
303 
304         final ServiceState serviceState = tmForSubId.getServiceState();
305         final NetworkRegistrationInfo regInfo = (serviceState == null)
306                 ? null
307                 : serviceState.getNetworkRegistrationInfo(
308                         NetworkRegistrationInfo.DOMAIN_PS,
309                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
310 
311         final boolean isDataInService = (regInfo == null)
312                 ? false
313                 : regInfo.isRegistered();
314         final boolean isVoiceInService = (serviceState == null)
315                 ? false
316                 : (serviceState.getState() == ServiceState.STATE_IN_SERVICE);
317         if (isDataInService || isVoiceInService || isCarrierNetworkActive) {
318             icon = mSubsPrefCtrlInjector.getIcon(mContext, level, numLevels,
319                     !tmForSubId.isDataEnabled());
320         }
321 
322         final boolean isActiveCellularNetwork =
323                 mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext);
324         if (isActiveCellularNetwork || isCarrierNetworkActive) {
325             icon.setTint(Utils.getColorAccentDefaultColor(mContext));
326         }
327 
328         return icon;
329     }
330 
resetProviderPreferenceSummary()331     private void resetProviderPreferenceSummary() {
332         if (mSubsGearPref == null) {
333             return;
334         }
335         mSubsGearPref.setSummary("");
336     }
337 
startMobileNetworkActivity(Context context, int subId)338     private static void startMobileNetworkActivity(Context context, int subId) {
339         final Intent intent = new Intent(context, MobileNetworkActivity.class);
340         intent.putExtra(Settings.EXTRA_SUB_ID, subId);
341         // MobileNetworkActivity is singleTask, set SplitPairRule to show in 2-pane.
342         MobileNetworkTwoPaneUtils.registerTwoPaneForMobileNetwork(context, intent, null);
343         context.startActivity(intent);
344     }
345 
346     @VisibleForTesting
shouldInflateSignalStrength(int subId)347     boolean shouldInflateSignalStrength(int subId) {
348         return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
349     }
350 
351     @VisibleForTesting
setIcon(Preference pref, int subId, boolean isDefaultForData)352     void setIcon(Preference pref, int subId, boolean isDefaultForData) {
353         final TelephonyManager mgr = mContext.getSystemService(
354                 TelephonyManager.class).createForSubscriptionId(subId);
355         final SignalStrength strength = mgr.getSignalStrength();
356         int level = (strength == null) ? 0 : strength.getLevel();
357         int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
358         if (shouldInflateSignalStrength(subId)) {
359             level += 1;
360             numLevels += 1;
361         }
362 
363         final boolean showCutOut = !isDefaultForData || !mgr.isDataEnabled();
364         pref.setIcon(mSubsPrefCtrlInjector.getIcon(mContext, level, numLevels, showCutOut));
365     }
366 
367     /**
368      * The summary can have either 1 or 2 lines depending on which services (calls, SMS, data) this
369      * subscription is the default for.
370      *
371      * If this subscription is the default for calls and/or SMS, we add a line to show that.
372      *
373      * If this subscription is the default for data, we add a line with detail about
374      * whether the data connection is active.
375      *
376      * If a subscription isn't the default for anything, we just say it is available.
377      */
getSummary(int subId, boolean isDefaultForData)378     protected String getSummary(int subId, boolean isDefaultForData) {
379         final int callsDefaultSubId = mSubsPrefCtrlInjector.getDefaultVoiceSubscriptionId();
380         final int smsDefaultSubId = mSubsPrefCtrlInjector.getDefaultSmsSubscriptionId();
381 
382         String line1 = null;
383         if (subId == callsDefaultSubId && subId == smsDefaultSubId) {
384             line1 = mContext.getString(R.string.default_for_calls_and_sms);
385         } else if (subId == callsDefaultSubId) {
386             line1 = mContext.getString(R.string.default_for_calls);
387         } else if (subId == smsDefaultSubId) {
388             line1 = mContext.getString(R.string.default_for_sms);
389         }
390 
391         String line2 = null;
392         if (isDefaultForData) {
393             final TelephonyManager telMgrForSub = mContext.getSystemService(
394                     TelephonyManager.class).createForSubscriptionId(subId);
395             final boolean dataEnabled = telMgrForSub.isDataEnabled();
396             if (dataEnabled && mSubsPrefCtrlInjector.isActiveCellularNetwork(mContext)) {
397                 line2 = mContext.getString(R.string.mobile_data_active);
398             } else if (!dataEnabled) {
399                 line2 = mContext.getString(R.string.mobile_data_off);
400             } else {
401                 line2 = mContext.getString(R.string.default_for_mobile_data);
402             }
403         }
404 
405         if (line1 != null && line2 != null) {
406             return String.join(System.lineSeparator(), line1, line2);
407         } else if (line1 != null) {
408             return line1;
409         } else if (line2 != null) {
410             return line2;
411         } else {
412             return mContext.getString(R.string.subscription_available);
413         }
414     }
415 
416     /**
417      * @return true if there is at least 1 available subscription.
418      */
419     @Override
isAvailable()420     public boolean isAvailable() {
421         if (mSubscriptionsListener.isAirplaneModeOn()
422                 && (!mWifiManager.isWifiEnabled() || !isCarrierNetworkActive())) {
423             return false;
424         }
425         List<SubscriptionInfo> subInfoList =
426                 SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager);
427         if (subInfoList == null) {
428             return false;
429         }
430 
431         return subInfoList.stream()
432                 // Avoid from showing subscription(SIM)s which has been marked as hidden
433                 // For example, only one subscription will be shown when there're multiple
434                 // subscriptions with same group UUID.
435                 .filter(subInfo ->
436                         mSubsPrefCtrlInjector.canSubscriptionBeDisplayed(mContext,
437                                 subInfo.getSubscriptionId()))
438                 .count() >= 1;
439     }
440 
441     @Override
getPreferenceKey()442     public String getPreferenceKey() {
443         return null;
444     }
445 
446     @Override
onAirplaneModeChanged(boolean airplaneModeEnabled)447     public void onAirplaneModeChanged(boolean airplaneModeEnabled) {
448         update();
449     }
450 
451     @Override
onSubscriptionsChanged()452     public void onSubscriptionsChanged() {
453         // See if we need to change which sub id we're using to listen for enabled/disabled changes.
454         int defaultDataSubId = mSubsPrefCtrlInjector.getDefaultDataSubscriptionId();
455         if (defaultDataSubId != mDataEnabledListener.getSubId()) {
456             mDataEnabledListener.stop();
457             mDataEnabledListener.start(defaultDataSubId);
458         }
459         update();
460     }
461 
462     @Override
onMobileDataEnabledChange()463     public void onMobileDataEnabledChange() {
464         update();
465     }
466 
467     @Override
onDataConnectivityChange()468     public void onDataConnectivityChange() {
469         update();
470     }
471 
472     @Override
onSignalStrengthChanged()473     public void onSignalStrengthChanged() {
474         update();
475     }
476 
477     @Override
onTelephonyDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo)478     public void onTelephonyDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo) {
479         mTelephonyDisplayInfo = telephonyDisplayInfo;
480         update();
481     }
482 
setWifiPickerTrackerHelper(WifiPickerTrackerHelper helper)483     public void setWifiPickerTrackerHelper(WifiPickerTrackerHelper helper) {
484         mWifiPickerTrackerHelper = helper;
485     }
486 
487     @VisibleForTesting
connectCarrierNetwork()488     public void connectCarrierNetwork() {
489         if (!MobileNetworkUtils.isMobileDataEnabled(mContext)) {
490             return;
491         }
492         if (mWifiPickerTrackerHelper != null) {
493             mWifiPickerTrackerHelper.connectCarrierNetwork(null /* ConnectCallback */);
494         }
495     }
496 
createSubsPrefCtrlInjector()497     SubsPrefCtrlInjector createSubsPrefCtrlInjector() {
498         return new SubsPrefCtrlInjector();
499     }
500 
isCarrierNetworkActive()501     boolean isCarrierNetworkActive() {
502         return mWifiPickerTrackerHelper != null
503                 && mWifiPickerTrackerHelper.isCarrierNetworkActive();
504     }
505 
506     /**
507      * To inject necessary data from each static api.
508      */
509     @VisibleForTesting
510     public static class SubsPrefCtrlInjector {
511         /**
512          * Uses to inject function and value for class and test class.
513          */
canSubscriptionBeDisplayed(Context context, int subId)514         public boolean canSubscriptionBeDisplayed(Context context, int subId) {
515             return (SubscriptionUtil.getAvailableSubscription(context,
516                     ProxySubscriptionManager.getInstance(context), subId) != null);
517         }
518 
519         /**
520          * Check SIM be able to display on UI.
521          */
getDefaultSmsSubscriptionId()522         public int getDefaultSmsSubscriptionId() {
523             return SubscriptionManager.getDefaultSmsSubscriptionId();
524         }
525 
526         /**
527          * Gets default voice subscription ID.
528          */
getDefaultVoiceSubscriptionId()529         public int getDefaultVoiceSubscriptionId() {
530             return SubscriptionManager.getDefaultVoiceSubscriptionId();
531         }
532 
533         /**
534          * Gets default data subscription ID.
535          */
getDefaultDataSubscriptionId()536         public int getDefaultDataSubscriptionId() {
537             return SubscriptionManager.getDefaultDataSubscriptionId();
538         }
539 
540         /**
541          * Confirms the current network is cellular and active.
542          */
isActiveCellularNetwork(Context context)543         public boolean isActiveCellularNetwork(Context context) {
544             return MobileNetworkUtils.activeNetworkIsCellular(context);
545         }
546 
547         /**
548          * Gets config for carrier customization.
549          */
getConfig(Context context)550         public Config getConfig(Context context) {
551             return MobileMappings.Config.readConfig(context);
552         }
553 
554         /**
555          * Gets current mobile network type.
556          */
getNetworkType(Context context, Config config, TelephonyDisplayInfo telephonyDisplayInfo, int subId)557         public String getNetworkType(Context context, Config config,
558                 TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
559             String iconKey = getIconKey(telephonyDisplayInfo);
560             MobileIconGroup iconGroup = mapIconSets(config).get(iconKey);
561             int resId = 0;
562             if (iconGroup != null) {
563                 resId = iconGroup.dataContentDescription;
564             }
565             return resId != 0
566                     ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId)
567                     : "";
568         }
569 
570         /**
571          * Gets current network type of Carrier Wi-Fi Network or Cellular.
572          */
getNetworkType(Context context, Config config, TelephonyDisplayInfo telephonyDisplayInfo, int subId, boolean isCarrierWifiNetwork)573         public String getNetworkType(Context context, Config config,
574                 TelephonyDisplayInfo telephonyDisplayInfo, int subId,
575                 boolean isCarrierWifiNetwork) {
576             if (isCarrierWifiNetwork) {
577                 MobileIconGroup carrierMergedWifiIconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
578                 int resId = carrierMergedWifiIconGroup.dataContentDescription;
579                 return resId != 0
580                         ? SubscriptionManager.getResourcesForSubId(context, subId)
581                         .getString(resId) : "";
582             } else {
583                 return getNetworkType(context, config, telephonyDisplayInfo, subId);
584             }
585         }
586 
587         /**
588          * Gets signal icon with different signal level.
589          */
getIcon(Context context, int level, int numLevels, boolean cutOut)590         public Drawable getIcon(Context context, int level, int numLevels, boolean cutOut) {
591             return MobileNetworkUtils.getSignalStrengthIcon(context, level, numLevels,
592                     NO_CELL_DATA_TYPE_ICON, cutOut);
593         }
594     }
595 }
596