Update account visibility API.

Remove no longer used functions and in-memory visibility table.
Add stubs for new methods.
Actual implementation will be added in follow up CLs.
Bug: https://b.corp.google.com/issues/33046496
Test: manual tests, cts tests.

Change-Id: I990759b20c57df70bc944e27b84e59b9f77b9bd4
This commit is contained in:
Dmitry Dementyev
2016-12-02 10:27:45 -08:00
parent 0de5d03c9d
commit 527454795f
3 changed files with 500 additions and 663 deletions

View File

@@ -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,15 +339,54 @@ 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
*/
@@ -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).
* 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 @@ public class AccountManager {
/**
* @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 @@ 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>
* 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}.
*
* <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.
*
* @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 @@ 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();
}
@@ -802,10 +943,38 @@ 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}.
* 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 @@ 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,21 +2954,46 @@ 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

View File

@@ -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);
/* 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);

View File

@@ -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,17 +443,19 @@ 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 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();
@@ -493,203 +466,22 @@ public class AccountManagerService
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 @@ 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);
}