Clean up PhoneAccountRegistrar.

Cleanup lots of duplicate code and consolidate all phone account
retrievals into a single method: getPhoneAccounts(...).

Consolidate checks for capabilities, package-name, uri, and
isVisibleForUser(...). This will be very helpful when
implementing enable/disable for phone accounts.

Bug: 20303449
Change-Id: I24d63cd313876f5d9390f2eb31c0ce79d7cb0ab2
diff --git a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
index 0dec0f4..dd2e5dd 100644
--- a/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
+++ b/src/com/android/server/telecom/BluetoothPhoneServiceImpl.java
@@ -742,13 +742,15 @@
         PhoneAccount account = null;
         if (call != null) {
             // First try to get the network name of the foreground call.
-            account = mPhoneAccountRegistrar.getPhoneAccount(call.getTargetPhoneAccount());
+            account = mPhoneAccountRegistrar.getPhoneAccountCheckCallingUser(
+                    call.getTargetPhoneAccount());
         }
 
         if (account == null) {
             // Second, Try to get the label for the default Phone Account.
-            account = mPhoneAccountRegistrar.getPhoneAccount(
-                    mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL));
+            account = mPhoneAccountRegistrar.getPhoneAccountCheckCallingUser(
+                    mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(
+                        PhoneAccount.SCHEME_TEL));
         }
         return account;
     }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 182590d..a638a3e 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -547,12 +547,8 @@
         if (phoneAccountHandle == null) {
             // No preset account, check if default exists that supports the URI scheme for the
             // handle.
-            PhoneAccountHandle defaultAccountHandle =
-                    mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(
-                            handle.getScheme());
-            if (defaultAccountHandle != null) {
-                phoneAccountHandle = defaultAccountHandle;
-            }
+            phoneAccountHandle =
+                    mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme());
         }
 
         call.setTargetPhoneAccount(phoneAccountHandle);
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 854a645..82b99cc 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -957,15 +957,11 @@
         // Make a list of ConnectionServices that are listed as being associated with SIM accounts
         final Set<ConnectionServiceWrapper> simServices = Collections.newSetFromMap(
                 new ConcurrentHashMap<ConnectionServiceWrapper, Boolean>(8, 0.9f, 1));
-        for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getCallCapablePhoneAccounts()) {
-            PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount(handle);
-            if ((account.getCapabilities() & PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) != 0) {
-                ConnectionServiceWrapper service =
-                        mConnectionServiceRepository.getService(handle.getComponentName(),
-                                handle.getUserHandle());
-                if (service != null) {
-                    simServices.add(service);
-                }
+        for (PhoneAccountHandle handle : mPhoneAccountRegistrar.getSimPhoneAccounts()) {
+            ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
+                    handle.getComponentName(), handle.getUserHandle());
+            if (service != null) {
+                simServices.add(service);
             }
         }
 
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 13ee4e9..c90f9de 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -168,7 +168,7 @@
         if (mAttemptRecordIterator.hasNext()) {
             attempt = mAttemptRecordIterator.next();
 
-            if (!mPhoneAccountRegistrar.phoneAccountHasPermission(
+            if (!mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
                     attempt.connectionManagerPhoneAccount)) {
                 Log.w(this,
                         "Connection mgr does not have BIND_CONNECTION_SERVICE for attempt: %s",
@@ -178,9 +178,10 @@
             }
 
             // If the target PhoneAccount differs from the ConnectionManager phone acount, ensure it
-            // also has BIND_CONNECTION_SERVICE permission.
+            // also requires the BIND_CONNECTION_SERVICE permission.
             if (!attempt.connectionManagerPhoneAccount.equals(attempt.targetPhoneAccount) &&
-                    !mPhoneAccountRegistrar.phoneAccountHasPermission(attempt.targetPhoneAccount)) {
+                    !mPhoneAccountRegistrar.phoneAccountRequiresBindPermission(
+                            attempt.targetPhoneAccount)) {
                 Log.w(this,
                         "Target PhoneAccount does not have BIND_CONNECTION_SERVICE for attempt: %s",
                         attempt);
@@ -265,7 +266,8 @@
         }
 
         // Connection managers are only allowed to manage SIM subscriptions.
-        PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount(
+        // TODO: Should this really be checking the "calling user" test for phone account?
+        PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccountCheckCallingUser(
                 targetPhoneAccountHandle);
         if (targetPhoneAccount == null) {
             Log.d(this, "shouldSetConnectionManager, phone account not found");
@@ -328,13 +330,14 @@
             // Next, add the connection manager account as a backup if it can place emergency calls.
             PhoneAccountHandle callManagerHandle = mPhoneAccountRegistrar.getSimCallManager();
             if (mShouldUseConnectionManager && callManagerHandle != null) {
+                // TODO: Should this really be checking the "calling user" test for phone account?
                 PhoneAccount callManager = mPhoneAccountRegistrar
-                        .getPhoneAccount(callManagerHandle);
+                        .getPhoneAccountCheckCallingUser(callManagerHandle);
                 if (callManager != null && callManager.hasCapabilities(
                         PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)) {
                     CallAttemptRecord callAttemptRecord = new CallAttemptRecord(callManagerHandle,
                             mPhoneAccountRegistrar.
-                                    getDefaultOutgoingPhoneAccount(mCall.getHandle().getScheme())
+                                    getOutgoingPhoneAccountForScheme(mCall.getHandle().getScheme())
                     );
 
                     if (!mAttemptRecords.contains(callAttemptRecord)) {
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 759a31d..dd1fd6e 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -74,10 +74,34 @@
 
 /**
  * Handles writing and reading PhoneAccountHandle registration entries. This is a simple verbatim
- * delegate for all the account handling methods on {@link android.telecom.TelecomManager} as implemented in
- * {@link TelecomServiceImpl}, with the notable exception that {@link TelecomServiceImpl} is
- * responsible for security checking to make sure that the caller has proper authority over
- * the {@code ComponentName}s they are declaring in their {@code PhoneAccountHandle}s.
+ * delegate for all the account handling methods on {@link android.telecom.TelecomManager} as
+ * implemented in {@link TelecomServiceImpl}, with the notable exception that
+ * {@link TelecomServiceImpl} is responsible for security checking to make sure that the caller has
+ * proper authority over the {@code ComponentName}s they are declaring in their
+ * {@code PhoneAccountHandle}s.
+ *
+ *
+ *  -- About Users and Phone Accounts --
+ *
+ * When it comes to PhoneAccounts, we store all phone account in a single place,
+ * which means that there are three users that we deal with:
+ * 1) The Android User that is currently active on the device.
+ * 2) The user which owns/registers the phone account.
+ * 3) The user running the app that is requesting the phone account information.
+ *
+ * For example, I have a device with 2 users, primary (A) and secondary (B), and the secondary user
+ * has a work profile running as another user (B2). Lets say that user B opens the phone settings
+ * (not currently supported, but theoretically speaking), and phone settings queries for a phone
+ * account list. Lets also say that an app running in the work profile has registered a phone account.
+ * This means that:
+ *
+ * Since phone settings always runs as the primary user, We have the following situation:
+ * User A (settings) is requesting a list of phone accounts while the active user is User B, and that
+ * list contains a phone account for profile User B2.
+ *
+ * In practice, (2) is stored with the phone account handle and is part of the handle's ID. (1) is
+ * saved in {@link #mCurrentUserHandle} and (3) we get from Binder.getCallingUser(). We check these
+ * users for visibility before returning any phone accounts.
  */
 public final class PhoneAccountRegistrar {
 
@@ -139,11 +163,9 @@
      * @return The value of the subscription id or -1 if it does not exist or is not valid.
      */
     public int getSubscriptionIdForPhoneAccount(PhoneAccountHandle accountHandle) {
-        PhoneAccount account = getPhoneAccountInternal(accountHandle);
+        PhoneAccount account = getPhoneAccountCheckCallingUser(accountHandle);
 
-        if (account != null
-                && account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
-                && isVisibleForUser(accountHandle)) {
+        if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
             TelephonyManager tm =
                     (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
             return tm.getSubIdForPhoneAccount(account);
@@ -159,15 +181,14 @@
      * @param uriScheme The URI scheme for the outgoing call.
      * @return The {@link PhoneAccountHandle} to use.
      */
-    public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
+    public PhoneAccountHandle getOutgoingPhoneAccountForScheme(String uriScheme) {
         final PhoneAccountHandle userSelected = getUserSelectedOutgoingPhoneAccount();
 
         if (userSelected != null) {
             // If there is a default PhoneAccount, ensure it supports calls to handles with the
             // specified uriScheme.
-            final PhoneAccount userSelectedAccount = getPhoneAccountInternal(userSelected);
-            if (userSelectedAccount.supportsUriScheme(uriScheme)
-                    && isVisibleForUser(userSelected)) {
+            final PhoneAccount userSelectedAccount = getPhoneAccountCheckCallingUser(userSelected);
+            if (userSelectedAccount.supportsUriScheme(uriScheme)) {
                 return userSelected;
             }
         }
@@ -179,11 +200,7 @@
                 return null;
             case 1:
                 // There is only one account, which is by definition the default.
-                PhoneAccountHandle onlyHandle = outgoing.get(0);
-                if (isVisibleForUser(onlyHandle)) {
-                    return outgoing.get(0);
-                }
-                return null;
+                return outgoing.get(0);
             default:
                 // There are multiple accounts with no selected default
                 return null;
@@ -195,49 +212,37 @@
      *      if it was set by another user).
      */
     PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
-        if (mState.defaultOutgoing != null) {
-            // Return the registered outgoing default iff it still exists (we keep a sticky
-            // default to survive account deletion and re-addition)
-            for (int i = 0; i < mState.accounts.size(); i++) {
-                if (mState.accounts.get(i).getAccountHandle().equals(mState.defaultOutgoing)
-                        && isVisibleForUser(mState.defaultOutgoing)) {
-                    return mState.defaultOutgoing;
-                }
-            }
-            // At this point, there was a registered default but it has been deleted; proceed
-            // as though there were no default
+        PhoneAccount account = getPhoneAccountCheckCallingUser(mState.defaultOutgoing);
+        if (account != null) {
+            return mState.defaultOutgoing;
         }
         return null;
     }
 
+    /**
+     * Sets the phone account with which to place all calls by default. Set by the user
+     * within phone settings.
+     */
     public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
         if (accountHandle == null) {
             // Asking to clear the default outgoing is a valid request
             mState.defaultOutgoing = null;
         } else {
-            boolean found = false;
-            for (PhoneAccount m : mState.accounts) {
-                if (Objects.equals(accountHandle, m.getAccountHandle())) {
-                    found = true;
-                    break;
-                }
-            }
-
-            if (!found) {
+            // TODO: Do we really want to return for *any* user?
+            PhoneAccount account = getPhoneAccount(accountHandle);
+            if (account == null) {
                 Log.w(this, "Trying to set nonexistent default outgoing %s",
                         accountHandle);
                 return;
             }
 
-            if (!getPhoneAccountInternal(accountHandle).hasCapabilities(
-                    PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
+            if (!account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)) {
                 Log.w(this, "Trying to set non-call-provider default outgoing %s",
                         accountHandle);
                 return;
             }
 
-            if (getPhoneAccountInternal(accountHandle).hasCapabilities(
-                    PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+            if (account.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
                 // If the account selected is a SIM account, propagate down to the subscription
                 // record.
                 int subId = getSubscriptionIdForPhoneAccount(accountHandle);
@@ -258,7 +263,8 @@
 
     public void setSimCallManager(PhoneAccountHandle callManager) {
         if (callManager != null) {
-            PhoneAccount callManagerAccount = getPhoneAccountInternal(callManager);
+            // TODO: Do we really want to return for *any* user?
+            PhoneAccount callManagerAccount = getPhoneAccount(callManager);
             if (callManagerAccount == null) {
                 Log.d(this, "setSimCallManager: Nonexistent call manager: %s", callManager);
                 return;
@@ -280,22 +286,15 @@
      * @return The {@link PhoneAccount}s which are visible to {@link #mCurrentUserHandle}.
      */
     public PhoneAccountHandle getSimCallManager() {
-        if (mState.simCallManager != null) {
-            if (NO_ACCOUNT_SELECTED.equals(mState.simCallManager)) {
-                return null;
-            }
-            // Return the registered sim call manager iff it still exists (we keep a sticky
-            // setting to survive account deletion and re-addition)
-            for (int i = 0; i < mState.accounts.size(); i++) {
-                if (mState.accounts.get(i).getAccountHandle().equals(mState.simCallManager)
-                        && !resolveComponent(mState.simCallManager).isEmpty()
-                        && isVisibleForUser(mState.simCallManager)) {
-                    return mState.simCallManager;
-                }
-            }
+        PhoneAccount account = getPhoneAccountCheckCallingUser(mState.simCallManager);
+
+        // Return the registered sim call manager iff it still exists (we keep a sticky
+        // setting to survive account deletion and re-addition)
+        if (account != null && !resolveComponent(mState.simCallManager).isEmpty()) {
+            return mState.simCallManager;
         }
 
-        // See if the OEM has specified a default one.
+        // We have no set call manager, but check to see if the OEM has specified a default one.
         String defaultConnectionMgr =
                 mContext.getResources().getString(R.string.default_connection_manager_component);
         if (!TextUtils.isEmpty(defaultConnectionMgr)) {
@@ -314,8 +313,7 @@
                 // See if there is registered PhoneAccount by this component.
                 List<PhoneAccountHandle> handles = getAllPhoneAccountHandles();
                 for (PhoneAccountHandle handle : handles) {
-                    if (componentName.equals(handle.getComponentName())
-                            && isVisibleForUser(handle)) {
+                    if (componentName.equals(handle.getComponentName())) {
                         return handle;
                     }
                 }
@@ -331,24 +329,11 @@
     }
 
     /**
-     * A version of {@link #getPhoneAccount} which does not guard for the current user.
-     *
-     * @param handle
-     * @return
-     */
-    PhoneAccount getPhoneAccountInternal(PhoneAccountHandle handle) {
-        for (PhoneAccount m : mState.accounts) {
-            if (Objects.equals(handle, m.getAccountHandle())) {
-                return m;
-            }
-        }
-        return null;
-    }
-
-    /**
      * Update the current UserHandle to track when users are switched. This will allow the
      * PhoneAccountRegistar to self-filter the PhoneAccounts to make sure we don't leak anything
      * across users.
+     * We cannot simply check the calling user because that would always return the primary user for
+     * all invocations originating with the system process.
      *
      * @param userHandle The {@link UserHandle}, as delivered by
      *          {@link Intent#ACTION_USER_SWITCHED}.
@@ -362,14 +347,6 @@
         mCurrentUserHandle = userHandle;
     }
 
-    private boolean isVisibleForUser(PhoneAccountHandle accountHandle) {
-        if (accountHandle == null) {
-            return false;
-        }
-
-        return isVisibleForUser(getPhoneAccountInternal(accountHandle));
-    }
-
     private boolean isVisibleForUser(PhoneAccount account) {
         if (account == null) {
             return false;
@@ -395,6 +372,7 @@
             return true;
         }
 
+        // Special check for work profiles.
         // Unlike in TelecomServiceImpl, we only care about *profiles* here. We want to make sure
         // that we don't resolve PhoneAccount across *users*, but resolving across *profiles* is
         // fine.
@@ -439,32 +417,11 @@
      * @return The list of {@link PhoneAccountHandle}s.
      */
     public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
-        List<PhoneAccountHandle> accountHandles = new ArrayList<>();
-        for (PhoneAccount m : mState.accounts) {
-            if (isVisibleForUser(m)) {
-                accountHandles.add(m.getAccountHandle());
-            }
-        }
-        return accountHandles;
+        return getPhoneAccountHandles(0, null, null);
     }
 
     public List<PhoneAccount> getAllPhoneAccounts() {
-        List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size());
-        for (PhoneAccount account : mState.accounts) {
-            if (isVisibleForUser(account)) {
-                accounts.add(account);
-            }
-        }
-        return accounts;
-    }
-
-    /**
-     * Retrieves a list of all call provider phone accounts.
-     *
-     * @return The phone account handles.
-     */
-    public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
-        return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CALL_PROVIDER);
+        return getPhoneAccounts(0, null, null);
     }
 
     /**
@@ -475,7 +432,16 @@
      * @return The phone account handles.
      */
     public List<PhoneAccountHandle> getCallCapablePhoneAccounts(String uriScheme) {
-        return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CALL_PROVIDER, uriScheme);
+        return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CALL_PROVIDER, uriScheme, null);
+    }
+
+    /**
+     * Retrieves a list of all the SIM-based phone accounts.
+     */
+    public List<PhoneAccountHandle> getSimPhoneAccounts() {
+        return getPhoneAccountHandles(
+                PhoneAccount.CAPABILITY_CALL_PROVIDER | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION,
+                null, null);
     }
 
     /**
@@ -485,16 +451,7 @@
      * @return The phone account handles.
      */
     public List<PhoneAccountHandle> getPhoneAccountsForPackage(String packageName) {
-        List<PhoneAccountHandle> accountHandles = new ArrayList<>();
-        for (PhoneAccount m : mState.accounts) {
-            if (Objects.equals(
-                    packageName,
-                    m.getAccountHandle().getComponentName().getPackageName())
-                    && isVisibleForUser(m)) {
-                accountHandles.add(m.getAccountHandle());
-            }
-        }
-        return accountHandles;
+        return getPhoneAccountHandles(0, null, packageName);
     }
 
     /**
@@ -503,18 +460,7 @@
      * @return The phone account handles.
      */
     public List<PhoneAccountHandle> getConnectionManagerPhoneAccounts() {
-        return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CONNECTION_MANAGER,
-                null /* supportedUriScheme */);
-    }
-
-    public PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
-        for (PhoneAccount m : mState.accounts) {
-            if (Objects.equals(handle, m.getAccountHandle())
-                    && isVisibleForUser(m)) {
-                return m;
-            }
-        }
-        return null;
+        return getPhoneAccountHandles(PhoneAccount.CAPABILITY_CONNECTION_MANAGER, null, null);
     }
 
     // TODO: Should we implement an artificial limit for # of accounts associated with a single
@@ -522,7 +468,7 @@
     public void registerPhoneAccount(PhoneAccount account) {
         // Enforce the requirement that a connection service for a phone account has the correct
         // permission.
-        if (!phoneAccountHasPermission(account.getAccountHandle())) {
+        if (!phoneAccountRequiresBindPermission(account.getAccountHandle())) {
             Log.w(this, "Phone account %s does not have BIND_CONNECTION_SERVICE permission.",
                     account.getAccountHandle());
             throw new SecurityException(
@@ -541,32 +487,24 @@
         Log.d(this, "addOrReplacePhoneAccount(%s -> %s)",
                 account.getAccountHandle(), account);
 
-        mState.accounts.add(account);
-        // Search for duplicates and remove any that are found.
-        for (int i = 0; i < mState.accounts.size() - 1; i++) {
-            if (Objects.equals(
-                    account.getAccountHandle(), mState.accounts.get(i).getAccountHandle())) {
-                // replace existing entry.
-                mState.accounts.remove(i);
-                break;
-            }
+        PhoneAccount oldAccount = getPhoneAccount(account.getAccountHandle());
+        if (oldAccount != null) {
+            mState.accounts.remove(oldAccount);
         }
+        mState.accounts.add(account);
 
         write();
         fireAccountsChanged();
     }
 
     public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
-        for (int i = 0; i < mState.accounts.size(); i++) {
-            PhoneAccountHandle handle = mState.accounts.get(i).getAccountHandle();
-            if (Objects.equals(accountHandle, handle)) {
-                mState.accounts.remove(i);
-                break;
+        PhoneAccount account = getPhoneAccount(accountHandle);
+        if (account != null) {
+            if (mState.accounts.remove(account)) {
+                write();
+                fireAccountsChanged();
             }
         }
-
-        write();
-        fireAccountsChanged();
     }
 
     /**
@@ -629,13 +567,13 @@
     }
 
     /**
-     * Determines if the connection service specified by a {@link PhoneAccountHandle} has the
+     * Determines if the connection service specified by a {@link PhoneAccountHandle} requires the
      * {@link Manifest.permission#BIND_CONNECTION_SERVICE} permission.
      *
      * @param phoneAccountHandle The phone account to check.
      * @return {@code True} if the phone account has permission.
      */
-    public boolean phoneAccountHasPermission(PhoneAccountHandle phoneAccountHandle) {
+    public boolean phoneAccountRequiresBindPermission(PhoneAccountHandle phoneAccountHandle) {
         List<ResolveInfo> resolveInfos = resolveComponent(phoneAccountHandle);
         if (resolveInfos.isEmpty()) {
             Log.w(this, "phoneAccount %s not found", phoneAccountHandle.getComponentName());
@@ -643,37 +581,73 @@
         }
         for (ResolveInfo resolveInfo : resolveInfos) {
             ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-            if (serviceInfo == null || !Objects.equals(serviceInfo.permission,
-                    Manifest.permission.BIND_CONNECTION_SERVICE)) {
+            if (serviceInfo == null ||
+                    !Manifest.permission.BIND_CONNECTION_SERVICE.equals(serviceInfo.permission)) {
                 return false;
             }
         }
         return true;
     }
 
-    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //
+    // Methods for retrieving PhoneAccounts and PhoneAccountHandles
+    //
 
     /**
-     * Returns a list of phone account handles with the specified flag.
+     * Returns the PhoneAccount for the specified handle.  Does no user checking.
      *
-     * @param flags Flags which the {@code PhoneAccount} must have.
+     * @param handle
+     * @return The corresponding phone account if one exists.
      */
-    private List<PhoneAccountHandle> getPhoneAccountHandles(int flags) {
-        return getPhoneAccountHandles(flags, null);
+    PhoneAccount getPhoneAccount(PhoneAccountHandle handle) {
+        for (PhoneAccount m : mState.accounts) {
+            if (Objects.equals(handle, m.getAccountHandle())) {
+                return m;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Like getPhoneAccount, but checks to see if the current user is allowed to see the phone
+     * account before returning it. The current user is the active user on the actual android
+     * device.
+     */
+    public PhoneAccount getPhoneAccountCheckCallingUser(PhoneAccountHandle handle) {
+        PhoneAccount account = getPhoneAccount(handle);
+        if (account != null && isVisibleForUser(account)) {
+            return account;
+        }
+        return null;
+    }
+
+    /**
+     * Returns a list of phone account handles with the specified capabilities, uri scheme,
+     * and package name.
+     */
+    private List<PhoneAccountHandle> getPhoneAccountHandles(
+            int capabilities, String uriScheme, String packageName) {
+        List<PhoneAccountHandle> handles = new ArrayList<>();
+        for (PhoneAccount account : getPhoneAccounts(capabilities, uriScheme, packageName)) {
+            handles.add(account.getAccountHandle());
+        }
+        return handles;
     }
 
     /**
      * Returns a list of phone account handles with the specified flag, supporting the specified
-     * URI scheme.
+     * URI scheme, within the specified package name.
      *
-     * @param flags Flags which the {@code PhoneAccount} must have.
-     * @param uriScheme URI schemes the PhoneAccount must handle.  {@code Null} bypasses the
+     * @param capabilities Capabilities which the {@code PhoneAccount} must have. Ignored if 0.
+     * @param uriScheme URI schemes the PhoneAccount must handle.  {@code null} bypasses the
      *                  URI scheme check.
+     * @param packageName Package name of the PhoneAccount. {@code null} bypasses packageName check.
      */
-    private List<PhoneAccountHandle> getPhoneAccountHandles(int flags, String uriScheme) {
-        List<PhoneAccountHandle> accountHandles = new ArrayList<>();
+    private List<PhoneAccount> getPhoneAccounts(
+            int capabilities, String uriScheme, String packageName) {
+        List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size());
         for (PhoneAccount m : mState.accounts) {
-            if (!m.hasCapabilities(flags)) {
+            if (capabilities != 0 && !m.hasCapabilities(capabilities)) {
                 // Account doesn't have the right capabilities; skip this one.
                 continue;
             }
@@ -681,19 +655,30 @@
                 // Account doesn't support this URI scheme; skip this one.
                 continue;
             }
-            if (resolveComponent(m.getAccountHandle()).isEmpty()) {
+            PhoneAccountHandle handle = m.getAccountHandle();
+
+            if (resolveComponent(handle).isEmpty()) {
                 // This component cannot be resolved anymore; skip this one.
                 continue;
             }
+            if (packageName != null &&
+                    !packageName.equals(handle.getComponentName().getPackageName())) {
+                // Not the right package name; skip this one.
+                continue;
+            }
             if (!isVisibleForUser(m)) {
                 // Account is not visible for the current user; skip this one.
                 continue;
             }
-            accountHandles.add(m.getAccountHandle());
+            accounts.add(m);
         }
-        return accountHandles;
+        return accounts;
     }
 
+    //
+    // State Implementation for PhoneAccountRegistrar
+    //
+
     /**
      * The state of this {@code PhoneAccountRegistrar}.
      */
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index f9ee74c..ac9d4cc 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -68,7 +68,7 @@
                 long token = Binder.clearCallingIdentity();
                 try {
                     PhoneAccountHandle defaultOutgoingPhoneAccount =
-                            mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(uriScheme);
+                            mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(uriScheme);
                     // Make sure that the calling user can see this phone account.
                     if (defaultOutgoingPhoneAccount != null
                             && !isVisibleToCaller(defaultOutgoingPhoneAccount)) {
@@ -127,7 +127,7 @@
                 long token = Binder.clearCallingIdentity();
                 try {
                     return filterForAccountsVisibleToCaller(
-                            mPhoneAccountRegistrar.getCallCapablePhoneAccounts());
+                            mPhoneAccountRegistrar.getCallCapablePhoneAccounts(null));
                 } catch (Exception e) {
                     Log.e(this, e, "getCallCapablePhoneAccounts");
                     throw e;
@@ -179,7 +179,8 @@
                         Log.w(this, "%s is not visible for the calling user", accountHandle);
                         return null;
                     }
-                    return mPhoneAccountRegistrar.getPhoneAccountInternal(accountHandle);
+                    // TODO: Do we really want to return for *any* user?
+                    return mPhoneAccountRegistrar.getPhoneAccount(accountHandle);
                 } catch (Exception e) {
                     Log.e(this, e, "getPhoneAccount %s", accountHandle);
                     throw e;
@@ -833,9 +834,7 @@
         if (accountHandle == null) {
             return false;
         }
-
-        return isVisibleToCaller(mPhoneAccountRegistrar
-                .getPhoneAccountInternal(accountHandle));
+        return isVisibleToCaller(mPhoneAccountRegistrar.getPhoneAccount(accountHandle));
     }
 
     private boolean isVisibleToCaller(PhoneAccount account) {
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index a5fc04c..34575a7 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -130,9 +130,9 @@
                 .build());
 
         assertEquals(4, mRegistrar.getAllPhoneAccountHandles().size());
-        assertEquals(3, mRegistrar.getCallCapablePhoneAccounts().size());
+        assertEquals(3, mRegistrar.getCallCapablePhoneAccounts(null).size());
         assertEquals(null, mRegistrar.getSimCallManager());
-        assertEquals(null, mRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL));
+        assertEquals(null, mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL));
     }
 
     public void testSimCallManager() throws Exception {
@@ -180,7 +180,7 @@
                 Mockito.mock(IConnectionService.class));
 
         // By default, there is no default outgoing account (nothing has been registered)
-        assertNull(mRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL));
+        assertNull(mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL));
 
         // Register one tel: account
         PhoneAccountHandle telAccount = makeQuickAccountHandle("tel_acct");
@@ -189,7 +189,7 @@
                 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
                 .build());
         PhoneAccountHandle defaultAccount =
-                mRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
+                mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL);
         assertEquals(telAccount, defaultAccount);
 
         // Add a SIP account, make sure tel: doesn't change
@@ -198,9 +198,9 @@
                 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
                 .build());
-        defaultAccount = mRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_SIP);
+        defaultAccount = mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_SIP);
         assertEquals(sipAccount, defaultAccount);
-        defaultAccount = mRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
+        defaultAccount = mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL);
         assertEquals(telAccount, defaultAccount);
 
         // Add a connection manager, make sure tel: doesn't change
@@ -209,12 +209,12 @@
                 .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
                 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
                 .build());
-        defaultAccount = mRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
+        defaultAccount = mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL);
         assertEquals(telAccount, defaultAccount);
 
         // Unregister the tel: account, make sure there is no tel: default now.
         mRegistrar.unregisterPhoneAccount(telAccount);
-        assertNull(mRegistrar.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL));
+        assertNull(mRegistrar.getOutgoingPhoneAccountForScheme(PhoneAccount.SCHEME_TEL));
     }
 
     public void testPhoneAccountParceling() throws Exception {