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