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