1 package com.android.phone.settings;
2 
3 import android.content.ComponentName;
4 import android.content.Context;
5 import android.content.Intent;
6 import android.content.pm.PackageManager;
7 import android.content.pm.ResolveInfo;
8 import android.graphics.drawable.Icon;
9 import android.net.sip.SipManager;
10 import android.os.Bundle;
11 import android.os.UserManager;
12 import android.preference.ListPreference;
13 import android.preference.Preference;
14 import android.preference.PreferenceCategory;
15 import android.preference.PreferenceFragment;
16 import android.preference.SwitchPreference;
17 import android.telecom.PhoneAccount;
18 import android.telecom.PhoneAccountHandle;
19 import android.telecom.TelecomManager;
20 import android.telephony.CarrierConfigManager;
21 import android.telephony.SubscriptionInfo;
22 import android.telephony.SubscriptionManager;
23 import android.telephony.TelephonyManager;
24 import android.text.TextUtils;
25 import android.util.Log;
26 
27 import com.android.internal.telephony.Phone;
28 import com.android.phone.PhoneUtils;
29 import com.android.phone.R;
30 import com.android.phone.SubscriptionInfoHelper;
31 import com.android.services.telephony.sip.SipAccountRegistry;
32 import com.android.services.telephony.sip.SipPreferences;
33 import com.android.services.telephony.sip.SipUtil;
34 
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Comparator;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.stream.Collectors;
41 
42 public class PhoneAccountSettingsFragment extends PreferenceFragment
43         implements Preference.OnPreferenceChangeListener,
44                 AccountSelectionPreference.AccountSelectionListener {
45 
46     private static final String ACCOUNTS_LIST_CATEGORY_KEY =
47             "phone_accounts_accounts_list_category_key";
48 
49     private static final String ALL_CALLING_ACCOUNTS_KEY = "phone_accounts_all_calling_accounts";
50 
51     private static final String SIP_SETTINGS_CATEGORY_PREF_KEY =
52             "phone_accounts_sip_settings_category_key";
53     private static final String USE_SIP_PREF_KEY = "use_sip_calling_options_key";
54     private static final String SIP_RECEIVE_CALLS_PREF_KEY = "sip_receive_calls_key";
55 
56     private static final String MAKE_AND_RECEIVE_CALLS_CATEGORY_KEY =
57             "make_and_receive_calls_settings_category_key";
58     private static final String DEFAULT_OUTGOING_ACCOUNT_KEY = "default_outgoing_account";
59     private static final String SMART_FORWARDING_CONFIGURATION_PREF_KEY =
60             "smart_forwarding_configuration_key";
61 
62     private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
63             "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
64 
65     /**
66      * Value to start ordering of phone accounts relative to other preferences. By setting this
67      * value on the phone account listings, we ensure that anything that is ordered before
68      * {value} in the preference XML comes before the phone account list and anything with
69      * a value significantly larger will list after.
70      */
71     private static final int ACCOUNT_ORDERING_START_VALUE = 100;
72 
73     private static final String LOG_TAG = PhoneAccountSettingsFragment.class.getSimpleName();
74 
75     private TelecomManager mTelecomManager;
76     private TelephonyManager mTelephonyManager;
77     private SubscriptionManager mSubscriptionManager;
78 
79     private PreferenceCategory mAccountList;
80 
81     private AccountSelectionPreference mDefaultOutgoingAccount;
82     private Preference mAllCallingAccounts;
83 
84     private PreferenceCategory mMakeAndReceiveCallsCategory;
85     private boolean mMakeAndReceiveCallsCategoryPresent;
86 
87     private ListPreference mUseSipCalling;
88     private SwitchPreference mSipReceiveCallsPreference;
89     private SipPreferences mSipPreferences;
90 
91     private final SubscriptionManager.OnSubscriptionsChangedListener
92             mOnSubscriptionsChangeListener =
93             new SubscriptionManager.OnSubscriptionsChangedListener() {
94         @Override
95         public void onSubscriptionsChanged() {
96             updateAccounts();
97         }
98     };
99 
100     @Override
onCreate(Bundle icicle)101     public void onCreate(Bundle icicle) {
102         super.onCreate(icicle);
103 
104         mTelecomManager = getActivity().getSystemService(TelecomManager.class);
105         mTelephonyManager = TelephonyManager.from(getActivity());
106         mSubscriptionManager = SubscriptionManager.from(getActivity());
107     }
108 
109     @Override
onResume()110     public void onResume() {
111         super.onResume();
112 
113         if (getPreferenceScreen() != null) {
114             getPreferenceScreen().removeAll();
115         }
116 
117         addPreferencesFromResource(R.xml.phone_account_settings);
118 
119         /**
120          * Here we make decisions about what we will and will not display with regards to phone-
121          * account settings.  The basic settings structure is this:
122          * (1) <Make Calls With...>  // Lets user pick a default account for outgoing calls
123          * (2) <Account List>
124          *       <Account>
125          *       ...
126          *       <Account>
127          *     </Account List>
128          * (3) <All Accounts>  // Lets user enable/disable third-party accounts. SIM-based accounts
129          *                     // are always enabled and so aren't relevant here.
130          *
131          * Here are the rules that we follow:
132          * - (1) is only shown if there are multiple enabled accounts, including SIM accounts.
133          *   This can be 2+ SIM accounts, 2+ third party accounts or any combination.
134          * - (2) The account list only lists (a) enabled third party accounts and (b) SIM-based
135          *   accounts. However, for single-SIM devices, if the only account to show is the
136          *   SIM-based account, we don't show the list at all under the assumption that the user
137          *   already knows about the account.
138          * - (3) Is only shown if there exist any third party accounts.  If none exist, then the
139          *   option is hidden since there is nothing that can be done in it.
140          *
141          * By far, the most common case for users will be the single-SIM device without any
142          * third party accounts. IOW, the great majority of users won't see any of these options.
143          */
144         mAccountList = (PreferenceCategory) getPreferenceScreen().findPreference(
145                 ACCOUNTS_LIST_CATEGORY_KEY);
146         mDefaultOutgoingAccount = (AccountSelectionPreference)
147                 getPreferenceScreen().findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
148         mAllCallingAccounts = getPreferenceScreen().findPreference(ALL_CALLING_ACCOUNTS_KEY);
149 
150         mMakeAndReceiveCallsCategory = (PreferenceCategory) getPreferenceScreen().findPreference(
151                 MAKE_AND_RECEIVE_CALLS_CATEGORY_KEY);
152         mMakeAndReceiveCallsCategoryPresent = false;
153 
154         updateAccounts();
155         updateMakeCallsOptions();
156 
157         if (isPrimaryUser() && SipUtil.isVoipSupported(getActivity())) {
158             mSipPreferences = new SipPreferences(getActivity());
159 
160             mUseSipCalling = (ListPreference)
161                     getPreferenceScreen().findPreference(USE_SIP_PREF_KEY);
162             mUseSipCalling.setEntries(!SipManager.isSipWifiOnly(getActivity())
163                     ? R.array.sip_call_options_wifi_only_entries
164                     : R.array.sip_call_options_entries);
165             mUseSipCalling.setOnPreferenceChangeListener(this);
166 
167             int optionsValueIndex =
168                     mUseSipCalling.findIndexOfValue(mSipPreferences.getSipCallOption());
169             if (optionsValueIndex == -1) {
170                 // If the option is invalid (eg. deprecated value), default to SIP_ADDRESS_ONLY.
171                 mSipPreferences.setSipCallOption(
172                         getResources().getString(R.string.sip_address_only));
173                 optionsValueIndex =
174                         mUseSipCalling.findIndexOfValue(mSipPreferences.getSipCallOption());
175             }
176             mUseSipCalling.setValueIndex(optionsValueIndex);
177             mUseSipCalling.setSummary(mUseSipCalling.getEntry());
178 
179             mSipReceiveCallsPreference = (SwitchPreference)
180                     getPreferenceScreen().findPreference(SIP_RECEIVE_CALLS_PREF_KEY);
181             mSipReceiveCallsPreference.setEnabled(SipUtil.isPhoneIdle(getActivity()));
182             mSipReceiveCallsPreference.setChecked(
183                     mSipPreferences.isReceivingCallsEnabled());
184             mSipReceiveCallsPreference.setOnPreferenceChangeListener(this);
185         } else {
186             getPreferenceScreen().removePreference(
187                     getPreferenceScreen().findPreference(SIP_SETTINGS_CATEGORY_PREF_KEY));
188         }
189 
190         SubscriptionManager.from(getActivity()).addOnSubscriptionsChangedListener(
191                 mOnSubscriptionsChangeListener);
192     }
193 
194     @Override
onPause()195     public void onPause() {
196         SubscriptionManager.from(getActivity()).removeOnSubscriptionsChangedListener(
197                 mOnSubscriptionsChangeListener);
198         super.onPause();
199     }
200 
201     /**
202      * Handles changes to the preferences.
203      *
204      * @param pref The preference changed.
205      * @param objValue The changed value.
206      * @return True if the preference change has been handled, and false otherwise.
207      */
208     @Override
onPreferenceChange(Preference pref, Object objValue)209     public boolean onPreferenceChange(Preference pref, Object objValue) {
210         if (pref == mUseSipCalling) {
211             String option = objValue.toString();
212             mSipPreferences.setSipCallOption(option);
213             mUseSipCalling.setValueIndex(mUseSipCalling.findIndexOfValue(option));
214             mUseSipCalling.setSummary(mUseSipCalling.getEntry());
215             return true;
216         } else if (pref == mSipReceiveCallsPreference) {
217             final boolean isEnabled = !mSipReceiveCallsPreference.isChecked();
218             new Thread(new Runnable() {
219                 public void run() {
220                     handleSipReceiveCallsOption(isEnabled);
221                 }
222             }).start();
223             return true;
224         }
225         return false;
226     }
227 
228     /**
229      * Handles a phone account selection for the default outgoing phone account.
230      *
231      * @param pref The account selection preference which triggered the account selected event.
232      * @param account The account selected.
233      * @return True if the account selection has been handled, and false otherwise.
234      */
235     @Override
onAccountSelected(AccountSelectionPreference pref, PhoneAccountHandle account)236     public boolean onAccountSelected(AccountSelectionPreference pref, PhoneAccountHandle account) {
237         if (pref == mDefaultOutgoingAccount) {
238             mTelecomManager.setUserSelectedOutgoingPhoneAccount(account);
239             return true;
240         }
241         return false;
242     }
243 
244     /**
245      * Repopulate the dialog to pick up changes before showing.
246      *
247      * @param pref The account selection preference dialog being shown.
248      */
249     @Override
onAccountSelectionDialogShow(AccountSelectionPreference pref)250     public void onAccountSelectionDialogShow(AccountSelectionPreference pref) {
251         if (pref == mDefaultOutgoingAccount) {
252             updateDefaultOutgoingAccountsModel();
253         }
254     }
255 
256     @Override
onAccountChanged(AccountSelectionPreference pref)257     public void onAccountChanged(AccountSelectionPreference pref) {}
258 
handleSipReceiveCallsOption(boolean isEnabled)259     private synchronized void handleSipReceiveCallsOption(boolean isEnabled) {
260         Context context = getActivity();
261         if (context == null) {
262             // Return if the fragment is detached from parent activity before executed by thread.
263             return;
264         }
265 
266         mSipPreferences.setReceivingCallsEnabled(isEnabled);
267 
268         SipUtil.useSipToReceiveIncomingCalls(context, isEnabled);
269 
270         // Restart all Sip services to ensure we reflect whether we are receiving calls.
271         SipAccountRegistry sipAccountRegistry = SipAccountRegistry.getInstance();
272         sipAccountRegistry.restartSipService(context);
273     }
274 
275     /**
276      * Queries the telcomm manager to update the default outgoing account selection preference
277      * with the list of outgoing accounts and the current default outgoing account.
278      */
updateDefaultOutgoingAccountsModel()279     private void updateDefaultOutgoingAccountsModel() {
280         mDefaultOutgoingAccount.setModel(
281                 mTelecomManager,
282                 getCallingAccounts(true /* includeSims */, false /* includeDisabled */),
283                 mTelecomManager.getUserSelectedOutgoingPhoneAccount(),
284                 getString(R.string.phone_accounts_ask_every_time));
285     }
286 
initAccountList(List<PhoneAccountHandle> enabledAccounts)287     private void initAccountList(List<PhoneAccountHandle> enabledAccounts) {
288 
289         boolean isMultiSimDevice = mTelephonyManager.isMultiSimEnabled();
290 
291         // On a single-SIM device, do not list any accounts if the only account is the SIM-based
292         // one. This is because on single-SIM devices, we do not expose SIM settings through the
293         // account listing entry so showing it does nothing to help the user. Nor does the lack of
294         // action match the "Settings" header above the listing.
295         if (!isMultiSimDevice && getCallingAccounts(
296                 false /* includeSims */, false /* includeDisabled */).isEmpty()){
297             return;
298         }
299 
300         // Obtain the list of phone accounts.
301         List<PhoneAccount> accounts = new ArrayList<>();
302         for (PhoneAccountHandle handle : enabledAccounts) {
303             PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
304             if (account != null) {
305                 accounts.add(account);
306             }
307         }
308 
309         // Sort the accounts according to how we want to display them.
310         Collections.sort(accounts, new Comparator<PhoneAccount>() {
311             @Override
312             public int compare(PhoneAccount account1, PhoneAccount account2) {
313                 int retval = 0;
314 
315                 // SIM accounts go first
316                 boolean isSim1 = account1.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
317                 boolean isSim2 = account2.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
318                 if (isSim1 != isSim2) {
319                     retval = isSim1 ? -1 : 1;
320                 }
321 
322                 int subId1 = mTelephonyManager.getSubIdForPhoneAccount(account1);
323                 int subId2 = mTelephonyManager.getSubIdForPhoneAccount(account2);
324                 if (subId1 != SubscriptionManager.INVALID_SUBSCRIPTION_ID &&
325                         subId2 != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
326                     retval = (mSubscriptionManager.getSlotIndex(subId1) <
327                         mSubscriptionManager.getSlotIndex(subId2)) ? -1 : 1;
328                 }
329 
330                 // Then order by package
331                 if (retval == 0) {
332                     String pkg1 = account1.getAccountHandle().getComponentName().getPackageName();
333                     String pkg2 = account2.getAccountHandle().getComponentName().getPackageName();
334                     retval = pkg1.compareTo(pkg2);
335                 }
336 
337                 // Finally, order by label
338                 if (retval == 0) {
339                     String label1 = nullToEmpty(account1.getLabel().toString());
340                     String label2 = nullToEmpty(account2.getLabel().toString());
341                     retval = label1.compareTo(label2);
342                 }
343 
344                 // Then by hashcode
345                 if (retval == 0) {
346                     retval = account1.hashCode() - account2.hashCode();
347                 }
348                 return retval;
349             }
350         });
351 
352         int order = ACCOUNT_ORDERING_START_VALUE;
353 
354         // Add an entry for each account.
355         for (PhoneAccount account : accounts) {
356             PhoneAccountHandle handle = account.getAccountHandle();
357             Intent intent = null;
358 
359             // SIM phone accounts use a different setting intent and are thus handled differently.
360             if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
361 
362                 // For SIM-based accounts, we only expose the settings through the account list
363                 // if we are on a multi-SIM device. For single-SIM devices, the settings are
364                 // more spread out so there is no good single place to take the user, so we don't.
365                 if (isMultiSimDevice) {
366                     SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(
367                             mTelephonyManager.getSubIdForPhoneAccount(account));
368 
369                     if (subInfo != null) {
370                         intent = new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS);
371                         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
372                         SubscriptionInfoHelper.addExtrasToIntent(intent, subInfo);
373                     }
374                 }
375             } else {
376                 intent = buildPhoneAccountConfigureIntent(getActivity(), handle);
377             }
378 
379             // Create the preference & add the label
380             Preference accountPreference = new Preference(getActivity());
381             CharSequence accountLabel = account.getLabel();
382             boolean isSimAccount =
383                     account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
384             accountPreference.setTitle((TextUtils.isEmpty(accountLabel) && isSimAccount)
385                     ? getString(R.string.phone_accounts_default_account_label) : accountLabel);
386 
387             // Add an icon.
388             Icon icon = account.getIcon();
389             if (icon != null) {
390                 accountPreference.setIcon(icon.loadDrawable(getActivity()));
391             }
392 
393             // Add an intent to send the user to the account's settings.
394             if (intent != null) {
395                 accountPreference.setIntent(intent);
396             }
397 
398             accountPreference.setOrder(order++);
399             mAccountList.addPreference(accountPreference);
400         }
401     }
402 
shouldShowConnectionServiceList(List<PhoneAccountHandle> allNonSimAccounts)403     private boolean shouldShowConnectionServiceList(List<PhoneAccountHandle> allNonSimAccounts) {
404         return mTelephonyManager.isMultiSimEnabled() || allNonSimAccounts.size() > 0;
405     }
406 
updateAccounts()407     private void updateAccounts() {
408         if (mAccountList != null) {
409             mAccountList.removeAll();
410             List<PhoneAccountHandle> allNonSimAccounts =
411                     getCallingAccounts(false /* includeSims */, true /* includeDisabled */);
412             // Check to see if we should show the entire section at all.
413             if (shouldShowConnectionServiceList(allNonSimAccounts)) {
414                 List<PhoneAccountHandle> enabledAccounts =
415                         getCallingAccounts(true /* includeSims */, false /* includeDisabled */);
416                 // Initialize the account list with the set of enabled & SIM accounts.
417                 initAccountList(enabledAccounts);
418 
419                 // Only show the 'Make Calls With..." option if there are multiple accounts.
420                 if (enabledAccounts.size() > 1) {
421                     mMakeAndReceiveCallsCategory.addPreference(mDefaultOutgoingAccount);
422                     mMakeAndReceiveCallsCategoryPresent = true;
423                     mDefaultOutgoingAccount.setListener(this);
424                     updateDefaultOutgoingAccountsModel();
425                 } else {
426                     mMakeAndReceiveCallsCategory.removePreference(mDefaultOutgoingAccount);
427                 }
428 
429                 // If there are no third party (nonSim) accounts,
430                 // then don't show enable/disable dialog.
431                 if (!allNonSimAccounts.isEmpty()) {
432                     mAccountList.addPreference(mAllCallingAccounts);
433                 } else {
434                     mAccountList.removePreference(mAllCallingAccounts);
435                 }
436             } else {
437                 getPreferenceScreen().removePreference(mAccountList);
438             }
439         }
440     }
441 
getCallingAccounts( boolean includeSims, boolean includeDisabledAccounts)442     private List<PhoneAccountHandle> getCallingAccounts(
443             boolean includeSims, boolean includeDisabledAccounts) {
444         PhoneAccountHandle emergencyAccountHandle = getEmergencyPhoneAccount();
445 
446         List<PhoneAccountHandle> accountHandles =
447                 mTelecomManager.getCallCapablePhoneAccounts(includeDisabledAccounts);
448         for (Iterator<PhoneAccountHandle> i = accountHandles.iterator(); i.hasNext();) {
449             PhoneAccountHandle handle = i.next();
450             if (handle.equals(emergencyAccountHandle)) {
451                 // never include emergency call accounts in this piece of code.
452                 i.remove();
453                 continue;
454             }
455 
456             PhoneAccount account = mTelecomManager.getPhoneAccount(handle);
457             if (account == null) {
458                 i.remove();
459             } else if (!includeSims &&
460                     account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
461                 i.remove();
462             }
463         }
464         return accountHandles;
465     }
466 
nullToEmpty(String str)467     private String nullToEmpty(String str) {
468         return str == null ? "" : str;
469     }
470 
getEmergencyPhoneAccount()471     private PhoneAccountHandle getEmergencyPhoneAccount() {
472         return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
473                 (Phone) null, "" /* prefix */, true /* isEmergency */);
474     }
475 
buildPhoneAccountConfigureIntent( Context context, PhoneAccountHandle accountHandle)476     public static Intent buildPhoneAccountConfigureIntent(
477             Context context, PhoneAccountHandle accountHandle) {
478         Intent intent = buildConfigureIntent(
479                 context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT);
480 
481         if (intent == null) {
482             // If the new configuration didn't work, try the old configuration intent.
483             intent = buildConfigureIntent(
484                     context, accountHandle, LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT);
485             if (intent != null) {
486                 Log.w(LOG_TAG, "Phone account using old configuration intent: " + accountHandle);
487             }
488         }
489         return intent;
490     }
491 
buildConfigureIntent( Context context, PhoneAccountHandle accountHandle, String actionStr)492     private static Intent buildConfigureIntent(
493             Context context, PhoneAccountHandle accountHandle, String actionStr) {
494         if (accountHandle == null || accountHandle.getComponentName() == null ||
495                 TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) {
496             return null;
497         }
498 
499         // Build the settings intent.
500         Intent intent = new Intent(actionStr);
501         intent.setPackage(accountHandle.getComponentName().getPackageName());
502         intent.addCategory(Intent.CATEGORY_DEFAULT);
503         intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
504 
505         // Check to see that the phone account package can handle the setting intent.
506         PackageManager pm = context.getPackageManager();
507         List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
508         if (resolutions.size() == 0) {
509             intent = null;  // set no intent if the package cannot handle it.
510         }
511 
512         return intent;
513     }
514 
515     /**
516      * @return Whether the current user is the primary user.
517      */
isPrimaryUser()518     private boolean isPrimaryUser() {
519         final UserManager userManager = (UserManager) getActivity()
520                 .getSystemService(Context.USER_SERVICE);
521         return userManager.isPrimaryUser();
522     }
523 
updateMakeCallsOptions()524     private void updateMakeCallsOptions() {
525         if (mMakeAndReceiveCallsCategory == null) {
526             return;
527         }
528 
529         Intent smartForwardingUiIntent = getLaunchSmartForwardingMenuIntent();
530         if (smartForwardingUiIntent != null) {
531             mMakeAndReceiveCallsCategory.findPreference(SMART_FORWARDING_CONFIGURATION_PREF_KEY)
532                     .setIntent(smartForwardingUiIntent);
533             mMakeAndReceiveCallsCategoryPresent = true;
534         } else {
535             mMakeAndReceiveCallsCategory.removePreference(
536                     getPreferenceScreen().findPreference(SMART_FORWARDING_CONFIGURATION_PREF_KEY));
537         }
538 
539         if (!mMakeAndReceiveCallsCategoryPresent) {
540             getPreferenceScreen().removePreference(mMakeAndReceiveCallsCategory);
541         }
542     }
543 
544     /**
545      * @return Smart forwarding configuration UI Intent when supported
546      */
getLaunchSmartForwardingMenuIntent()547     private Intent getLaunchSmartForwardingMenuIntent() {
548         if (mTelephonyManager.getPhoneCount() <= 1) {
549             return null;
550         }
551 
552         final CarrierConfigManager configManager = (CarrierConfigManager)
553                 getActivity().getSystemService(Context.CARRIER_CONFIG_SERVICE);
554         if (configManager == null) {
555             return null;
556         }
557 
558         List<SubscriptionInfo> subscriptions =
559                 mSubscriptionManager.getActiveSubscriptionInfoList();
560         if (subscriptions == null) {
561             return null;
562         }
563 
564         List<SubscriptionInfo> effectiveSubscriptions = subscriptions.stream()
565                 .filter(subInfo -> !subInfo.isOpportunistic())
566                 .collect(Collectors.toList());
567         if (effectiveSubscriptions.size() < 2) {
568             return null;
569         }
570 
571         List<String> componentNames = effectiveSubscriptions.stream()
572                 .map(subInfo -> configManager.getConfigForSubId(subInfo.getSubscriptionId()))
573                 .filter(bundle -> (bundle != null))
574                 .map(bundle -> bundle.getString(
575                         CarrierConfigManager.KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING))
576                 .filter(componentName -> !TextUtils.isEmpty(componentName))
577                 .collect(Collectors.toList());
578 
579         String componentNameOfMenu = null;
580         for (String componentName : componentNames) {
581             if (componentNameOfMenu == null) {
582                 componentNameOfMenu = componentName;
583             }
584             else if (!componentNameOfMenu.equals(componentName)) {
585                 Log.w(LOG_TAG, "ignore smart forward component: " + componentName);
586             }
587         }
588 
589         if (TextUtils.isEmpty(componentNameOfMenu)) {
590             return null;
591         }
592 
593         Intent intent = new Intent(Intent.ACTION_MAIN);
594         intent.setComponent(ComponentName.unflattenFromString(componentNameOfMenu));
595 
596         PackageManager pm = getActivity().getPackageManager();
597         List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
598         if (resolutions.size() == 0) {
599             intent = null;  // set no intent if no package can handle it.
600         }
601 
602         return intent;
603     }
604 }
605