am ffb46022: Permissions: Fix account related permissions.

* commit 'ffb46022b30dffdf1cc4cbd08d4bcbe51e1f8814':
  Permissions: Fix account related permissions.
This commit is contained in:
Carlos Valdivia
2015-06-11 18:22:59 +00:00
committed by Android Git Automerger
7 changed files with 230 additions and 284 deletions

View File

@@ -17,7 +17,6 @@ package android {
field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE"; field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL";
field public static final java.lang.String AUTHENTICATE_ACCOUNTS = "android.permission.AUTHENTICATE_ACCOUNTS";
field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
@@ -86,7 +85,6 @@ package android {
field public static final java.lang.String INTERNET = "android.permission.INTERNET"; field public static final java.lang.String INTERNET = "android.permission.INTERNET";
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES"; field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS"; field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS"; field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR"; field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -145,7 +143,6 @@ package android {
field public static final java.lang.String TRANSMIT_IR = "android.permission.TRANSMIT_IR"; field public static final java.lang.String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
field public static final java.lang.String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT"; field public static final java.lang.String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final java.lang.String USE_CREDENTIALS = "android.permission.USE_CREDENTIALS";
field public static final java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; field public static final java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
field public static final java.lang.String USE_SIP = "android.permission.USE_SIP"; field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
field public static final java.lang.String VIBRATE = "android.permission.VIBRATE"; field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";

View File

@@ -24,7 +24,6 @@ package android {
field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER"; field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"; field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL";
field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"; field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
field public static final java.lang.String AUTHENTICATE_ACCOUNTS = "android.permission.AUTHENTICATE_ACCOUNTS";
field public static final java.lang.String BACKUP = "android.permission.BACKUP"; field public static final java.lang.String BACKUP = "android.permission.BACKUP";
field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
@@ -117,7 +116,6 @@ package android {
field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS"; field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE"; field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO"; field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS"; field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES"; field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
@@ -217,7 +215,6 @@ package android {
field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
field public static final java.lang.String UPDATE_LOCK = "android.permission.UPDATE_LOCK"; field public static final java.lang.String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY"; field public static final java.lang.String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field public static final java.lang.String USE_CREDENTIALS = "android.permission.USE_CREDENTIALS";
field public static final java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; field public static final java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
field public static final java.lang.String USE_SIP = "android.permission.USE_SIP"; field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
field public static final java.lang.String VIBRATE = "android.permission.VIBRATE"; field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";

View File

@@ -51,10 +51,7 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import static android.Manifest.permission.AUTHENTICATE_ACCOUNTS;
import static android.Manifest.permission.GET_ACCOUNTS; import static android.Manifest.permission.GET_ACCOUNTS;
import static android.Manifest.permission.MANAGE_ACCOUNTS;
import static android.Manifest.permission.USE_CREDENTIALS;
/** /**
* This class provides access to a centralized registry of the user's * This class provides access to a centralized registry of the user's
@@ -319,14 +316,12 @@ public class AccountManager {
* *
* <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 hold the permission * <p>This method requires the caller to have a signature match with the
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} * authenticator that owns the specified account.
* and to have the same UID as the account's authenticator.
* *
* @param account The account to query for a password * @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 * @return The account's password, null if none or if the account doesn't exist
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String getPassword(final Account account) { public String getPassword(final Account account) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
try { try {
@@ -345,14 +340,12 @@ public class AccountManager {
* *
* <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 hold the permission * <p>This method requires the caller to have a signature match with the
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} * authenticator that owns the specified account.
* and to have the same UID as the account's authenticator.
* *
* @param account The account to query for user data * @param account The account to query for user data
* @return The user data, null if the account or key doesn't exist * @return The user data, null if the account or key doesn't exist
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String getUserData(final Account account, final String key) { public String getUserData(final Account account, final String key) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (key == null) throw new IllegalArgumentException("key is null"); if (key == null) throw new IllegalArgumentException("key is null");
@@ -662,10 +655,8 @@ public class AccountManager {
* wizards associated with authenticators, not directly by applications. * wizards associated with authenticators, not directly by applications.
* *
* <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
* <p>This method requires the caller to hold the permission * authenticator that owns the specified account.
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS}
* and to have the same UID as the added account's authenticator.
* *
* @param account The {@link Account} to add * @param account The {@link Account} to add
* @param password The password to associate with the account, null for none * @param password The password to associate with the account, null for none
@@ -673,7 +664,6 @@ public class AccountManager {
* @return True if the account was successfully added, false if the account * @return True if the account was successfully added, false if the account
* already exists, the account is null, or another error occurs. * already exists, the account is null, or another error occurs.
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public boolean addAccountExplicitly(Account account, String password, Bundle userdata) { public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
try { try {
@@ -692,14 +682,13 @@ public class AccountManager {
* <p> * <p>
* It is not safe to call this method from the main thread. As such, call it * It is not safe to call this method from the main thread. As such, call it
* from another thread. * from another thread.
* <p> * <p>This method requires the caller to have a signature match with the
* This method requires the caller to hold the permission * authenticator that owns the specified account.
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and should be
* called from the account's authenticator.
* *
* @param account The {@link Account} to be updated. * @param account The {@link Account} to be updated.
* @return boolean {@code true} if the authentication of the account has been successfully
* acknowledged. Otherwise {@code false}.
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public boolean notifyAccountAuthenticated(Account account) { public boolean notifyAccountAuthenticated(Account account) {
if (account == null) if (account == null)
throw new IllegalArgumentException("account is null"); throw new IllegalArgumentException("account is null");
@@ -717,9 +706,8 @@ public class AccountManager {
* *
* <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 hold the permission * <p>This method requires the caller to have a signature match with the
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} * authenticator that manages the specified account.
* and have the same UID as the account's authenticator.
* *
* @param account The {@link Account} to rename * @param account The {@link Account} to rename
* @param newName String name to be associated with the account. * @param newName String name to be associated with the account.
@@ -731,7 +719,6 @@ public class AccountManager {
* after the name change. If successful the account's name will be the * after the name change. If successful the account's name will be the
* specified new name. * specified new name.
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public AccountManagerFuture<Account> renameAccount( public AccountManagerFuture<Account> renameAccount(
final Account account, final Account account,
@Size(min = 1) final String newName, @Size(min = 1) final String newName,
@@ -783,11 +770,8 @@ public class AccountManager {
* The authenticator may have its own policies preventing account * The authenticator may have its own policies preventing account
* deletion, in which case the account will not be deleted. * deletion, in which case the account will not be deleted.
* *
* <p>This method may be called from any thread, but the returned * <p>This method requires the caller to have a signature match with the
* {@link AccountManagerFuture} must not be used on the main thread. * authenticator that manages the specified account.
*
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
* *
* @param account The {@link Account} to remove * @param account The {@link Account} to remove
* @param callback Callback to invoke when the request completes, * @param callback Callback to invoke when the request completes,
@@ -800,15 +784,16 @@ public class AccountManager {
* {@link #removeAccount(Account, Activity, AccountManagerCallback, Handler)} * {@link #removeAccount(Account, Activity, AccountManagerCallback, Handler)}
* instead * instead
*/ */
@RequiresPermission(MANAGE_ACCOUNTS)
@Deprecated @Deprecated
public AccountManagerFuture<Boolean> removeAccount(final Account account, public AccountManagerFuture<Boolean> removeAccount(final Account account,
AccountManagerCallback<Boolean> callback, Handler handler) { AccountManagerCallback<Boolean> callback, Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
return new Future2Task<Boolean>(handler, callback) { return new Future2Task<Boolean>(handler, callback) {
@Override
public void doWork() throws RemoteException { public void doWork() throws RemoteException {
mService.removeAccount(mResponse, account, false); mService.removeAccount(mResponse, account, false);
} }
@Override
public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) { if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
throw new AuthenticatorException("no result in response"); throw new AuthenticatorException("no result in response");
@@ -827,8 +812,8 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission * <p>This method requires the caller to have a signature match with the
* {@link android.Manifest.permission#MANAGE_ACCOUNTS}. * authenticator that manages the specified account.
* *
* @param account The {@link Account} to remove * @param account The {@link Account} to remove
* @param activity The {@link Activity} context to use for launching a new * @param activity The {@link Activity} context to use for launching a new
@@ -855,11 +840,11 @@ public class AccountManager {
* adding accounts (of this type) has been disabled by policy * adding accounts (of this type) has been disabled by policy
* </ul> * </ul>
*/ */
@RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> removeAccount(final Account account, public AccountManagerFuture<Bundle> removeAccount(final Account account,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
return new AmsTask(activity, handler, callback) { return new AmsTask(activity, handler, callback) {
@Override
public void doWork() throws RemoteException { public void doWork() throws RemoteException {
mService.removeAccount(mResponse, account, activity != null); mService.removeAccount(mResponse, account, activity != null);
} }
@@ -880,9 +865,11 @@ public class AccountManager {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (userHandle == null) throw new IllegalArgumentException("userHandle is null"); if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
return new Future2Task<Boolean>(handler, callback) { return new Future2Task<Boolean>(handler, callback) {
@Override
public void doWork() throws RemoteException { public void doWork() throws RemoteException {
mService.removeAccountAsUser(mResponse, account, false, userHandle.getIdentifier()); mService.removeAccountAsUser(mResponse, account, false, userHandle.getIdentifier());
} }
@Override
public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) { if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
throw new AuthenticatorException("no result in response"); throw new AuthenticatorException("no result in response");
@@ -918,17 +905,14 @@ public class AccountManager {
* in which case the account will not be deleted. * in which case the account will not be deleted.
* <p> * <p>
* It is safe to call this method from the main thread. * It is safe to call this method from the main thread.
* <p> * <p>This method requires the caller to have a signature match with the
* This method requires the caller to hold the permission * authenticator that manages the specified account.
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and to have the
* same UID or signature as the account's authenticator.
* *
* @param account The {@link Account} to delete. * @param account The {@link Account} to delete.
* @return True if the account was successfully deleted, false if the * @return True if the account was successfully deleted, false if the
* account did not exist, the account is null, or another error * account did not exist, the account is null, or another error
* occurs. * occurs.
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public boolean removeAccountExplicitly(Account account) { public boolean removeAccountExplicitly(Account account) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
try { try {
@@ -948,14 +932,9 @@ public class AccountManager {
* *
* <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 hold the permission
* {@link android.Manifest.permission#MANAGE_ACCOUNTS} or
* {@link android.Manifest.permission#USE_CREDENTIALS}
*
* @param accountType The account type of the auth token to invalidate, must not be null * @param accountType The account type of the auth token to invalidate, must not be null
* @param authToken The auth token to invalidate, may be null * @param authToken The auth token to invalidate, may be null
*/ */
@RequiresPermission(anyOf = {MANAGE_ACCOUNTS, USE_CREDENTIALS})
public void invalidateAuthToken(final String accountType, final String authToken) { public void invalidateAuthToken(final String accountType, final String authToken) {
if (accountType == null) throw new IllegalArgumentException("accountType is null"); if (accountType == null) throw new IllegalArgumentException("accountType is null");
try { try {
@@ -976,16 +955,15 @@ public class AccountManager {
* *
* <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 hold the permission * <p>This method requires the caller to have a signature match with the
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} * authenticator that manages the specified account.
* and to have the same UID as the account's authenticator.
* *
* @param account The account to fetch an auth token for * @param account The account for which an auth token is to be fetched. Cannot be {@code null}.
* @param authTokenType The type of auth token to fetch, see {#getAuthToken} * @param authTokenType The type of auth token to fetch. Cannot be {@code null}.
* @return The cached auth token for this account and type, or null if * @return The cached auth token for this account and type, or null if
* no auth token is cached or the account does not exist. * no auth token is cached or the account does not exist.
* @see #getAuthToken
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String peekAuthToken(final Account account, final String authTokenType) { public String peekAuthToken(final Account account, final String authTokenType) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -1005,14 +983,12 @@ public class AccountManager {
* *
* <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 hold the permission * <p>This method requires the caller to have a signature match with the
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} * authenticator that manages the specified account.
* and have the same UID as the account's authenticator.
* *
* @param account The account to set a password for * @param account The account whose password is to be set. Cannot be {@code null}.
* @param password The password to set, null to clear the password * @param password The password to set, null to clear the password
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setPassword(final Account account, final String password) { public void setPassword(final Account account, final String password) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
try { try {
@@ -1030,14 +1006,14 @@ public class AccountManager {
* permissions, and may be used by applications or management interfaces * permissions, and may be used by applications or management interfaces
* to "sign out" from an account. * to "sign out" from an account.
* *
* <p>It is safe to call this method from the main thread. * <p>This method only successfully clear the account's password when the
* caller has the same signature as the authenticator that owns the
* specified account. Otherwise, this method will silently fail.
* *
* <p>This method requires the caller to hold the permission * <p>It is safe to call this method from the main thread.
* {@link android.Manifest.permission#MANAGE_ACCOUNTS}
* *
* @param account The account whose password to clear * @param account The account whose password to clear
*/ */
@RequiresPermission(MANAGE_ACCOUNTS)
public void clearPassword(final Account account) { public void clearPassword(final Account account) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
try { try {
@@ -1055,15 +1031,13 @@ public class AccountManager {
* *
* <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 hold the permission * <p>This method requires the caller to have a signature match with the
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} * authenticator that manages the specified account.
* and to have the same UID as the account's authenticator.
* *
* @param account The account to set the userdata for * @param account Account whose user data is to be set. Must not be {@code null}.
* @param key The userdata key to set. Must not be null * @param key String user data key to set. Must not be null
* @param value The value to set, null to clear this userdata key * @param value String value to set, {@code null} to clear this user data key
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setUserData(final Account account, final String key, final String value) { public void setUserData(final Account account, final String key, final String value) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (key == null) throw new IllegalArgumentException("key is null"); if (key == null) throw new IllegalArgumentException("key is null");
@@ -1083,15 +1057,13 @@ public class AccountManager {
* *
* <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 hold the permission * <p>This method requires the caller to have a signature match with the
* {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} * authenticator that manages the specified account.
* and to have the same UID as the account's authenticator.
* *
* @param account The account to set an auth token for * @param account The account to set an auth token for
* @param authTokenType The type of the auth token, see {#getAuthToken} * @param authTokenType The type of the auth token, see {#getAuthToken}
* @param authToken The auth token to add to the cache * @param authToken The auth token to add to the cache
*/ */
@RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setAuthToken(Account account, final String authTokenType, final String authToken) { public void setAuthToken(Account account, final String authTokenType, final String authToken) {
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -1110,9 +1082,6 @@ public class AccountManager {
* <p>This method may block while a network request completes, and must * <p>This method may block while a network request completes, and must
* never be made from the main thread. * never be made from the main thread.
* *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#USE_CREDENTIALS}.
*
* @param account The account to fetch an auth token for * @param account The account to fetch an auth token for
* @param authTokenType The auth token type, see {@link #getAuthToken getAuthToken()} * @param authTokenType The auth token type, see {@link #getAuthToken getAuthToken()}
* @param notifyAuthFailure If true, display a notification and return null * @param notifyAuthFailure If true, display a notification and return null
@@ -1126,7 +1095,6 @@ public class AccountManager {
* @throws java.io.IOException if the authenticator experienced an I/O problem * @throws java.io.IOException if the authenticator experienced an I/O problem
* creating a new auth token, usually because of network trouble * creating a new auth token, usually because of network trouble
*/ */
@RequiresPermission(USE_CREDENTIALS)
public String blockingGetAuthToken(Account account, String authTokenType, public String blockingGetAuthToken(Account account, String authTokenType,
boolean notifyAuthFailure) boolean notifyAuthFailure)
throws OperationCanceledException, IOException, AuthenticatorException { throws OperationCanceledException, IOException, AuthenticatorException {
@@ -1165,9 +1133,6 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#USE_CREDENTIALS}.
*
* @param account The account to fetch an auth token for * @param account The account to fetch an auth token for
* @param authTokenType The auth token type, an authenticator-dependent * @param authTokenType The auth token type, an authenticator-dependent
* string token, must not be null * string token, must not be null
@@ -1201,7 +1166,6 @@ public class AccountManager {
* authenticator-dependent. The caller should verify the validity of the * authenticator-dependent. The caller should verify the validity of the
* account before requesting an auth token. * account before requesting an auth token.
*/ */
@RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken( public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options, final Account account, final String authTokenType, final Bundle options,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) { final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
@@ -1253,9 +1217,6 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#USE_CREDENTIALS}.
*
* @param account The account to fetch an auth token for * @param account The account to fetch an auth token for
* @param authTokenType The auth token type, an authenticator-dependent * @param authTokenType The auth token type, an authenticator-dependent
* string token, must not be null * string token, must not be null
@@ -1292,7 +1253,6 @@ public class AccountManager {
* boolean, AccountManagerCallback, android.os.Handler)} instead * boolean, AccountManagerCallback, android.os.Handler)} instead
*/ */
@Deprecated @Deprecated
@RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken( public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Account account, final String authTokenType,
final boolean notifyAuthFailure, final boolean notifyAuthFailure,
@@ -1333,9 +1293,6 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#USE_CREDENTIALS}.
*
* @param account The account to fetch an auth token for * @param account The account to fetch an auth token for
* @param authTokenType The auth token type, an authenticator-dependent * @param authTokenType The auth token type, an authenticator-dependent
* string token, must not be null * string token, must not be null
@@ -1371,7 +1328,6 @@ public class AccountManager {
* authenticator-dependent. The caller should verify the validity of the * authenticator-dependent. The caller should verify the validity of the
* account before requesting an auth token. * account before requesting an auth token.
*/ */
@RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken( public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options, final Account account, final String authTokenType, final Bundle options,
final boolean notifyAuthFailure, final boolean notifyAuthFailure,
@@ -1401,9 +1357,6 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param accountType The type of account to add; must not be null * @param accountType The type of account to add; must not be null
* @param authTokenType The type of auth token (see {@link #getAuthToken}) * @param authTokenType The type of auth token (see {@link #getAuthToken})
* this account will need to be able to generate, null for none * this account will need to be able to generate, null for none
@@ -1441,7 +1394,6 @@ public class AccountManager {
* creating a new account, usually because of network trouble * creating a new account, usually because of network trouble
* </ul> * </ul>
*/ */
@RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> addAccount(final String accountType, public AccountManagerFuture<Bundle> addAccount(final String accountType,
final String authTokenType, final String[] requiredFeatures, final String authTokenType, final String[] requiredFeatures,
final Bundle addAccountOptions, final Bundle addAccountOptions,
@@ -1586,9 +1538,6 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param account The account to confirm password knowledge for * @param account The account to confirm password knowledge for
* @param options Authenticator-specific options for the request; * @param options Authenticator-specific options for the request;
* if the {@link #KEY_PASSWORD} string field is present, the * if the {@link #KEY_PASSWORD} string field is present, the
@@ -1615,11 +1564,11 @@ public class AccountManager {
* If no activity or password was specified, the returned Bundle contains * If no activity or password was specified, the returned Bundle contains
* {@link #KEY_INTENT} with the {@link Intent} needed to launch the * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
* password prompt. * password prompt.
* *
* <p>Also the returning Bundle may contain {@link * <p>Also the returning Bundle may contain {@link
* #KEY_LAST_AUTHENTICATED_TIME} indicating the last time the * #KEY_LAST_AUTHENTICATED_TIME} indicating the last time the
* credential was validated/created. * credential was validated/created.
* *
* If an error occurred,{@link AccountManagerFuture#getResult()} throws: * If an error occurred,{@link AccountManagerFuture#getResult()} throws:
* <ul> * <ul>
* <li> {@link AuthenticatorException} if the authenticator failed to respond * <li> {@link AuthenticatorException} if the authenticator failed to respond
@@ -1629,7 +1578,6 @@ public class AccountManager {
* verifying the password, usually because of network trouble * verifying the password, usually because of network trouble
* </ul> * </ul>
*/ */
@RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> confirmCredentials(final Account account, public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
final Bundle options, final Bundle options,
final Activity activity, final Activity activity,
@@ -1668,9 +1616,6 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param account The account to update credentials for * @param account The account to update credentials for
* @param authTokenType The credentials entered must allow an auth token * @param authTokenType The credentials entered must allow an auth token
* of this type to be created (but no actual auth token is returned); * of this type to be created (but no actual auth token is returned);
@@ -1706,7 +1651,6 @@ public class AccountManager {
* verifying the password, usually because of network trouble * verifying the password, usually because of network trouble
* </ul> * </ul>
*/ */
@RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> updateCredentials(final Account account, public AccountManagerFuture<Bundle> updateCredentials(final Account account,
final String authTokenType, final String authTokenType,
final Bundle options, final Activity activity, final Bundle options, final Activity activity,
@@ -1729,8 +1673,8 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission * <p>This method requires the caller to have the same signature as the
* {@link android.Manifest.permission#MANAGE_ACCOUNTS}. * authenticator associated with the specified account type.
* *
* @param accountType The account type associated with the authenticator * @param accountType The account type associated with the authenticator
* to adjust * to adjust
@@ -1758,7 +1702,6 @@ public class AccountManager {
* updating settings, usually because of network trouble * updating settings, usually because of network trouble
* </ul> * </ul>
*/ */
@RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> editProperties(final String accountType, public AccountManagerFuture<Bundle> editProperties(final String accountType,
final Activity activity, final AccountManagerCallback<Bundle> callback, final Activity activity, final AccountManagerCallback<Bundle> callback,
final Handler handler) { final Handler handler) {
@@ -2253,9 +2196,6 @@ public class AccountManager {
* <p>This method may be called from any thread, but the returned * <p>This method may be called from any thread, but the returned
* {@link AccountManagerFuture} must not be used on the main thread. * {@link AccountManagerFuture} must not be used on the main thread.
* *
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#MANAGE_ACCOUNTS}.
*
* @param accountType The account type required * @param accountType The account type required
* (see {@link #getAccountsByType}), must not be null * (see {@link #getAccountsByType}), must not be null
* @param authTokenType The desired auth token type * @param authTokenType The desired auth token type
@@ -2292,7 +2232,6 @@ public class AccountManager {
* updating settings, usually because of network trouble * updating settings, usually because of network trouble
* </ul> * </ul>
*/ */
@RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> getAuthTokenByFeatures( public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
final String accountType, final String authTokenType, final String[] features, final String accountType, final String authTokenType, final String[] features,
final Activity activity, final Bundle addAccountOptions, final Activity activity, final Bundle addAccountOptions,

View File

@@ -980,33 +980,11 @@
<!-- Allows access to the list of accounts in the Accounts Service --> <!-- Allows access to the list of accounts in the Accounts Service -->
<permission android:name="android.permission.GET_ACCOUNTS" <permission android:name="android.permission.GET_ACCOUNTS"
android:permissionGroup="android.permission-group.ACCOUNTS" android:permissionGroup="android.permission-group.CONTACTS"
android:protectionLevel="normal" android:protectionLevel="normal"
android:description="@string/permdesc_getAccounts" android:description="@string/permdesc_getAccounts"
android:label="@string/permlab_getAccounts" /> android:label="@string/permlab_getAccounts" />
<!-- Allows an application to act as an AccountAuthenticator for
the AccountManager -->
<permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"
android:permissionGroup="android.permission-group.ACCOUNTS"
android:protectionLevel="dangerous"
android:label="@string/permlab_authenticateAccounts"
android:description="@string/permdesc_authenticateAccounts" />
<!-- Allows an application to request authtokens from the AccountManager -->
<permission android:name="android.permission.USE_CREDENTIALS"
android:permissionGroup="android.permission-group.ACCOUNTS"
android:protectionLevel="dangerous"
android:label="@string/permlab_useCredentials"
android:description="@string/permdesc_useCredentials" />
<!-- Allows an application to manage the list of accounts in the AccountManager -->
<permission android:name="android.permission.MANAGE_ACCOUNTS"
android:permissionGroup="android.permission-group.ACCOUNTS"
android:protectionLevel="dangerous"
android:label="@string/permlab_manageAccounts"
android:description="@string/permdesc_manageAccounts" />
<!-- @SystemApi Allows applications to call into AccountAuthenticators. <!-- @SystemApi Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. --> <p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER" <permission android:name="android.permission.ACCOUNT_MANAGER"

View File

@@ -1113,27 +1113,6 @@
the list of accounts known by the phone. This may include any accounts the list of accounts known by the phone. This may include any accounts
created by applications you have installed.</string> created by applications you have installed.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_authenticateAccounts">create accounts and set passwords</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_authenticateAccounts">Allows the app
to use the account authenticator capabilities of the
AccountManager, including creating accounts and getting and
setting their passwords.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_manageAccounts">add or remove accounts</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_manageAccounts">Allows the app to
perform operations like adding and removing accounts, and deleting
their password.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_useCredentials">use accounts on the device</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_useCredentials">Allows the app to request authentication tokens.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_accessNetworkState">view network connections</string> <string name="permlab_accessNetworkState">view network connections</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->

View File

@@ -583,13 +583,6 @@ public class MainActivity extends FragmentActivity {
running the sync adapter, see <a href="running-sync-adapter.html" running the sync adapter, see <a href="running-sync-adapter.html"
>Running A Sync Adapter</a>. >Running A Sync Adapter</a>.
</dd> </dd>
<dt>
{@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS android.permission.AUTHENTICATE_ACCOUNTS}
</dt>
<dd>
Allows you to use the authenticator component you created in the lesson
<a href="creating-authenticator.html">Creating a Stub Authenticator</a>.
</dd>
</dl> </dl>
<p> <p>
The following snippet shows how to add the permissions: The following snippet shows how to add the permissions:

View File

@@ -87,11 +87,8 @@ import java.io.File;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -100,7 +97,6 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -526,14 +522,20 @@ public class AccountManagerService
@Override @Override
public String getPassword(Account account) { public String getPassword(Account account) {
int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getPassword: " + account Log.v(TAG, "getPassword: " + account
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + Binder.getCallingUid()
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot get secrets for accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -617,15 +619,21 @@ public class AccountManagerService
@Override @Override
public String getUserData(Account account, String key) { public String getUserData(Account account, String key) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getUserData: " + account String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
+ ", key " + key account, key, callingUid, Binder.getCallingPid());
+ ", caller's uid " + Binder.getCallingUid() Log.v(TAG, msg);
+ ", pid " + Binder.getCallingPid());
} }
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (key == null) throw new IllegalArgumentException("key is null"); if (key == null) throw new IllegalArgumentException("key is null");
checkAuthenticateAccountsPermission(account); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot get user data for accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -676,13 +684,20 @@ public class AccountManagerService
@Override @Override
public boolean addAccountExplicitly(Account account, String password, Bundle extras) { public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "addAccountExplicitly: " + account Log.v(TAG, "addAccountExplicitly: " + account
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot explicitly add accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
/* /*
* Child users are not allowed to add accounts. Only the accounts that are * Child users are not allowed to add accounts. Only the accounts that are
* shared by the parent profile can be added to child profile. * shared by the parent profile can be added to child profile.
@@ -758,10 +773,24 @@ public class AccountManagerService
@Override @Override
public boolean accountAuthenticated(final Account account) { public boolean accountAuthenticated(final Account account) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
String msg = String.format(
"accountAuthenticated( account: %s, callerUid: %s)",
account,
callingUid);
Log.v(TAG, msg);
}
if (account == null) { if (account == null) {
throw new IllegalArgumentException("account is null"); throw new IllegalArgumentException("account is null");
} }
checkAuthenticateAccountsPermission(account); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot notify authentication for accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
int userId = Binder.getCallingUserHandle().getIdentifier(); int userId = Binder.getCallingUserHandle().getIdentifier();
if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) { if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
return false; return false;
@@ -1007,16 +1036,21 @@ public class AccountManagerService
@Override @Override
public void renameAccount( public void renameAccount(
IAccountManagerResponse response, Account accountToRename, String newName) { IAccountManagerResponse response, Account accountToRename, String newName) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (accountToRename == null) throw new IllegalArgumentException("account is null"); if (accountToRename == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(accountToRename); if (!isAccountOwnedByCallingUid(accountToRename.type, callingUid)) {
String msg = String.format(
"uid %s cannot rename accounts of type: %s",
callingUid,
accountToRename.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
int callingUid = getCallingUid();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName, Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName,
@@ -1125,65 +1159,21 @@ public class AccountManagerService
@Override @Override
public void removeAccount(IAccountManagerResponse response, Account account, public void removeAccount(IAccountManagerResponse response, Account account,
boolean expectActivityLaunch) { boolean expectActivityLaunch) {
if (Log.isLoggable(TAG, Log.VERBOSE)) { removeAccountAsUser(
Log.v(TAG, "removeAccount: " + account response,
+ ", response " + response account,
+ ", caller's uid " + Binder.getCallingUid() expectActivityLaunch,
+ ", pid " + Binder.getCallingPid()); UserHandle.getCallingUserId());
}
if (response == null) throw new IllegalArgumentException("response is null");
if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
UserHandle user = Binder.getCallingUserHandle();
UserAccounts accounts = getUserAccountsForCaller();
int userId = Binder.getCallingUserHandle().getIdentifier();
if (!canUserModifyAccounts(userId)) {
try {
// TODO: This should be ERROR_CODE_USER_RESTRICTED instead. See http://b/16322768
response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
"User cannot modify accounts");
} catch (RemoteException re) {
}
return;
}
if (!canUserModifyAccountsForType(userId, account.type)) {
try {
response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
"User cannot modify accounts of this type (policy).");
} catch (RemoteException re) {
}
return;
}
long identityToken = clearCallingIdentity();
cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
synchronized (accounts.credentialsPermissionNotificationIds) {
for (Pair<Pair<Account, String>, Integer> pair:
accounts.credentialsPermissionNotificationIds.keySet()) {
if (account.equals(pair.first.first)) {
int id = accounts.credentialsPermissionNotificationIds.get(pair);
cancelNotification(id, user);
}
}
}
logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS);
try {
new RemoveAccountSession(accounts, response, account, expectActivityLaunch).bind();
} finally {
restoreCallingIdentity(identityToken);
}
} }
@Override @Override
public void removeAccountAsUser(IAccountManagerResponse response, Account account, public void removeAccountAsUser(IAccountManagerResponse response, Account account,
boolean expectActivityLaunch, int userId) { boolean expectActivityLaunch, int userId) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "removeAccount: " + account Log.v(TAG, "removeAccount: " + account
+ ", response " + response + ", response " + response
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid() + ", pid " + Binder.getCallingPid()
+ ", for user id " + userId); + ", for user id " + userId);
} }
@@ -1193,7 +1183,18 @@ public class AccountManagerService
// Only allow the system process to modify accounts of other users // Only allow the system process to modify accounts of other users
enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId() enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
+ " trying to remove account for " + userId); + " trying to remove account for " + userId);
checkManageAccountsPermission(); /*
* Only the system or authenticator should be allowed to remove accounts for that
* authenticator. This will let users remove accounts (via Settings in the system) but not
* arbitrary applications (like competing authenticators).
*/
if (!isAccountOwnedByCallingUid(account.type, callingUid) && !isSystemUid(callingUid)) {
String msg = String.format(
"uid %s cannot remove accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccounts(userId); UserAccounts accounts = getUserAccounts(userId);
if (!canUserModifyAccounts(userId)) { if (!canUserModifyAccounts(userId)) {
@@ -1238,13 +1239,26 @@ public class AccountManagerService
@Override @Override
public boolean removeAccountExplicitly(Account account) { public boolean removeAccountExplicitly(Account account) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "removeAccountExplicitly: " + account Log.v(TAG, "removeAccountExplicitly: " + account
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) {
checkAuthenticateAccountsPermission(account); /*
* Null accounts should result in returning false, as per
* AccountManage.addAccountExplicitly(...) java doc.
*/
Log.e(TAG, "account is null");
return false;
} else if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot explicitly add accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
int userId = Binder.getCallingUserHandle().getIdentifier(); int userId = Binder.getCallingUserHandle().getIdentifier();
@@ -1357,7 +1371,6 @@ public class AccountManagerService
} }
if (accountType == null) throw new IllegalArgumentException("accountType is null"); if (accountType == null) throw new IllegalArgumentException("accountType is null");
if (authToken == null) throw new IllegalArgumentException("authToken is null"); if (authToken == null) throw new IllegalArgumentException("authToken is null");
checkManageAccountsOrUseCredentialsPermissions();
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -1490,15 +1503,22 @@ public class AccountManagerService
@Override @Override
public String peekAuthToken(Account account, String authTokenType) { public String peekAuthToken(Account account, String authTokenType) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "peekAuthToken: " + account Log.v(TAG, "peekAuthToken: " + account
+ ", authTokenType " + authTokenType + ", authTokenType " + authTokenType
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkAuthenticateAccountsPermission(account); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot peek the authtokens associated with accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -1510,15 +1530,22 @@ public class AccountManagerService
@Override @Override
public void setAuthToken(Account account, String authTokenType, String authToken) { public void setAuthToken(Account account, String authTokenType, String authToken) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setAuthToken: " + account Log.v(TAG, "setAuthToken: " + account
+ ", authTokenType " + authTokenType + ", authTokenType " + authTokenType
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkAuthenticateAccountsPermission(account); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot set auth tokens associated with accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -1530,15 +1557,21 @@ public class AccountManagerService
@Override @Override
public void setPassword(Account account, String password) { public void setPassword(Account account, String password) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setAuthToken: " + account Log.v(TAG, "setAuthToken: " + account
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot set secrets for accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
int callingUid = getCallingUid();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
setPasswordInternal(accounts, account, password, callingUid); setPasswordInternal(accounts, account, password, callingUid);
@@ -1594,16 +1627,21 @@ public class AccountManagerService
@Override @Override
public void clearPassword(Account account) { public void clearPassword(Account account) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "clearPassword: " + account Log.v(TAG, "clearPassword: " + account
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission(); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot clear passwords for accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
int callingUid = getCallingUid();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
setPasswordInternal(accounts, account, null, callingUid); setPasswordInternal(accounts, account, null, callingUid);
@@ -1614,15 +1652,22 @@ public class AccountManagerService
@Override @Override
public void setUserData(Account account, String key, String value) { public void setUserData(Account account, String key, String value) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setUserData: " + account Log.v(TAG, "setUserData: " + account
+ ", key " + key + ", key " + key
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (key == null) throw new IllegalArgumentException("key is null"); if (key == null) throw new IllegalArgumentException("key is null");
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account); if (!isAccountOwnedByCallingUid(account.type, callingUid)) {
String msg = String.format(
"uid %s cannot set user data for accounts of type: %s",
callingUid,
account.type);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -1769,7 +1814,6 @@ public class AccountManagerService
return; return;
} }
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
final UserAccounts accounts = getUserAccountsForCaller(); final UserAccounts accounts = getUserAccountsForCaller();
final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
authenticatorInfo = mAuthenticatorCache.getServiceInfo( authenticatorInfo = mAuthenticatorCache.getServiceInfo(
@@ -2047,7 +2091,6 @@ public class AccountManagerService
} }
if (response == null) throw new IllegalArgumentException("response is null"); if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null"); if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission();
// Is user disallowed from modifying accounts? // Is user disallowed from modifying accounts?
int userId = Binder.getCallingUserHandle().getIdentifier(); int userId = Binder.getCallingUserHandle().getIdentifier();
@@ -2122,7 +2165,6 @@ public class AccountManagerService
} }
if (response == null) throw new IllegalArgumentException("response is null"); if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null"); if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission();
// Only allow the system process to add accounts of other users // Only allow the system process to add accounts of other users
enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId() enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
@@ -2213,7 +2255,6 @@ public class AccountManagerService
} }
if (response == null) throw new IllegalArgumentException("response is null"); if (response == null) throw new IllegalArgumentException("response is null");
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
UserAccounts accounts = getUserAccounts(userId); UserAccounts accounts = getUserAccounts(userId);
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -2250,7 +2291,6 @@ public class AccountManagerService
if (response == null) throw new IllegalArgumentException("response is null"); if (response == null) throw new IllegalArgumentException("response is null");
if (account == null) throw new IllegalArgumentException("account is null"); if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkManageAccountsPermission();
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -2278,16 +2318,23 @@ public class AccountManagerService
@Override @Override
public void editProperties(IAccountManagerResponse response, final String accountType, public void editProperties(IAccountManagerResponse response, final String accountType,
final boolean expectActivityLaunch) { final boolean expectActivityLaunch) {
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "editProperties: accountType " + accountType Log.v(TAG, "editProperties: accountType " + accountType
+ ", response " + response + ", response " + response
+ ", expectActivityLaunch " + expectActivityLaunch + ", expectActivityLaunch " + expectActivityLaunch
+ ", caller's uid " + Binder.getCallingUid() + ", caller's uid " + callingUid
+ ", pid " + Binder.getCallingPid()); + ", pid " + Binder.getCallingPid());
} }
if (response == null) throw new IllegalArgumentException("response is null"); if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null"); if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission(); if (!isAccountOwnedByCallingUid(accountType, callingUid) && !isSystemUid(callingUid)) {
String msg = String.format(
"uid %s cannot edit authenticator properites for account type: %s",
callingUid,
accountType);
throw new SecurityException(msg);
}
UserAccounts accounts = getUserAccountsForCaller(); UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
@@ -3588,7 +3635,7 @@ public class AccountManagerService
private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
final boolean isPrivileged = isPrivileged(callerUid); final boolean isPrivileged = isPrivileged(callerUid);
final boolean fromAuthenticator = account != null final boolean fromAuthenticator = account != null
&& hasAuthenticatorUid(account.type, callerUid); && isAccountManagedByCaller(account.type, callerUid);
final boolean hasExplicitGrants = account != null final boolean hasExplicitGrants = account != null
&& hasExplicitlyGrantedPermission(account, authTokenType, callerUid); && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -3600,14 +3647,17 @@ public class AccountManagerService
return fromAuthenticator || hasExplicitGrants || isPrivileged; return fromAuthenticator || hasExplicitGrants || isPrivileged;
} }
private boolean hasAuthenticatorUid(String accountType, int callingUid) { private boolean isAccountManagedByCaller(String accountType, int callingUid) {
final int callingUserId = UserHandle.getUserId(callingUid); final int callingUserId = UserHandle.getUserId(callingUid);
for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
mAuthenticatorCache.getAllServices(callingUserId)) { mAuthenticatorCache.getAllServices(callingUserId)) {
if (serviceInfo.type.type.equals(accountType)) { if (serviceInfo.type.type.equals(accountType)) {
return (serviceInfo.uid == callingUid) || /*
(mPackageManager.checkSignatures(serviceInfo.uid, callingUid) * We can't simply compare uids because uids can be recycled before the
== PackageManager.SIGNATURE_MATCH); * authenticator cache is updated.
*/
final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
return sigChk == PackageManager.SIGNATURE_MATCH;
} }
} }
return false; return false;
@@ -3648,36 +3698,49 @@ public class AccountManagerService
} }
} }
private void checkCallingUidAgainstAuthenticator(Account account) { private boolean isSystemUid(int callingUid) {
final int uid = Binder.getCallingUid(); String[] packages = null;
if (account == null || !hasAuthenticatorUid(account.type, uid)) { long ident = Binder.clearCallingIdentity();
String msg = "caller uid " + uid + " is different than the authenticator's uid"; try {
Log.w(TAG, msg); packages = mPackageManager.getPackagesForUid(callingUid);
throw new SecurityException(msg); } finally {
Binder.restoreCallingIdentity(ident);
} }
if (Log.isLoggable(TAG, Log.VERBOSE)) { if (packages != null) {
Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid"); for (String name : packages) {
try {
PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
if (packageInfo != null
&& (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM)
!= 0) {
return true;
}
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, String.format("Could not find package [%s]", name), e);
}
}
} else {
Log.w(TAG, "No known packages with uid " + callingUid);
} }
return false;
} }
private void checkAuthenticateAccountsPermission(Account account) { private boolean isAccountOwnedByCallingUid(String accountType, int callingUid) {
checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS); if (!isAccountManagedByCaller(accountType, callingUid)) {
checkCallingUidAgainstAuthenticator(account); String msg = "caller uid " + callingUid + " is different than the authenticator's uid";
Log.w(TAG, msg);
return false;
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "caller uid " + callingUid + " is the same as the authenticator's uid");
}
return true;
} }
private void checkReadAccountsPermission() { private void checkReadAccountsPermission() {
checkBinderPermission(Manifest.permission.GET_ACCOUNTS); checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
} }
private void checkManageAccountsPermission() {
checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
}
private void checkManageAccountsOrUseCredentialsPermissions() {
checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS,
Manifest.permission.USE_CREDENTIALS);
}
private boolean canUserModifyAccounts(int userId) { private boolean canUserModifyAccounts(int userId) {
if (getUserManager().getUserRestrictions(new UserHandle(userId)) if (getUserManager().getUserRestrictions(new UserHandle(userId))
.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) { .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {