diff options
3 files changed, 500 insertions, 663 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 1ad43fa7cfea..8185818ccd29 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -18,6 +18,7 @@ package android.accounts; import static android.Manifest.permission.GET_ACCOUNTS; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.Size; @@ -30,6 +31,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.res.Resources; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.database.SQLException; import android.os.Build; import android.os.Bundle; @@ -46,6 +49,9 @@ import com.android.internal.R; import com.google.android.collect.Maps; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.SuppressWarnings; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -150,6 +156,7 @@ import java.util.concurrent.TimeoutException; * {@link IllegalStateException} if they are used on the main thread. */ public class AccountManager { + private static final String TAG = "AccountManager"; public static final int ERROR_CODE_REMOTE_EXCEPTION = 1; @@ -270,6 +277,54 @@ public class AccountManager { "android.accounts.AccountAuthenticator"; public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator"; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({VISIBILITY_UNDEFINED, VISIBILITY_VISIBLE, VISIBILITY_USER_MANAGED_VISIBLE, + VISIBILITY_NOT_VISIBLE, VISIBILITY_USER_MANAGED_NOT_VISIBLE}) + public @interface AccountVisibility { + } + + /** + * Account visibility was not set. + * @hide + */ + public static final int VISIBILITY_UNDEFINED = 0; + + /** + * Account is always visible to given application and only authenticator can revoke visibility. + * @hide + */ + public static final int VISIBILITY_VISIBLE = 1; + + /** + * Account is visible to given application, but user can revoke visibility. + * @hide + */ + public static final int VISIBILITY_USER_MANAGED_VISIBLE = 2; + + /** + * Account is not visible to given application and only authenticator can grant visibility. + * @hide + */ + public static final int VISIBILITY_NOT_VISIBLE = 3; + + /** + * Account is not visible to given application, but user can reveal it, for example, using + * {@link #newChooseAccountIntent(Account, List, String[], String, String, String[], Bundle)} + * @hide + */ + public static final int VISIBILITY_USER_MANAGED_NOT_VISIBLE = 4; + + /** + * Key to manifest entry with a list of account types in which application is interested. + * Example value: "com.google;com.customtype". If it is specified then the application + * will only get notifications related to the types in the list (see + * {@link #ACTION_VISIBLE_ACCOUNTS_CHANGED}). Authenticators managing whitelisted types will be + * able to know about the application using {@link #ACTION_ACCOUNTS_LISTENER_PACKAGE_INSTALLED} + * @hide + */ + public static final String SUPPORTED_ACCOUNT_TYPES = "android.accounts.SupportedAccountTypes"; + /** * Token type for the special case where a UID has access only to an account * but no authenticator specific auth token types. @@ -284,16 +339,55 @@ public class AccountManager { private final Handler mMainHandler; /** - * Action sent as a broadcast Intent by the AccountsService - * when accounts are added, accounts are removed, or an - * account's credentials (saved password, etc) are changed. + * Action sent as a broadcast Intent by the AccountsService when accounts are added, accounts + * are removed, or an account's credentials (saved password, etc) are changed. * * @see #addOnAccountsUpdatedListener + * + * Deprecated - use ACTION_VISIBLE_ACCOUNTS_CHANGED instead. */ public static final String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED"; /** + * Action sent as a broadcast Intent by the AccountsService when accounts potentially visible to + * the applications are added, accounts are removed, or an account's credentials (saved + * password, etc) are changed. List of supported account types shoud be specified in the + * Manifest file using {@link #SUPPORTED_ACCOUNT_TYPES} + * + * @see #addOnAccountsUpdatedListener + * @hide + */ + public static final String ACTION_VISIBLE_ACCOUNTS_CHANGED = + "android.accounts.action.VISIBLE_ACCOUNTS_CHANGED"; + + /** + * Authenticators may subscribe to get notifications about apps interested in their managed account + * types using {@link #SUPPORTED_ACCOUNT_TYPES}. + * @hide + */ + public static final String ACTION_ACCOUNTS_LISTENER_PACKAGE_INSTALLED = + "android.accounts.action.ACCOUNTS_LISTENER_PACKAGE_INSTALLED"; + + /** + * Uid key to set default visibility for applications targeting API level + * {@link android.os.Build.VERSION_CODES#O} or above. See {@link #getAccountVisibility}. If the + * value was not set by authenticator USER_MANAGED_NOT_VISIBLE is used. + * @hide + */ + public static final int DEFAULT_VISIBILITY = -2; + + /** + * Uid key to set visibility for applications targeting API level below + * {@link android.os.Build.VERSION_CODES#O}, which were able to see the account before. It + * includes applications with GET_ACCOUNTS permission or with the same signature as + * authenticator. See {@link #getAccountVisibility}. If the value was not set by authenticator + * USER_MANAGED_VISIBLE is used. + * @hide + */ + public static final int DEFAULT_LEGACY_VISIBILITY = -3; + + /** * @hide */ public AccountManager(Context context, IAccountManager service) { @@ -346,18 +440,21 @@ public class AccountManager { } /** - * Gets the saved password associated with the account. - * This is intended for authenticators and related code; applications - * should get an auth token instead. + * Gets the saved password associated with the account. This is intended for authenticators and + * related code; applications should get an auth token instead. * - * <p>It is safe to call this method from the main thread. + * <p> + * It is safe to call this method from the main thread. * - * <p>This method requires the caller to have a signature match with the - * authenticator that owns the specified account. + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. * - * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, - * AUTHENTICATE_ACCOUNTS permission is needed for those platforms. See docs for - * this function in API level 22. + * <p> + * <b>NOTE:</b> If targeting your app to work on API level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and before, AUTHENTICATE_ACCOUNTS + * permission is needed for those platforms. See docs for this function in API level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}. * * @param account The account to query for a password. Must not be {@code null}. * @return The account's password, null if none or if the account doesn't exist @@ -372,19 +469,22 @@ public class AccountManager { } /** - * Gets the user data named by "key" associated with the account. - * This is intended for authenticators and related code to store - * arbitrary metadata along with accounts. The meaning of the keys - * and values is up to the authenticator for the account. + * Gets the user data named by "key" associated with the account. This is intended for + * authenticators and related code to store arbitrary metadata along with accounts. The meaning + * of the keys and values is up to the authenticator for the account. * - * <p>It is safe to call this method from the main thread. + * <p> + * It is safe to call this method from the main thread. * - * <p>This method requires the caller to have a signature match with the - * authenticator that owns the specified account. + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. * - * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, - * AUTHENTICATE_ACCOUNTS permission is needed for those platforms. See docs - * for this function in API level 22. + * <p> + * <b>NOTE:</b> If targeting your app to work on API level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and before, AUTHENTICATE_ACCOUNTS + * permission is needed for those platforms. See docs for this function in API level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}. * * @param account The account to query for user data * @return The user data, null if the account or key doesn't exist @@ -440,21 +540,18 @@ public class AccountManager { } /** - * Lists all accounts of any type registered on the device. - * Equivalent to getAccountsByType(null). - * - * <p>It is safe to call this method from the main thread. + * Lists all accounts visible to the caller regardless of type. Equivalent to + * getAccountsByType(null). These accounts may be visible because the user granted access to the + * account, or the AbstractAcccountAuthenticator managing the account did so or because the + * client shares a signature with the managing AbstractAccountAuthenticator. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p> + * It is safe to call this method from the main thread. * - * @return An array of {@link Account}, one for each account. Empty - * (never null) if no accounts have been added. + * @return An array of {@link Account}, one for each account. Empty (never null) if no accounts + * have been added. */ @NonNull - @RequiresPermission(GET_ACCOUNTS) public Account[] getAccounts() { try { return mService.getAccounts(null, mContext.getOpPackageName()); @@ -465,21 +562,16 @@ public class AccountManager { /** * @hide - * Lists all accounts of any type registered on the device for a given - * user id. Equivalent to getAccountsByType(null). - * - * <p>It is safe to call this method from the main thread. + * Lists all accounts visible to caller regardless of type for a given user id. Equivalent to + * getAccountsByType(null). * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p> + * It is safe to call this method from the main thread. * - * @return An array of {@link Account}, one for each account. Empty - * (never null) if no accounts have been added. + * @return An array of {@link Account}, one for each account. Empty (never null) if no accounts + * have been added. */ @NonNull - @RequiresPermission(GET_ACCOUNTS) public Account[] getAccountsAsUser(int userId) { try { return mService.getAccountsAsUser(null, userId, mContext.getOpPackageName()); @@ -524,29 +616,41 @@ public class AccountManager { } /** - * Lists all accounts of a particular type. The account type is a - * string token corresponding to the authenticator and useful domain - * of the account. For example, there are types corresponding to Google - * and Facebook. The exact string token to use will be published somewhere - * associated with the authenticator in question. + * Lists all accounts of particular type visible to the caller. These accounts may be visible + * because the user granted access to the account, or the AbstractAcccountAuthenticator managing + * the account did so or because the client shares a signature with the managing + * AbstractAccountAuthenticator. * - * <p>It is safe to call this method from the main thread. + * <p> + * The account type is a string token corresponding to the authenticator and useful domain of + * the account. For example, there are types corresponding to Google and Facebook. The exact + * string token to use will be published somewhere associated with the authenticator in + * question. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p> + * It is safe to call this method from the main thread. * - * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, - * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid - * or signature match. See docs for this function in API level 22. + * <p> + * Caller targeting API level {@link android.os.Build.VERSION_CODES#O} and above, will get list + * of accounts made visible to it by user or AbstractAcccountAuthenticator and + * {@link android.Manifest.permission#GET_ACCOUNTS} permission is not used. + * + * <p> + * Caller targeting API level below {@link android.os.Build.VERSION_CODES#O} that have not been + * granted the {@link android.Manifest.permission#GET_ACCOUNTS} permission, will only see those + * accounts managed by AbstractAccountAuthenticators whose signature matches the client. + * + * <p> + * <b>NOTE:</b> If targeting your app to work on API level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and before, GET_ACCOUNTS permission is + * needed for those platforms, irrespective of uid or signature match. See docs for this + * function in API level {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}. * * @param type The type of accounts to return, null to retrieve all accounts - * @return An array of {@link Account}, one per matching account. Empty - * (never null) if no accounts of the specified type have been added. + * @return An array of {@link Account}, one per matching account. Empty (never null) if no + * accounts of the specified type have been added. */ @NonNull - @RequiresPermission(GET_ACCOUNTS) public Account[] getAccountsByType(String type) { return getAccountsByTypeAsUser(type, Process.myUserHandle()); } @@ -612,30 +716,28 @@ public class AccountManager { } /** - * Finds out whether a particular account has all the specified features. - * Account features are authenticator-specific string tokens identifying - * boolean account properties. For example, features are used to tell - * whether Google accounts have a particular service (such as Google - * Calendar or Google Talk) enabled. The feature names and their meanings - * are published somewhere associated with the authenticator in question. + * Finds out whether a particular account has all the specified features. Account features are + * authenticator-specific string tokens identifying boolean account properties. For example, + * features are used to tell whether Google accounts have a particular service (such as Google + * Calendar or Google Talk) enabled. The feature names and their meanings are published + * somewhere associated with the authenticator in question. * - * <p>This method may be called from any thread, but the returned - * {@link AccountManagerFuture} must not be used on the main thread. + * <p> + * This method may be called from any thread, but the returned {@link AccountManagerFuture} must + * not be used on the main thread. * - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS} or be a signature - * match with the AbstractAccountAuthenticator that manages the account. + * <p> + * If caller target API level is below {@link android.os.Build.VERSION_CODES#O}, it is + * required to hold the permission {@link android.Manifest.permission#GET_ACCOUNTS} or have a + * signature match with the AbstractAccountAuthenticator that manages the account. * * @param account The {@link Account} to test * @param features An array of the account features to check - * @param callback Callback to invoke when the request completes, - * null for no callback - * @param handler {@link Handler} identifying the callback thread, - * null for the main thread - * @return An {@link AccountManagerFuture} which resolves to a Boolean, - * true if the account exists and has all of the specified features. + * @param callback Callback to invoke when the request completes, null for no callback + * @param handler {@link Handler} identifying the callback thread, null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Boolean, true if the account + * exists and has all of the specified features. */ - @RequiresPermission(GET_ACCOUNTS) public AccountManagerFuture<Boolean> hasFeatures(final Account account, final String[] features, AccountManagerCallback<Boolean> callback, Handler handler) { @@ -657,40 +759,42 @@ public class AccountManager { } /** - * Lists all accounts of a type which have certain features. The account - * type identifies the authenticator (see {@link #getAccountsByType}). - * Account features are authenticator-specific string tokens identifying - * boolean account properties (see {@link #hasFeatures}). + * Lists all accounts of a type which have certain features. The account type identifies the + * authenticator (see {@link #getAccountsByType}). Account features are authenticator-specific + * string tokens identifying boolean account properties (see {@link #hasFeatures}). * - * <p>Unlike {@link #getAccountsByType}, this method calls the authenticator, - * which may contact the server or do other work to check account features, - * so the method returns an {@link AccountManagerFuture}. + * <p> + * Unlike {@link #getAccountsByType}, this method calls the authenticator, which may contact the + * server or do other work to check account features, so the method returns an + * {@link AccountManagerFuture}. * - * <p>This method may be called from any thread, but the returned - * {@link AccountManagerFuture} must not be used on the main thread. + * <p> + * This method may be called from any thread, but the returned {@link AccountManagerFuture} must + * not be used on the main thread. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p> + * Caller targeting API level {@link android.os.Build.VERSION_CODES#O} and above, will get list + * of accounts made visible to it by user or AbstractAcccountAuthenticator and + * {@link android.Manifest.permission#GET_ACCOUNTS} permission is not used. * - * @param type The type of accounts to return, must not be null - * @param features An array of the account features to require, - * may be null or empty + * <p> + * Caller targeting API level below {@link android.os.Build.VERSION_CODES#O} that have not been + * granted the {@link android.Manifest.permission#GET_ACCOUNTS} permission, will only see those + * accounts managed by AbstractAccountAuthenticators whose signature matches the client. + * <p> + * <b>NOTE:</b> If targeting your app to work on API level + * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and before, GET_ACCOUNTS permission is + * needed for those platforms, irrespective of uid or signature match. See docs for this + * function in API level {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}. * - * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, - * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid - * or signature match. See docs for this function in API level 22. * - * @param callback Callback to invoke when the request completes, - * null for no callback - * @param handler {@link Handler} identifying the callback thread, - * null for the main thread - * @return An {@link AccountManagerFuture} which resolves to an array of - * {@link Account}, one per account of the specified type which - * matches the requested features. + * @param type The type of accounts to return, must not be null + * @param features An array of the account features to require, may be null or empty * + * @param callback Callback to invoke when the request completes, null for no callback + * @param handler {@link Handler} identifying the callback thread, null for the main thread + * @return An {@link AccountManagerFuture} which resolves to an array of {@link Account}, one + * per account of the specified type which matches the requested features. */ - @RequiresPermission(GET_ACCOUNTS) public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures( final String type, final String[] features, AccountManagerCallback<Account[]> callback, Handler handler) { @@ -751,33 +855,70 @@ public class AccountManager { } /** - * Adds an account directly to the AccountManager. Additionally this - * makes the Account visible to desired UIDs of applications on the device, - * and sends directed broadcasts to these individual applications. - * <p>Normally used by sign-up wizards associated with authenticators, not - * directly by applications. - * <p>Calling this method does not update the last authenticated timestamp, - * referred by {@link #KEY_LAST_AUTHENTICATED_TIME}. To update it, call + * Adds an account directly to the AccountManager. Additionally this makes the Account visible + * to desired UIDs of applications on the device, and sends directed broadcasts to these + * individual applications. + * <p> + * Normally used by sign-up wizards associated with authenticators, not directly by + * applications. + * <p> + * Calling this method does not update the last authenticated timestamp, referred by + * {@link #KEY_LAST_AUTHENTICATED_TIME}. To update it, call * {@link #notifyAccountAuthenticated(Account)} after getting success. - * <p>It is safe to call this method from the main thread. - * <p>This method requires the caller to have a signature match with the - * authenticator that owns the specified account. + * <p> + * It is safe to call this method from the main thread. + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. * * @param account The {@link Account} to add * @param password The password to associate with the account, null for none - * @param extras String values to use for the account's userdata, null for - * none - * @param selectedUids Array of uids whose associated applications can access - * this account without any additional user approval. + * @param extras String values to use for the account's userdata, null for none + * @param selectedUids Array of uids whose associated applications can access this account + * without any additional user approval. * - * @return True if the account was successfully added, false if the account - * already exists, the account is null, or another error occurs. + * @return True if the account was successfully added, false if the account already exists, the + * account is null, or another error occurs. */ public boolean addAccountExplicitly(Account account, String password, Bundle extras, - int[] selectedUids) { - if (account == null) throw new IllegalArgumentException("account is null"); + int[] selectedUids) { + return false; // TODO remove this method. + } + + /** + * Adds an account directly to the AccountManager. Additionally this makes the Account visible + * to desired UIDs of applications on the device, and sends directed broadcasts to these + * individual applications. + * <p> + * Normally used by sign-up wizards associated with authenticators, not directly by + * applications. + * <p> + * Calling this method does not update the last authenticated timestamp, referred by + * {@link #KEY_LAST_AUTHENTICATED_TIME}. To update it, call + * {@link #notifyAccountAuthenticated(Account)} after getting success. + * <p> + * It is safe to call this method from the main thread. + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. + * + * @param account The {@link Account} to add + * @param password The password to associate with the account, null for none + * @param extras String values to use for the account's userdata, null for none + * @param visibility Map from uid to visibility values which will be set before account is + * added. See getAccountVisibility for possilbe values. + * + * @return True if the account was successfully added, false if the account already exists, the + * account is null, or another error occurs. + * @hide + */ + public boolean addAccountExplicitly(Account account, String password, Bundle extras, + Map<Integer, Integer> visibility) { + if (account == null) + throw new IllegalArgumentException("account is null"); try { - return mService.addAccountExplicitlyWithUid(account, password, extras, selectedUids); + return mService.addAccountExplicitlyWithVisibility(account, password, extras, + visibility); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -803,9 +944,37 @@ public class AccountManager { } /** + * Gets all accounts of given type and their visibility for specific package. This method + * requires the caller to have a signature match with the authenticator that manages + * accountType. It is a helper method which combines calls to {@link #getAccountsByType} by + * authenticator and {@link #getAccountVisibility} for every returned account. + * + * <p> + * + * @param packageName Package name. + * @param accountType Account type. + * + * @return Map with visibility for all accounts of given type. See {@link #getAccountVisibility} + * for possilbe values. + * @hide + */ + public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName, + String accountType) { + try { + @SuppressWarnings("unchecked") + Map<Account, Integer> result = (Map<Account, Integer>) mService + .getAccountsAndVisibilityForPackage(packageName, accountType); + return result; + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Gives a certain UID, represented a application, access to an account - * <p>This method requires the caller to have a signature match with the authenticator - * that owns the specified account. + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. * * @param account Account to make visible. * @param uid The UID of the application to add account access. @@ -814,18 +983,18 @@ public class AccountManager { */ public boolean makeAccountVisible(Account account, int uid) { try { - return mService.makeAccountVisible(account, uid); + return mService.setAccountVisibility(account, uid, VISIBILITY_USER_MANAGED_VISIBLE); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** - * Removes visibility of certain account of a process identified - * by a given UID to an application. - * This is called by the Authenticator. - * <p>This method requires the caller to have a signature match with the authenticator - * that owns the specified account. + * Removes visibility of certain account of a process identified by a given UID to an + * application. This is called by the Authenticator. + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. * * @param account Remove visibility of this account.. * @param uid The UID of the application to remove account access. @@ -834,17 +1003,18 @@ public class AccountManager { */ public boolean removeAccountVisibility(Account account, int uid) { try { - return mService.removeAccountVisibility(account, uid); + return mService.setAccountVisibility(account, uid, VISIBILITY_USER_MANAGED_NOT_VISIBLE); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** - * Checks visibility of certain account of a process identified - * by a given UID. This is called by the Authenticator. - * <p>This method requires the caller to have a signature match with the authenticator - * that owns the specified account. + * Checks visibility of certain account of a process identified by a given UID. This is called + * by the Authenticator. + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. * * @param account Account to check visibility. * @param uid The UID of the application to check account access. @@ -853,7 +1023,60 @@ public class AccountManager { */ public boolean isAccountVisible(Account account, int uid) { try { - return mService.isAccountVisible(account, uid); + Integer visibility = mService.getAccountVisibility(account, uid); + return visibility == VISIBILITY_USER_MANAGED_NOT_VISIBLE + || visibility == VISIBILITY_VISIBLE; + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Set visibility value of given account to certain UID. + * <p> + * See {@link #getAccountVisibility} for possible values. + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. + * + * @param account Account to make visible. + * @param uid The UID of the application to modify account visibility. + * @param visibility - new visibility value. + * + * @return True if visibility value was succesfully updated. + * @hide + */ + public boolean setAccountVisibility(Account account, int uid, + @AccountVisibility int visibility) { + try { + return mService.setAccountVisibility(account, uid, visibility); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Gets visibility of certain account for given UID. Possible returned values are: + * <ul> + * <li>{@link #VISIBILITY_VISIBLE}</li> + * <li>{@link #VISIBILITY_USER_MANAGED_VISIBLE}</li> + * <li>{@link #VISIBILITY_NOT_VISIBLE} + * <li>{@link #VISIBILITY_USER_MANAGED_NOT_VISIBLE}</li> + * </ul> + * + * <p> + * This method requires the caller to have a signature match with the authenticator that owns + * the specified account. + * + * @param account Account to get visibility. + * @param uid The UID of the application to get account visibility. + * + * @return int Visibility for given account and uid. + * @hide + */ + public @AccountVisibility int getAccountVisibility(Account account, int uid) { + try { + return mService.getAccountVisibility(account, uid); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } @@ -2428,6 +2651,9 @@ public class AccountManager { }; // have many accounts, launch the chooser Intent intent = new Intent(); + // TODO - this activity will not include + // USER_MANAGED_NOT_VISIBLE + // accounts. We need to move method to service ComponentName componentName = ComponentName.unflattenFromString( Resources.getSystem().getString( R.string.config_chooseAccountActivity)); @@ -2483,58 +2709,57 @@ public class AccountManager { } /** - * This convenience helper combines the functionality of - * {@link #getAccountsByTypeAndFeatures}, {@link #getAuthToken}, and - * {@link #addAccount}. - * - * <p>This method gets a list of the accounts matching the - * specified type and feature set; if there is exactly one, it is - * used; if there are more than one, the user is prompted to pick one; - * if there are none, the user is prompted to add one. Finally, - * an auth token is acquired for the chosen account. + * This convenience helper combines the functionality of {@link #getAccountsByTypeAndFeatures}, + * {@link #getAuthToken}, and {@link #addAccount}. * - * <p>This method may be called from any thread, but the returned - * {@link AccountManagerFuture} must not be used on the main thread. + * <p> + * This method gets a list of the accounts matching specific type and feature set which are + * visible to the caller or for which user can grant access (see {@link #getAccountsByType} for + * details); if there is exactly one already visible account, it is used; if there are some + * accounts for which user grant visibility, the user is prompted to pick one; if there are + * none, the user is prompted to add one. Finally, an auth token is acquired for the chosen + * account. * - * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, - * MANAGE_ACCOUNTS permission is needed for those platforms. See docs for - * this function in API level 22. + * <p> + * This method may be called from any thread, but the returned {@link AccountManagerFuture} must + * not be used on the main thread. * - * @param accountType The account type required - * (see {@link #getAccountsByType}), must not be null - * @param authTokenType The desired auth token type - * (see {@link #getAuthToken}), must not be null - * @param features Required features for the account - * (see {@link #getAccountsByTypeAndFeatures}), may be null or empty - * @param activity The {@link Activity} context to use for launching new - * sub-Activities to prompt to add an account, select an account, - * and/or enter a password, as necessary; used only to call - * startActivity(); should not be null - * @param addAccountOptions Authenticator-specific options to use for - * adding new accounts; may be null or empty - * @param getAuthTokenOptions Authenticator-specific options to use for - * getting auth tokens; may be null or empty - * @param callback Callback to invoke when the request completes, - * null for no callback - * @param handler {@link Handler} identifying the callback thread, - * null for the main thread - * @return An {@link AccountManagerFuture} which resolves to a Bundle with - * at least the following fields: - * <ul> - * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account - * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account - * <li> {@link #KEY_AUTHTOKEN} - the auth token you wanted - * </ul> + * <p> + * <b>NOTE:</b> If targeting your app to work on API level 22 and before, MANAGE_ACCOUNTS + * permission is needed for those platforms. See docs for this function in API level 22. + * + * @param accountType The account type required (see {@link #getAccountsByType}), must not be + * null + * @param authTokenType The desired auth token type (see {@link #getAuthToken}), must not be + * null + * @param features Required features for the account (see + * {@link #getAccountsByTypeAndFeatures}), may be null or empty + * @param activity The {@link Activity} context to use for launching new sub-Activities to + * prompt to add an account, select an account, and/or enter a password, as necessary; + * used only to call startActivity(); should not be null + * @param addAccountOptions Authenticator-specific options to use for adding new accounts; may + * be null or empty + * @param getAuthTokenOptions Authenticator-specific options to use for getting auth tokens; may + * be null or empty + * @param callback Callback to invoke when the request completes, null for no callback + * @param handler {@link Handler} identifying the callback thread, null for the main thread + * @return An {@link AccountManagerFuture} which resolves to a Bundle with at least the + * following fields: + * <ul> + * <li>{@link #KEY_ACCOUNT_NAME} - the name of the account + * <li>{@link #KEY_ACCOUNT_TYPE} - the type of the account + * <li>{@link #KEY_AUTHTOKEN} - the auth token you wanted + * </ul> * - * If an error occurred, {@link AccountManagerFuture#getResult()} throws: - * <ul> - * <li> {@link AuthenticatorException} if no authenticator was registered for - * this account type or the authenticator failed to respond - * <li> {@link OperationCanceledException} if the operation was canceled for - * any reason, including the user canceling any operation - * <li> {@link IOException} if the authenticator experienced an I/O problem - * updating settings, usually because of network trouble - * </ul> + * If an error occurred, {@link AccountManagerFuture#getResult()} throws: + * <ul> + * <li>{@link AuthenticatorException} if no authenticator was registered for this + * account type or the authenticator failed to respond + * <li>{@link OperationCanceledException} if the operation was canceled for any reason, + * including the user canceling any operation + * <li>{@link IOException} if the authenticator experienced an I/O problem updating + * settings, usually because of network trouble + * </ul> */ public AccountManagerFuture<Bundle> getAuthTokenByFeatures( final String accountType, final String authTokenType, final String[] features, @@ -2689,33 +2914,33 @@ public class AccountManager { }; /** - * Adds an {@link OnAccountsUpdateListener} to this instance of the - * {@link AccountManager}. This listener will be notified whenever the - * list of accounts on the device changes. + * Adds an {@link OnAccountsUpdateListener} to this instance of the {@link AccountManager}. This + * listener will be notified whenever user or AbstractAcccountAuthenticator made changes to + * accounts related to the caller - either list of accounts returned by {@link #getAccounts()} + * was changed, or new account was added for which user can grant access to the caller. * - * <p>As long as this listener is present, the AccountManager instance - * will not be garbage-collected, and neither will the {@link Context} - * used to retrieve it, which may be a large Activity instance. To avoid - * memory leaks, you must remove this listener before then. Normally - * listeners are added in an Activity or Service's {@link Activity#onCreate} - * and removed in {@link Activity#onDestroy}. + * <p> + * As long as this listener is present, the AccountManager instance will not be + * garbage-collected, and neither will the {@link Context} used to retrieve it, which may be a + * large Activity instance. To avoid memory leaks, you must remove this listener before then. + * Normally listeners are added in an Activity or Service's {@link Activity#onCreate} and + * removed in {@link Activity#onDestroy}. * - * <p>The listener will only be informed of accounts that would be returned - * to the caller via {@link #getAccounts()}. Typically this means that to - * get any accounts, the caller will need to be grated the GET_ACCOUNTS - * permission. * - * <p>It is safe to call this method from the main thread. + * If SUPPORTED_ACCOUNT_TYPES is specified in the manifest file, listener will only be + * notified about whitelisted types. + * + * <p> + * It is safe to call this method from the main thread. * * @param listener The listener to send notifications to - * @param handler {@link Handler} identifying the thread to use - * for notifications, null for the main thread - * @param updateImmediately If true, the listener will be invoked - * (on the handler thread) right away with the current account list + * @param handler {@link Handler} identifying the thread to use for notifications, null for the + * main thread + * @param updateImmediately If true, the listener will be invoked (on the handler thread) right + * away with the current account list * @throws IllegalArgumentException if listener is null * @throws IllegalStateException if listener was already added */ - @RequiresPermission(GET_ACCOUNTS) public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener, Handler handler, boolean updateImmediately) { if (listener == null) { @@ -2729,22 +2954,47 @@ public class AccountManager { mAccountsUpdatedListeners.put(listener, handler); + if (wasEmpty) { // Register a broadcast receiver to monitor account changes IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION); + if (isVisibleAccountsChangedBroadcastSupported()) { + intentFilter.addAction(ACTION_VISIBLE_ACCOUNTS_CHANGED); + } else { + intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION); + } // To recover from disk-full. intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); + // Register a broadcast receiver to monitor account changes mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter); } } - if (updateImmediately) { postToHandler(handler, listener, getAccounts()); } } /** + * @hide + */ + private boolean isVisibleAccountsChangedBroadcastSupported() { + String interestedTypes = null; + try { + String packageName = mContext.getOpPackageName(); + ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(packageName, + PackageManager.GET_META_DATA); + Bundle b = ai.metaData; + if (b == null) { + return false; + } + interestedTypes = b.getString(SUPPORTED_ACCOUNT_TYPES); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + return !TextUtils.isEmpty(interestedTypes); + } + + /** * Removes an {@link OnAccountsUpdateListener} previously registered with * {@link #addOnAccountsUpdatedListener}. The listener will no longer * receive notifications of account changes. diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index fc10990c6867..66c3ca31afdb 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -24,6 +24,8 @@ import android.os.Bundle; import android.os.RemoteCallback; import android.os.UserHandle; +import java.util.Map; + /** * Central application service that provides account management. * @hide @@ -106,18 +108,17 @@ interface IAccountManager { void isCredentialsUpdateSuggested(in IAccountManagerResponse response, in Account account, String statusToken); - /* Allows Authenticator to view what packages or UIDs on phone have requested it. */ + /* Allows Authenticator to get UIDs of packages which registered to receive updates about given account type.*/ int[] getRequestingUidsForType(String accountType); - /* Allows authenticator to add an account explicitly that is only visible to - certain uids; the authenticator learns of these UIDs */ - boolean addAccountExplicitlyWithUid(in Account account, String password, in Bundle extras, - in int[] selectedUids); + boolean addAccountExplicitlyWithVisibility(in Account account, String password, in Bundle extras, + in Map visibility); + + boolean setAccountVisibility(in Account a, int uid, int newVisibility); + int getAccountVisibility(in Account a, int uid); - /* Controls visibility of UIDs of applications to Accounts */ - boolean removeAccountVisibility(in Account a, in int uid); - boolean makeAccountVisible(in Account a, in int uid); - boolean isAccountVisible(in Account a, in int uid); + /* 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 */ boolean hasAccountAccess(in Account account, String packageName, in UserHandle userHandle); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index c65aed7565f3..11e1a9d9b05f 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -201,25 +201,9 @@ public class AccountManagerService private final Map<Account, Map<String, String>> userDataCache = new HashMap<>(); /** protected by the {@link #cacheLock} */ private final Map<Account, Map<String, String>> authTokenCache = new HashMap<>(); - /** protected by the {@link #cacheLock} */ private final TokenCache accountTokenCaches = new TokenCache(); - /** protected by the {@link #cacheLock} */ - private final Map<String, ArrayList<Integer>> mApplicationAccountRequestMappings = - new HashMap<>(); - - /* Together the below two Sparse Arrays serve as visible list. One maps UID to account - number. Another maps Account number to Account.*/ - - /** protected by the {@link #cacheLock} */ - private final SparseArray<ArrayList<Integer>> mVisibleListUidToMockAccountNumbers = - new SparseArray<>(); - - //TODO: Instead of using Mock Account IDs, use the actual account IDs. - /** protected by the {@link #cacheLock} */ - private final SparseArray<Account> mMockAccountIdToAccount = new SparseArray<>(); - /** * protected by the {@link #cacheLock} * @@ -301,20 +285,7 @@ public class AccountManagerService @Override public void run() { purgeOldGrantsAll(); - - /* clears application request's for account types supported */ - int uidOfUninstalledApplication = - intent.getIntExtra(Intent.EXTRA_UID, -1); - if(uidOfUninstalledApplication != -1) { - clearRequestedAccountVisibility(uidOfUninstalledApplication, - getUserAccounts(UserHandle.getUserId( - uidOfUninstalledApplication))); - } - - /* removes visibility of previous UID of this uninstalled application*/ - removeAccountVisibilityAllAccounts(uidOfUninstalledApplication, - getUserAccounts(UserHandle.getUserId( - uidOfUninstalledApplication))); + // TODO remove visibility entries. } }; mHandler.post(purgingRunnable); @@ -472,225 +443,46 @@ public class AccountManagerService } @Override - public boolean addAccountExplicitlyWithUid(Account account, String password, Bundle extras, - int[] selectedUids) { - if(addAccountExplicitly(account,password,extras)) { - for(int thisUid : selectedUids) { - makeAccountVisible(account, thisUid); - } - return true; - } + public boolean addAccountExplicitlyWithVisibility(Account account, String password, Bundle extras, + Map uidToVisibility) { + // TODO implementation return false; } @Override - public int[] getRequestingUidsForType(String accountType) { - 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); - throw new SecurityException(msg); - } - return getRequestingUidsForType(accountType, getUserAccounts( - UserHandle.getUserId(callingUid))); - } - - /** - * Returns all UIDs for applications that requested the account type. This method - * is called indirectly by the Authenticator and AccountManager - * - * @param accountType authenticator would like to know the requesting apps of - * @param ua UserAccount that currently hosts the account and application - * - * @return ArrayList of all UIDs that support accounts of this - * account type that seek approval (to be used to know which accounts for - * the authenticator to include in addAccountExplicitly). Null if none. - */ - private int[] getRequestingUidsForType(String accountType, UserAccounts ua) { - synchronized(ua.cacheLock) { - Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings = - ua.mApplicationAccountRequestMappings; - ArrayList<Integer> allUidsForAccountType = userApplicationAccountRequestMappings.get( - accountType); - if(allUidsForAccountType == null) { - return null; - } - int[] toReturn = new int[allUidsForAccountType.size()]; - for(int i = 0 ; i < toReturn.length ; i++) { - toReturn[i] = allUidsForAccountType.get(i); - } - return toReturn; - } + public Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName, + String accountType) { + // TODO Implement. + return new HashMap<Account, Integer>(); } @Override - public boolean isAccountVisible(Account a, int uid) { + public int[] getRequestingUidsForType(String accountType) { int callingUid = Binder.getCallingUid(); - if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) { + if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) { String msg = String.format( "uid %s cannot get secrets for accounts of type: %s", callingUid, - a.type); + accountType); throw new SecurityException(msg); } - return isAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid))); - } - - /** - * Checks visibility of certain account of a process identified - * by a given UID. This is called by the Authenticator indirectly. - * - * @param a The account to check visibility of - * @param uid UID to check visibility of - * @param ua UserAccount that currently hosts the account and application - * - * @return True if application has access to the account - * - */ - private boolean isAccountVisible(Account a, int uid, UserAccounts ua) { - if(isAccountManagedByCaller(a.type, uid, UserHandle.getUserId(uid))) { - return true; - } - int accountMapping = getMockAccountNumber(a, ua); - if(accountMapping < 0) { - return true; - } - synchronized(ua.cacheLock) { - SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount; - SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums = - ua.mVisibleListUidToMockAccountNumbers; - ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid); - int indexOfAccountMapping = userAcctIdToAcctMap.indexOfValueByValue(a); - return indexOfAccountMapping == -1 || (linkedAccountsToUid != null - && linkedAccountsToUid.contains(accountMapping)); - } + // TODO Implement. + return new int[]{}; } @Override - public boolean makeAccountVisible(Account a, int uid) { - int callingUid = Binder.getCallingUid(); - if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) { - String msg = String.format( - "uid %s cannot get secrets for accounts of type: %s", - callingUid, - a.type); - throw new SecurityException(msg); - } - return makeAccountVisible(a, uid, getUserAccounts(UserHandle.getUserId(callingUid))); - } - - /** - * Gives a certain UID, represented a application, access to an account. This method - * is called indirectly by the Authenticator. - * - * @param a Account to make visible - * @param uid to add visibility of the Account from - * @param ua UserAccount that currently hosts the account and application - * - * @return True if account made visible to application and was not previously visible. - */ - private boolean makeAccountVisible(Account a, int uid, UserAccounts ua) { - int accountMapping = getMockAccountNumber(a, ua); - if(accountMapping < 0) { - accountMapping = makeAccountNumber(a, ua); - } - synchronized(ua.cacheLock) { - SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums = - ua.mVisibleListUidToMockAccountNumbers; - ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid); - if(linkedAccountsToUid == null) { - linkedAccountsToUid = new ArrayList<>(); - linkedAccountsToUid.add(accountMapping); - userWlUidToMockAccountNums.put(uid, linkedAccountsToUid); - } else if(!linkedAccountsToUid.contains(accountMapping)) { - linkedAccountsToUid.add(accountMapping); - } else { - return false; - } - } - - String[] subPackages = mPackageManager.getPackagesForUid(uid); - if(subPackages != null) { - for(String subPackage : subPackages) { - sendNotification(subPackage, a); - } - } - return true; + public int getAccountVisibility(Account a, int uid) { + // TODO Implement. + return 0; } @Override - public boolean removeAccountVisibility(Account a, int uid) { - int callingUid = Binder.getCallingUid(); - if (!isAccountManagedByCaller(a.type, callingUid, UserHandle.getUserId(callingUid))) { - String msg = String.format( - "uid %s cannot get secrets for accounts of type: %s", - callingUid, - a.type); - throw new SecurityException(msg); - } - return removeAccountVisibility(a, uid, getUserAccounts(UserHandle.getUserId(callingUid))); - } - - /** - * Removes visibility of certain account of a process identified - * by a given UID to an application. This is called directly by the - * AccountManager and indirectly by the Authenticator. - * - * @param a Account to remove visibility from - * @param uid UID to remove visibility of the Account from - * @param ua UserAccount that hosts the account and application - * - * @return True if application access to account removed and was previously visible. - */ - private boolean removeAccountVisibility(Account a, int uid, UserAccounts ua) { - int accountMapping = getMockAccountNumber(a, ua); - if(accountMapping < 0) { - return false; - } - synchronized(ua.cacheLock) { - SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums = - ua.mVisibleListUidToMockAccountNumbers; - ArrayList<Integer> linkedAccountsToUid = userWlUidToMockAccountNums.get(uid); - if(linkedAccountsToUid != null) { - boolean toReturn = linkedAccountsToUid.remove((Integer) accountMapping); - if(linkedAccountsToUid.size() == 0) { - userWlUidToMockAccountNums.remove(uid); - } - return toReturn; - } - } + public boolean setAccountVisibility(Account a, int uid, int visibility) { + // TODO Implement. return false; } /** - * Registers an application's preferences for supported account types for login. This is - * a helper method of requestAccountVisibility and indirectly called by AccountManager. - * - * @param accountTypes account types third party app is willing to support - * @param uid of application requesting account visibility - * @param ua UserAccount that hosts the account and application - */ - private void addRequestedAccountsVisibility(String[] accountTypes, int uid, UserAccounts ua) { - synchronized(ua.cacheLock) { - Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings = - ua.mApplicationAccountRequestMappings; - for(String accountType : accountTypes) { - ArrayList<Integer> accountUidAppList = userApplicationAccountRequestMappings - .get(accountType); - if(accountUidAppList == null) { - accountUidAppList = new ArrayList<>(); - accountUidAppList.add(uid); - userApplicationAccountRequestMappings.put(accountType, accountUidAppList); - } else if (!accountUidAppList.contains(uid)) { - accountUidAppList.add(uid); - } - } - } - } - - /** * Registers the requested login account types requested by all the applications already * installed on the device. */ @@ -706,124 +498,8 @@ public class AccountManagerService } /** - * Clears all preferences an application had for login account types it offered - * support for. This method is used by AccountManager after application is - * uninstalled. - * - * @param uid Uid of the application to clear account type preferences - * @param ua UserAccount that hosted the account and application - * - * @return true if any previous settings were overridden. - */ - private boolean clearRequestedAccountVisibility(int uid, UserAccounts ua) { - boolean accountsDeleted = false; - ArrayList<String> accountTypesToRemove = new ArrayList<>(); - synchronized(ua.cacheLock) { - Map<String, ArrayList<Integer>> userApplicationAccountRequestMappings = - ua.mApplicationAccountRequestMappings; - Set<Entry<String, ArrayList<Integer>>> accountTypeAppListEntries = - userApplicationAccountRequestMappings.entrySet(); - - for(Entry<String, ArrayList<Integer>> entry : accountTypeAppListEntries) { - ArrayList<Integer> supportedApps = entry.getValue(); - if(supportedApps.remove((Integer) uid)) { - accountsDeleted = true; - } - - if(supportedApps.isEmpty()) { - accountTypesToRemove.add(entry.getKey()); - } - } - - for(String s : accountTypesToRemove) { - userApplicationAccountRequestMappings.remove(s); - } - } - - return accountsDeleted; - } - - /** - * Retrieves the mock account number associated with an Account in order to later retrieve - * the account from the Integer-Account Mapping. An account number is not the same as - * accountId in the database. This method can be indirectly called by AccountManager and - * indirectly by the Authenticator. - * - * @param a account to retrieve account number mapping - * @param ua UserAccount that currently hosts the account and application - * - * @return account number affiliated with the Account in question. Negative number if none. - */ - private int getMockAccountNumber(Account a, UserAccounts ua) { - //TODO: Each account is linked to AccountId rather than generated mock account numbers - SparseArray<Account> userAcctIdToAcctMap = - ua.mMockAccountIdToAccount; - synchronized(ua.cacheLock) { - int indexOfAccount = userAcctIdToAcctMap.indexOfValueByValue(a); - if(indexOfAccount < 0) { - return -1; - } - return userAcctIdToAcctMap.keyAt(indexOfAccount); - } - } - - /** - * Returns a full list of accounts that a certain UID is allowed access - * based on the visible list entries. - * - * @param uid of application to retrieve visible listed accounts for - * @param ua UserAccount that currently hosts the account and application - * - * @return array of Account values that are accessible by the given uids - */ - private Account[] getVisibleListedAccounts(int uid, UserAccounts ua) { - ArrayList<Account> visibleListedAccounts = new ArrayList<>(); - synchronized(ua.cacheLock) { - SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount; - SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums = - ua.mVisibleListUidToMockAccountNumbers; - ArrayList<Integer> visibleListedUidAccountNumbers = - userWlUidToMockAccountNums.get(uid); - if(visibleListedUidAccountNumbers != null) { - for(Integer accountNumber : visibleListedUidAccountNumbers) { - Account currentAccount = userAcctIdToAcctMap.get(accountNumber); - visibleListedAccounts.add(currentAccount); - } - } - } - Account[] arrVisibleListedAccounts = new Account[visibleListedAccounts.size()]; - return visibleListedAccounts.toArray(arrVisibleListedAccounts); - } - - /** - * Makes an account number for a given Account to be mapped to. - * This method is called by makeVisible if an Account does not have - * a mapping for the visible list. This method is thus indirectly - * called by the Authenticator. - * - * @param a account to make an account number mapping of - * @param ua UserAccount that currently hosts the account and application - * - * @return account number created to map to the given account - */ - // TODO: Remove this method and use accountId from DB. - private int makeAccountNumber(Account a, UserAccounts ua) { - synchronized(ua.cacheLock) { - SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount; - int newAccountMapping = 0; - while(userAcctIdToAcctMap.get(newAccountMapping) != null) { - newAccountMapping++; - } - userAcctIdToAcctMap.put(newAccountMapping, a); - return newAccountMapping; - } - } - - - - /** - * 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. + * 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. * * @param uid UID of application * @param ua UserAccount that currently hosts the account and application @@ -834,105 +510,27 @@ public class AccountManagerService try { String[] allPackages = mPackageManager.getPackagesForUid(uid); if (allPackages != null) { - for(String aPackage : allPackages) { + for (String aPackage : allPackages) { ApplicationInfo ai = mPackageManager.getApplicationInfo(aPackage, PackageManager.GET_META_DATA); Bundle b = ai.metaData; - if(b == null) { + if (b == null) { return; } - interestedPackages = b.getString("android.accounts.SupportedLoginTypes"); + interestedPackages = b.getString(AccountManager.SUPPORTED_ACCOUNT_TYPES); } } } catch (PackageManager.NameNotFoundException e) { Log.d("NameNotFoundException", e.getMessage()); } - if(interestedPackages != null) { - /* request remote account types directly from here. Reads from Android Manifest */ - requestAccountVisibility(interestedPackages.split(";"), uid, ua); + if (interestedPackages != null) { + // TODO request visibility + // requestAccountVisibility(interestedPackages.split(";"), uid, ua); } } /** - * Allows AccountManager to register account types that an application has login - * support for. This method over-writes all of the application's previous settings - * for accounts it supported. - * - * @param accountTypes array of account types application wishes to support - * @param uid of application registering requested account types - * @param ua UserAccount that hosts the account and application - */ - private void requestAccountVisibility(String[] accountTypes, int uid, UserAccounts ua) { - if(accountTypes.length > 0) { - clearRequestedAccountVisibility(uid, ua); - addRequestedAccountsVisibility(accountTypes, uid, ua); - } - } - - /** - * Removes visibility of all Accounts to this particular UID. This is called when an - * application is uninstalled so another application that is installed with the same - * UID cannot access Accounts. This is called by AccountManager. - * - * @param uid of application to remove all Account visibility to - * @param ua UserAccount that hosts the current Account - */ - private void removeAccountVisibilityAllAccounts(int uid, UserAccounts ua) { - synchronized(ua.cacheLock) { - SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums = - ua.mVisibleListUidToMockAccountNumbers; - SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount; - ArrayList<Integer> allAccountNumbersList = userWlUidToMockAccountNums.get(uid); - if(allAccountNumbersList != null) { - Integer[] allAccountNumbers = allAccountNumbersList.toArray( - new Integer[allAccountNumbersList.size()]); - for(int accountNum : allAccountNumbers) { - removeAccountVisibility(userAcctIdToAcctMap.get(accountNum), uid, ua); - } - } - } - } - - /** - * Removes visible list functionality of a certain Account. - * This method is currently called by (1) addAccountExplicitly (as opposed to - * addAccountExplicitlyWithUid) and (2) removeAccountExplicitly. - * - * @param a the account to clear the visible list functionality for - * @param ua currently UserAccounts profile containing Account - * - * @return true if account previously had visible list functionality - */ - private boolean removeVisibleListFunctionality(Account a, UserAccounts ua) { - int mockAccountNum = getMockAccountNumber(a, ua); - if(mockAccountNum < 0) { - return false; - } - synchronized(ua.cacheLock) { - SparseArray<ArrayList<Integer>> userWlUidToMockAccountNums = - ua.mVisibleListUidToMockAccountNumbers; - SparseArray<Account> userAcctIdToAcctMap = ua.mMockAccountIdToAccount; - - /* Removing mapping from account number to account removes visible list functionality*/ - userAcctIdToAcctMap.remove(mockAccountNum); - - for(int i = userWlUidToMockAccountNums.size() - 1 ; i >= 0 ; i--) { - int uidKey = userWlUidToMockAccountNums.keyAt(i); - ArrayList<Integer> allAccountNumbers = userWlUidToMockAccountNums.get(uidKey); - if(allAccountNumbers != null) { - allAccountNumbers.remove(mockAccountNum); - if(allAccountNumbers.isEmpty()) { - userWlUidToMockAccountNums.remove(uidKey); - } - } - } - } - return true; - } - - /** - * Sends a direct intent to a package, notifying it of a visible account. This - * method is a helper method of makeAccountVisible. + * 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 @@ -940,9 +538,10 @@ public class AccountManagerService private void sendNotification(String desiredPackage, Account visibleAccount) { Intent intent = new Intent(); intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - intent.setAction(NEW_ACCOUNT_VISIBLE); + intent.setAction(AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED); intent.setPackage(desiredPackage); - intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount); + // TODO update documentation, add account extra if new account became visible + // intent.putExtra("android.accounts.KEY_ACCOUNT", (Account) visibleAccount); mContext.sendBroadcast(intent); } @@ -1471,7 +1070,7 @@ public class AccountManagerService account.type); throw new SecurityException(msg); } - removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid))); + /* * Child users are not allowed to add accounts. Only the accounts that are * shared by the parent profile can be added to child profile. @@ -2050,7 +1649,6 @@ public class AccountManagerService account.type); throw new SecurityException(msg); } - removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid))); UserAccounts accounts = getUserAccountsForCaller(); final long accountId = accounts.accountsDb.findDeAccountId(account); logRecord( @@ -3303,7 +2901,7 @@ public class AccountManagerService throw new IllegalArgumentException("sessionBundle is empty"); } - // Only allow the system process to finish session for other users + // Only allow the system process to finish session for other users. if (isCrossUser(callingUid, userId)) { throw new SecurityException( String.format( @@ -4095,23 +3693,11 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); - Account[] accountsToReturn = getAccountsInternal( + return getAccountsInternal( accounts, callingUid, callingPackage, visibleAccountTypes); - ArrayList<Account> accountsToReturnList = new - ArrayList<Account>(Arrays.asList(accountsToReturn)); - for(int i = accountsToReturnList.size() - 1; i >= 0 ; i--) { - // if account not visible to caller or managed by caller, remove from - // accounts to return. Note that all accounts visible by default unless - // visible list functionality implemented - if(!(isAccountVisible(accountsToReturnList.get(i), callingUid, - getUserAccounts(userId)))) { - accountsToReturnList.remove(i); - } - } - return accountsToReturnList.toArray(new Account[accountsToReturnList.size()]); } finally { restoreCallingIdentity(identityToken); } |