Merge "Update account visibility API."
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 1ad43fa..8185818 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -18,6 +18,7 @@
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.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.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 @@
* {@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 @@
"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 @@
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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * Lists all accounts of any type registered on the device.
- * Equivalent to getAccountsByType(null).
+ * 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>It is safe to call this method from the main thread.
+ * <p>
+ * It is safe to call this method from 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.
- *
- * @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 @@
/**
* @hide
- * Lists all accounts of any type registered on the device for a given
- * user id. Equivalent to getAccountsByType(null).
+ * Lists all accounts visible to caller regardless of type for a given user id. Equivalent to
+ * getAccountsByType(null).
*
- * <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>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.
- *
- * @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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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.
+ *
+ * <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, must not be null
- * @param features An array of the account features to require,
- * may be null or empty
- *
- * <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 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 @@
}
/**
- * 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 @@
}
/**
+ * 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 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 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 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 @@
};
// 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 @@
}
/**
- * This convenience helper combines the functionality of
- * {@link #getAccountsByTypeAndFeatures}, {@link #getAuthToken}, and
- * {@link #addAccount}.
+ * 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.
+ * <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>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><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>
+ * <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>
+ * @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 @@
};
/**
- * 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 @@
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 fc10990..66c3ca3 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -24,6 +24,8 @@
import android.os.RemoteCallback;
import android.os.UserHandle;
+import java.util.Map;
+
/**
* Central application service that provides account management.
* @hide
@@ -106,18 +108,17 @@
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);
- /* 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);
+ boolean setAccountVisibility(in Account a, int uid, int newVisibility);
+ int getAccountVisibility(in Account a, 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 c65aed7..11e1a9d 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 @@
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 @@
@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,18 +443,20 @@
}
@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 Map<Account, Integer> getAccountsAndVisibilityForPackage(String packageName,
+ String accountType) {
+ // TODO Implement.
+ return new HashMap<Account, Integer>();
+ }
+
+ @Override
public int[] getRequestingUidsForType(String accountType) {
int callingUid = Binder.getCallingUid();
if (!isAccountManagedByCaller(accountType, callingUid, UserHandle.getUserId(callingUid))) {
@@ -493,204 +466,23 @@
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;
- }
+ // TODO Implement.
+ return new int[]{};
}
@Override
- public boolean isAccountVisible(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 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));
- }
+ public int getAccountVisibility(Account a, int uid) {
+ // TODO Implement.
+ return 0;
}
@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;
- }
-
- @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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
}