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