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