Implement Account Discovery API.

Inludes temporary flow for notifications filtered by accountType
Bug: 33046496
Test: cts tests, manual tests.
Change-Id: I2d767030e851579a0666efd7e243a1239af740c7
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index b27fa24..def0ff9 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -53,9 +53,12 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.SuppressWarnings;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
@@ -864,11 +867,17 @@
      *
      * @param account The account for which visibility data should be returned.
      *
-     * @return Map from uid to visibility for given account
+     * @return Map from uid to visibility for given account.
      */
     public Map<Integer, Integer> getUidsAndVisibilityForAccount(Account account) {
-        // TODO implement.
-        return null;
+        try {
+            @SuppressWarnings("unchecked")
+            Map<Integer, Integer> result = (Map<Integer, Integer>) mService
+                    .getUidsAndVisibilityForAccount(account);
+            return result;
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -2110,10 +2119,23 @@
                 synchronized (mAccountsUpdatedListeners) {
                     try {
                         if (mAccountsUpdatedListeners.containsKey(listener)) {
-                            listener.onAccountsUpdated(accountsCopy);
+                            Set<String> types = mAccountsUpdatedListenersTypes.get(listener);
+                            if (types != null) {
+                                // filter by account type;
+                                ArrayList<Account> filtered = new ArrayList<>();
+                                for (Account account : accountsCopy) {
+                                    if (types.contains(account.type)) {
+                                        filtered.add(account);
+                                    }
+                                }
+                                listener.onAccountsUpdated(
+                                        filtered.toArray(new Account[filtered.size()]));
+                            } else {
+                                listener.onAccountsUpdated(accountsCopy);
+                            }
                         }
                     } catch (SQLException e) {
-                        // Better luck next time.  If the problem was disk-full,
+                        // Better luck next time. If the problem was disk-full,
                         // the STORAGE_OK intent will re-trigger the update.
                         Log.e(TAG, "Can't update accounts", e);
                     }
@@ -2759,6 +2781,9 @@
     private final HashMap<OnAccountsUpdateListener, Handler> mAccountsUpdatedListeners =
             Maps.newHashMap();
 
+    private final HashMap<OnAccountsUpdateListener, Set<String> > mAccountsUpdatedListenersTypes =
+            Maps.newHashMap();
+
     /**
      * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
      * so that it can read the updated list of accounts and send them to the listener
@@ -2784,7 +2809,7 @@
      * accounts of any type related to the caller. This method is equivalent to
      * addOnAccountsUpdatedListener(listener, handler, updateImmediately, null)
      *
-     * @see #addOnAccountsUpdatedListener(OnAccountsUpdateListener, Handler, boolean, Handler,
+     * @see #addOnAccountsUpdatedListener(OnAccountsUpdateListener, Handler, boolean,
      *      String[])
      */
     public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
@@ -2828,7 +2853,10 @@
             final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
 
             mAccountsUpdatedListeners.put(listener, handler);
-
+            if (accountTypes != null) {
+                mAccountsUpdatedListenersTypes.put(listener,
+                        new HashSet<String>(Arrays.asList(accountTypes)));
+            }
 
             if (wasEmpty) {
                 // Register a broadcast receiver to monitor account changes
@@ -2870,6 +2898,7 @@
                 return;
             }
             mAccountsUpdatedListeners.remove(listener);
+            mAccountsUpdatedListenersTypes.remove(listener);
             if (mAccountsUpdatedListeners.isEmpty()) {
                 mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
             }
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
index 242b3ea..16a45ba 100644
--- a/core/java/android/accounts/ChooseAccountActivity.java
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -52,7 +52,9 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
+        // TODO This activity is only used by getAuthTokenByFeatures and can not see
+        // VISIBILITY_USER_MANAGED_NOT_VISIBLE accounts. It should be moved to account managed
+        // service.
         mAccounts = getIntent().getParcelableArrayExtra(AccountManager.KEY_ACCOUNTS);
         mAccountManagerResponse =
                 getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_MANAGER_RESPONSE);
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 8c71f50..95fdfef 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -40,7 +40,9 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -110,7 +112,7 @@
     private static final String KEY_INSTANCE_STATE_EXISTING_ACCOUNTS = "existingAccounts";
     private static final String KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME = "selectedAccountName";
     private static final String KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT = "selectedAddAccount";
-    private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = "accountList";
+    private static final String KEY_INSTANCE_STATE_ACCOUNT_LIST = "accountAndVisibilityList";
 
     private static final int SELECTED_ITEM_NONE = -1;
 
@@ -120,7 +122,11 @@
     private boolean mSelectedAddNewAccount = false;
     private String mDescriptionOverride;
 
-    private ArrayList<Account> mAccounts;
+    private Map<Account, Integer> mAccounts;
+    // TODO Redesign flow to show NOT_VISIBLE accounts
+    // and display a warning if they are selected.
+    // Currently NOT_VISBILE accounts are not shown at all.
+    private ArrayList<Account> mPossiblyVisibleAccounts;
     private int mPendingRequest = REQUEST_NULL;
     private Parcelable[] mExistingAccounts = null;
     private int mSelectedItemIndex;
@@ -164,12 +170,12 @@
                     savedInstanceState.getParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS);
 
             // Makes sure that any user selection is preserved across orientation changes.
-            mSelectedAccountName = savedInstanceState.getString(
-                    KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
-
-            mSelectedAddNewAccount = savedInstanceState.getBoolean(
-                    KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
-            mAccounts = savedInstanceState.getParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST);
+            mSelectedAccountName =
+                    savedInstanceState.getString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME);
+            mSelectedAddNewAccount =
+                    savedInstanceState.getBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
+            mAccounts = (Map<Account, Integer>) savedInstanceState
+                    .getSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST);
         } else {
             mPendingRequest = REQUEST_NULL;
             mExistingAccounts = null;
@@ -220,9 +226,15 @@
             }
         }
 
-        String[] listItems = getListOfDisplayableOptions(mAccounts);
-        mSelectedItemIndex = getItemIndexToSelect(
-            mAccounts, mSelectedAccountName, mSelectedAddNewAccount);
+        mPossiblyVisibleAccounts = new ArrayList<>(mAccounts.size());
+        for (Map.Entry<Account, Integer> entry : mAccounts.entrySet()) {
+            if (AccountManager.VISIBILITY_NOT_VISIBLE != entry.getValue()) {
+                mPossiblyVisibleAccounts.add(entry.getKey());
+            }
+        }
+        String[] listItems = getListOfDisplayableOptions(mPossiblyVisibleAccounts);
+        mSelectedItemIndex = getItemIndexToSelect(mPossiblyVisibleAccounts, mSelectedAccountName,
+                mSelectedAddNewAccount);
 
         super.onCreate(savedInstanceState);
         setContentView(R.layout.choose_type_and_account);
@@ -250,15 +262,18 @@
             outState.putParcelableArray(KEY_INSTANCE_STATE_EXISTING_ACCOUNTS, mExistingAccounts);
         }
         if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
-            if (mSelectedItemIndex == mAccounts.size()) {
+            if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) {
                 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, true);
             } else {
                 outState.putBoolean(KEY_INSTANCE_STATE_SELECTED_ADD_ACCOUNT, false);
                 outState.putString(KEY_INSTANCE_STATE_SELECTED_ACCOUNT_NAME,
-                        mAccounts.get(mSelectedItemIndex).name);
+                        mPossiblyVisibleAccounts.get(mSelectedItemIndex).name);
             }
         }
-        outState.putParcelableArrayList(KEY_INSTANCE_STATE_ACCOUNT_LIST, mAccounts);
+        // should be HashMap by default.
+        HashMap<Account, Integer> accountsHashMap = (mAccounts instanceof HashMap)
+                ? (HashMap) mAccounts : new HashMap<Account, Integer>(mAccounts);
+        outState.putSerializable(KEY_INSTANCE_STATE_ACCOUNT_LIST, accountsHashMap);
     }
 
     public void onCancelButtonClicked(View view) {
@@ -266,11 +281,11 @@
     }
 
     public void onOkButtonClicked(View view) {
-        if (mSelectedItemIndex == mAccounts.size()) {
+        if (mSelectedItemIndex == mPossiblyVisibleAccounts.size()) {
             // Selected "Add New Account" option
             startChooseAccountTypeActivity();
         } else if (mSelectedItemIndex != SELECTED_ITEM_NONE) {
-            onAccountSelected(mAccounts.get(mSelectedItemIndex));
+            onAccountSelected(mPossiblyVisibleAccounts.get(mSelectedItemIndex));
         }
     }
 
@@ -321,6 +336,7 @@
                 }
 
                 if (accountName == null || accountType == null) {
+                    // new account was added.
                     Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage(
                             mCallingPackage, mCallingUid);
                     Set<Account> preExistingAccounts = new HashSet<Account>();
@@ -328,6 +344,7 @@
                         preExistingAccounts.add((Account) accountParcel);
                     }
                     for (Account account : currentAccounts) {
+                        // New account is visible to the app - return it.
                         if (!preExistingAccounts.contains(account)) {
                             accountName = account.name;
                             accountType = account.type;
@@ -409,13 +426,27 @@
     }
 
     private void setResultAndFinish(final String accountName, final String accountType) {
+        // Mark account as visible since user chose it.
+        Account account = new Account(accountName, accountType);
+        Integer oldVisibility = mAccounts.get(account);
+        // oldVisibility is null if new account was added
+        if (oldVisibility == null) {
+            Map<Account, Integer> accountsAndVisibility = AccountManager.get(this)
+                    .getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */);
+            oldVisibility = accountsAndVisibility.get(account);
+        }
+        if (oldVisibility != null
+                && oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE) {
+            AccountManager.get(this).setAccountVisibility(account, mCallingUid,
+                    AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
+        }
         Bundle bundle = new Bundle();
         bundle.putString(AccountManager.KEY_ACCOUNT_NAME, accountName);
         bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, accountType);
         setResult(Activity.RESULT_OK, new Intent().putExtras(bundle));
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: "
-                    + "selected account " + accountName + ", " + accountType);
+            Log.v(TAG, "ChooseTypeAndAccountActivity.setResultAndFinish: selected account "
+                    + accountName + ", " + accountType);
         }
         finish();
     }
@@ -474,25 +505,28 @@
     }
 
     /**
-     * Create a list of Account objects for each account that is acceptable. Filter out
-     * accounts that don't match the allowable types, if provided, or that don't match the
-     * allowable accounts, if provided.
+     * Create a list of Account objects for each account that is acceptable. Filter out accounts
+     * that don't match the allowable types, if provided, or that don't match the allowable
+     * accounts, if provided.
      */
-    private ArrayList<Account> getAcceptableAccountChoices(AccountManager accountManager) {
-      final Account[] accounts = accountManager.getAccountsForPackage(mCallingPackage,
-              mCallingUid);
-      ArrayList<Account> accountsToPopulate = new ArrayList<Account>(accounts.length);
-      for (Account account : accounts) {
-          if (mSetOfAllowableAccounts != null && !mSetOfAllowableAccounts.contains(account)) {
-              continue;
-          }
-          if (mSetOfRelevantAccountTypes != null
-                  && !mSetOfRelevantAccountTypes.contains(account.type)) {
-              continue;
-          }
-          accountsToPopulate.add(account);
-      }
-      return accountsToPopulate;
+    private Map<Account, Integer> getAcceptableAccountChoices(AccountManager accountManager) {
+        Map<Account, Integer> accountsAndVisibility =
+                accountManager.getAccountsAndVisibilityForPackage(mCallingPackage, null /* type */);
+
+        Map<Account, Integer> accountsToPopulate =
+                new HashMap<Account, Integer>(accountsAndVisibility.size());
+        for (Map.Entry<Account, Integer> entry : accountsAndVisibility.entrySet()) {
+            if (mSetOfAllowableAccounts != null
+                    && !mSetOfAllowableAccounts.contains(entry.getKey())) {
+                continue;
+            }
+            if (mSetOfRelevantAccountTypes != null
+                    && !mSetOfRelevantAccountTypes.contains(entry.getKey().type)) {
+                continue;
+            }
+            accountsToPopulate.put(entry.getKey(), entry.getValue());
+        }
+        return accountsToPopulate;
     }
 
     /**
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 66c3ca3..63a0919 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -108,8 +108,8 @@
     void isCredentialsUpdateSuggested(in IAccountManagerResponse response, in Account account,
         String statusToken);
 
-    /* Allows Authenticator to get UIDs of packages which registered to receive updates about given account type.*/
-    int[] getRequestingUidsForType(String accountType);
+    /* Returns Map<Integer, Integer> from UID to visibility with all values stored for given account*/
+    Map getUidsAndVisibilityForAccount(in Account account);
 
     boolean addAccountExplicitlyWithVisibility(in Account account, String password, in Bundle extras,
             in Map visibility);
@@ -117,7 +117,7 @@
     boolean setAccountVisibility(in Account a, int uid, int newVisibility);
     int getAccountVisibility(in Account a, int uid);
 
-    /* Type may be null  returns Map <Account, Integer>*/
+    /* Type may be null returns Map <Account, Integer>*/
     Map getAccountsAndVisibilityForPackage(in String packageName, in String accountType);
 
     /* Check if the package in a user can access an account */
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 0a6c62f..bfa3f04 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -63,8 +63,8 @@
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteStatement;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -76,6 +76,7 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -111,15 +112,16 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
-import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -174,17 +176,18 @@
     private static final String PRE_N_DATABASE_NAME = "accounts.db";
     private static final Intent ACCOUNTS_CHANGED_INTENT;
 
+    private static final int SIGNATURE_CHECK_MISMATCH = 0;
+    private static final int SIGNATURE_CHECK_MATCH = 1;
+    private static final int SIGNATURE_CHECK_UID_MATCH = 2;
+
     static {
         ACCOUNTS_CHANGED_INTENT = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
         ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
     }
 
-
     private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
     private final AtomicInteger mNotificationIds = new AtomicInteger(1);
 
-    private static final String NEW_ACCOUNT_VISIBLE = "android.accounts.NEW_ACCOUNT_VISIBLE";
-
     static class UserAccounts {
         private final int userId;
         final AccountsDb accountsDb;
@@ -204,6 +207,11 @@
         /** protected by the {@link #cacheLock} */
         private final TokenCache accountTokenCaches = new TokenCache();
 
+        /** protected by the {@link #cacheLock} */
+        // TODO use callback to set up the map.
+        private final Map<String, LinkedHashSet<String>> mApplicationAccountRequestMappings =
+                new HashMap<>();
+
         /**
          * protected by the {@link #cacheLock}
          *
@@ -261,8 +269,6 @@
 
         sThis.set(this);
 
-        addRequestsForPreInstalledApplications();
-
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
@@ -285,7 +291,7 @@
                         @Override
                         public void run() {
                             purgeOldGrantsAll();
-                            // TODO remove visibility entries.
+                            // Notify authenticator about removed app?
                         }
                     };
                     mHandler.post(purgingRunnable);
@@ -294,29 +300,6 @@
             }
         }, intentFilter);
 
-        IntentFilter packageAddedOrChangedFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageAddedOrChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageAddedOrChangedFilter.addDataScheme("package");
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context1, Intent intent) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        int uidOfInstalledApplication =
-                                intent.getIntExtra(Intent.EXTRA_UID, -1);
-                        if(uidOfInstalledApplication != -1) {
-                            registerAccountTypesSupported(
-                                    uidOfInstalledApplication,
-                                    getUserAccounts(
-                                    UserHandle.getUserId(uidOfInstalledApplication)));
-                        }
-                    }
-                });
-            }
-        }, UserHandle.ALL, packageAddedOrChangedFilter, null, null);
-
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
@@ -380,11 +363,13 @@
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     for (String packageName : packageNames) {
-                        if (mPackageManager.checkPermission(
-                                Manifest.permission.GET_ACCOUNTS, packageName)
-                                        != PackageManager.PERMISSION_GRANTED) {
-                            continue;
-                        }
+                                // if app asked for permission we need to cancel notification even
+                                // for O+ applications.
+                                if (mPackageManager.checkPermission(
+                                        Manifest.permission.GET_ACCOUNTS,
+                                        packageName) != PackageManager.PERMISSION_GRANTED) {
+                                    continue;
+                                }
 
                         if (accounts == null) {
                             accounts = getAccountsAsUser(null, userId, "android");
@@ -443,112 +428,376 @@
     }
 
     @Override
-    public boolean addAccountExplicitlyWithVisibility(Account account, String password, Bundle extras,
-            Map uidToVisibility) {
-        // TODO implementation
-        return false;
+    public boolean addAccountExplicitlyWithVisibility(Account account, String password,
+            Bundle extras, Map uidToVisibility) {
+        Bundle.setDefusable(extras, true);
+
+        final int callingUid = Binder.getCallingUid();
+        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+            Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + callingUid
+                    + ", pid " + Binder.getCallingPid());
+        }
+        Preconditions.checkNotNull(account, "account cannot be null");
+        int userId = UserHandle.getCallingUserId();
+        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
+            String msg = String.format("uid %s cannot explicitly add accounts of type: %s",
+                    callingUid, account.type);
+            throw new SecurityException(msg);
+        }
+        /*
+         * Child users are not allowed to add accounts. Only the accounts that are shared by the
+         * parent profile can be added to child profile.
+         *
+         * TODO: Only allow accounts that were shared to be added by a limited user.
+         */
+        // fails if the account already exists
+        long identityToken = clearCallingIdentity();
+        try {
+            UserAccounts accounts = getUserAccounts(userId);
+            return addAccountInternal(accounts, account, password, extras, callingUid,
+                    (Map<Integer, Integer>) uidToVisibility);
+        } finally {
+            restoreCallingIdentity(identityToken);
+        }
     }
 
     @Override
     public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
             String accountType) {
-        // TODO Implement.
-        return new HashMap<Account, Integer>();
+        int callingUid = Binder.getCallingUid();
+        boolean isSystemUid = UserHandle.isSameApp(callingUid, Process.SYSTEM_UID);
+        List<String> managedTypes =
+                getTypesForCaller(callingUid, UserHandle.getUserId(callingUid), isSystemUid);
+
+        if ((accountType != null && !managedTypes.contains(accountType))
+                || (accountType == null && !isSystemUid)) {
+            throw new SecurityException(
+                    "getAccountsAndVisibilityForPackage() called from unauthorized uid "
+                            + callingUid + " with packageName=" + packageName);
+        }
+        if (accountType != null) {
+            managedTypes = new ArrayList<String>();
+            managedTypes.add(accountType);
+        }
+
+        return getAccountsAndVisibilityForPackage(packageName, managedTypes, callingUid,
+                getUserAccounts(UserHandle.getUserId(callingUid)));
+    }
+
+    /*
+     * accountTypes may not be null
+     */
+    private Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
+            List<String> accountTypes, Integer callingUid, UserAccounts accounts) {
+        int uid = 0;
+        try {
+            uid = mPackageManager.getPackageUidAsUser(packageName,
+                    UserHandle.getUserId(callingUid));
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found " + e.getMessage());
+            return new HashMap<>();
+        }
+
+        Map<Account, Integer> result = new HashMap<>();
+        for (String accountType : accountTypes) {
+            synchronized (accounts.cacheLock) {
+                final Account[] accountsOfType = accounts.accountCache.get(accountType);
+                if (accountsOfType != null) {
+                    for (Account account : accountsOfType) {
+                        result.put(account,
+                                resolveAccountVisibility(account, uid, packageName, accounts));
+                    }
+                }
+            }
+        }
+        return filterSharedAccounts(accounts, result, callingUid, packageName);
     }
 
     @Override
-    public int[] getRequestingUidsForType(String accountType) {
+    public Map<Integer, Integer> getUidsAndVisibilityForAccount(Account account) {
+        if (account == null) throw new IllegalArgumentException("account is null");
         int callingUid = Binder.getCallingUid();
-        if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
-            String msg = String.format(
-                    "uid %s cannot get secrets for accounts of type: %s",
-                    callingUid,
-                    accountType);
+        int userId = UserHandle.getUserId(callingUid);
+        UserAccounts accounts = getUserAccounts(userId);
+        if (!isAccountManagedByCaller(account.type, callingUid, userId)
+                && !isSystemUid(callingUid)) {
+            String msg =
+                    String.format("uid %s cannot get secrets for account %s", callingUid, account);
             throw new SecurityException(msg);
         }
-        // TODO Implement.
-        return new int[]{};
+        return getUidsAndVisibilityForAccount(account, accounts);
+    }
+
+    /**
+     * Returns all UIDs and visibility values, which were set for given account
+     *
+     * @param account account
+     * @param accounts UserAccount that currently hosts the account and application
+     *
+     * @return Map from uid to visibility.
+     */
+    private Map<Integer, Integer> getUidsAndVisibilityForAccount(Account account,
+            UserAccounts accounts) {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            return accounts.accountsDb.findAllVisibilityValuesForAccount(account);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+
     }
 
     @Override
     public int getAccountVisibility(Account a, int uid) {
-        // TODO Implement.
-        return 0;
+        if (a == null) throw new IllegalArgumentException("account is null");
+        int callingUid = Binder.getCallingUid();
+        if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))
+            && !isSystemUid(callingUid)) {
+            String msg = String.format(
+                    "uid %s cannot get secrets for accounts of type: %s",
+                    callingUid,
+                    a.type);
+            throw new SecurityException(msg);
+        }
+        return getAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid)));
+    }
+
+    /**
+     * Method gets visibility for given account and UID from the database
+     *
+     * @param account The account to check visibility of
+     * @param uid UID to check visibility of
+     * @param accounts UserAccount that currently hosts the account and application
+     *
+     * @return Visibility value, AccountManager.VISIBILITY_UNDEFINED if no value was stored.
+     *
+     */
+    private int getAccountVisibility(Account account, int uid, UserAccounts accounts) {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            Integer visibility = accounts.accountsDb.findAccountVisibility(account, uid);
+            return visibility != null ? visibility : AccountManager.VISIBILITY_UNDEFINED;
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    /**
+     * Method which handles default values for Account visibility.
+     *
+     * @param account The account to check visibility.
+     * @param uid UID to check visibility.
+     * @param packageName Package name to check visibility - the method assumes that it has the same
+     *        uid as specified in the parameter.
+     * @param accounts UserAccount that currently hosts the account and application
+     *
+     * @return Visibility value, the method never returns AccountManager.VISIBILITY_UNDEFINED
+     *
+     */
+    private Integer resolveAccountVisibility(Account account, int uid, String packageName,
+            UserAccounts accounts) {
+        if (packageName == null) {
+            packageName = getPackageNameForUid(uid);
+        }
+        // System visibility can not be restricted.
+        if (UserHandle.isSameApp(uid, Process.SYSTEM_UID)) {
+            return AccountManager.VISIBILITY_VISIBLE;
+        }
+
+        int signatureCheckResult =
+                checkPackageSignature(account.type, uid, accounts.userId);
+
+        // Authenticator can not restrict visibility to itself.
+        if (signatureCheckResult == SIGNATURE_CHECK_UID_MATCH) {
+            return AccountManager.VISIBILITY_VISIBLE; // Authenticator can always see the account
+        }
+
+        // Return stored value if it was set.
+        int visibility = getAccountVisibility(account, uid, accounts);
+
+        if (AccountManager.VISIBILITY_UNDEFINED != visibility) {
+            return visibility;
+        }
+
+        if (isPermittedForPackage(packageName, accounts.userId,
+                Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) {
+            return AccountManager.VISIBILITY_VISIBLE;
+
+        }
+        // Profile owner gets visibility by default.
+        if(isProfileOwner(uid)) {
+            return AccountManager.VISIBILITY_VISIBLE;
+        }
+        // Apps with READ_CONTACTS permission get visibility by default even post O.
+        boolean canReadContacts = checkReadContactsPermission(packageName, accounts.userId);
+
+        boolean preO = isPreOApplication(packageName);
+        if ((signatureCheckResult != SIGNATURE_CHECK_MISMATCH)
+                || (preO && checkGetAccountsPermission(packageName, accounts.userId))
+                || canReadContacts) {
+            // Use legacy for preO apps with GET_ACCOUNTS permission or pre/postO with signature
+            // match.
+            visibility = getAccountVisibility(account,
+                    AccountManager.UID_KEY_DEFAULT_LEGACY_VISIBILITY, accounts);
+            if (AccountManager.VISIBILITY_UNDEFINED == visibility) {
+                visibility = AccountManager.VISIBILITY_USER_MANAGED_VISIBLE;
+            }
+        } else {
+            visibility = getAccountVisibility(account, AccountManager.UID_KEY_DEFAULT_VISIBILITY,
+                    accounts);
+            if (AccountManager.VISIBILITY_UNDEFINED == visibility) {
+                visibility = AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE;
+            }
+        }
+        return visibility;
+    }
+
+    /**
+     * Checks targetSdk for a package;
+     *
+     * @param packageName Package Name
+     *
+     * @return True if package's target SDK is below {@link android.os.Build.VERSION_CODES#O}, or
+     *         undefined
+     */
+    private boolean isPreOApplication(String packageName) {
+        try {
+            long identityToken = clearCallingIdentity();
+            ApplicationInfo applicationInfo;
+            try {
+                applicationInfo = mPackageManager.getApplicationInfo(packageName, 0);
+            } finally {
+                restoreCallingIdentity(identityToken);
+            }
+
+            if (applicationInfo != null) {
+                int version = applicationInfo.targetSdkVersion;
+                return version < android.os.Build.VERSION_CODES.O;
+            }
+            return true;
+        } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found " + e.getMessage());
+            return true;
+        }
     }
 
     @Override
-    public boolean setAccountVisibility(Account a, int uid, int visibility) {
-        // TODO Implement.
-        return false;
-    }
-
-    /**
-     * Registers the requested login account types requested by all the applications already
-     * installed on the device.
-     */
-    private void addRequestsForPreInstalledApplications() {
-        List<PackageInfo> allInstalledPackages = mPackageManager.getInstalledPackages(0);
-        for(PackageInfo pi : allInstalledPackages) {
-            int currentUid = pi.applicationInfo.uid;
-            if(currentUid != -1) {
-                registerAccountTypesSupported(currentUid,
-                        getUserAccounts(UserHandle.getUserId(currentUid)));
-            }
+    public boolean setAccountVisibility(Account a, int uid, int newVisibility) {
+        if (a == null) throw new IllegalArgumentException("account is null");
+        int callingUid = Binder.getCallingUid();
+        if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))
+            && !isSystemUid(callingUid)) {
+            String msg = String.format(
+                    "uid %s cannot get secrets for accounts of type: %s",
+                    callingUid,
+                    a.type);
+            throw new SecurityException(msg);
         }
+        return setAccountVisibility(a, uid, newVisibility, true /* notify */,
+                getUserAccounts(UserHandle.getUserId(callingUid)));
     }
 
     /**
-     * Registers an application, represented by a UID, to support account types detailed in the
-     * applications manifest as well as allowing it to opt for notifications.
+     * Gives a certain UID, represented a application, access to an account. This method
+     * is called indirectly by the Authenticator.
      *
-     * @param uid UID of application
-     * @param ua UserAccount that currently hosts the account and application
+     * @param account Account to update visibility
+     * @param uid to add visibility of the Account
+     * @param newVisibility new visibility
+     * @param notify if the flag is set applications will get notification about visibility change
+     * @param accounts UserAccount that currently hosts the account and application
+     *
+     * @return True if account visibility was changed.
      */
-    private void registerAccountTypesSupported(int uid, UserAccounts ua) {
-        return;
-        // TODO clean up the code, manifest entry is deprecated
-        /*
-        String interestedPackages = null;
-        try {
-            String[] allPackages = mPackageManager.getPackagesForUid(uid);
-            if (allPackages != null) {
-                for (String aPackage : allPackages) {
-                    ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage,
-                            PackageManager.GET_META_DATA);
-                    Bundle b = ai.metaData;
-                    if (b == null) {
-                        return;
+    private boolean setAccountVisibility(Account account, int uid, int newVisibility,
+            boolean notify, UserAccounts accounts) {
+        synchronized (accounts.cacheLock) {
+            LinkedHashSet<String> interestedPackages;
+            if (notify) {
+                if (uid < 0) {
+                    interestedPackages = getRequestingPackageNames(account.type, accounts);
+                } else {
+                    interestedPackages = new LinkedHashSet<>();
+                    String[] subPackages = mPackageManager.getPackagesForUid(uid);
+                    if (subPackages != null) {
+                        Collections.addAll(interestedPackages, subPackages);
                     }
-                    interestedPackages = b.getString(AccountManager.SUPPORTED_ACCOUNT_TYPES);
+                }
+            } else {
+                // Notifications will not be send.
+                interestedPackages = new LinkedHashSet<>();
+            }
+            Integer[] interestedPackagesVisibility = new Integer[interestedPackages.size()];
+
+            final long accountId = accounts.accountsDb.findDeAccountId(account);
+            if (accountId < 0) {
+                return false;
+            }
+            int index = 0;
+            for (String packageName : interestedPackages) {
+                interestedPackagesVisibility[index++] =
+                        resolveAccountVisibility(account, uid, packageName, accounts);
+            }
+
+            final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+            try {
+                if (!accounts.accountsDb.setAccountVisibility(accountId, uid, newVisibility)) {
+                    return false;
+                }
+            } finally {
+                StrictMode.setThreadPolicy(oldPolicy);
+            }
+
+            index = 0;
+            for (String packageName : interestedPackages) {
+                int visibility = resolveAccountVisibility(account, uid, packageName, accounts);
+                if (visibility != interestedPackagesVisibility[index++]) {
+                        sendNotification(packageName, account, accounts.userId);
                 }
             }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.d("NameNotFoundException", e.getMessage());
+            if (notify) {
+                sendAccountsChangedBroadcast(accounts.userId);
+            }
+            return true;
         }
-        if (interestedPackages != null) {
-            // TODO request visibility
-            // requestAccountVisibility(interestedPackages.split(";"), uid, ua);
-        }
-        */
     }
 
     /**
      * Sends a direct intent to a package, notifying it of a visible account change.
      *
-     * @param desiredPackage to send Account to
-     * @param visibleAccount to send to package
+     * @param packageName to send Account to
+     * @param account to send to package
+     * @param userId User
      */
-    private void sendNotification(String desiredPackage, Account visibleAccount) {
-        // TODO replace with callback
-        /*
-        Intent intent = new Intent();
-        intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
-        intent.setAction(AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED);
-        intent.setPackage(desiredPackage);
-        // TODO update documentation, add account extra if new account became visible
-        // intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount);
-        mContext.sendBroadcast(intent);
-        */
+    private void sendNotification(String packageName, Account account, int userId) {
+        // TODO send notification so apps subscribed in runtime.
+    }
+
+    private void sendNotification(Account account, UserAccounts accounts) {
+        LinkedHashSet<String> interestedPackages = getRequestingPackageNames(account.type,
+                accounts);
+        for (String packageName : interestedPackages) {
+            try {
+                final int uid = mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
+                int visibility = resolveAccountVisibility(account, uid, packageName, accounts);
+                if (visibility != AccountManager.VISIBILITY_NOT_VISIBLE) {
+                    sendNotification(packageName, account, accounts.userId);
+                }
+            } catch (NameNotFoundException e) {
+                // ignore
+            }
+        }
+    }
+
+    LinkedHashSet<String> getRequestingPackageNames(String accountType, UserAccounts accouns) {
+        // TODO return packages registered to get notifications.
+        return new LinkedHashSet<String>();
+    }
+
+    private void sendAccountsChangedBroadcast(int userId) {
+        Log.i(TAG, "the accounts changed, sending broadcast of "
+                + ACCOUNTS_CHANGED_INTENT.getAction());
+        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
     }
 
     @Override
@@ -688,6 +937,12 @@
                         accounts.userDataCache.remove(account);
                         accounts.authTokenCache.remove(account);
                         accounts.accountTokenCaches.remove(account);
+                        LinkedHashSet<String> interestedPackages =
+                                getRequestingPackageNames(account.type, accounts);
+                        for (String packageName : interestedPackages) {
+                            sendNotification(packageName, null, accounts.userId);
+                        }
+
                     } else {
                         ArrayList<String> accountNames = accountNamesByType.get(account.type);
                         if (accountNames == null) {
@@ -792,7 +1047,7 @@
                     AccountsDb.TABLE_ACCOUNTS);
 
             for (Account account : accountsToRemove) {
-                removeAccountInternal(accounts, account, Process.myUid());
+                removeAccountInternal(accounts, account, Process.SYSTEM_UID);
             }
         }
     }
@@ -1049,7 +1304,7 @@
 
     private boolean isCrossUser(int callingUid, int userId) {
         return (userId != UserHandle.getCallingUserId()
-                && callingUid != Process.myUid()
+                && callingUid != Process.SYSTEM_UID
                 && mContext.checkCallingOrSelfPermission(
                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                                 != PackageManager.PERMISSION_GRANTED);
@@ -1057,42 +1312,7 @@
 
     @Override
     public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
-        Bundle.setDefusable(extras, true);
-        // clears the visible list functionality for this account because this method allows
-        // default account access to all applications for account.
-
-        final int callingUid = Binder.getCallingUid();
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "addAccountExplicitly: " + account
-                    + ", caller's uid " + callingUid
-                    + ", pid " + Binder.getCallingPid());
-        }
-        if (account == null) throw new IllegalArgumentException("account is null");
-        int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
-            String msg = String.format(
-                    "uid %s cannot explicitly add accounts of type: %s",
-                    callingUid,
-                    account.type);
-            throw new SecurityException(msg);
-        }
-
-        /*
-         * Child users are not allowed to add accounts. Only the accounts that are
-         * shared by the parent profile can be added to child profile.
-         *
-         * TODO: Only allow accounts that were shared to be added by
-         *     a limited user.
-         */
-
-        // fails if the account already exists
-        long identityToken = clearCallingIdentity();
-        try {
-            UserAccounts accounts = getUserAccounts(userId);
-            return addAccountInternal(accounts, account, password, extras, callingUid);
-        } finally {
-            restoreCallingIdentity(identityToken);
-        }
+        return addAccountExplicitlyWithVisibility(account, password, extras, null);
     }
 
     @Override
@@ -1233,6 +1453,8 @@
                     // TODO: Anything to do if if succedded?
                     // TODO: If it failed: Show error notification? Should we remove the shadow
                     // account to avoid retries?
+                    // TODO: what we do with the visibility?
+
                     super.onResult(result);
                 }
 
@@ -1250,7 +1472,7 @@
     }
 
     private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
-            Bundle extras, int callingUid) {
+            Bundle extras, int callingUid, Map<Integer, Integer> uidToVisibility) {
         Bundle.setDefusable(extras, true);
         if (account == null) {
             return false;
@@ -1290,10 +1512,17 @@
                         }
                     }
                 }
+
+                if (uidToVisibility != null) {
+                    for (Entry<Integer, Integer> entry : uidToVisibility.entrySet()) {
+                        setAccountVisibility(account, entry.getKey() /* uid */,
+                                entry.getValue() /* visibility */, false /* notify */, accounts);
+                    }
+                }
                 accounts.accountsDb.setTransactionSuccessful();
 
-                logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
-                        accountId, accounts, callingUid);
+                logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS, accountId,
+                        accounts, callingUid);
 
                 insertAccountIntoCacheLocked(accounts, account);
             } finally {
@@ -1304,8 +1533,10 @@
             addAccountToLinkedRestrictedUsers(account, accounts.userId);
         }
 
+        sendNotification(account, accounts);
         // Only send LOGIN_ACCOUNTS_CHANGED when the database changed.
         sendAccountsChangedBroadcast(accounts.userId);
+
         return true;
     }
 
@@ -1482,6 +1713,10 @@
         synchronized (accounts.cacheLock) {
             accounts.accountsDb.beginTransaction();
             Account renamedAccount = new Account(newName, accountToRename.type);
+            if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) {
+                Log.e(TAG, "renameAccount failed - account with new name already exists");
+                return null;
+            }
             try {
                 final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
                 if (accountId >= 0) {
@@ -1493,6 +1728,9 @@
                         Log.e(TAG, "renameAccount failed");
                         return null;
                     }
+                } else {
+                    Log.e(TAG, "renameAccount failed - old account does not exist");
+                    return null;
                 }
             } finally {
                 accounts.accountsDb.endTransaction();
@@ -1535,6 +1773,9 @@
                     }
                 }
             }
+
+            // Notify authenticator.
+            sendNotification(resultAccount, accounts);
             sendAccountsChangedBroadcast(accounts.userId);
         }
         return resultAccount;
@@ -1733,6 +1974,24 @@
                     + " is still locked. CE data will be removed later");
         }
         synchronized (accounts.cacheLock) {
+            LinkedHashSet<String> interestedPackages =
+                    accounts.mApplicationAccountRequestMappings.get(account.type);
+            if (interestedPackages == null) {
+                interestedPackages = new LinkedHashSet<>();
+            }
+            int[] visibilityForInterestedPackages = new int[interestedPackages.size()];
+            int index = 0;
+            for (String packageName : interestedPackages) {
+                int visibility = AccountManager.VISIBILITY_NOT_VISIBLE;
+                try {
+                    final int uid = mPackageManager.getPackageUidAsUser(packageName,
+                            UserHandle.getUserId(callingUid));
+                    visibility = resolveAccountVisibility(account, uid, packageName, accounts);
+                } catch (NameNotFoundException e) {
+                    // ignore
+                }
+                visibilityForInterestedPackages[index++] = visibility;
+            }
             accounts.accountsDb.beginTransaction();
             // Set to a dummy value, this will only be used if the database
             // transaction succeeds.
@@ -1756,6 +2015,17 @@
             }
             if (isChanged) {
                 removeAccountFromCacheLocked(accounts, account);
+                index = 0;
+                for (String packageName : interestedPackages) {
+                    if ((visibilityForInterestedPackages[index]
+                            != AccountManager.VISIBILITY_NOT_VISIBLE)
+                            && (visibilityForInterestedPackages[index]
+                                    != AccountManager.VISIBILITY_UNDEFINED)) {
+                        sendNotification(packageName, account, accounts.userId);
+                    }
+                    ++index;
+                }
+
                 // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occured.
                 sendAccountsChangedBroadcast(accounts.userId);
                 String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
@@ -2022,18 +2292,13 @@
                 accounts.accountsDb.endTransaction();
                 if (isChanged) {
                     // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
+                    sendNotification(account, accounts);
                     sendAccountsChangedBroadcast(accounts.userId);
                 }
             }
         }
     }
 
-    private void sendAccountsChangedBroadcast(int userId) {
-        Log.i(TAG, "the accounts changed, sending broadcast of "
-                + ACCOUNTS_CHANGED_INTENT.getAction());
-        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
-    }
-
     @Override
     public void clearPassword(Account account) {
         final int callingUid = Binder.getCallingUid();
@@ -2412,8 +2677,10 @@
                             checkKeyIntent(
                                     Binder.getCallingUid(),
                                     intent);
-                            doNotification(mAccounts,
-                                    account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
+                            doNotification(
+                                    mAccounts,
+                                    account,
+                                    result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
                                     intent, "android", accounts.userId);
                         }
                     }
@@ -3298,7 +3565,8 @@
         if (response == null) throw new IllegalArgumentException("response is null");
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         int userId = UserHandle.getCallingUserId();
-        if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) {
+        if (!isAccountManagedByCaller(accountType, callingUid, userId)
+                && !isSystemUid(callingUid)) {
             String msg = String.format(
                     "uid %s cannot edit authenticator properites for account type: %s",
                     callingUid,
@@ -3341,23 +3609,50 @@
         Preconditions.checkArgumentInRange(userId, 0, Integer.MAX_VALUE, "user must be concrete");
 
         try {
-            final int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+            int uid = mPackageManager.getPackageUidAsUser(packageName, userId);
             return hasAccountAccess(account, packageName, uid);
         } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found " + e.getMessage());
             return false;
         }
     }
 
+    // Returns package with oldest target SDK for given UID.
+    private String getPackageNameForUid(int uid) {
+        String[] packageNames = mPackageManager.getPackagesForUid(uid);
+        if (ArrayUtils.isEmpty(packageNames)) {
+            return null;
+        }
+        // For app op checks related to permissions all packages in the UID
+        // have the same app op state, so doesn't matter which one we pick.
+        // Update: due to visibility changes we want to use package with oldest target SDK,
+
+        String packageName = packageNames[0];
+        int oldestVersion = Integer.MAX_VALUE;
+        for (String name : packageNames) {
+            try {
+                ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(name, 0);
+                if (applicationInfo != null) {
+                    int version = applicationInfo.targetSdkVersion;
+                    if (version < oldestVersion) {
+                        oldestVersion = version;
+                        packageName = name;
+                    }
+                }
+            } catch (NameNotFoundException e) {
+                // skip
+            }
+        }
+        return packageName;
+    }
+
     private boolean hasAccountAccess(@NonNull Account account, @Nullable String packageName,
             int uid) {
         if (packageName == null) {
-            String[] packageNames = mPackageManager.getPackagesForUid(uid);
-            if (ArrayUtils.isEmpty(packageNames)) {
+            packageName = getPackageNameForUid(uid);
+            if (packageName == null) {
                 return false;
             }
-            // For app op checks related to permissions all packages in the UID
-            // have the same app op state, so doesn't matter which one we pick.
-            packageName = packageNames[0];
         }
 
         // Use null token which means any token. Having a token means the package
@@ -3366,27 +3661,12 @@
             return true;
         }
         // In addition to the permissions required to get an auth token we also allow
-        // the account to be accessed by holders of the get accounts permissions.
-        return checkUidPermission(Manifest.permission.GET_ACCOUNTS_PRIVILEGED, uid, packageName)
-                || checkUidPermission(Manifest.permission.GET_ACCOUNTS, uid, packageName);
-    }
+        // the account to be accessed by apps for which user or authenticator granted visibility.
 
-    private boolean checkUidPermission(String permission, int uid, String opPackageName) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            IPackageManager pm = ActivityThread.getPackageManager();
-            if (pm.checkUidPermission(permission, uid) != PackageManager.PERMISSION_GRANTED) {
-                return false;
-            }
-            final int opCode = AppOpsManager.permissionToOpCode(permission);
-            return (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow(
-                    opCode, uid, opPackageName) == AppOpsManager.MODE_ALLOWED);
-        } catch (RemoteException e) {
-            /* ignore - local call */
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        return false;
+        int visibility = resolveAccountVisibility(account, uid, packageName,
+                getUserAccounts(UserHandle.getUserId(uid)));
+        return (visibility == AccountManager.VISIBILITY_VISIBLE
+                || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
     }
 
     @Override
@@ -3482,21 +3762,28 @@
         private volatile ArrayList<Account> mAccountsWithFeatures = null;
         private volatile int mCurrentAccount = 0;
         private final int mCallingUid;
+        private final String mPackageName;
 
-        public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
-                IAccountManagerResponse response, String type, String[] features, int callingUid) {
+        public GetAccountsByTypeAndFeatureSession(
+                UserAccounts accounts,
+                IAccountManagerResponse response,
+                String type,
+                String[] features,
+                int callingUid,
+                String packageName) {
             super(accounts, response, type, false /* expectActivityLaunch */,
                     true /* stripAuthTokenFromResult */, null /* accountName */,
                     false /* authDetailsRequired */);
             mCallingUid = callingUid;
             mFeatures = features;
+            mPackageName = packageName;
         }
 
         @Override
         public void run() throws RemoteException {
             synchronized (mAccounts.cacheLock) {
                 mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
-                        null);
+                        mPackageName, false /* include managed not visible*/);
             }
             // check whether each account matches the requested features
             mAccountsWithFeatures = new ArrayList<>(mAccountsOfType.length);
@@ -3569,7 +3856,6 @@
             }
         }
 
-
         @Override
         protected String toDebugString(long now) {
             return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
@@ -3595,8 +3881,9 @@
             return getAccountsInternal(
                     accounts,
                     callingUid,
-                    null,  // packageName
-                    visibleAccountTypes);
+                    opPackageName,
+                    visibleAccountTypes,
+                    false /* includeUserManagedNotVisible */);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -3638,7 +3925,9 @@
             if (userAccounts == null) continue;
             synchronized (userAccounts.cacheLock) {
                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
-                        Binder.getCallingUid(), null);
+                        Binder.getCallingUid(),
+                        null,
+                        false /* incl managed not visible*/);
                 for (int a = 0; a < accounts.length; a++) {
                     runningAccounts.add(new AccountAndUser(accounts[a], userId));
                 }
@@ -3652,7 +3941,9 @@
     @Override
     @NonNull
     public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
-        return getAccountsAsUser(type, userId, null, -1, opPackageName);
+        // Need calling package
+        return getAccountsAsUser(type, userId, null /* callingPackage */, -1, opPackageName,
+               false /* includeUserManagedNotVisible */);
     }
 
     @NonNull
@@ -3661,11 +3952,12 @@
             int userId,
             String callingPackage,
             int packageUid,
-            String opPackageName) {
+            String opPackageName,
+            boolean includeUserManagedNotVisible) {
         int callingUid = Binder.getCallingUid();
         // Only allow the system process to read accounts of other users
         if (userId != UserHandle.getCallingUserId()
-                && callingUid != Process.myUid()
+                && callingUid != Process.SYSTEM_UID
                 && mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -3678,9 +3970,15 @@
                     + ", caller's uid " + Binder.getCallingUid()
                     + ", pid " + Binder.getCallingPid());
         }
-        // If the original calling app was using the framework account chooser activity, we'll
-        // be passed in the original caller's uid here, which is what should be used for filtering.
-        if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
+
+        // If the original calling app was using account choosing activity
+        // provided by the framework or authenticator we'll passing in
+        // the original caller's uid here, which is what should be used for filtering.
+        List<String> managedTypes =
+                getTypesManagedByCaller(callingUid, UserHandle.getUserId(callingUid));
+        if (packageUid != -1 &&
+                ((UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
+                || (type != null && managedTypes.contains(type))))) {
             callingUid = packageUid;
             opPackageName = callingPackage;
         }
@@ -3688,7 +3986,7 @@
                 opPackageName);
         if (visibleAccountTypes.isEmpty()
                 || (type != null && !visibleAccountTypes.contains(type))) {
-            return new Account[0];
+            return new Account[]{};
         } else if (visibleAccountTypes.contains(type)) {
             // Prune the list down to just the requested type.
             visibleAccountTypes = new ArrayList<>();
@@ -3703,7 +4001,8 @@
                     accounts,
                     callingUid,
                     callingPackage,
-                    visibleAccountTypes);
+                    visibleAccountTypes,
+                    includeUserManagedNotVisible);
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -3714,12 +4013,14 @@
             UserAccounts userAccounts,
             int callingUid,
             String callingPackage,
-            List<String> visibleAccountTypes) {
+            List<String> visibleAccountTypes,
+            boolean includeUserManagedNotVisible) {
         synchronized (userAccounts.cacheLock) {
             ArrayList<Account> visibleAccounts = new ArrayList<>();
             for (String visibleType : visibleAccountTypes) {
                 Account[] accountsForType = getAccountsFromCacheLocked(
-                        userAccounts, visibleType, callingUid, callingPackage);
+                        userAccounts, visibleType, callingUid, callingPackage,
+                        includeUserManagedNotVisible);
                 if (accountsForType != null) {
                     visibleAccounts.addAll(Arrays.asList(accountsForType));
                 }
@@ -3810,29 +4111,34 @@
     @NonNull
     public Account[] getAccountsForPackage(String packageName, int uid, String opPackageName) {
         int callingUid = Binder.getCallingUid();
-        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
+        if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)) {
             throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
                     + callingUid + " with uid=" + uid);
         }
         return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
-                opPackageName);
+                opPackageName, true /* includeUserManagedNotVisible */);
     }
 
     @Override
     @NonNull
     public Account[] getAccountsByTypeForPackage(String type, String packageName,
             String opPackageName) {
+        int callingUid =  Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
         int packageUid = -1;
         try {
-            packageUid = AppGlobals.getPackageManager().getPackageUid(
-                    packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
-                    UserHandle.getCallingUserId());
-        } catch (RemoteException re) {
+            packageUid = mPackageManager.getPackageUidAsUser(packageName, userId);
+        } catch (NameNotFoundException re) {
             Slog.e(TAG, "Couldn't determine the packageUid for " + packageName + re);
             return new Account[0];
         }
-        return getAccountsAsUser(type, UserHandle.getCallingUserId(), packageName,
-                packageUid, opPackageName);
+        if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
+                && !isAccountManagedByCaller(type, callingUid, userId)) {
+            return new Account[0];
+        }
+
+        return getAccountsAsUser(type, userId,
+                packageName, packageUid, opPackageName, true /* includeUserManagedNotVisible */);
     }
 
     @Override
@@ -3872,7 +4178,8 @@
             if (features == null || features.length == 0) {
                 Account[] accounts;
                 synchronized (userAccounts.cacheLock) {
-                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
+                    accounts = getAccountsFromCacheLocked(
+                            userAccounts, type, callingUid, opPackageName, false);
                 }
                 Bundle result = new Bundle();
                 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
@@ -3884,7 +4191,8 @@
                     response,
                     type,
                     features,
-                    callingUid).bind();
+                    callingUid,
+                    opPackageName).bind();
         } finally {
             restoreCallingIdentity(identityToken);
         }
@@ -3903,6 +4211,7 @@
                 if (Objects.equals(account.getAccessId(), token)) {
                     // An app just accessed the account. At this point it knows about
                     // it and there is not need to hide this account from the app.
+                    // Do we need to update account visibility here?
                     if (!hasAccountAccess(account, null, uid)) {
                         updateAppPermission(account, AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE,
                                 uid, true);
@@ -4429,7 +4738,7 @@
                 userAccounts.accountsDb.dumpDeAccountsTable(fout);
             } else {
                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
-                        Process.myUid(), null);
+                        Process.SYSTEM_UID, null, false);
                 fout.println("Accounts: " + accounts.length);
                 for (Account account : accounts) {
                     fout.println("  " + account);
@@ -4522,6 +4831,24 @@
         }
     }
 
+    private boolean isPermittedForPackage(String packageName, int userId, String... permissions) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            IPackageManager pm = ActivityThread.getPackageManager();
+            for (String perm : permissions) {
+                if (pm.checkPermission(perm, packageName, userId)
+                        == PackageManager.PERMISSION_GRANTED) {
+                    return true;
+                }
+            }
+        } catch (RemoteException e) {
+            /* ignore - local call */
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return false;
+    }
+
     private boolean isPermitted(String opPackageName, int callingUid, String... permissions) {
         for (String perm : permissions) {
             if (mContext.checkCallingOrSelfPermission(perm) == PackageManager.PERMISSION_GRANTED) {
@@ -4556,6 +4883,7 @@
             userPackageManager = mContext.createPackageContextAsUser(
                     "android", 0, new UserHandle(callingUserId)).getPackageManager();
         } catch (NameNotFoundException e) {
+            Log.d(TAG, "Package not found " + e.getMessage());
             return false;
         }
 
@@ -4569,6 +4897,7 @@
                     return true;
                 }
             } catch (PackageManager.NameNotFoundException e) {
+                Log.d(TAG, "Package not found " + e.getMessage());
                 return false;
             }
         }
@@ -4623,6 +4952,53 @@
         }
     }
 
+    // Method checks visibility for applications targeing API level below {@link
+    // android.os.Build.VERSION_CODES#O},
+    // returns true if the the app has GET_ACCOUNTS or GET_ACCOUNTS_PRIVELEGED permission.
+    private boolean checkGetAccountsPermission(String packageName, int userId) {
+        return isPermittedForPackage(packageName, userId, Manifest.permission.GET_ACCOUNTS,
+                Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
+    }
+
+    private boolean checkReadContactsPermission(String packageName, int userId) {
+        return isPermittedForPackage(packageName, userId, Manifest.permission.READ_CONTACTS);
+    }
+
+    /**
+     * Method checks package uid and signature with Authenticator which manages accountType.
+     *
+     * @return SIGNATURE_CHECK_UID_MATCH for uid match, SIGNATURE_CHECK_MATCH for signature match,
+     *         SIGNATURE_CHECK_MISMATCH otherwise.
+     */
+    private int checkPackageSignature(String accountType, int callingUid, int userId) {
+        if (accountType == null) {
+            return SIGNATURE_CHECK_MISMATCH;
+        }
+
+        long identityToken = Binder.clearCallingIdentity();
+        Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> serviceInfos;
+        try {
+            serviceInfos = mAuthenticatorCache.getAllServices(userId);
+        } finally {
+            Binder.restoreCallingIdentity(identityToken);
+        }
+        // Check for signature match with Authenticator.
+        for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo
+                : serviceInfos) {
+            if (accountType.equals(serviceInfo.type.type)) {
+                if (serviceInfo.uid == callingUid) {
+                    return SIGNATURE_CHECK_UID_MATCH;
+                }
+                final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
+                if (sigChk == PackageManager.SIGNATURE_MATCH) {
+                    return SIGNATURE_CHECK_MATCH;
+                }
+            }
+        }
+        return SIGNATURE_CHECK_MISMATCH;
+    }
+
+    // returns true for applications with the same signature as authenticator.
     private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) {
         if (accountType == null) {
             return false;
@@ -4633,10 +5009,7 @@
 
     private List<String> getTypesVisibleToCaller(int callingUid, int userId,
             String opPackageName) {
-        boolean isPermitted =
-                isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS,
-                        Manifest.permission.GET_ACCOUNTS_PRIVILEGED);
-        return getTypesForCaller(callingUid, userId, isPermitted);
+        return getTypesForCaller(callingUid, userId, true /* isOtherwisePermitted*/);
     }
 
     private List<String> getTypesManagedByCaller(int callingUid, int userId) {
@@ -4655,8 +5028,8 @@
         }
         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
                 serviceInfos) {
-            final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
-            if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) {
+            if (isOtherwisePermitted || (mPackageManager.checkSignatures(serviceInfo.uid,
+                    callingUid) == PackageManager.SIGNATURE_MATCH)) {
                 managedAccountTypes.add(serviceInfo.type.type);
             }
         }
@@ -4738,7 +5111,7 @@
                                     != 0) {
                         return true;
                     }
-                } catch (PackageManager.NameNotFoundException e) {
+                } catch (NameNotFoundException e) {
                     Log.w(TAG, String.format("Could not find package [%s]", name), e);
                 }
             }
@@ -4929,15 +5302,41 @@
         return newAccountsForType[oldLength];
     }
 
-    private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
-            int callingUid, String callingPackage) {
+    private Account[] filterAccounts(UserAccounts accounts, Account[] unfiltered, int callingUid,
+            String callingPackage, boolean includeManagedNotVisible) {
+        // filter based on visibility.
+        Map<Account, Integer> firstPass = new HashMap<>();
+        for (Account account : unfiltered) {
+            int visibility =
+                    resolveAccountVisibility(account, callingUid, callingPackage, accounts);
+            if ((visibility == AccountManager.VISIBILITY_VISIBLE
+                    || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE)
+                    || (includeManagedNotVisible
+                            && (visibility
+                                    == AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE))) {
+                firstPass.put(account, visibility);
+            }
+        }
+        Map<Account, Integer> secondPass =
+                filterSharedAccounts(accounts, firstPass, callingUid, callingPackage);
+
+        Account[] filtered = new Account[secondPass.size()];
+        filtered = secondPass.keySet().toArray(filtered);
+        return filtered;
+    }
+
+    private Map<Account, Integer> filterSharedAccounts(UserAccounts userAccounts,
+            Map<Account, Integer> unfiltered, int callingUid, String callingPackage) {
+        // first part is to filter shared accounts.
+        // unfiltered type check is not necessary.
         if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
-                || callingUid == Process.myUid()) {
+                || callingUid == Process.SYSTEM_UID) {
             return unfiltered;
         }
         UserInfo user = getUserManager().getUserInfo(userAccounts.userId);
         if (user != null && user.isRestricted()) {
-            String[] packages = mPackageManager.getPackagesForUid(callingUid);
+            String[] packages =
+                    mPackageManager.getPackagesForUid(callingUid);
             // If any of the packages is a visible listed package, return the full set,
             // otherwise return non-shared accounts only.
             // This might be a temporary way to specify a visible list
@@ -4948,9 +5347,10 @@
                     return unfiltered;
                 }
             }
-            ArrayList<Account> allowed = new ArrayList<>();
             Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
-            if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
+            if (ArrayUtils.isEmpty(sharedAccounts)) {
+                return unfiltered;
+            }
             String requiredAccountType = "";
             try {
                 // If there's an explicit callingPackage specified, check if that package
@@ -4970,11 +5370,14 @@
                         }
                     }
                 }
-            } catch (NameNotFoundException nnfe) {
+            } catch (NameNotFoundException e) {
+                Log.d(TAG, "Package not found " + e.getMessage());
             }
-            for (Account account : unfiltered) {
+            Map<Account, Integer> filtered = new HashMap<>();
+            for (Map.Entry<Account, Integer> entry : unfiltered.entrySet()) {
+                Account account = entry.getKey();
                 if (account.type.equals(requiredAccountType)) {
-                    allowed.add(account);
+                    filtered.put(account, entry.getValue());
                 } else {
                     boolean found = false;
                     for (Account shared : sharedAccounts) {
@@ -4984,12 +5387,10 @@
                         }
                     }
                     if (!found) {
-                        allowed.add(account);
+                        filtered.put(account, entry.getValue());
                     }
                 }
             }
-            Account[] filtered = new Account[allowed.size()];
-            allowed.toArray(filtered);
             return filtered;
         } else {
             return unfiltered;
@@ -5001,14 +5402,14 @@
      * that the package is not allowed to access.
      */
     protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
-            int callingUid, String callingPackage) {
+            int callingUid, String callingPackage, boolean includeManagedNotVisible) {
         if (accountType != null) {
             final Account[] accounts = userAccounts.accountCache.get(accountType);
             if (accounts == null) {
                 return EMPTY_ACCOUNT_ARRAY;
             } else {
-                return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
-                        callingUid, callingPackage);
+                return filterAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
+                        callingUid, callingPackage, includeManagedNotVisible);
             }
         } else {
             int totalLength = 0;
@@ -5025,7 +5426,8 @@
                         accountsOfType.length);
                 totalLength += accountsOfType.length;
             }
-            return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
+            return filterAccounts(userAccounts, accounts, callingUid, callingPackage,
+                    includeManagedNotVisible);
         }
     }
 
@@ -5253,19 +5655,21 @@
             if (userId == 0) {
                 // Migrate old file, if it exists, to the new location.
                 // Make sure the new file doesn't already exist. A dummy file could have been
-                // accidentally created in the old location, causing the new one to become corrupted
-                // as well.
+                // accidentally created in the old location,
+                // causing the new one to become corrupted as well.
                 File oldFile = new File(systemDir, PRE_N_DATABASE_NAME);
                 if (oldFile.exists() && !databaseFile.exists()) {
                     // Check for use directory; create if it doesn't exist, else renameTo will fail
                     File userDir = Environment.getUserSystemDirectory(userId);
                     if (!userDir.exists()) {
                         if (!userDir.mkdirs()) {
-                            throw new IllegalStateException("User dir cannot be created: " + userDir);
+                            throw new IllegalStateException(
+                                    "User dir cannot be created: " + userDir);
                         }
                     }
                     if (!oldFile.renameTo(databaseFile)) {
-                        throw new IllegalStateException("User dir cannot be migrated: " + databaseFile);
+                        throw new IllegalStateException(
+                                "User dir cannot be migrated: " + databaseFile);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 5ca74711..85e4b5f 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -355,7 +355,7 @@
     boolean deleteAuthtokensByAccountIdAndType(long accountId, String authtokenType) {
         SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
         return db.delete(CE_TABLE_AUTHTOKENS,
-                AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+                AUTHTOKENS_ACCOUNTS_ID + "=?" + " AND " + AUTHTOKENS_TYPE + "=?",
                 new String[]{String.valueOf(accountId), authtokenType}) > 0;
     }
 
@@ -946,12 +946,13 @@
     /**
      * Returns a map from uid to visibility value.
      */
-    Map<Integer, Integer> findAccountVisibilityForAccountId(long accountId) {
+    Map<Integer, Integer> findAllVisibilityValuesForAccount(Account account) {
         SQLiteDatabase db = mDeDatabase.getReadableDatabase();
         Map<Integer, Integer> result = new HashMap<>();
-        final Cursor cursor = db.query(TABLE_VISIBILITY,
-                new String[] {VISIBILITY_UID, VISIBILITY_VALUE}, VISIBILITY_ACCOUNTS_ID + "=? ",
-                new String[] {String.valueOf(accountId)}, null, null, null);
+        final Cursor cursor =
+                db.query(TABLE_VISIBILITY, new String[] {VISIBILITY_UID, VISIBILITY_VALUE},
+                        SELECTION_ACCOUNTS_ID_BY_ACCOUNT,
+                        new String[] {account.name, account.type}, null, null, null);
         try {
             while (cursor.moveToNext()) {
                 result.put(cursor.getInt(0), cursor.getInt(1));
@@ -1306,4 +1307,4 @@
         return new AccountsDb(deDatabaseHelper, context, preNDatabaseFile);
     }
 
-}
\ No newline at end of file
+}
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 969cb36..213fb27 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -1263,25 +1263,6 @@
     }
 
     @SmallTest
-    public void testHasFeaturesReadAccountsNotPermitted() throws Exception {
-        unlockSystemUser();
-        when(mMockContext.checkCallingOrSelfPermission(anyString())).thenReturn(
-                PackageManager.PERMISSION_DENIED);
-        when(mMockPackageManager.checkSignatures(anyInt(), anyInt()))
-                    .thenReturn(PackageManager.SIGNATURE_NO_MATCH);
-        try {
-            mAms.hasFeatures(
-                mMockAccountManagerResponse, // response
-                AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, // account
-                new String[] {"feature1", "feature2"}, // features
-                "testPackage"); // opPackageName
-            fail("SecurityException expected. But no exception was thrown.");
-        } catch (SecurityException e) {
-            // SecurityException is expected.
-        }
-    }
-
-    @SmallTest
     public void testHasFeaturesReturnNullResult() throws Exception {
         unlockSystemUser();
         final CountDownLatch latch = new CountDownLatch(1);
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
index 8591dae..2e045ff 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -374,7 +374,7 @@
         mAccountsDb.setAccountVisibility(accId, uid2, 3);
         assertEquals(mAccountsDb.findAccountVisibility(accId, uid2), Integer.valueOf(3));
 
-        Map<Integer, Integer> vis = mAccountsDb.findAccountVisibilityForAccountId(accId);
+        Map<Integer, Integer> vis = mAccountsDb.findAllVisibilityValuesForAccount(account);
         assertEquals(vis.size(), 2);
         assertEquals(vis.get(uid1), Integer.valueOf(1));
         assertEquals(vis.get(uid2), Integer.valueOf(3));