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