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