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 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 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 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 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 237 public void onAccountSelectionDialogShow(AccountSelectionPreference pref) { 238 if (pref == mDefaultOutgoingAccount) { 239 updateDefaultOutgoingAccountsModel(); 240 } 241 } 242 243 @Override 244 public void onAccountChanged(AccountSelectionPreference pref) {} 245 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 */ 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 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 390 private boolean shouldShowConnectionServiceList(List<PhoneAccountHandle> allNonSimAccounts) { 391 return mTelephonyManager.isMultiSimEnabled() || allNonSimAccounts.size() > 0; 392 } 393 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 419 private String nullToEmpty(String str) { 420 return str == null ? "" : str; 421 } 422 423 private PhoneAccountHandle getEmergencyPhoneAccount() { 424 return PhoneUtils.makePstnPhoneAccountHandleWithPrefix( 425 (Phone) null, "" /* prefix */, true /* isEmergency */); 426 } 427 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 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 */ 470 private boolean isPrimaryUser() { 471 final UserManager userManager = (UserManager) getActivity() 472 .getSystemService(Context.USER_SERVICE); 473 return userManager.isPrimaryUser(); 474 } 475 } 476