1 /* 2 * Copyright (C) 2014 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.server.telecom; 18 19 import android.Manifest; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.content.pm.ServiceInfo; 26 import android.content.pm.UserInfo; 27 import android.graphics.Bitmap; 28 import android.graphics.BitmapFactory; 29 import android.graphics.drawable.Icon; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.os.AsyncTask; 33 import android.os.PersistableBundle; 34 import android.os.Process; 35 import android.os.UserHandle; 36 import android.os.UserManager; 37 import android.provider.Settings; 38 import android.telecom.CallAudioState; 39 import android.telecom.ConnectionService; 40 import android.telecom.DefaultDialerManager; 41 import android.telecom.PhoneAccount; 42 import android.telecom.PhoneAccountHandle; 43 import android.telephony.CarrierConfigManager; 44 import android.telephony.PhoneNumberUtils; 45 import android.telephony.SubscriptionManager; 46 import android.telephony.TelephonyManager; 47 import android.text.TextUtils; 48 import android.util.AtomicFile; 49 import android.util.Base64; 50 import android.util.Xml; 51 52 // TODO: Needed for move to system service: import com.android.internal.R; 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.internal.util.FastXmlSerializer; 55 import com.android.internal.util.IndentingPrintWriter; 56 import com.android.internal.util.XmlUtils; 57 58 import org.xmlpull.v1.XmlPullParser; 59 import org.xmlpull.v1.XmlPullParserException; 60 import org.xmlpull.v1.XmlSerializer; 61 62 import java.io.BufferedInputStream; 63 import java.io.ByteArrayInputStream; 64 import java.io.ByteArrayOutputStream; 65 import java.io.File; 66 import java.io.FileNotFoundException; 67 import java.io.FileOutputStream; 68 import java.io.IOException; 69 import java.io.InputStream; 70 import java.lang.Integer; 71 import java.lang.SecurityException; 72 import java.lang.String; 73 import java.util.ArrayList; 74 import java.util.Collections; 75 import java.util.Iterator; 76 import java.util.List; 77 import java.util.Map; 78 import java.util.Objects; 79 import java.util.Optional; 80 import java.util.concurrent.ConcurrentHashMap; 81 import java.util.concurrent.CopyOnWriteArrayList; 82 import java.util.stream.Collector; 83 import java.util.stream.Collectors; 84 import java.util.stream.Stream; 85 86 /** 87 * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim 88 * delegate for all the account handling methods on {@link android.telecom.TelecomManager} as 89 * implemented in {@link TelecomServiceImpl}, with the notable exception that 90 * {@link TelecomServiceImpl} is responsible for security checking to make sure that the caller has 91 * proper authority over the {@code ComponentName}s they are declaring in their 92 * {@code PhoneAccountHandle}s. 93 * 94 * 95 * -- About Users and Phone Accounts -- 96 * 97 * We store all phone accounts for all users in a single place, which means that there are three 98 * users that we have to deal with in code: 99 * 1) The Android User that is currently active on the device. 100 * 2) The user which owns/registers the phone account. 101 * 3) The user running the app that is requesting the phone account information. 102 * 103 * For example, I have a device with 2 users, primary (A) and secondary (B), and the secondary user 104 * has a work profile running as another user (B2). Each user/profile only have the visibility of 105 * phone accounts owned by them. Lets say, user B (settings) is requesting a list of phone accounts, 106 * and the list only contains phone accounts owned by user B and accounts with 107 * {@link PhoneAccount#CAPABILITY_MULTI_USER}. 108 * 109 * In practice, (2) is stored with the phone account handle and is part of the handle's ID. (1) is 110 * saved in {@link #mCurrentUserHandle} and (3) we get from Binder.getCallingUser(). We check these 111 * users for visibility before returning any phone accounts. 112 */ 113 public class PhoneAccountRegistrar { 114 115 public static final PhoneAccountHandle NO_ACCOUNT_SELECTED = 116 new PhoneAccountHandle(new ComponentName("null", "null"), "NO_ACCOUNT_SELECTED"); 117 118 public abstract static class Listener { onAccountsChanged(PhoneAccountRegistrar registrar)119 public void onAccountsChanged(PhoneAccountRegistrar registrar) {} onDefaultOutgoingChanged(PhoneAccountRegistrar registrar)120 public void onDefaultOutgoingChanged(PhoneAccountRegistrar registrar) {} onSimCallManagerChanged(PhoneAccountRegistrar registrar)121 public void onSimCallManagerChanged(PhoneAccountRegistrar registrar) {} 122 } 123 124 private static final String FILE_NAME = "phone-account-registrar-state.xml"; 125 @VisibleForTesting 126 public static final int EXPECTED_STATE_VERSION = 9; 127 128 /** Keep in sync with the same in SipSettings.java */ 129 private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES"; 130 131 private final List<Listener> mListeners = new CopyOnWriteArrayList<>(); 132 private final AtomicFile mAtomicFile; 133 private final Context mContext; 134 private final UserManager mUserManager; 135 private final SubscriptionManager mSubscriptionManager; 136 private State mState; 137 private UserHandle mCurrentUserHandle; 138 private interface PhoneAccountRegistrarWriteLock {} 139 private final PhoneAccountRegistrarWriteLock mWriteLock = 140 new PhoneAccountRegistrarWriteLock() {}; 141 142 @VisibleForTesting PhoneAccountRegistrar(Context context)143 public PhoneAccountRegistrar(Context context) { 144 this(context, FILE_NAME); 145 } 146 147 @VisibleForTesting PhoneAccountRegistrar(Context context, String fileName)148 public PhoneAccountRegistrar(Context context, String fileName) { 149 // TODO: This file path is subject to change -- it is storing the phone account registry 150 // state file in the path /data/system/users/0/, which is likely not correct in a 151 // multi-user setting. 152 /** UNCOMMENT_FOR_MOVE_TO_SYSTEM_SERVICE 153 String filePath = Environment.getUserSystemDirectory(UserHandle.myUserId()). 154 getAbsolutePath(); 155 mAtomicFile = new AtomicFile(new File(filePath, fileName)); 156 UNCOMMENT_FOR_MOVE_TO_SYSTEM_SERVICE */ 157 mAtomicFile = new AtomicFile(new File(context.getFilesDir(), fileName)); 158 159 mState = new State(); 160 mContext = context; 161 mUserManager = UserManager.get(context); 162 mSubscriptionManager = SubscriptionManager.from(mContext); 163 mCurrentUserHandle = Process.myUserHandle(); 164 read(); 165 } 166 167 /** 168 * Retrieves the subscription id for a given phone account if it exists. Subscription ids 169 * apply only to PSTN/SIM card phone accounts so all other accounts should not have a 170 * subscription id. 171 * @param accountHandle The handle for the phone account for which to retrieve the 172 * subscription id. 173 * @return The value of the subscription id or -1 if it does not exist or is not valid. 174 */ getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle)175 public int getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) { 176 PhoneAccount account = getPhoneAccountUnchecked(accountHandle); 177 178 if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { 179 TelephonyManager tm = 180 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 181 return tm.getSubIdForPhoneAccount(account); 182 } 183 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 184 } 185 186 /** 187 * Retrieves the default outgoing phone account supporting the specified uriScheme. Note that if 188 * {@link #mCurrentUserHandle} does not have visibility into the current default, {@code null} 189 * will be returned. 190 * 191 * @param uriScheme The URI scheme for the outgoing call. 192 * @return The {@link PhoneAccountHandle} to use. 193 */ getOutgoingPhoneAccountForScheme(String uriScheme, UserHandle userHandle)194 public PhoneAccountHandle getOutgoingPhoneAccountForScheme(String uriScheme, 195 UserHandle userHandle) { 196 final PhoneAccountHandle userSelected = getUserSelectedOutgoingPhoneAccount(userHandle); 197 198 if (userSelected != null) { 199 // If there is a default PhoneAccount, ensure it supports calls to handles with the 200 // specified uriScheme. 201 final PhoneAccount userSelectedAccount = getPhoneAccountUnchecked(userSelected); 202 if (userSelectedAccount.supportsUriScheme(uriScheme)) { 203 return userSelected; 204 } 205 } 206 207 List<PhoneAccountHandle> outgoing = getCallCapablePhoneAccounts(uriScheme, false, 208 userHandle); 209 switch (outgoing.size()) { 210 case 0: 211 // There are no accounts, so there can be no default 212 return null; 213 case 1: 214 // There is only one account, which is by definition the default. 215 return outgoing.get(0); 216 default: 217 // There are multiple accounts with no selected default 218 return null; 219 } 220 } 221 getOutgoingPhoneAccountForSchemeOfCurrentUser(String uriScheme)222 public PhoneAccountHandle getOutgoingPhoneAccountForSchemeOfCurrentUser(String uriScheme) { 223 return getOutgoingPhoneAccountForScheme(uriScheme, mCurrentUserHandle); 224 } 225 226 /** 227 * @return The user-selected outgoing {@link PhoneAccount}, or null if it hasn't been set (or 228 * if it was set by another user). 229 */ 230 @VisibleForTesting getUserSelectedOutgoingPhoneAccount(UserHandle userHandle)231 public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount(UserHandle userHandle) { 232 if (userHandle == null) { 233 return null; 234 } 235 DefaultPhoneAccountHandle defaultPhoneAccountHandle = mState.defaultOutgoingAccountHandles 236 .get(userHandle); 237 if (defaultPhoneAccountHandle == null) { 238 return null; 239 } 240 // Make sure the account is still registered and owned by the user. 241 PhoneAccount account = getPhoneAccount(defaultPhoneAccountHandle.phoneAccountHandle, 242 userHandle); 243 244 if (account != null) { 245 return defaultPhoneAccountHandle.phoneAccountHandle; 246 } 247 return null; 248 } 249 250 /** 251 * @return The {@link DefaultPhoneAccountHandle} containing the user-selected default calling 252 * account and group Id for the {@link UserHandle} specified. 253 */ getUserSelectedDefaultPhoneAccount(UserHandle userHandle)254 private DefaultPhoneAccountHandle getUserSelectedDefaultPhoneAccount(UserHandle userHandle) { 255 if (userHandle == null) { 256 return null; 257 } 258 DefaultPhoneAccountHandle defaultPhoneAccountHandle = mState.defaultOutgoingAccountHandles 259 .get(userHandle); 260 if (defaultPhoneAccountHandle == null) { 261 return null; 262 } 263 264 return defaultPhoneAccountHandle; 265 } 266 267 /** 268 * @return The currently registered PhoneAccount in Telecom that has the same group Id. 269 */ getPhoneAccountByGroupId(String groupId, ComponentName groupComponentName, UserHandle userHandle, PhoneAccountHandle excludePhoneAccountHandle)270 private PhoneAccount getPhoneAccountByGroupId(String groupId, ComponentName groupComponentName, 271 UserHandle userHandle, PhoneAccountHandle excludePhoneAccountHandle) { 272 if (groupId == null || groupId.isEmpty() || userHandle == null) { 273 return null; 274 } 275 // Get the PhoneAccount with the same group Id (and same ComponentName) that is not the 276 // newAccount that was just added 277 List<PhoneAccount> accounts = getAllPhoneAccounts(userHandle).stream() 278 .filter(account -> groupId.equals(account.getGroupId()) && 279 !account.getAccountHandle().equals(excludePhoneAccountHandle) && 280 Objects.equals(account.getAccountHandle().getComponentName(), 281 groupComponentName)) 282 .collect(Collectors.toList()); 283 // There should be one or no PhoneAccounts with the same group Id 284 if (accounts.size() > 1) { 285 Log.w(this, "Found multiple PhoneAccounts registered to the same Group Id!"); 286 } 287 return accounts.isEmpty() ? null : accounts.get(0); 288 } 289 290 /** 291 * Sets the phone account with which to place all calls by default. Set by the user 292 * within phone settings. 293 */ setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle, UserHandle userHandle)294 public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle, 295 UserHandle userHandle) { 296 if (userHandle == null) { 297 return; 298 } 299 if (accountHandle == null) { 300 // Asking to clear the default outgoing is a valid request 301 mState.defaultOutgoingAccountHandles.remove(userHandle); 302 } else { 303 PhoneAccount account = getPhoneAccount(accountHandle, userHandle); 304 if (account == null) { 305 Log.w(this, "Trying to set nonexistent default outgoing %s", 306 accountHandle); 307 return; 308 } 309 310 if (!account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)) { 311 Log.w(this, "Trying to set non-call-provider default outgoing %s", 312 accountHandle); 313 return; 314 } 315 316 if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { 317 // If the account selected is a SIM account, propagate down to the subscription 318 // record. 319 int subId = getSubscriptionIdForPhoneAccount(accountHandle); 320 mSubscriptionManager.setDefaultVoiceSubId(subId); 321 } 322 323 mState.defaultOutgoingAccountHandles 324 .put(userHandle, new DefaultPhoneAccountHandle(userHandle, accountHandle, 325 account.getGroupId())); 326 } 327 328 write(); 329 fireDefaultOutgoingChanged(); 330 } 331 isUserSelectedSmsPhoneAccount(PhoneAccountHandle accountHandle)332 boolean isUserSelectedSmsPhoneAccount(PhoneAccountHandle accountHandle) { 333 return getSubscriptionIdForPhoneAccount(accountHandle) == 334 SubscriptionManager.getDefaultSmsSubscriptionId(); 335 } 336 getSystemSimCallManagerComponent()337 public ComponentName getSystemSimCallManagerComponent() { 338 String defaultSimCallManager = null; 339 CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService( 340 Context.CARRIER_CONFIG_SERVICE); 341 PersistableBundle configBundle = configManager.getConfig(); 342 if (configBundle != null) { 343 defaultSimCallManager = configBundle.getString( 344 CarrierConfigManager.KEY_DEFAULT_SIM_CALL_MANAGER_STRING); 345 } 346 return TextUtils.isEmpty(defaultSimCallManager) 347 ? null : ComponentName.unflattenFromString(defaultSimCallManager); 348 } 349 getSimCallManagerOfCurrentUser()350 public PhoneAccountHandle getSimCallManagerOfCurrentUser() { 351 return getSimCallManager(mCurrentUserHandle); 352 } 353 354 /** 355 * Returns the {@link PhoneAccountHandle} corresponding to the currently active SIM Call 356 * Manager. SIM Call Manager returned corresponds to the following priority order: 357 * 1. If a SIM Call Manager {@link PhoneAccount} is registered for the same package as the 358 * default dialer, then that one is returned. 359 * 2. If there is a SIM Call Manager {@link PhoneAccount} registered which matches the 360 * carrier configuration's default, then that one is returned. 361 * 3. Otherwise, we return null. 362 */ getSimCallManager(UserHandle userHandle)363 public PhoneAccountHandle getSimCallManager(UserHandle userHandle) { 364 // Get the default dialer in case it has a connection manager associated with it. 365 String dialerPackage = DefaultDialerManager 366 .getDefaultDialerApplication(mContext, userHandle.getIdentifier()); 367 368 // Check carrier config. 369 ComponentName systemSimCallManagerComponent = getSystemSimCallManagerComponent(); 370 371 PhoneAccountHandle dialerSimCallManager = null; 372 PhoneAccountHandle systemSimCallManager = null; 373 374 if (!TextUtils.isEmpty(dialerPackage) || systemSimCallManagerComponent != null) { 375 // loop through and look for any connection manager in the same package. 376 List<PhoneAccountHandle> allSimCallManagers = getPhoneAccountHandles( 377 PhoneAccount.CAPABILITY_CONNECTION_MANAGER, null, null, 378 true /* includeDisabledAccounts */, userHandle); 379 for (PhoneAccountHandle accountHandle : allSimCallManagers) { 380 ComponentName component = accountHandle.getComponentName(); 381 382 // Store the system connection manager if found 383 if (systemSimCallManager == null 384 && Objects.equals(component, systemSimCallManagerComponent) 385 && !resolveComponent(accountHandle).isEmpty()) { 386 systemSimCallManager = accountHandle; 387 388 // Store the dialer connection manager if found 389 } else if (dialerSimCallManager == null 390 && Objects.equals(component.getPackageName(), dialerPackage) 391 && !resolveComponent(accountHandle).isEmpty()) { 392 dialerSimCallManager = accountHandle; 393 } 394 } 395 } 396 397 PhoneAccountHandle retval = dialerSimCallManager != null ? 398 dialerSimCallManager : systemSimCallManager; 399 400 Log.i(this, "SimCallManager queried, returning: %s", retval); 401 402 return retval; 403 } 404 405 /** 406 * If it is a outgoing call, sim call manager of call-initiating user is returned. 407 * Otherwise, we return the sim call manager of the user associated with the 408 * target phone account. 409 * @return phone account handle of sim call manager based on the ongoing call. 410 */ getSimCallManagerFromCall(Call call)411 public PhoneAccountHandle getSimCallManagerFromCall(Call call) { 412 if (call == null) { 413 return null; 414 } 415 UserHandle userHandle = call.getInitiatingUser(); 416 if (userHandle == null) { 417 userHandle = call.getTargetPhoneAccount().getUserHandle(); 418 } 419 return getSimCallManager(userHandle); 420 } 421 422 /** 423 * Update the current UserHandle to track when users are switched. This will allow the 424 * PhoneAccountRegistar to self-filter the PhoneAccounts to make sure we don't leak anything 425 * across users. 426 * We cannot simply check the calling user because that would always return the primary user for 427 * all invocations originating with the system process. 428 * 429 * @param userHandle The {@link UserHandle}, as delivered by 430 * {@link Intent#ACTION_USER_SWITCHED}. 431 */ setCurrentUserHandle(UserHandle userHandle)432 public void setCurrentUserHandle(UserHandle userHandle) { 433 if (userHandle == null) { 434 Log.d(this, "setCurrentUserHandle, userHandle = null"); 435 userHandle = Process.myUserHandle(); 436 } 437 Log.d(this, "setCurrentUserHandle, %s", userHandle); 438 mCurrentUserHandle = userHandle; 439 } 440 441 /** 442 * @return {@code true} if the phone account was successfully enabled/disabled, {@code false} 443 * otherwise. 444 */ enablePhoneAccount(PhoneAccountHandle accountHandle, boolean isEnabled)445 public boolean enablePhoneAccount(PhoneAccountHandle accountHandle, boolean isEnabled) { 446 PhoneAccount account = getPhoneAccountUnchecked(accountHandle); 447 if (account == null) { 448 Log.w(this, "Could not find account to enable: " + accountHandle); 449 return false; 450 } else if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { 451 // We never change the enabled state of SIM-based accounts. 452 Log.w(this, "Could not change enable state of SIM account: " + accountHandle); 453 return false; 454 } 455 456 if (account.isEnabled() != isEnabled) { 457 account.setIsEnabled(isEnabled); 458 if (!isEnabled) { 459 // If the disabled account is the default, remove it. 460 removeDefaultPhoneAccountHandle(accountHandle); 461 } 462 write(); 463 fireAccountsChanged(); 464 } 465 return true; 466 } 467 removeDefaultPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle)468 private void removeDefaultPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) { 469 Iterator<Map.Entry<UserHandle, DefaultPhoneAccountHandle>> iterator = 470 mState.defaultOutgoingAccountHandles.entrySet().iterator(); 471 while (iterator.hasNext()) { 472 Map.Entry<UserHandle, DefaultPhoneAccountHandle> entry = iterator.next(); 473 if (phoneAccountHandle.equals(entry.getValue().phoneAccountHandle)) { 474 iterator.remove(); 475 } 476 } 477 } 478 isVisibleForUser(PhoneAccount account, UserHandle userHandle, boolean acrossProfiles)479 private boolean isVisibleForUser(PhoneAccount account, UserHandle userHandle, 480 boolean acrossProfiles) { 481 if (account == null) { 482 return false; 483 } 484 485 if (userHandle == null) { 486 Log.w(this, "userHandle is null in isVisibleForUser"); 487 return false; 488 } 489 490 // If this PhoneAccount has CAPABILITY_MULTI_USER, it should be visible to all users and 491 // all profiles. Only Telephony and SIP accounts should have this capability. 492 if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) { 493 return true; 494 } 495 496 UserHandle phoneAccountUserHandle = account.getAccountHandle().getUserHandle(); 497 if (phoneAccountUserHandle == null) { 498 return false; 499 } 500 501 if (mCurrentUserHandle == null) { 502 // In case we need to have emergency phone calls from the lock screen. 503 Log.d(this, "Current user is null; assuming true"); 504 return true; 505 } 506 507 if (acrossProfiles) { 508 return UserManager.get(mContext).isSameProfileGroup(userHandle.getIdentifier(), 509 phoneAccountUserHandle.getIdentifier()); 510 } else { 511 return phoneAccountUserHandle.equals(userHandle); 512 } 513 } 514 resolveComponent(PhoneAccountHandle phoneAccountHandle)515 private List<ResolveInfo> resolveComponent(PhoneAccountHandle phoneAccountHandle) { 516 return resolveComponent(phoneAccountHandle.getComponentName(), 517 phoneAccountHandle.getUserHandle()); 518 } 519 resolveComponent(ComponentName componentName, UserHandle userHandle)520 private List<ResolveInfo> resolveComponent(ComponentName componentName, 521 UserHandle userHandle) { 522 PackageManager pm = mContext.getPackageManager(); 523 Intent intent = new Intent(ConnectionService.SERVICE_INTERFACE); 524 intent.setComponent(componentName); 525 try { 526 if (userHandle != null) { 527 return pm.queryIntentServicesAsUser(intent, 0, userHandle.getIdentifier()); 528 } else { 529 return pm.queryIntentServices(intent, 0); 530 } 531 } catch (SecurityException e) { 532 Log.e(this, e, "%s is not visible for the calling user", componentName); 533 return Collections.EMPTY_LIST; 534 } 535 } 536 537 /** 538 * Retrieves a list of all {@link PhoneAccountHandle}s registered. 539 * Only returns accounts which are enabled. 540 * 541 * @return The list of {@link PhoneAccountHandle}s. 542 */ getAllPhoneAccountHandles(UserHandle userHandle)543 public List<PhoneAccountHandle> getAllPhoneAccountHandles(UserHandle userHandle) { 544 return getPhoneAccountHandles(0, null, null, false, userHandle); 545 } 546 getAllPhoneAccounts(UserHandle userHandle)547 public List<PhoneAccount> getAllPhoneAccounts(UserHandle userHandle) { 548 return getPhoneAccounts(0, null, null, false, userHandle); 549 } 550 getAllPhoneAccountsOfCurrentUser()551 public List<PhoneAccount> getAllPhoneAccountsOfCurrentUser() { 552 return getAllPhoneAccounts(mCurrentUserHandle); 553 } 554 555 /** 556 * Retrieves a list of all phone account call provider phone accounts supporting the 557 * specified URI scheme. 558 * 559 * @param uriScheme The URI scheme. 560 * @return The phone account handles. 561 */ getCallCapablePhoneAccounts( String uriScheme, boolean includeDisabledAccounts, UserHandle userHandle)562 public List<PhoneAccountHandle> getCallCapablePhoneAccounts( 563 String uriScheme, boolean includeDisabledAccounts, UserHandle userHandle) { 564 return getPhoneAccountHandles( 565 PhoneAccount.CAPABILITY_CALL_PROVIDER, 566 PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY /*excludedCapabilities*/, 567 uriScheme, null, includeDisabledAccounts, userHandle); 568 } 569 getCallCapablePhoneAccountsOfCurrentUser( String uriScheme, boolean includeDisabledAccounts)570 public List<PhoneAccountHandle> getCallCapablePhoneAccountsOfCurrentUser( 571 String uriScheme, boolean includeDisabledAccounts) { 572 return getCallCapablePhoneAccounts(uriScheme, includeDisabledAccounts, mCurrentUserHandle); 573 } 574 575 /** 576 * Retrieves a list of all the SIM-based phone accounts. 577 */ getSimPhoneAccounts(UserHandle userHandle)578 public List<PhoneAccountHandle> getSimPhoneAccounts(UserHandle userHandle) { 579 return getPhoneAccountHandles( 580 PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION, 581 null, null, false, userHandle); 582 } 583 getSimPhoneAccountsOfCurrentUser()584 public List<PhoneAccountHandle> getSimPhoneAccountsOfCurrentUser() { 585 return getSimPhoneAccounts(mCurrentUserHandle); 586 } 587 588 /** 589 * Retrieves a list of all phone accounts registered by a specified package. 590 * 591 * @param packageName The name of the package that registered the phone accounts. 592 * @return The phone account handles. 593 */ getPhoneAccountsForPackage(String packageName, UserHandle userHandle)594 public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName, 595 UserHandle userHandle) { 596 return getPhoneAccountHandles(0, null, packageName, false, userHandle); 597 } 598 599 // TODO: Should we implement an artificial limit for # of accounts associated with a single 600 // ComponentName? registerPhoneAccount(PhoneAccount account)601 public void registerPhoneAccount(PhoneAccount account) { 602 // Enforce the requirement that a connection service for a phone account has the correct 603 // permission. 604 if (!phoneAccountRequiresBindPermission(account.getAccountHandle())) { 605 Log.w(this, 606 "Phone account %s does not have BIND_TELECOM_CONNECTION_SERVICE permission.", 607 account.getAccountHandle()); 608 throw new SecurityException("PhoneAccount connection service requires " 609 + "BIND_TELECOM_CONNECTION_SERVICE permission."); 610 } 611 612 addOrReplacePhoneAccount(account); 613 } 614 615 /** 616 * Adds a {@code PhoneAccount}, replacing an existing one if found. 617 * 618 * @param account The {@code PhoneAccount} to add or replace. 619 */ addOrReplacePhoneAccount(PhoneAccount account)620 private void addOrReplacePhoneAccount(PhoneAccount account) { 621 Log.d(this, "addOrReplacePhoneAccount(%s -> %s)", 622 account.getAccountHandle(), account); 623 624 // Start _enabled_ property as false. 625 // !!! IMPORTANT !!! It is important that we do not read the enabled state that the 626 // source app provides or else an third party app could enable itself. 627 boolean isEnabled = false; 628 629 PhoneAccount oldAccount = getPhoneAccountUnchecked(account.getAccountHandle()); 630 if (oldAccount != null) { 631 mState.accounts.remove(oldAccount); 632 isEnabled = oldAccount.isEnabled(); 633 Log.i(this, getAccountDiffString(account, oldAccount)); 634 } else { 635 Log.i(this, "New phone account registered: " + account); 636 } 637 638 mState.accounts.add(account); 639 // Set defaults and replace based on the group Id. 640 maybeReplaceOldAccount(account); 641 // Reset enabled state to whatever the value was if the account was already registered, 642 // or _true_ if this is a SIM-based account. All SIM-based accounts are always enabled. 643 account.setIsEnabled( 644 isEnabled || account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)); 645 646 write(); 647 fireAccountsChanged(); 648 } 649 unregisterPhoneAccount(PhoneAccountHandle accountHandle)650 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) { 651 PhoneAccount account = getPhoneAccountUnchecked(accountHandle); 652 if (account != null) { 653 if (mState.accounts.remove(account)) { 654 write(); 655 fireAccountsChanged(); 656 } 657 } 658 } 659 660 /** 661 * Un-registers all phone accounts associated with a specified package. 662 * 663 * @param packageName The package for which phone accounts will be removed. 664 * @param userHandle The {@link UserHandle} the package is running under. 665 */ clearAccounts(String packageName, UserHandle userHandle)666 public void clearAccounts(String packageName, UserHandle userHandle) { 667 boolean accountsRemoved = false; 668 Iterator<PhoneAccount> it = mState.accounts.iterator(); 669 while (it.hasNext()) { 670 PhoneAccount phoneAccount = it.next(); 671 PhoneAccountHandle handle = phoneAccount.getAccountHandle(); 672 if (Objects.equals(packageName, handle.getComponentName().getPackageName()) 673 && Objects.equals(userHandle, handle.getUserHandle())) { 674 Log.i(this, "Removing phone account " + phoneAccount.getLabel()); 675 mState.accounts.remove(phoneAccount); 676 accountsRemoved = true; 677 } 678 } 679 680 if (accountsRemoved) { 681 write(); 682 fireAccountsChanged(); 683 } 684 } 685 isVoiceMailNumber(PhoneAccountHandle accountHandle, String number)686 public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) { 687 int subId = getSubscriptionIdForPhoneAccount(accountHandle); 688 return PhoneNumberUtils.isVoiceMailNumber(mContext, subId, number); 689 } 690 addListener(Listener l)691 public void addListener(Listener l) { 692 mListeners.add(l); 693 } 694 removeListener(Listener l)695 public void removeListener(Listener l) { 696 if (l != null) { 697 mListeners.remove(l); 698 } 699 } 700 fireAccountsChanged()701 private void fireAccountsChanged() { 702 for (Listener l : mListeners) { 703 l.onAccountsChanged(this); 704 } 705 } 706 fireDefaultOutgoingChanged()707 private void fireDefaultOutgoingChanged() { 708 for (Listener l : mListeners) { 709 l.onDefaultOutgoingChanged(this); 710 } 711 } 712 getAccountDiffString(PhoneAccount account1, PhoneAccount account2)713 private String getAccountDiffString(PhoneAccount account1, PhoneAccount account2) { 714 if (account1 == null || account2 == null) { 715 return "Diff: " + account1 + ", " + account2; 716 } 717 718 StringBuffer sb = new StringBuffer(); 719 sb.append("[").append(account1.getAccountHandle()); 720 appendDiff(sb, "addr", Log.piiHandle(account1.getAddress()), 721 Log.piiHandle(account2.getAddress())); 722 appendDiff(sb, "cap", account1.getCapabilities(), account2.getCapabilities()); 723 appendDiff(sb, "hl", account1.getHighlightColor(), account2.getHighlightColor()); 724 appendDiff(sb, "icon", account1.getIcon(), account2.getIcon()); 725 appendDiff(sb, "lbl", account1.getLabel(), account2.getLabel()); 726 appendDiff(sb, "desc", account1.getShortDescription(), account2.getShortDescription()); 727 appendDiff(sb, "subAddr", Log.piiHandle(account1.getSubscriptionAddress()), 728 Log.piiHandle(account2.getSubscriptionAddress())); 729 appendDiff(sb, "uris", account1.getSupportedUriSchemes(), 730 account2.getSupportedUriSchemes()); 731 sb.append("]"); 732 return sb.toString(); 733 } 734 appendDiff(StringBuffer sb, String attrName, Object obj1, Object obj2)735 private void appendDiff(StringBuffer sb, String attrName, Object obj1, Object obj2) { 736 if (!Objects.equals(obj1, obj2)) { 737 sb.append("(") 738 .append(attrName) 739 .append(": ") 740 .append(obj1) 741 .append(" -> ") 742 .append(obj2) 743 .append(")"); 744 } 745 } 746 maybeReplaceOldAccount(PhoneAccount newAccount)747 private void maybeReplaceOldAccount(PhoneAccount newAccount) { 748 UserHandle newAccountUserHandle = newAccount.getAccountHandle().getUserHandle(); 749 DefaultPhoneAccountHandle defaultHandle = 750 getUserSelectedDefaultPhoneAccount(newAccountUserHandle); 751 if (defaultHandle == null || defaultHandle.groupId.isEmpty()) { 752 Log.v(this, "maybeReplaceOldAccount: Not replacing PhoneAccount, no group Id or " + 753 "default."); 754 return; 755 } 756 if (!defaultHandle.groupId.equals(newAccount.getGroupId())) { 757 Log.v(this, "maybeReplaceOldAccount: group Ids are not equal."); 758 return; 759 } 760 if (Objects.equals(newAccount.getAccountHandle().getComponentName(), 761 defaultHandle.phoneAccountHandle.getComponentName())) { 762 // Move default calling account over to new user, since the ComponentNames and Group Ids 763 // are the same. 764 setUserSelectedOutgoingPhoneAccount(newAccount.getAccountHandle(), 765 newAccountUserHandle); 766 } else { 767 Log.v(this, "maybeReplaceOldAccount: group Ids are equal, but ComponentName is not" + 768 " the same as the default. Not replacing default PhoneAccount."); 769 } 770 PhoneAccount replacementAccount = getPhoneAccountByGroupId(newAccount.getGroupId(), 771 newAccount.getAccountHandle().getComponentName(), newAccountUserHandle, 772 newAccount.getAccountHandle()); 773 if (replacementAccount != null) { 774 // Unregister the old PhoneAccount. 775 Log.v(this, "maybeReplaceOldAccount: Unregistering old PhoneAccount: " + 776 replacementAccount.getAccountHandle()); 777 unregisterPhoneAccount(replacementAccount.getAccountHandle()); 778 } 779 } 780 781 /** 782 * Determines if the connection service specified by a {@link PhoneAccountHandle} requires the 783 * {@link Manifest.permission#BIND_TELECOM_CONNECTION_SERVICE} permission. 784 * 785 * @param phoneAccountHandle The phone account to check. 786 * @return {@code True} if the phone account has permission. 787 */ phoneAccountRequiresBindPermission(PhoneAccountHandle phoneAccountHandle)788 public boolean phoneAccountRequiresBindPermission(PhoneAccountHandle phoneAccountHandle) { 789 List<ResolveInfo> resolveInfos = resolveComponent(phoneAccountHandle); 790 if (resolveInfos.isEmpty()) { 791 Log.w(this, "phoneAccount %s not found", phoneAccountHandle.getComponentName()); 792 return false; 793 } 794 for (ResolveInfo resolveInfo : resolveInfos) { 795 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 796 if (serviceInfo == null) { 797 return false; 798 } 799 800 if (!Manifest.permission.BIND_CONNECTION_SERVICE.equals(serviceInfo.permission) && 801 !Manifest.permission.BIND_TELECOM_CONNECTION_SERVICE.equals( 802 serviceInfo.permission)) { 803 // The ConnectionService must require either the deprecated BIND_CONNECTION_SERVICE, 804 // or the public BIND_TELECOM_CONNECTION_SERVICE permissions, both of which are 805 // system/signature only. 806 return false; 807 } 808 } 809 return true; 810 } 811 812 // 813 // Methods for retrieving PhoneAccounts and PhoneAccountHandles 814 // 815 816 /** 817 * Returns the PhoneAccount for the specified handle. Does no user checking. 818 * 819 * @param handle 820 * @return The corresponding phone account if one exists. 821 */ getPhoneAccountUnchecked(PhoneAccountHandle handle)822 public PhoneAccount getPhoneAccountUnchecked(PhoneAccountHandle handle) { 823 for (PhoneAccount m : mState.accounts) { 824 if (Objects.equals(handle, m.getAccountHandle())) { 825 return m; 826 } 827 } 828 return null; 829 } 830 831 /** 832 * Like getPhoneAccount, but checks to see if the current user is allowed to see the phone 833 * account before returning it. The current user is the active user on the actual android 834 * device. 835 */ getPhoneAccount(PhoneAccountHandle handle, UserHandle userHandle)836 public PhoneAccount getPhoneAccount(PhoneAccountHandle handle, UserHandle userHandle) { 837 return getPhoneAccount(handle, userHandle, /* acrossProfiles */ false); 838 } 839 getPhoneAccount(PhoneAccountHandle handle, UserHandle userHandle, boolean acrossProfiles)840 public PhoneAccount getPhoneAccount(PhoneAccountHandle handle, 841 UserHandle userHandle, boolean acrossProfiles) { 842 PhoneAccount account = getPhoneAccountUnchecked(handle); 843 if (account != null && (isVisibleForUser(account, userHandle, acrossProfiles))) { 844 return account; 845 } 846 return null; 847 } 848 getPhoneAccountOfCurrentUser(PhoneAccountHandle handle)849 public PhoneAccount getPhoneAccountOfCurrentUser(PhoneAccountHandle handle) { 850 return getPhoneAccount(handle, mCurrentUserHandle); 851 } 852 getPhoneAccountHandles( int capabilities, String uriScheme, String packageName, boolean includeDisabledAccounts, UserHandle userHandle)853 private List<PhoneAccountHandle> getPhoneAccountHandles( 854 int capabilities, 855 String uriScheme, 856 String packageName, 857 boolean includeDisabledAccounts, 858 UserHandle userHandle) { 859 return getPhoneAccountHandles(capabilities, 0 /*excludedCapabilities*/, uriScheme, 860 packageName, includeDisabledAccounts, userHandle); 861 } 862 863 /** 864 * Returns a list of phone account handles with the specified capabilities, uri scheme, 865 * and package name. 866 */ getPhoneAccountHandles( int capabilities, int excludedCapabilities, String uriScheme, String packageName, boolean includeDisabledAccounts, UserHandle userHandle)867 private List<PhoneAccountHandle> getPhoneAccountHandles( 868 int capabilities, 869 int excludedCapabilities, 870 String uriScheme, 871 String packageName, 872 boolean includeDisabledAccounts, 873 UserHandle userHandle) { 874 List<PhoneAccountHandle> handles = new ArrayList<>(); 875 876 for (PhoneAccount account : getPhoneAccounts( 877 capabilities, excludedCapabilities, uriScheme, packageName, 878 includeDisabledAccounts, userHandle)) { 879 handles.add(account.getAccountHandle()); 880 } 881 return handles; 882 } 883 getPhoneAccounts( int capabilities, String uriScheme, String packageName, boolean includeDisabledAccounts, UserHandle userHandle)884 private List<PhoneAccount> getPhoneAccounts( 885 int capabilities, 886 String uriScheme, 887 String packageName, 888 boolean includeDisabledAccounts, 889 UserHandle userHandle) { 890 return getPhoneAccounts(capabilities, 0 /*excludedCapabilities*/, uriScheme, packageName, 891 includeDisabledAccounts, userHandle); 892 } 893 894 /** 895 * Returns a list of phone account handles with the specified flag, supporting the specified 896 * URI scheme, within the specified package name. 897 * 898 * @param capabilities Capabilities which the {@code PhoneAccount} must have. Ignored if 0. 899 * @param excludedCapabilities Capabilities which the {@code PhoneAccount} must not have. 900 * Ignored if 0. 901 * @param uriScheme URI schemes the PhoneAccount must handle. {@code null} bypasses the 902 * URI scheme check. 903 * @param packageName Package name of the PhoneAccount. {@code null} bypasses packageName check. 904 */ getPhoneAccounts( int capabilities, int excludedCapabilities, String uriScheme, String packageName, boolean includeDisabledAccounts, UserHandle userHandle)905 private List<PhoneAccount> getPhoneAccounts( 906 int capabilities, 907 int excludedCapabilities, 908 String uriScheme, 909 String packageName, 910 boolean includeDisabledAccounts, 911 UserHandle userHandle) { 912 List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size()); 913 for (PhoneAccount m : mState.accounts) { 914 if (!(m.isEnabled() || includeDisabledAccounts)) { 915 // Do not include disabled accounts. 916 continue; 917 } 918 919 if ((m.getCapabilities() & excludedCapabilities) != 0) { 920 // If an excluded capability is present, skip. 921 continue; 922 } 923 924 if (capabilities != 0 && !m.hasCapabilities(capabilities)) { 925 // Account doesn't have the right capabilities; skip this one. 926 continue; 927 } 928 if (uriScheme != null && !m.supportsUriScheme(uriScheme)) { 929 // Account doesn't support this URI scheme; skip this one. 930 continue; 931 } 932 PhoneAccountHandle handle = m.getAccountHandle(); 933 934 if (resolveComponent(handle).isEmpty()) { 935 // This component cannot be resolved anymore; skip this one. 936 continue; 937 } 938 if (packageName != null && 939 !packageName.equals(handle.getComponentName().getPackageName())) { 940 // Not the right package name; skip this one. 941 continue; 942 } 943 if (!isVisibleForUser(m, userHandle, false)) { 944 // Account is not visible for the current user; skip this one. 945 continue; 946 } 947 accounts.add(m); 948 } 949 return accounts; 950 } 951 952 // 953 // State Implementation for PhoneAccountRegistrar 954 // 955 956 /** 957 * The state of this {@code PhoneAccountRegistrar}. 958 */ 959 @VisibleForTesting 960 public static class State { 961 /** 962 * Store the default phone account handle of users. If no record of a user can be found in 963 * the map, it means that no default phone account handle is set in that user. 964 */ 965 public final Map<UserHandle, DefaultPhoneAccountHandle> defaultOutgoingAccountHandles 966 = new ConcurrentHashMap<>(); 967 968 /** 969 * The complete list of {@code PhoneAccount}s known to the Telecom subsystem. 970 */ 971 public final List<PhoneAccount> accounts = new CopyOnWriteArrayList<>(); 972 973 /** 974 * The version number of the State data. 975 */ 976 public int versionNumber; 977 } 978 979 /** 980 * The default {@link PhoneAccountHandle} of a user. 981 */ 982 public static class DefaultPhoneAccountHandle { 983 984 public final UserHandle userHandle; 985 986 public final PhoneAccountHandle phoneAccountHandle; 987 988 public final String groupId; 989 DefaultPhoneAccountHandle(UserHandle userHandle, PhoneAccountHandle phoneAccountHandle, String groupId)990 public DefaultPhoneAccountHandle(UserHandle userHandle, 991 PhoneAccountHandle phoneAccountHandle, String groupId) { 992 this.userHandle = userHandle; 993 this.phoneAccountHandle = phoneAccountHandle; 994 this.groupId = groupId; 995 } 996 } 997 998 /** 999 * Dumps the state of the {@link CallsManager}. 1000 * 1001 * @param pw The {@code IndentingPrintWriter} to write the state to. 1002 */ dump(IndentingPrintWriter pw)1003 public void dump(IndentingPrintWriter pw) { 1004 if (mState != null) { 1005 pw.println("xmlVersion: " + mState.versionNumber); 1006 DefaultPhoneAccountHandle defaultPhoneAccountHandle 1007 = mState.defaultOutgoingAccountHandles.get(Process.myUserHandle()); 1008 pw.println("defaultOutgoing: " + (defaultPhoneAccountHandle == null ? "none" : 1009 defaultPhoneAccountHandle.phoneAccountHandle)); 1010 pw.println("simCallManager: " + getSimCallManager(mCurrentUserHandle)); 1011 pw.println("phoneAccounts:"); 1012 pw.increaseIndent(); 1013 for (PhoneAccount phoneAccount : mState.accounts) { 1014 pw.println(phoneAccount); 1015 } 1016 pw.decreaseIndent(); 1017 } 1018 } 1019 1020 //////////////////////////////////////////////////////////////////////////////////////////////// 1021 // 1022 // State management 1023 // 1024 1025 private class AsyncXmlWriter extends AsyncTask<ByteArrayOutputStream, Void, Void> { 1026 @Override doInBackground(ByteArrayOutputStream... args)1027 public Void doInBackground(ByteArrayOutputStream... args) { 1028 final ByteArrayOutputStream buffer = args[0]; 1029 FileOutputStream fileOutput = null; 1030 try { 1031 synchronized (mWriteLock) { 1032 fileOutput = mAtomicFile.startWrite(); 1033 buffer.writeTo(fileOutput); 1034 mAtomicFile.finishWrite(fileOutput); 1035 } 1036 } catch (IOException e) { 1037 Log.e(this, e, "Writing state to XML file"); 1038 mAtomicFile.failWrite(fileOutput); 1039 } 1040 return null; 1041 } 1042 } 1043 write()1044 private void write() { 1045 try { 1046 ByteArrayOutputStream os = new ByteArrayOutputStream(); 1047 XmlSerializer serializer = new FastXmlSerializer(); 1048 serializer.setOutput(os, "utf-8"); 1049 writeToXml(mState, serializer, mContext); 1050 serializer.flush(); 1051 new AsyncXmlWriter().execute(os); 1052 } catch (IOException e) { 1053 Log.e(this, e, "Writing state to XML buffer"); 1054 } 1055 } 1056 read()1057 private void read() { 1058 final InputStream is; 1059 try { 1060 is = mAtomicFile.openRead(); 1061 } catch (FileNotFoundException ex) { 1062 return; 1063 } 1064 1065 boolean versionChanged = false; 1066 1067 XmlPullParser parser; 1068 try { 1069 parser = Xml.newPullParser(); 1070 parser.setInput(new BufferedInputStream(is), null); 1071 parser.nextTag(); 1072 mState = readFromXml(parser, mContext); 1073 versionChanged = mState.versionNumber < EXPECTED_STATE_VERSION; 1074 1075 } catch (IOException | XmlPullParserException e) { 1076 Log.e(this, e, "Reading state from XML file"); 1077 mState = new State(); 1078 } finally { 1079 try { 1080 is.close(); 1081 } catch (IOException e) { 1082 Log.e(this, e, "Closing InputStream"); 1083 } 1084 } 1085 1086 // Verify all of the UserHandles. 1087 List<PhoneAccount> badAccounts = new ArrayList<>(); 1088 for (PhoneAccount phoneAccount : mState.accounts) { 1089 UserHandle userHandle = phoneAccount.getAccountHandle().getUserHandle(); 1090 if (userHandle == null) { 1091 Log.w(this, "Missing UserHandle for %s", phoneAccount); 1092 badAccounts.add(phoneAccount); 1093 } else if (mUserManager.getSerialNumberForUser(userHandle) == -1) { 1094 Log.w(this, "User does not exist for %s", phoneAccount); 1095 badAccounts.add(phoneAccount); 1096 } 1097 } 1098 mState.accounts.removeAll(badAccounts); 1099 1100 // If an upgrade occurred, write out the changed data. 1101 if (versionChanged || !badAccounts.isEmpty()) { 1102 write(); 1103 } 1104 } 1105 1106 private static void writeToXml(State state, XmlSerializer serializer, Context context) 1107 throws IOException { 1108 sStateXml.writeToXml(state, serializer, context); 1109 } 1110 1111 private static State readFromXml(XmlPullParser parser, Context context) 1112 throws IOException, XmlPullParserException { 1113 State s = sStateXml.readFromXml(parser, 0, context); 1114 return s != null ? s : new State(); 1115 } 1116 1117 //////////////////////////////////////////////////////////////////////////////////////////////// 1118 // 1119 // XML serialization 1120 // 1121 1122 @VisibleForTesting 1123 public abstract static class XmlSerialization<T> { 1124 private static final String TAG_VALUE = "value"; 1125 private static final String ATTRIBUTE_LENGTH = "length"; 1126 private static final String ATTRIBUTE_KEY = "key"; 1127 private static final String ATTRIBUTE_VALUE_TYPE = "type"; 1128 private static final String VALUE_TYPE_STRING = "string"; 1129 private static final String VALUE_TYPE_INTEGER = "integer"; 1130 private static final String VALUE_TYPE_BOOLEAN = "boolean"; 1131 1132 /** 1133 * Write the supplied object to XML 1134 */ 1135 public abstract void writeToXml(T o, XmlSerializer serializer, Context context) 1136 throws IOException; 1137 1138 /** 1139 * Read from the supplied XML into a new object, returning null in case of an 1140 * unrecoverable schema mismatch or other data error. 'parser' must be already 1141 * positioned at the first tag that is expected to have been emitted by this 1142 * object's writeToXml(). This object tries to fail early without modifying 1143 * 'parser' if it does not recognize the data it sees. 1144 */ 1145 public abstract T readFromXml(XmlPullParser parser, int version, Context context) 1146 throws IOException, XmlPullParserException; 1147 1148 protected void writeTextIfNonNull(String tagName, Object value, XmlSerializer serializer) 1149 throws IOException { 1150 if (value != null) { 1151 serializer.startTag(null, tagName); 1152 serializer.text(Objects.toString(value)); 1153 serializer.endTag(null, tagName); 1154 } 1155 } 1156 1157 /** 1158 * Serializes a string array. 1159 * 1160 * @param tagName The tag name for the string array. 1161 * @param values The string values to serialize. 1162 * @param serializer The serializer. 1163 * @throws IOException 1164 */ 1165 protected void writeStringList(String tagName, List<String> values, 1166 XmlSerializer serializer) 1167 throws IOException { 1168 1169 serializer.startTag(null, tagName); 1170 if (values != null) { 1171 serializer.attribute(null, ATTRIBUTE_LENGTH, Objects.toString(values.size())); 1172 for (String toSerialize : values) { 1173 serializer.startTag(null, TAG_VALUE); 1174 if (toSerialize != null ){ 1175 serializer.text(toSerialize); 1176 } 1177 serializer.endTag(null, TAG_VALUE); 1178 } 1179 } else { 1180 serializer.attribute(null, ATTRIBUTE_LENGTH, "0"); 1181 } 1182 serializer.endTag(null, tagName); 1183 } 1184 1185 protected void writeBundle(String tagName, Bundle values, XmlSerializer serializer) 1186 throws IOException { 1187 1188 serializer.startTag(null, tagName); 1189 if (values != null) { 1190 for (String key : values.keySet()) { 1191 Object value = values.get(key); 1192 1193 if (value == null) { 1194 continue; 1195 } 1196 1197 String valueType; 1198 if (value instanceof String) { 1199 valueType = VALUE_TYPE_STRING; 1200 } else if (value instanceof Integer) { 1201 valueType = VALUE_TYPE_INTEGER; 1202 } else if (value instanceof Boolean) { 1203 valueType = VALUE_TYPE_BOOLEAN; 1204 } else { 1205 Log.w(this, 1206 "PhoneAccounts support only string, integer and boolean extras TY."); 1207 continue; 1208 } 1209 1210 serializer.startTag(null, TAG_VALUE); 1211 serializer.attribute(null, ATTRIBUTE_KEY, key); 1212 serializer.attribute(null, ATTRIBUTE_VALUE_TYPE, valueType); 1213 serializer.text(Objects.toString(value)); 1214 serializer.endTag(null, TAG_VALUE); 1215 } 1216 } 1217 serializer.endTag(null, tagName); 1218 } 1219 1220 protected void writeIconIfNonNull(String tagName, Icon value, XmlSerializer serializer) 1221 throws IOException { 1222 if (value != null) { 1223 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 1224 value.writeToStream(stream); 1225 byte[] iconByteArray = stream.toByteArray(); 1226 String text = Base64.encodeToString(iconByteArray, 0, iconByteArray.length, 0); 1227 1228 serializer.startTag(null, tagName); 1229 serializer.text(text); 1230 serializer.endTag(null, tagName); 1231 } 1232 } 1233 1234 protected void writeLong(String tagName, long value, XmlSerializer serializer) 1235 throws IOException { 1236 serializer.startTag(null, tagName); 1237 serializer.text(Long.valueOf(value).toString()); 1238 serializer.endTag(null, tagName); 1239 } 1240 1241 protected void writeNonNullString(String tagName, String value, XmlSerializer serializer) 1242 throws IOException { 1243 serializer.startTag(null, tagName); 1244 serializer.text(value != null ? value : ""); 1245 serializer.endTag(null, tagName); 1246 } 1247 1248 /** 1249 * Reads a string array from the XML parser. 1250 * 1251 * @param parser The XML parser. 1252 * @return String array containing the parsed values. 1253 * @throws IOException Exception related to IO. 1254 * @throws XmlPullParserException Exception related to parsing. 1255 */ 1256 protected List<String> readStringList(XmlPullParser parser) 1257 throws IOException, XmlPullParserException { 1258 1259 int length = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_LENGTH)); 1260 List<String> arrayEntries = new ArrayList<String>(length); 1261 String value = null; 1262 1263 if (length == 0) { 1264 return arrayEntries; 1265 } 1266 1267 int outerDepth = parser.getDepth(); 1268 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1269 if (parser.getName().equals(TAG_VALUE)) { 1270 parser.next(); 1271 value = parser.getText(); 1272 arrayEntries.add(value); 1273 } 1274 } 1275 1276 return arrayEntries; 1277 } 1278 1279 /** 1280 * Reads a bundle from the XML parser. 1281 * 1282 * @param parser The XML parser. 1283 * @return Bundle containing the parsed values. 1284 * @throws IOException Exception related to IO. 1285 * @throws XmlPullParserException Exception related to parsing. 1286 */ 1287 protected Bundle readBundle(XmlPullParser parser) 1288 throws IOException, XmlPullParserException { 1289 1290 Bundle bundle = null; 1291 int outerDepth = parser.getDepth(); 1292 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1293 if (parser.getName().equals(TAG_VALUE)) { 1294 String valueType = parser.getAttributeValue(null, ATTRIBUTE_VALUE_TYPE); 1295 String key = parser.getAttributeValue(null, ATTRIBUTE_KEY); 1296 parser.next(); 1297 String value = parser.getText(); 1298 1299 if (bundle == null) { 1300 bundle = new Bundle(); 1301 } 1302 1303 // Do not write null values to the bundle. 1304 if (value == null) { 1305 continue; 1306 } 1307 1308 if (VALUE_TYPE_STRING.equals(valueType)) { 1309 bundle.putString(key, value); 1310 } else if (VALUE_TYPE_INTEGER.equals(valueType)) { 1311 try { 1312 int intValue = Integer.parseInt(value); 1313 bundle.putInt(key, intValue); 1314 } catch (NumberFormatException nfe) { 1315 Log.w(this, "Invalid integer PhoneAccount extra."); 1316 } 1317 } else if (VALUE_TYPE_BOOLEAN.equals(valueType)) { 1318 boolean boolValue = Boolean.parseBoolean(value); 1319 bundle.putBoolean(key, boolValue); 1320 } else { 1321 Log.w(this, "Invalid type " + valueType + " for PhoneAccount bundle."); 1322 } 1323 } 1324 } 1325 return bundle; 1326 } 1327 1328 protected Bitmap readBitmap(XmlPullParser parser) { 1329 byte[] imageByteArray = Base64.decode(parser.getText(), 0); 1330 return BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length); 1331 } 1332 1333 protected Icon readIcon(XmlPullParser parser) throws IOException { 1334 byte[] iconByteArray = Base64.decode(parser.getText(), 0); 1335 ByteArrayInputStream stream = new ByteArrayInputStream(iconByteArray); 1336 return Icon.createFromStream(stream); 1337 } 1338 } 1339 1340 @VisibleForTesting 1341 public static final XmlSerialization<State> sStateXml = 1342 new XmlSerialization<State>() { 1343 private static final String CLASS_STATE = "phone_account_registrar_state"; 1344 private static final String DEFAULT_OUTGOING = "default_outgoing"; 1345 private static final String ACCOUNTS = "accounts"; 1346 private static final String VERSION = "version"; 1347 1348 @Override 1349 public void writeToXml(State o, XmlSerializer serializer, Context context) 1350 throws IOException { 1351 if (o != null) { 1352 serializer.startTag(null, CLASS_STATE); 1353 serializer.attribute(null, VERSION, Objects.toString(EXPECTED_STATE_VERSION)); 1354 1355 serializer.startTag(null, DEFAULT_OUTGOING); 1356 for (DefaultPhoneAccountHandle defaultPhoneAccountHandle : o 1357 .defaultOutgoingAccountHandles.values()) { 1358 sDefaultPhoneAcountHandleXml 1359 .writeToXml(defaultPhoneAccountHandle, serializer, context); 1360 } 1361 serializer.endTag(null, DEFAULT_OUTGOING); 1362 1363 serializer.startTag(null, ACCOUNTS); 1364 for (PhoneAccount m : o.accounts) { 1365 sPhoneAccountXml.writeToXml(m, serializer, context); 1366 } 1367 serializer.endTag(null, ACCOUNTS); 1368 1369 serializer.endTag(null, CLASS_STATE); 1370 } 1371 } 1372 1373 @Override 1374 public State readFromXml(XmlPullParser parser, int version, Context context) 1375 throws IOException, XmlPullParserException { 1376 if (parser.getName().equals(CLASS_STATE)) { 1377 State s = new State(); 1378 1379 String rawVersion = parser.getAttributeValue(null, VERSION); 1380 s.versionNumber = TextUtils.isEmpty(rawVersion) ? 1 : Integer.parseInt(rawVersion); 1381 1382 int outerDepth = parser.getDepth(); 1383 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1384 if (parser.getName().equals(DEFAULT_OUTGOING)) { 1385 if (s.versionNumber < 9) { 1386 // Migrate old default phone account handle here by assuming the 1387 // default phone account handle belongs to the primary user. Also, 1388 // assume there are no groups. 1389 parser.nextTag(); 1390 PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleXml 1391 .readFromXml(parser, s.versionNumber, context); 1392 UserManager userManager = UserManager.get(context); 1393 UserInfo primaryUser = userManager.getPrimaryUser(); 1394 if (primaryUser != null) { 1395 UserHandle userHandle = primaryUser.getUserHandle(); 1396 DefaultPhoneAccountHandle defaultPhoneAccountHandle 1397 = new DefaultPhoneAccountHandle(userHandle, 1398 phoneAccountHandle, "" /* groupId */); 1399 s.defaultOutgoingAccountHandles 1400 .put(userHandle, defaultPhoneAccountHandle); 1401 } 1402 } else { 1403 int defaultAccountHandlesDepth = parser.getDepth(); 1404 while (XmlUtils.nextElementWithin(parser, defaultAccountHandlesDepth)) { 1405 DefaultPhoneAccountHandle accountHandle 1406 = sDefaultPhoneAcountHandleXml 1407 .readFromXml(parser, s.versionNumber, context); 1408 if (accountHandle != null && s.accounts != null) { 1409 s.defaultOutgoingAccountHandles 1410 .put(accountHandle.userHandle, accountHandle); 1411 } 1412 } 1413 } 1414 } else if (parser.getName().equals(ACCOUNTS)) { 1415 int accountsDepth = parser.getDepth(); 1416 while (XmlUtils.nextElementWithin(parser, accountsDepth)) { 1417 PhoneAccount account = sPhoneAccountXml.readFromXml(parser, 1418 s.versionNumber, context); 1419 1420 if (account != null && s.accounts != null) { 1421 s.accounts.add(account); 1422 } 1423 } 1424 } 1425 } 1426 return s; 1427 } 1428 return null; 1429 } 1430 }; 1431 1432 @VisibleForTesting 1433 public static final XmlSerialization<DefaultPhoneAccountHandle> sDefaultPhoneAcountHandleXml = 1434 new XmlSerialization<DefaultPhoneAccountHandle>() { 1435 private static final String CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE 1436 = "default_outgoing_phone_account_handle"; 1437 private static final String USER_SERIAL_NUMBER = "user_serial_number"; 1438 private static final String GROUP_ID = "group_id"; 1439 private static final String ACCOUNT_HANDLE = "account_handle"; 1440 1441 @Override 1442 public void writeToXml(DefaultPhoneAccountHandle o, XmlSerializer serializer, 1443 Context context) throws IOException { 1444 if (o != null) { 1445 final UserManager userManager = UserManager.get(context); 1446 final long serialNumber = userManager.getSerialNumberForUser(o.userHandle); 1447 if (serialNumber != -1) { 1448 serializer.startTag(null, CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE); 1449 writeLong(USER_SERIAL_NUMBER, serialNumber, serializer); 1450 writeNonNullString(GROUP_ID, o.groupId, serializer); 1451 serializer.startTag(null, ACCOUNT_HANDLE); 1452 sPhoneAccountHandleXml.writeToXml(o.phoneAccountHandle, serializer, 1453 context); 1454 serializer.endTag(null, ACCOUNT_HANDLE); 1455 serializer.endTag(null, CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE); 1456 } 1457 } 1458 } 1459 1460 @Override 1461 public DefaultPhoneAccountHandle readFromXml(XmlPullParser parser, int version, 1462 Context context) 1463 throws IOException, XmlPullParserException { 1464 if (parser.getName().equals(CLASS_DEFAULT_OUTGOING_PHONE_ACCOUNT_HANDLE)) { 1465 int outerDepth = parser.getDepth(); 1466 PhoneAccountHandle accountHandle = null; 1467 String userSerialNumberString = null; 1468 String groupId = ""; 1469 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1470 if (parser.getName().equals(ACCOUNT_HANDLE)) { 1471 parser.nextTag(); 1472 accountHandle = sPhoneAccountHandleXml.readFromXml(parser, version, 1473 context); 1474 } else if (parser.getName().equals(USER_SERIAL_NUMBER)) { 1475 parser.next(); 1476 userSerialNumberString = parser.getText(); 1477 } else if (parser.getName().equals(GROUP_ID)) { 1478 parser.next(); 1479 groupId = parser.getText(); 1480 } 1481 } 1482 UserHandle userHandle = null; 1483 if (userSerialNumberString != null) { 1484 try { 1485 long serialNumber = Long.parseLong(userSerialNumberString); 1486 userHandle = UserManager.get(context) 1487 .getUserForSerialNumber(serialNumber); 1488 } catch (NumberFormatException e) { 1489 Log.e(this, e, 1490 "Could not parse UserHandle " + userSerialNumberString); 1491 } 1492 } 1493 if (accountHandle != null && userHandle != null && groupId != null) { 1494 return new DefaultPhoneAccountHandle(userHandle, accountHandle, 1495 groupId); 1496 } 1497 } 1498 return null; 1499 } 1500 }; 1501 1502 1503 @VisibleForTesting 1504 public static final XmlSerialization<PhoneAccount> sPhoneAccountXml = 1505 new XmlSerialization<PhoneAccount>() { 1506 private static final String CLASS_PHONE_ACCOUNT = "phone_account"; 1507 private static final String ACCOUNT_HANDLE = "account_handle"; 1508 private static final String ADDRESS = "handle"; 1509 private static final String SUBSCRIPTION_ADDRESS = "subscription_number"; 1510 private static final String CAPABILITIES = "capabilities"; 1511 private static final String SUPPORTED_AUDIO_ROUTES = "supported_audio_routes"; 1512 private static final String ICON_RES_ID = "icon_res_id"; 1513 private static final String ICON_PACKAGE_NAME = "icon_package_name"; 1514 private static final String ICON_BITMAP = "icon_bitmap"; 1515 private static final String ICON_TINT = "icon_tint"; 1516 private static final String HIGHLIGHT_COLOR = "highlight_color"; 1517 private static final String LABEL = "label"; 1518 private static final String SHORT_DESCRIPTION = "short_description"; 1519 private static final String SUPPORTED_URI_SCHEMES = "supported_uri_schemes"; 1520 private static final String ICON = "icon"; 1521 private static final String EXTRAS = "extras"; 1522 private static final String ENABLED = "enabled"; 1523 1524 @Override 1525 public void writeToXml(PhoneAccount o, XmlSerializer serializer, Context context) 1526 throws IOException { 1527 if (o != null) { 1528 serializer.startTag(null, CLASS_PHONE_ACCOUNT); 1529 1530 if (o.getAccountHandle() != null) { 1531 serializer.startTag(null, ACCOUNT_HANDLE); 1532 sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer, context); 1533 serializer.endTag(null, ACCOUNT_HANDLE); 1534 } 1535 1536 writeTextIfNonNull(ADDRESS, o.getAddress(), serializer); 1537 writeTextIfNonNull(SUBSCRIPTION_ADDRESS, o.getSubscriptionAddress(), serializer); 1538 writeTextIfNonNull(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer); 1539 writeIconIfNonNull(ICON, o.getIcon(), serializer); 1540 writeTextIfNonNull(HIGHLIGHT_COLOR, 1541 Integer.toString(o.getHighlightColor()), serializer); 1542 writeTextIfNonNull(LABEL, o.getLabel(), serializer); 1543 writeTextIfNonNull(SHORT_DESCRIPTION, o.getShortDescription(), serializer); 1544 writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer); 1545 writeBundle(EXTRAS, o.getExtras(), serializer); 1546 writeTextIfNonNull(ENABLED, o.isEnabled() ? "true" : "false" , serializer); 1547 writeTextIfNonNull(SUPPORTED_AUDIO_ROUTES, Integer.toString( 1548 o.getSupportedAudioRoutes()), serializer); 1549 1550 serializer.endTag(null, CLASS_PHONE_ACCOUNT); 1551 } 1552 } 1553 1554 public PhoneAccount readFromXml(XmlPullParser parser, int version, Context context) 1555 throws IOException, XmlPullParserException { 1556 if (parser.getName().equals(CLASS_PHONE_ACCOUNT)) { 1557 int outerDepth = parser.getDepth(); 1558 PhoneAccountHandle accountHandle = null; 1559 Uri address = null; 1560 Uri subscriptionAddress = null; 1561 int capabilities = 0; 1562 int supportedAudioRoutes = 0; 1563 int iconResId = PhoneAccount.NO_RESOURCE_ID; 1564 String iconPackageName = null; 1565 Bitmap iconBitmap = null; 1566 int iconTint = PhoneAccount.NO_ICON_TINT; 1567 int highlightColor = PhoneAccount.NO_HIGHLIGHT_COLOR; 1568 String label = null; 1569 String shortDescription = null; 1570 List<String> supportedUriSchemes = null; 1571 Icon icon = null; 1572 boolean enabled = false; 1573 Bundle extras = null; 1574 1575 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1576 if (parser.getName().equals(ACCOUNT_HANDLE)) { 1577 parser.nextTag(); 1578 accountHandle = sPhoneAccountHandleXml.readFromXml(parser, version, 1579 context); 1580 } else if (parser.getName().equals(ADDRESS)) { 1581 parser.next(); 1582 address = Uri.parse(parser.getText()); 1583 } else if (parser.getName().equals(SUBSCRIPTION_ADDRESS)) { 1584 parser.next(); 1585 String nextText = parser.getText(); 1586 subscriptionAddress = nextText == null ? null : Uri.parse(nextText); 1587 } else if (parser.getName().equals(CAPABILITIES)) { 1588 parser.next(); 1589 capabilities = Integer.parseInt(parser.getText()); 1590 } else if (parser.getName().equals(ICON_RES_ID)) { 1591 parser.next(); 1592 iconResId = Integer.parseInt(parser.getText()); 1593 } else if (parser.getName().equals(ICON_PACKAGE_NAME)) { 1594 parser.next(); 1595 iconPackageName = parser.getText(); 1596 } else if (parser.getName().equals(ICON_BITMAP)) { 1597 parser.next(); 1598 iconBitmap = readBitmap(parser); 1599 } else if (parser.getName().equals(ICON_TINT)) { 1600 parser.next(); 1601 iconTint = Integer.parseInt(parser.getText()); 1602 } else if (parser.getName().equals(HIGHLIGHT_COLOR)) { 1603 parser.next(); 1604 highlightColor = Integer.parseInt(parser.getText()); 1605 } else if (parser.getName().equals(LABEL)) { 1606 parser.next(); 1607 label = parser.getText(); 1608 } else if (parser.getName().equals(SHORT_DESCRIPTION)) { 1609 parser.next(); 1610 shortDescription = parser.getText(); 1611 } else if (parser.getName().equals(SUPPORTED_URI_SCHEMES)) { 1612 supportedUriSchemes = readStringList(parser); 1613 } else if (parser.getName().equals(ICON)) { 1614 parser.next(); 1615 icon = readIcon(parser); 1616 } else if (parser.getName().equals(ENABLED)) { 1617 parser.next(); 1618 enabled = "true".equalsIgnoreCase(parser.getText()); 1619 } else if (parser.getName().equals(EXTRAS)) { 1620 extras = readBundle(parser); 1621 } else if (parser.getName().equals(SUPPORTED_AUDIO_ROUTES)) { 1622 parser.next(); 1623 supportedAudioRoutes = Integer.parseInt(parser.getText()); 1624 } 1625 } 1626 1627 ComponentName pstnComponentName = new ComponentName("com.android.phone", 1628 "com.android.services.telephony.TelephonyConnectionService"); 1629 ComponentName sipComponentName = new ComponentName("com.android.phone", 1630 "com.android.services.telephony.sip.SipConnectionService"); 1631 1632 // Upgrade older phone accounts to specify the supported URI schemes. 1633 if (version < 2) { 1634 supportedUriSchemes = new ArrayList<>(); 1635 1636 // Handle the SIP connection service. 1637 // Check the system settings to see if it also should handle "tel" calls. 1638 if (accountHandle.getComponentName().equals(sipComponentName)) { 1639 boolean useSipForPstn = useSipForPstnCalls(context); 1640 supportedUriSchemes.add(PhoneAccount.SCHEME_SIP); 1641 if (useSipForPstn) { 1642 supportedUriSchemes.add(PhoneAccount.SCHEME_TEL); 1643 } 1644 } else { 1645 supportedUriSchemes.add(PhoneAccount.SCHEME_TEL); 1646 supportedUriSchemes.add(PhoneAccount.SCHEME_VOICEMAIL); 1647 } 1648 } 1649 1650 // Upgrade older phone accounts with explicit package name 1651 if (version < 5) { 1652 if (iconBitmap == null) { 1653 iconPackageName = accountHandle.getComponentName().getPackageName(); 1654 } 1655 } 1656 1657 if (version < 6) { 1658 // Always enable all SIP accounts on upgrade to version 6 1659 if (accountHandle.getComponentName().equals(sipComponentName)) { 1660 enabled = true; 1661 } 1662 } 1663 if (version < 7) { 1664 // Always enabled all PSTN acocunts on upgrade to version 7 1665 if (accountHandle.getComponentName().equals(pstnComponentName)) { 1666 enabled = true; 1667 } 1668 } 1669 if (version < 8) { 1670 // Migrate the SIP account handle ids to use SIP username instead of SIP URI. 1671 if (accountHandle.getComponentName().equals(sipComponentName)) { 1672 Uri accountUri = Uri.parse(accountHandle.getId()); 1673 if (accountUri.getScheme() != null && 1674 accountUri.getScheme().equals(PhoneAccount.SCHEME_SIP)) { 1675 accountHandle = new PhoneAccountHandle(accountHandle.getComponentName(), 1676 accountUri.getSchemeSpecificPart(), 1677 accountHandle.getUserHandle()); 1678 } 1679 } 1680 } 1681 1682 if (version < 9) { 1683 // Set supported audio routes to all by default 1684 supportedAudioRoutes = CallAudioState.ROUTE_ALL; 1685 } 1686 1687 PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, label) 1688 .setAddress(address) 1689 .setSubscriptionAddress(subscriptionAddress) 1690 .setCapabilities(capabilities) 1691 .setSupportedAudioRoutes(supportedAudioRoutes) 1692 .setShortDescription(shortDescription) 1693 .setSupportedUriSchemes(supportedUriSchemes) 1694 .setHighlightColor(highlightColor) 1695 .setExtras(extras) 1696 .setIsEnabled(enabled); 1697 1698 if (icon != null) { 1699 builder.setIcon(icon); 1700 } else if (iconBitmap != null) { 1701 builder.setIcon(Icon.createWithBitmap(iconBitmap)); 1702 } else if (!TextUtils.isEmpty(iconPackageName)) { 1703 builder.setIcon(Icon.createWithResource(iconPackageName, iconResId)); 1704 // TODO: Need to set tint. 1705 } 1706 1707 return builder.build(); 1708 } 1709 return null; 1710 } 1711 1712 /** 1713 * Determines if the SIP call settings specify to use SIP for all calls, including PSTN 1714 * calls. 1715 * 1716 * @param context The context. 1717 * @return {@code True} if SIP should be used for all calls. 1718 */ 1719 private boolean useSipForPstnCalls(Context context) { 1720 String option = Settings.System.getString(context.getContentResolver(), 1721 Settings.System.SIP_CALL_OPTIONS); 1722 option = (option != null) ? option : Settings.System.SIP_ADDRESS_ONLY; 1723 return option.equals(Settings.System.SIP_ALWAYS); 1724 } 1725 }; 1726 1727 @VisibleForTesting 1728 public static final XmlSerialization<PhoneAccountHandle> sPhoneAccountHandleXml = 1729 new XmlSerialization<PhoneAccountHandle>() { 1730 private static final String CLASS_PHONE_ACCOUNT_HANDLE = "phone_account_handle"; 1731 private static final String COMPONENT_NAME = "component_name"; 1732 private static final String ID = "id"; 1733 private static final String USER_SERIAL_NUMBER = "user_serial_number"; 1734 1735 @Override 1736 public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer, Context context) 1737 throws IOException { 1738 if (o != null) { 1739 serializer.startTag(null, CLASS_PHONE_ACCOUNT_HANDLE); 1740 1741 if (o.getComponentName() != null) { 1742 writeTextIfNonNull( 1743 COMPONENT_NAME, o.getComponentName().flattenToString(), serializer); 1744 } 1745 1746 writeTextIfNonNull(ID, o.getId(), serializer); 1747 1748 if (o.getUserHandle() != null && context != null) { 1749 UserManager userManager = UserManager.get(context); 1750 writeLong(USER_SERIAL_NUMBER, 1751 userManager.getSerialNumberForUser(o.getUserHandle()), serializer); 1752 } 1753 1754 serializer.endTag(null, CLASS_PHONE_ACCOUNT_HANDLE); 1755 } 1756 } 1757 1758 @Override 1759 public PhoneAccountHandle readFromXml(XmlPullParser parser, int version, Context context) 1760 throws IOException, XmlPullParserException { 1761 if (parser.getName().equals(CLASS_PHONE_ACCOUNT_HANDLE)) { 1762 String componentNameString = null; 1763 String idString = null; 1764 String userSerialNumberString = null; 1765 int outerDepth = parser.getDepth(); 1766 1767 UserManager userManager = UserManager.get(context); 1768 1769 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1770 if (parser.getName().equals(COMPONENT_NAME)) { 1771 parser.next(); 1772 componentNameString = parser.getText(); 1773 } else if (parser.getName().equals(ID)) { 1774 parser.next(); 1775 idString = parser.getText(); 1776 } else if (parser.getName().equals(USER_SERIAL_NUMBER)) { 1777 parser.next(); 1778 userSerialNumberString = parser.getText(); 1779 } 1780 } 1781 if (componentNameString != null) { 1782 UserHandle userHandle = null; 1783 if (userSerialNumberString != null) { 1784 try { 1785 long serialNumber = Long.parseLong(userSerialNumberString); 1786 userHandle = userManager.getUserForSerialNumber(serialNumber); 1787 } catch (NumberFormatException e) { 1788 Log.e(this, e, "Could not parse UserHandle " + userSerialNumberString); 1789 } 1790 } 1791 return new PhoneAccountHandle( 1792 ComponentName.unflattenFromString(componentNameString), 1793 idString, 1794 userHandle); 1795 } 1796 } 1797 return null; 1798 } 1799 }; 1800 } 1801