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