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