PhoneAccountRegistrar.java revision fc43ea86697d3d052415cbb4feda7bd508563532
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.app.ActivityManager; 20import android.Manifest; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.PackageManager; 25import android.content.pm.ResolveInfo; 26import android.content.pm.ServiceInfo; 27import android.content.pm.UserInfo; 28import android.graphics.Bitmap; 29import android.graphics.BitmapFactory; 30import android.graphics.drawable.Icon; 31import android.net.Uri; 32import android.os.Binder; 33import android.os.PersistableBundle; 34import android.os.Process; 35import android.os.UserHandle; 36import android.os.UserManager; 37import android.provider.Settings; 38import android.telecom.ConnectionService; 39import android.telecom.DefaultDialerManager; 40import android.telecom.PhoneAccount; 41import android.telecom.PhoneAccountHandle; 42import android.telephony.CarrierConfigManager; 43import android.telephony.PhoneNumberUtils; 44import android.telephony.SubscriptionManager; 45import android.telephony.TelephonyManager; 46import android.text.TextUtils; 47import android.util.AtomicFile; 48import android.util.Base64; 49import android.util.Xml; 50 51// TODO: Needed for move to system service: import com.android.internal.R; 52import com.android.internal.annotations.VisibleForTesting; 53import com.android.internal.util.FastXmlSerializer; 54import com.android.internal.util.IndentingPrintWriter; 55import com.android.internal.util.XmlUtils; 56 57import org.xmlpull.v1.XmlPullParser; 58import org.xmlpull.v1.XmlPullParserException; 59import org.xmlpull.v1.XmlSerializer; 60 61import java.io.BufferedInputStream; 62import java.io.BufferedOutputStream; 63import java.io.ByteArrayInputStream; 64import java.io.ByteArrayOutputStream; 65import java.io.File; 66import java.io.FileNotFoundException; 67import java.io.FileOutputStream; 68import java.io.IOException; 69import java.io.InputStream; 70import java.lang.Integer; 71import java.lang.SecurityException; 72import java.lang.String; 73import java.util.ArrayList; 74import java.util.Collections; 75import java.util.Iterator; 76import java.util.List; 77import java.util.Objects; 78import java.util.concurrent.CopyOnWriteArrayList; 79 80/** 81 * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim 82 * delegate for all the account handling methods on {@link android.telecom.TelecomManager} as 83 * implemented in {@link TelecomServiceImpl}, with the notable exception that 84 * {@link TelecomServiceImpl} is responsible for security checking to make sure that the caller has 85 * proper authority over the {@code ComponentName}s they are declaring in their 86 * {@code PhoneAccountHandle}s. 87 * 88 * 89 * -- About Users and Phone Accounts -- 90 * 91 * We store all phone accounts for all users in a single place, which means that there are three 92 * users that we have to deal with in code: 93 * 1) The Android User that is currently active on the device. 94 * 2) The user which owns/registers the phone account. 95 * 3) The user running the app that is requesting the phone account information. 96 * 97 * For example, I have a device with 2 users, primary (A) and secondary (B), and the secondary user 98 * has a work profile running as another user (B2). Lets say that user B opens the phone settings 99 * (not currently supported, but theoretically speaking), and phone settings queries for a phone 100 * account list. Lets also say that an app running in the work profile has registered a phone 101 * account. This means that: 102 * 103 * Since phone settings always runs as the primary user, We have the following situation: 104 * User A (settings) is requesting a list of phone accounts while the active user is User B, and 105 * that list contains a phone account for profile User B2. 106 * 107 * In practice, (2) is stored with the phone account handle and is part of the handle's ID. (1) is 108 * saved in {@link #mCurrentUserHandle} and (3) we get from Binder.getCallingUser(). We check these 109 * users for visibility before returning any phone accounts. 110 */ 111public final class PhoneAccountRegistrar { 112 113 public static final PhoneAccountHandle NO_ACCOUNT_SELECTED = 114 new PhoneAccountHandle(new ComponentName("null", "null"), "NO_ACCOUNT_SELECTED"); 115 116 public abstract static class Listener { 117 public void onAccountsChanged(PhoneAccountRegistrar registrar) {} 118 public void onDefaultOutgoingChanged(PhoneAccountRegistrar registrar) {} 119 public void onSimCallManagerChanged(PhoneAccountRegistrar registrar) {} 120 } 121 122 private static final String FILE_NAME = "phone-account-registrar-state.xml"; 123 @VisibleForTesting 124 public static final int EXPECTED_STATE_VERSION = 7; 125 126 /** Keep in sync with the same in SipSettings.java */ 127 private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES"; 128 129 private final List<Listener> mListeners = new CopyOnWriteArrayList<>(); 130 private final AtomicFile mAtomicFile; 131 private final Context mContext; 132 private final UserManager mUserManager; 133 private final SubscriptionManager mSubscriptionManager; 134 private State mState; 135 private UserHandle mCurrentUserHandle; 136 137 @VisibleForTesting 138 public PhoneAccountRegistrar(Context context) { 139 this(context, FILE_NAME); 140 } 141 142 @VisibleForTesting 143 public PhoneAccountRegistrar(Context context, String fileName) { 144 // TODO: This file path is subject to change -- it is storing the phone account registry 145 // state file in the path /data/system/users/0/, which is likely not correct in a 146 // multi-user setting. 147 /** UNCOMMENT_FOR_MOVE_TO_SYSTEM_SERVICE 148 String filePath = Environment.getUserSystemDirectory(UserHandle.myUserId()). 149 getAbsolutePath(); 150 mAtomicFile = new AtomicFile(new File(filePath, fileName)); 151 UNCOMMENT_FOR_MOVE_TO_SYSTEM_SERVICE */ 152 mAtomicFile = new AtomicFile(new File(context.getFilesDir(), fileName)); 153 154 mState = new State(); 155 mContext = context; 156 mUserManager = UserManager.get(context); 157 mSubscriptionManager = SubscriptionManager.from(mContext); 158 mCurrentUserHandle = Process.myUserHandle(); 159 read(); 160 } 161 162 /** 163 * Retrieves the subscription id for a given phone account if it exists. Subscription ids 164 * apply only to PSTN/SIM card phone accounts so all other accounts should not have a 165 * subscription id. 166 * @param accountHandle The handle for the phone account for which to retrieve the 167 * subscription id. 168 * @return The value of the subscription id or -1 if it does not exist or is not valid. 169 */ 170 public int getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) { 171 PhoneAccount account = getPhoneAccountCheckCallingUser(accountHandle); 172 173 if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { 174 TelephonyManager tm = 175 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 176 return tm.getSubIdForPhoneAccount(account); 177 } 178 return SubscriptionManager.INVALID_SUBSCRIPTION_ID; 179 } 180 181 /** 182 * Retrieves the default outgoing phone account supporting the specified uriScheme. Note that if 183 * {@link #mCurrentUserHandle} does not have visibility into the current default, {@code null} 184 * will be returned. 185 * 186 * @param uriScheme The URI scheme for the outgoing call. 187 * @return The {@link PhoneAccountHandle} to use. 188 */ 189 public PhoneAccountHandle getOutgoingPhoneAccountForScheme(String uriScheme) { 190 final PhoneAccountHandle userSelected = getUserSelectedOutgoingPhoneAccount(); 191 192 if (userSelected != null) { 193 // If there is a default PhoneAccount, ensure it supports calls to handles with the 194 // specified uriScheme. 195 final PhoneAccount userSelectedAccount = getPhoneAccountCheckCallingUser(userSelected); 196 if (userSelectedAccount.supportsUriScheme(uriScheme)) { 197 return userSelected; 198 } 199 } 200 201 List<PhoneAccountHandle> outgoing = getCallCapablePhoneAccounts(uriScheme, false); 202 switch (outgoing.size()) { 203 case 0: 204 // There are no accounts, so there can be no default 205 return null; 206 case 1: 207 // There is only one account, which is by definition the default. 208 return outgoing.get(0); 209 default: 210 // There are multiple accounts with no selected default 211 return null; 212 } 213 } 214 215 /** 216 * @return The user-selected outgoing {@link PhoneAccount}, or null if it hasn't been set (or 217 * if it was set by another user). 218 */ 219 PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() { 220 PhoneAccount account = getPhoneAccountCheckCallingUser(mState.defaultOutgoing); 221 if (account != null) { 222 return mState.defaultOutgoing; 223 } 224 return null; 225 } 226 227 /** 228 * Sets the phone account with which to place all calls by default. Set by the user 229 * within phone settings. 230 */ 231 public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) { 232 if (accountHandle == null) { 233 // Asking to clear the default outgoing is a valid request 234 mState.defaultOutgoing = null; 235 } else { 236 // TODO: Do we really want to return for *any* user? 237 PhoneAccount account = getPhoneAccount(accountHandle); 238 if (account == null) { 239 Log.w(this, "Trying to set nonexistent default outgoing %s", 240 accountHandle); 241 return; 242 } 243 244 if (!account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)) { 245 Log.w(this, "Trying to set non-call-provider default outgoing %s", 246 accountHandle); 247 return; 248 } 249 250 if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { 251 // If the account selected is a SIM account, propagate down to the subscription 252 // record. 253 int subId = getSubscriptionIdForPhoneAccount(accountHandle); 254 mSubscriptionManager.setDefaultVoiceSubId(subId); 255 } 256 257 mState.defaultOutgoing = accountHandle; 258 } 259 260 write(); 261 fireDefaultOutgoingChanged(); 262 } 263 264 boolean isUserSelectedSmsPhoneAccount(PhoneAccountHandle accountHandle) { 265 return getSubscriptionIdForPhoneAccount(accountHandle) == 266 SubscriptionManager.getDefaultSmsSubId(); 267 } 268 269 /** 270 * Returns the {@link PhoneAccountHandle} corresponding to the currently active SIM Call 271 * Manager. SIM Call Manager returned corresponds to the following priority order: 272 * 1. If a SIM Call Manager {@link PhoneAccount} is registered for the same package as the 273 * default dialer, then that one is returned. 274 * 2. If there is a SIM Call Manager {@link PhoneAccount} registered which matches the 275 * carrier configuration's default, then that one is returned. 276 * 3. Otherwise, we return null. 277 */ 278 public PhoneAccountHandle getSimCallManager() { 279 long token = Binder.clearCallingIdentity(); 280 int user; 281 try { 282 user = ActivityManager.getCurrentUser(); 283 } finally { 284 Binder.restoreCallingIdentity(token); 285 } 286 return getSimCallManager(user); 287 } 288 289 /** 290 * Returns the {@link PhoneAccountHandle} corresponding to the currently active SIM Call 291 * Manager. SIM Call Manager returned corresponds to the following priority order: 292 * 1. If a SIM Call Manager {@link PhoneAccount} is registered for the same package as the 293 * default dialer, then that one is returned. 294 * 2. If there is a SIM Call Manager {@link PhoneAccount} registered which matches the 295 * carrier configuration's default, then that one is returned. 296 * 3. Otherwise, we return null. 297 */ 298 public PhoneAccountHandle getSimCallManager(int user) { 299 // Get the default dialer in case it has a connection manager associated with it. 300 String dialerPackage = DefaultDialerManager.getDefaultDialerApplication(mContext, user); 301 302 // Check carrier config. 303 String defaultSimCallManager = null; 304 CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService( 305 Context.CARRIER_CONFIG_SERVICE); 306 PersistableBundle configBundle = configManager.getConfig(); 307 if (configBundle != null) { 308 defaultSimCallManager = configBundle.getString( 309 CarrierConfigManager.KEY_DEFAULT_SIM_CALL_MANAGER_STRING); 310 } 311 312 ComponentName systemSimCallManagerComponent = TextUtils.isEmpty(defaultSimCallManager) ? 313 null : ComponentName.unflattenFromString(defaultSimCallManager); 314 315 PhoneAccountHandle dialerSimCallManager = null; 316 PhoneAccountHandle systemSimCallManager = null; 317 318 if (!TextUtils.isEmpty(dialerPackage) || systemSimCallManagerComponent != null) { 319 // loop through and look for any connection manager in the same package. 320 List<PhoneAccountHandle> allSimCallManagers = getPhoneAccountHandles( 321 PhoneAccount.CAPABILITY_CONNECTION_MANAGER, null, null, 322 true /* includeDisabledAccounts */); 323 for (PhoneAccountHandle accountHandle : allSimCallManagers) { 324 ComponentName component = accountHandle.getComponentName(); 325 326 // Store the system connection manager if found 327 if (systemSimCallManager == null 328 && Objects.equals(component, systemSimCallManagerComponent) 329 && !resolveComponent(accountHandle).isEmpty()) { 330 systemSimCallManager = accountHandle; 331 332 // Store the dialer connection manager if found 333 } else if (dialerSimCallManager == null 334 && Objects.equals(component.getPackageName(), dialerPackage) 335 && !resolveComponent(accountHandle).isEmpty()) { 336 dialerSimCallManager = accountHandle; 337 } 338 } 339 } 340 341 PhoneAccountHandle retval = dialerSimCallManager != null ? 342 dialerSimCallManager : systemSimCallManager; 343 344 Log.i(this, "SimCallManager queried, returning: %s", retval); 345 346 return retval; 347 } 348 349 /** 350 * Update the current UserHandle to track when users are switched. This will allow the 351 * PhoneAccountRegistar to self-filter the PhoneAccounts to make sure we don't leak anything 352 * across users. 353 * We cannot simply check the calling user because that would always return the primary user for 354 * all invocations originating with the system process. 355 * 356 * @param userHandle The {@link UserHandle}, as delivered by 357 * {@link Intent#ACTION_USER_SWITCHED}. 358 */ 359 public void setCurrentUserHandle(UserHandle userHandle) { 360 if (userHandle == null) { 361 Log.d(this, "setCurrentUserHandle, userHandle = null"); 362 userHandle = Process.myUserHandle(); 363 } 364 Log.d(this, "setCurrentUserHandle, %s", userHandle); 365 mCurrentUserHandle = userHandle; 366 } 367 368 /** 369 * @return {@code true} if the phone account was successfully enabled/disabled, {@code false} 370 * otherwise. 371 */ 372 public boolean enablePhoneAccount(PhoneAccountHandle accountHandle, boolean isEnabled) { 373 PhoneAccount account = getPhoneAccount(accountHandle); 374 if (account == null) { 375 Log.w(this, "Could not find account to enable: " + accountHandle); 376 return false; 377 } else if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) { 378 // We never change the enabled state of SIM-based accounts. 379 Log.w(this, "Could not change enable state of SIM account: " + accountHandle); 380 return false; 381 } 382 383 if (account.isEnabled() != isEnabled) { 384 account.setIsEnabled(isEnabled); 385 write(); 386 fireAccountsChanged(); 387 } 388 return true; 389 } 390 391 private boolean isVisibleForUser(PhoneAccount account) { 392 if (account == null) { 393 return false; 394 } 395 396 // If this PhoneAccount has CAPABILITY_MULTI_USER, it should be visible to all users and 397 // all profiles. Only Telephony and SIP accounts should have this capability. 398 if (account.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) { 399 return true; 400 } 401 402 UserHandle phoneAccountUserHandle = account.getAccountHandle().getUserHandle(); 403 if (phoneAccountUserHandle == null) { 404 return false; 405 } 406 407 if (mCurrentUserHandle == null) { 408 Log.d(this, "Current user is null; assuming true"); 409 return true; 410 } 411 412 if (phoneAccountUserHandle.equals(Binder.getCallingUserHandle())) { 413 return true; 414 } 415 416 // Special check for work profiles. 417 // Unlike in TelecomServiceImpl, we only care about *profiles* here. We want to make sure 418 // that we don't resolve PhoneAccount across *users*, but resolving across *profiles* is 419 // fine. 420 if (UserHandle.getCallingUserId() == UserHandle.USER_OWNER) { 421 List<UserInfo> profileUsers = 422 mUserManager.getProfiles(mCurrentUserHandle.getIdentifier()); 423 for (UserInfo profileInfo : profileUsers) { 424 if (profileInfo.getUserHandle().equals(phoneAccountUserHandle)) { 425 return true; 426 } 427 } 428 } 429 430 return false; 431 } 432 433 private List<ResolveInfo> resolveComponent(PhoneAccountHandle phoneAccountHandle) { 434 return resolveComponent(phoneAccountHandle.getComponentName(), 435 phoneAccountHandle.getUserHandle()); 436 } 437 438 private List<ResolveInfo> resolveComponent(ComponentName componentName, 439 UserHandle userHandle) { 440 PackageManager pm = mContext.getPackageManager(); 441 Intent intent = new Intent(ConnectionService.SERVICE_INTERFACE); 442 intent.setComponent(componentName); 443 try { 444 if (userHandle != null) { 445 return pm.queryIntentServicesAsUser(intent, 0, userHandle.getIdentifier()); 446 } else { 447 return pm.queryIntentServices(intent, 0); 448 } 449 } catch (SecurityException e) { 450 Log.e(this, e, "%s is not visible for the calling user", componentName); 451 return Collections.EMPTY_LIST; 452 } 453 } 454 455 /** 456 * Retrieves a list of all {@link PhoneAccountHandle}s registered. 457 * Only returns accounts which are enabled. 458 * 459 * @return The list of {@link PhoneAccountHandle}s. 460 */ 461 public List<PhoneAccountHandle> getAllPhoneAccountHandles() { 462 return getPhoneAccountHandles(0, null, null, false); 463 } 464 465 public List<PhoneAccount> getAllPhoneAccounts() { 466 return getPhoneAccounts(0, null, null, false); 467 } 468 469 /** 470 * Retrieves a list of all phone account call provider phone accounts supporting the 471 * specified URI scheme. 472 * 473 * @param uriScheme The URI scheme. 474 * @return The phone account handles. 475 */ 476 public List<PhoneAccountHandle> getCallCapablePhoneAccounts( 477 String uriScheme, boolean includeDisabledAccounts) { 478 return getPhoneAccountHandles( 479 PhoneAccount.CAPABILITY_CALL_PROVIDER, uriScheme, null, includeDisabledAccounts); 480 } 481 482 /** 483 * Retrieves a list of all the SIM-based phone accounts. 484 */ 485 public List<PhoneAccountHandle> getSimPhoneAccounts() { 486 return getPhoneAccountHandles( 487 PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION, 488 null, null, false); 489 } 490 491 /** 492 * Retrieves a list of all phone accounts registered by a specified package. 493 * 494 * @param packageName The name of the package that registered the phone accounts. 495 * @return The phone account handles. 496 */ 497 public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) { 498 return getPhoneAccountHandles(0, null, packageName, false); 499 } 500 501 // TODO: Should we implement an artificial limit for # of accounts associated with a single 502 // ComponentName? 503 public void registerPhoneAccount(PhoneAccount account) { 504 // Enforce the requirement that a connection service for a phone account has the correct 505 // permission. 506 if (!phoneAccountRequiresBindPermission(account.getAccountHandle())) { 507 Log.w(this, 508 "Phone account %s does not have BIND_TELECOM_CONNECTION_SERVICE permission.", 509 account.getAccountHandle()); 510 throw new SecurityException("PhoneAccount connection service requires " 511 + "BIND_TELECOM_CONNECTION_SERVICE permission."); 512 } 513 514 addOrReplacePhoneAccount(account); 515 } 516 517 /** 518 * Adds a {@code PhoneAccount}, replacing an existing one if found. 519 * 520 * @param account The {@code PhoneAccount} to add or replace. 521 */ 522 private void addOrReplacePhoneAccount(PhoneAccount account) { 523 Log.d(this, "addOrReplacePhoneAccount(%s -> %s)", 524 account.getAccountHandle(), account); 525 526 // Start _enabled_ property as false. 527 // !!! IMPORTANT !!! It is important that we do not read the enabled state that the 528 // source app provides or else an third party app could enable itself. 529 boolean isEnabled = false; 530 531 PhoneAccount oldAccount = getPhoneAccount(account.getAccountHandle()); 532 if (oldAccount != null) { 533 mState.accounts.remove(oldAccount); 534 isEnabled = oldAccount.isEnabled(); 535 Log.i(this, getAccountDiffString(account, oldAccount)); 536 } else { 537 Log.i(this, "New phone account registered: " + account); 538 } 539 540 mState.accounts.add(account); 541 // Reset enabled state to whatever the value was if the account was already registered, 542 // or _true_ if this is a SIM-based account. All SIM-based accounts are always enabled. 543 account.setIsEnabled( 544 isEnabled || account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)); 545 546 write(); 547 fireAccountsChanged(); 548 } 549 550 public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) { 551 PhoneAccount account = getPhoneAccount(accountHandle); 552 if (account != null) { 553 if (mState.accounts.remove(account)) { 554 write(); 555 fireAccountsChanged(); 556 } 557 } 558 } 559 560 /** 561 * Un-registers all phone accounts associated with a specified package. 562 * 563 * @param packageName The package for which phone accounts will be removed. 564 * @param userHandle The {@link UserHandle} the package is running under. 565 */ 566 public void clearAccounts(String packageName, UserHandle userHandle) { 567 boolean accountsRemoved = false; 568 Iterator<PhoneAccount> it = mState.accounts.iterator(); 569 while (it.hasNext()) { 570 PhoneAccount phoneAccount = it.next(); 571 PhoneAccountHandle handle = phoneAccount.getAccountHandle(); 572 if (Objects.equals(packageName, handle.getComponentName().getPackageName()) 573 && Objects.equals(userHandle, handle.getUserHandle())) { 574 Log.i(this, "Removing phone account " + phoneAccount.getLabel()); 575 mState.accounts.remove(phoneAccount); 576 accountsRemoved = true; 577 } 578 } 579 580 if (accountsRemoved) { 581 write(); 582 fireAccountsChanged(); 583 } 584 } 585 586 public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) { 587 int subId = getSubscriptionIdForPhoneAccount(accountHandle); 588 return PhoneNumberUtils.isVoiceMailNumber(mContext, subId, number); 589 } 590 591 public void addListener(Listener l) { 592 mListeners.add(l); 593 } 594 595 public void removeListener(Listener l) { 596 if (l != null) { 597 mListeners.remove(l); 598 } 599 } 600 601 private void fireAccountsChanged() { 602 for (Listener l : mListeners) { 603 l.onAccountsChanged(this); 604 } 605 } 606 607 private void fireDefaultOutgoingChanged() { 608 for (Listener l : mListeners) { 609 l.onDefaultOutgoingChanged(this); 610 } 611 } 612 613 private void fireSimCallManagerChanged() { 614 for (Listener l : mListeners) { 615 l.onSimCallManagerChanged(this); 616 } 617 } 618 619 private String getAccountDiffString(PhoneAccount account1, PhoneAccount account2) { 620 if (account1 == null || account2 == null) { 621 return "Diff: " + account1 + ", " + account2; 622 } 623 624 StringBuffer sb = new StringBuffer(); 625 sb.append("[").append(account1.getAccountHandle()); 626 appendDiff(sb, "addr", account1.getAddress(), account2.getAddress()); 627 appendDiff(sb, "cap", account1.getCapabilities(), account2.getCapabilities()); 628 appendDiff(sb, "hl", account1.getHighlightColor(), account2.getHighlightColor()); 629 appendDiff(sb, "icon", account1.getIcon(), account2.getIcon()); 630 appendDiff(sb, "lbl", account1.getLabel(), account2.getLabel()); 631 appendDiff(sb, "desc", account1.getShortDescription(), account2.getShortDescription()); 632 appendDiff(sb, "subAddr", account1.getSubscriptionAddress(), 633 account2.getSubscriptionAddress()); 634 appendDiff(sb, "uris", account1.getSupportedUriSchemes(), 635 account2.getSupportedUriSchemes()); 636 sb.append("]"); 637 return sb.toString(); 638 } 639 640 private void appendDiff(StringBuffer sb, String attrName, Object obj1, Object obj2) { 641 if (!Objects.equals(obj1, obj2)) { 642 sb.append("(") 643 .append(attrName) 644 .append(": ") 645 .append(obj1) 646 .append(" -> ") 647 .append(obj2) 648 .append(")"); 649 } 650 } 651 652 /** 653 * Determines if the connection service specified by a {@link PhoneAccountHandle} requires the 654 * {@link Manifest.permission#BIND_TELECOM_CONNECTION_SERVICE} permission. 655 * 656 * @param phoneAccountHandle The phone account to check. 657 * @return {@code True} if the phone account has permission. 658 */ 659 public boolean phoneAccountRequiresBindPermission(PhoneAccountHandle phoneAccountHandle) { 660 List<ResolveInfo> resolveInfos = resolveComponent(phoneAccountHandle); 661 if (resolveInfos.isEmpty()) { 662 Log.w(this, "phoneAccount %s not found", phoneAccountHandle.getComponentName()); 663 return false; 664 } 665 for (ResolveInfo resolveInfo : resolveInfos) { 666 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 667 if (serviceInfo == null) { 668 return false; 669 } 670 671 if (!Manifest.permission.BIND_CONNECTION_SERVICE.equals(serviceInfo.permission) && 672 !Manifest.permission.BIND_TELECOM_CONNECTION_SERVICE.equals( 673 serviceInfo.permission)) { 674 // The ConnectionService must require either the deprecated BIND_CONNECTION_SERVICE, 675 // or the public BIND_TELECOM_CONNECTION_SERVICE permissions, both of which are 676 // system/signature only. 677 return false; 678 } 679 } 680 return true; 681 } 682 683 // 684 // Methods for retrieving PhoneAccounts and PhoneAccountHandles 685 // 686 687 /** 688 * Returns the PhoneAccount for the specified handle. Does no user checking. 689 * 690 * @param handle 691 * @return The corresponding phone account if one exists. 692 */ 693 PhoneAccount getPhoneAccount(PhoneAccountHandle handle) { 694 for (PhoneAccount m : mState.accounts) { 695 if (Objects.equals(handle, m.getAccountHandle())) { 696 return m; 697 } 698 } 699 return null; 700 } 701 702 /** 703 * Like getPhoneAccount, but checks to see if the current user is allowed to see the phone 704 * account before returning it. The current user is the active user on the actual android 705 * device. 706 */ 707 public PhoneAccount getPhoneAccountCheckCallingUser(PhoneAccountHandle handle) { 708 PhoneAccount account = getPhoneAccount(handle); 709 if (account != null && isVisibleForUser(account)) { 710 return account; 711 } 712 return null; 713 } 714 715 /** 716 * Returns a list of phone account handles with the specified capabilities, uri scheme, 717 * and package name. 718 */ 719 private List<PhoneAccountHandle> getPhoneAccountHandles( 720 int capabilities, 721 String uriScheme, 722 String packageName, 723 boolean includeDisabledAccounts) { 724 List<PhoneAccountHandle> handles = new ArrayList<>(); 725 726 for (PhoneAccount account : getPhoneAccounts( 727 capabilities, uriScheme, packageName, includeDisabledAccounts)) { 728 handles.add(account.getAccountHandle()); 729 } 730 return handles; 731 } 732 733 /** 734 * Returns a list of phone account handles with the specified flag, supporting the specified 735 * URI scheme, within the specified package name. 736 * 737 * @param capabilities Capabilities which the {@code PhoneAccount} must have. Ignored if 0. 738 * @param uriScheme URI schemes the PhoneAccount must handle. {@code null} bypasses the 739 * URI scheme check. 740 * @param packageName Package name of the PhoneAccount. {@code null} bypasses packageName check. 741 */ 742 private List<PhoneAccount> getPhoneAccounts( 743 int capabilities, 744 String uriScheme, 745 String packageName, 746 boolean includeDisabledAccounts) { 747 List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size()); 748 for (PhoneAccount m : mState.accounts) { 749 if (!(m.isEnabled() || includeDisabledAccounts)) { 750 // Do not include disabled accounts. 751 continue; 752 } 753 754 if (capabilities != 0 && !m.hasCapabilities(capabilities)) { 755 // Account doesn't have the right capabilities; skip this one. 756 continue; 757 } 758 if (uriScheme != null && !m.supportsUriScheme(uriScheme)) { 759 // Account doesn't support this URI scheme; skip this one. 760 continue; 761 } 762 PhoneAccountHandle handle = m.getAccountHandle(); 763 764 if (resolveComponent(handle).isEmpty()) { 765 // This component cannot be resolved anymore; skip this one. 766 continue; 767 } 768 if (packageName != null && 769 !packageName.equals(handle.getComponentName().getPackageName())) { 770 // Not the right package name; skip this one. 771 continue; 772 } 773 if (!isVisibleForUser(m)) { 774 // Account is not visible for the current user; skip this one. 775 continue; 776 } 777 accounts.add(m); 778 } 779 return accounts; 780 } 781 782 // 783 // State Implementation for PhoneAccountRegistrar 784 // 785 786 /** 787 * The state of this {@code PhoneAccountRegistrar}. 788 */ 789 @VisibleForTesting 790 public static class State { 791 /** 792 * The account selected by the user to be employed by default for making outgoing calls. 793 * If the user has not made such a selection, then this is null. 794 */ 795 public PhoneAccountHandle defaultOutgoing = null; 796 797 /** 798 * The complete list of {@code PhoneAccount}s known to the Telecom subsystem. 799 */ 800 public final List<PhoneAccount> accounts = new CopyOnWriteArrayList<>(); 801 802 /** 803 * The version number of the State data. 804 */ 805 public int versionNumber; 806 } 807 808 /** 809 * Dumps the state of the {@link CallsManager}. 810 * 811 * @param pw The {@code IndentingPrintWriter} to write the state to. 812 */ 813 public void dump(IndentingPrintWriter pw) { 814 if (mState != null) { 815 pw.println("xmlVersion: " + mState.versionNumber); 816 pw.println("defaultOutgoing: " + (mState.defaultOutgoing == null ? "none" : 817 mState.defaultOutgoing)); 818 pw.println("simCallManager: " + getSimCallManager()); 819 pw.println("phoneAccounts:"); 820 pw.increaseIndent(); 821 for (PhoneAccount phoneAccount : mState.accounts) { 822 pw.println(phoneAccount); 823 } 824 pw.decreaseIndent(); 825 } 826 } 827 828 //////////////////////////////////////////////////////////////////////////////////////////////// 829 // 830 // State management 831 // 832 833 private void write() { 834 final FileOutputStream os; 835 try { 836 os = mAtomicFile.startWrite(); 837 boolean success = false; 838 try { 839 XmlSerializer serializer = new FastXmlSerializer(); 840 serializer.setOutput(new BufferedOutputStream(os), "utf-8"); 841 writeToXml(mState, serializer, mContext); 842 serializer.flush(); 843 success = true; 844 } finally { 845 if (success) { 846 mAtomicFile.finishWrite(os); 847 } else { 848 mAtomicFile.failWrite(os); 849 } 850 } 851 } catch (IOException e) { 852 Log.e(this, e, "Writing state to XML file"); 853 } 854 } 855 856 private void read() { 857 final InputStream is; 858 try { 859 is = mAtomicFile.openRead(); 860 } catch (FileNotFoundException ex) { 861 return; 862 } 863 864 boolean versionChanged = false; 865 866 XmlPullParser parser; 867 try { 868 parser = Xml.newPullParser(); 869 parser.setInput(new BufferedInputStream(is), null); 870 parser.nextTag(); 871 mState = readFromXml(parser, mContext); 872 versionChanged = mState.versionNumber < EXPECTED_STATE_VERSION; 873 874 } catch (IOException | XmlPullParserException e) { 875 Log.e(this, e, "Reading state from XML file"); 876 mState = new State(); 877 } finally { 878 try { 879 is.close(); 880 } catch (IOException e) { 881 Log.e(this, e, "Closing InputStream"); 882 } 883 } 884 885 // Verify all of the UserHandles. 886 List<PhoneAccount> badAccounts = new ArrayList<>(); 887 for (PhoneAccount phoneAccount : mState.accounts) { 888 UserHandle userHandle = phoneAccount.getAccountHandle().getUserHandle(); 889 if (userHandle == null) { 890 Log.w(this, "Missing UserHandle for %s", phoneAccount); 891 badAccounts.add(phoneAccount); 892 } else if (mUserManager.getSerialNumberForUser(userHandle) == -1) { 893 Log.w(this, "User does not exist for %s", phoneAccount); 894 badAccounts.add(phoneAccount); 895 } 896 } 897 mState.accounts.removeAll(badAccounts); 898 899 // If an upgrade occurred, write out the changed data. 900 if (versionChanged || !badAccounts.isEmpty()) { 901 write(); 902 } 903 } 904 905 private static void writeToXml(State state, XmlSerializer serializer, Context context) 906 throws IOException { 907 sStateXml.writeToXml(state, serializer, context); 908 } 909 910 private static State readFromXml(XmlPullParser parser, Context context) 911 throws IOException, XmlPullParserException { 912 State s = sStateXml.readFromXml(parser, 0, context); 913 return s != null ? s : new State(); 914 } 915 916 //////////////////////////////////////////////////////////////////////////////////////////////// 917 // 918 // XML serialization 919 // 920 921 @VisibleForTesting 922 public abstract static class XmlSerialization<T> { 923 private static final String LENGTH_ATTRIBUTE = "length"; 924 private static final String VALUE_TAG = "value"; 925 926 /** 927 * Write the supplied object to XML 928 */ 929 public abstract void writeToXml(T o, XmlSerializer serializer, Context context) 930 throws IOException; 931 932 /** 933 * Read from the supplied XML into a new object, returning null in case of an 934 * unrecoverable schema mismatch or other data error. 'parser' must be already 935 * positioned at the first tag that is expected to have been emitted by this 936 * object's writeToXml(). This object tries to fail early without modifying 937 * 'parser' if it does not recognize the data it sees. 938 */ 939 public abstract T readFromXml(XmlPullParser parser, int version, Context context) 940 throws IOException, XmlPullParserException; 941 942 protected void writeTextIfNonNull(String tagName, Object value, XmlSerializer serializer) 943 throws IOException { 944 if (value != null) { 945 serializer.startTag(null, tagName); 946 serializer.text(Objects.toString(value)); 947 serializer.endTag(null, tagName); 948 } 949 } 950 951 /** 952 * Serializes a string array. 953 * 954 * @param tagName The tag name for the string array. 955 * @param values The string values to serialize. 956 * @param serializer The serializer. 957 * @throws IOException 958 */ 959 protected void writeStringList(String tagName, List<String> values, 960 XmlSerializer serializer) 961 throws IOException { 962 963 serializer.startTag(null, tagName); 964 if (values != null) { 965 serializer.attribute(null, LENGTH_ATTRIBUTE, Objects.toString(values.size())); 966 for (String toSerialize : values) { 967 serializer.startTag(null, VALUE_TAG); 968 if (toSerialize != null ){ 969 serializer.text(toSerialize); 970 } 971 serializer.endTag(null, VALUE_TAG); 972 } 973 } else { 974 serializer.attribute(null, LENGTH_ATTRIBUTE, "0"); 975 } 976 serializer.endTag(null, tagName); 977 } 978 979 protected void writeIconIfNonNull(String tagName, Icon value, XmlSerializer serializer) 980 throws IOException { 981 if (value != null) { 982 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 983 value.writeToStream(stream); 984 byte[] iconByteArray = stream.toByteArray(); 985 String text = Base64.encodeToString(iconByteArray, 0, iconByteArray.length, 0); 986 987 serializer.startTag(null, tagName); 988 serializer.text(text); 989 serializer.endTag(null, tagName); 990 } 991 } 992 993 protected void writeLong(String tagName, long value, XmlSerializer serializer) 994 throws IOException { 995 serializer.startTag(null, tagName); 996 serializer.text(Long.valueOf(value).toString()); 997 serializer.endTag(null, tagName); 998 } 999 1000 /** 1001 * Reads a string array from the XML parser. 1002 * 1003 * @param parser The XML parser. 1004 * @return String array containing the parsed values. 1005 * @throws IOException Exception related to IO. 1006 * @throws XmlPullParserException Exception related to parsing. 1007 */ 1008 protected List<String> readStringList(XmlPullParser parser) 1009 throws IOException, XmlPullParserException { 1010 1011 int length = Integer.parseInt(parser.getAttributeValue(null, LENGTH_ATTRIBUTE)); 1012 List<String> arrayEntries = new ArrayList<String>(length); 1013 String value = null; 1014 1015 if (length == 0) { 1016 return arrayEntries; 1017 } 1018 1019 int outerDepth = parser.getDepth(); 1020 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1021 if (parser.getName().equals(VALUE_TAG)) { 1022 parser.next(); 1023 value = parser.getText(); 1024 arrayEntries.add(value); 1025 } 1026 } 1027 1028 return arrayEntries; 1029 } 1030 1031 protected Bitmap readBitmap(XmlPullParser parser) { 1032 byte[] imageByteArray = Base64.decode(parser.getText(), 0); 1033 return BitmapFactory.decodeByteArray(imageByteArray, 0, imageByteArray.length); 1034 } 1035 1036 protected Icon readIcon(XmlPullParser parser) throws IOException { 1037 byte[] iconByteArray = Base64.decode(parser.getText(), 0); 1038 ByteArrayInputStream stream = new ByteArrayInputStream(iconByteArray); 1039 return Icon.createFromStream(stream); 1040 } 1041 } 1042 1043 @VisibleForTesting 1044 public static final XmlSerialization<State> sStateXml = 1045 new XmlSerialization<State>() { 1046 private static final String CLASS_STATE = "phone_account_registrar_state"; 1047 private static final String DEFAULT_OUTGOING = "default_outgoing"; 1048 private static final String ACCOUNTS = "accounts"; 1049 private static final String VERSION = "version"; 1050 1051 @Override 1052 public void writeToXml(State o, XmlSerializer serializer, Context context) 1053 throws IOException { 1054 if (o != null) { 1055 serializer.startTag(null, CLASS_STATE); 1056 serializer.attribute(null, VERSION, Objects.toString(EXPECTED_STATE_VERSION)); 1057 1058 if (o.defaultOutgoing != null) { 1059 serializer.startTag(null, DEFAULT_OUTGOING); 1060 sPhoneAccountHandleXml.writeToXml(o.defaultOutgoing, serializer, context); 1061 serializer.endTag(null, DEFAULT_OUTGOING); 1062 } 1063 1064 serializer.startTag(null, ACCOUNTS); 1065 for (PhoneAccount m : o.accounts) { 1066 sPhoneAccountXml.writeToXml(m, serializer, context); 1067 } 1068 serializer.endTag(null, ACCOUNTS); 1069 1070 serializer.endTag(null, CLASS_STATE); 1071 } 1072 } 1073 1074 @Override 1075 public State readFromXml(XmlPullParser parser, int version, Context context) 1076 throws IOException, XmlPullParserException { 1077 if (parser.getName().equals(CLASS_STATE)) { 1078 State s = new State(); 1079 1080 String rawVersion = parser.getAttributeValue(null, VERSION); 1081 s.versionNumber = TextUtils.isEmpty(rawVersion) ? 1 : 1082 Integer.parseInt(rawVersion); 1083 1084 int outerDepth = parser.getDepth(); 1085 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1086 if (parser.getName().equals(DEFAULT_OUTGOING)) { 1087 parser.nextTag(); 1088 s.defaultOutgoing = sPhoneAccountHandleXml.readFromXml(parser, 1089 s.versionNumber, context); 1090 } else if (parser.getName().equals(ACCOUNTS)) { 1091 int accountsDepth = parser.getDepth(); 1092 while (XmlUtils.nextElementWithin(parser, accountsDepth)) { 1093 PhoneAccount account = sPhoneAccountXml.readFromXml(parser, 1094 s.versionNumber, context); 1095 1096 if (account != null && s.accounts != null) { 1097 s.accounts.add(account); 1098 } 1099 } 1100 } 1101 } 1102 return s; 1103 } 1104 return null; 1105 } 1106 }; 1107 1108 @VisibleForTesting 1109 public static final XmlSerialization<PhoneAccount> sPhoneAccountXml = 1110 new XmlSerialization<PhoneAccount>() { 1111 private static final String CLASS_PHONE_ACCOUNT = "phone_account"; 1112 private static final String ACCOUNT_HANDLE = "account_handle"; 1113 private static final String ADDRESS = "handle"; 1114 private static final String SUBSCRIPTION_ADDRESS = "subscription_number"; 1115 private static final String CAPABILITIES = "capabilities"; 1116 private static final String ICON_RES_ID = "icon_res_id"; 1117 private static final String ICON_PACKAGE_NAME = "icon_package_name"; 1118 private static final String ICON_BITMAP = "icon_bitmap"; 1119 private static final String ICON_TINT = "icon_tint"; 1120 private static final String HIGHLIGHT_COLOR = "highlight_color"; 1121 private static final String LABEL = "label"; 1122 private static final String SHORT_DESCRIPTION = "short_description"; 1123 private static final String SUPPORTED_URI_SCHEMES = "supported_uri_schemes"; 1124 private static final String ICON = "icon"; 1125 private static final String ENABLED = "enabled"; 1126 1127 @Override 1128 public void writeToXml(PhoneAccount o, XmlSerializer serializer, Context context) 1129 throws IOException { 1130 if (o != null) { 1131 serializer.startTag(null, CLASS_PHONE_ACCOUNT); 1132 1133 if (o.getAccountHandle() != null) { 1134 serializer.startTag(null, ACCOUNT_HANDLE); 1135 sPhoneAccountHandleXml.writeToXml(o.getAccountHandle(), serializer, context); 1136 serializer.endTag(null, ACCOUNT_HANDLE); 1137 } 1138 1139 writeTextIfNonNull(ADDRESS, o.getAddress(), serializer); 1140 writeTextIfNonNull(SUBSCRIPTION_ADDRESS, o.getSubscriptionAddress(), serializer); 1141 writeTextIfNonNull(CAPABILITIES, Integer.toString(o.getCapabilities()), serializer); 1142 writeIconIfNonNull(ICON, o.getIcon(), serializer); 1143 writeTextIfNonNull(HIGHLIGHT_COLOR, 1144 Integer.toString(o.getHighlightColor()), serializer); 1145 writeTextIfNonNull(LABEL, o.getLabel(), serializer); 1146 writeTextIfNonNull(SHORT_DESCRIPTION, o.getShortDescription(), serializer); 1147 writeStringList(SUPPORTED_URI_SCHEMES, o.getSupportedUriSchemes(), serializer); 1148 writeTextIfNonNull(ENABLED, o.isEnabled() ? "true" : "false" , serializer); 1149 1150 serializer.endTag(null, CLASS_PHONE_ACCOUNT); 1151 } 1152 } 1153 1154 public PhoneAccount readFromXml(XmlPullParser parser, int version, Context context) 1155 throws IOException, XmlPullParserException { 1156 if (parser.getName().equals(CLASS_PHONE_ACCOUNT)) { 1157 int outerDepth = parser.getDepth(); 1158 PhoneAccountHandle accountHandle = null; 1159 Uri address = null; 1160 Uri subscriptionAddress = null; 1161 int capabilities = 0; 1162 int iconResId = PhoneAccount.NO_RESOURCE_ID; 1163 String iconPackageName = null; 1164 Bitmap iconBitmap = null; 1165 int iconTint = PhoneAccount.NO_ICON_TINT; 1166 int highlightColor = PhoneAccount.NO_HIGHLIGHT_COLOR; 1167 String label = null; 1168 String shortDescription = null; 1169 List<String> supportedUriSchemes = null; 1170 Icon icon = null; 1171 boolean enabled = false; 1172 1173 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1174 if (parser.getName().equals(ACCOUNT_HANDLE)) { 1175 parser.nextTag(); 1176 accountHandle = sPhoneAccountHandleXml.readFromXml(parser, version, 1177 context); 1178 } else if (parser.getName().equals(ADDRESS)) { 1179 parser.next(); 1180 address = Uri.parse(parser.getText()); 1181 } else if (parser.getName().equals(SUBSCRIPTION_ADDRESS)) { 1182 parser.next(); 1183 String nextText = parser.getText(); 1184 subscriptionAddress = nextText == null ? null : Uri.parse(nextText); 1185 } else if (parser.getName().equals(CAPABILITIES)) { 1186 parser.next(); 1187 capabilities = Integer.parseInt(parser.getText()); 1188 } else if (parser.getName().equals(ICON_RES_ID)) { 1189 parser.next(); 1190 iconResId = Integer.parseInt(parser.getText()); 1191 } else if (parser.getName().equals(ICON_PACKAGE_NAME)) { 1192 parser.next(); 1193 iconPackageName = parser.getText(); 1194 } else if (parser.getName().equals(ICON_BITMAP)) { 1195 parser.next(); 1196 iconBitmap = readBitmap(parser); 1197 } else if (parser.getName().equals(ICON_TINT)) { 1198 parser.next(); 1199 iconTint = Integer.parseInt(parser.getText()); 1200 } else if (parser.getName().equals(HIGHLIGHT_COLOR)) { 1201 parser.next(); 1202 highlightColor = Integer.parseInt(parser.getText()); 1203 } else if (parser.getName().equals(LABEL)) { 1204 parser.next(); 1205 label = parser.getText(); 1206 } else if (parser.getName().equals(SHORT_DESCRIPTION)) { 1207 parser.next(); 1208 shortDescription = parser.getText(); 1209 } else if (parser.getName().equals(SUPPORTED_URI_SCHEMES)) { 1210 supportedUriSchemes = readStringList(parser); 1211 } else if (parser.getName().equals(ICON)) { 1212 parser.next(); 1213 icon = readIcon(parser); 1214 } else if (parser.getName().equals(ENABLED)) { 1215 parser.next(); 1216 enabled = "true".equalsIgnoreCase(parser.getText()); 1217 } 1218 } 1219 1220 ComponentName pstnComponentName = new ComponentName("com.android.phone", 1221 "com.android.services.telephony.TelephonyConnectionService"); 1222 ComponentName sipComponentName = new ComponentName("com.android.phone", 1223 "com.android.services.telephony.sip.SipConnectionService"); 1224 1225 // Upgrade older phone accounts to specify the supported URI schemes. 1226 if (version < 2) { 1227 supportedUriSchemes = new ArrayList<>(); 1228 1229 // Handle the SIP connection service. 1230 // Check the system settings to see if it also should handle "tel" calls. 1231 if (accountHandle.getComponentName().equals(sipComponentName)) { 1232 boolean useSipForPstn = useSipForPstnCalls(context); 1233 supportedUriSchemes.add(PhoneAccount.SCHEME_SIP); 1234 if (useSipForPstn) { 1235 supportedUriSchemes.add(PhoneAccount.SCHEME_TEL); 1236 } 1237 } else { 1238 supportedUriSchemes.add(PhoneAccount.SCHEME_TEL); 1239 supportedUriSchemes.add(PhoneAccount.SCHEME_VOICEMAIL); 1240 } 1241 } 1242 1243 // Upgrade older phone accounts with explicit package name 1244 if (version < 5) { 1245 if (iconBitmap == null) { 1246 iconPackageName = accountHandle.getComponentName().getPackageName(); 1247 } 1248 } 1249 1250 if (version < 6) { 1251 // Always enable all SIP accounts on upgrade to version 6 1252 if (accountHandle.getComponentName().equals(sipComponentName)) { 1253 enabled = true; 1254 } 1255 } 1256 if (version < 7) { 1257 // Always enabled all PSTN acocunts on upgrade to version 7 1258 if (accountHandle.getComponentName().equals(pstnComponentName)) { 1259 enabled = true; 1260 } 1261 } 1262 1263 PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, label) 1264 .setAddress(address) 1265 .setSubscriptionAddress(subscriptionAddress) 1266 .setCapabilities(capabilities) 1267 .setShortDescription(shortDescription) 1268 .setSupportedUriSchemes(supportedUriSchemes) 1269 .setHighlightColor(highlightColor) 1270 .setIsEnabled(enabled); 1271 1272 if (icon != null) { 1273 builder.setIcon(icon); 1274 } else if (iconBitmap != null) { 1275 builder.setIcon(Icon.createWithBitmap(iconBitmap)); 1276 } else if (!TextUtils.isEmpty(iconPackageName)) { 1277 builder.setIcon(Icon.createWithResource(iconPackageName, iconResId)); 1278 // TODO: Need to set tint. 1279 } 1280 1281 return builder.build(); 1282 } 1283 return null; 1284 } 1285 1286 /** 1287 * Determines if the SIP call settings specify to use SIP for all calls, including PSTN 1288 * calls. 1289 * 1290 * @param context The context. 1291 * @return {@code True} if SIP should be used for all calls. 1292 */ 1293 private boolean useSipForPstnCalls(Context context) { 1294 String option = Settings.System.getString(context.getContentResolver(), 1295 Settings.System.SIP_CALL_OPTIONS); 1296 option = (option != null) ? option : Settings.System.SIP_ADDRESS_ONLY; 1297 return option.equals(Settings.System.SIP_ALWAYS); 1298 } 1299 }; 1300 1301 @VisibleForTesting 1302 public static final XmlSerialization<PhoneAccountHandle> sPhoneAccountHandleXml = 1303 new XmlSerialization<PhoneAccountHandle>() { 1304 private static final String CLASS_PHONE_ACCOUNT_HANDLE = "phone_account_handle"; 1305 private static final String COMPONENT_NAME = "component_name"; 1306 private static final String ID = "id"; 1307 private static final String USER_SERIAL_NUMBER = "user_serial_number"; 1308 1309 @Override 1310 public void writeToXml(PhoneAccountHandle o, XmlSerializer serializer, Context context) 1311 throws IOException { 1312 if (o != null) { 1313 serializer.startTag(null, CLASS_PHONE_ACCOUNT_HANDLE); 1314 1315 if (o.getComponentName() != null) { 1316 writeTextIfNonNull( 1317 COMPONENT_NAME, o.getComponentName().flattenToString(), serializer); 1318 } 1319 1320 writeTextIfNonNull(ID, o.getId(), serializer); 1321 1322 if (o.getUserHandle() != null && context != null) { 1323 UserManager userManager = UserManager.get(context); 1324 writeLong(USER_SERIAL_NUMBER, 1325 userManager.getSerialNumberForUser(o.getUserHandle()), serializer); 1326 } 1327 1328 serializer.endTag(null, CLASS_PHONE_ACCOUNT_HANDLE); 1329 } 1330 } 1331 1332 @Override 1333 public PhoneAccountHandle readFromXml(XmlPullParser parser, int version, Context context) 1334 throws IOException, XmlPullParserException { 1335 if (parser.getName().equals(CLASS_PHONE_ACCOUNT_HANDLE)) { 1336 String componentNameString = null; 1337 String idString = null; 1338 String userSerialNumberString = null; 1339 int outerDepth = parser.getDepth(); 1340 1341 UserManager userManager = UserManager.get(context); 1342 1343 while (XmlUtils.nextElementWithin(parser, outerDepth)) { 1344 if (parser.getName().equals(COMPONENT_NAME)) { 1345 parser.next(); 1346 componentNameString = parser.getText(); 1347 } else if (parser.getName().equals(ID)) { 1348 parser.next(); 1349 idString = parser.getText(); 1350 } else if (parser.getName().equals(USER_SERIAL_NUMBER)) { 1351 parser.next(); 1352 userSerialNumberString = parser.getText(); 1353 } 1354 } 1355 if (componentNameString != null) { 1356 UserHandle userHandle = null; 1357 if (userSerialNumberString != null) { 1358 try { 1359 long serialNumber = Long.parseLong(userSerialNumberString); 1360 userHandle = userManager.getUserForSerialNumber(serialNumber); 1361 } catch (NumberFormatException e) { 1362 Log.e(this, e, "Could not parse UserHandle " + userSerialNumberString); 1363 } 1364 } 1365 return new PhoneAccountHandle( 1366 ComponentName.unflattenFromString(componentNameString), 1367 idString, 1368 userHandle); 1369 } 1370 } 1371 return null; 1372 } 1373 }; 1374} 1375