Permissions: Fix account related permissions.

Requires updating the docs in AccountManaager as well as the logic in
AccountManagerService.

MANAGE_ACCOUNTS, USE_CREDENTIALS, and AUTHENTCATE_ACCOUNTS are going
away. Where AUTHENTCATE_ACCOUNTS was required we now do signature
matching.

GET_ACCOUNTS is kept but has been grouped under contacts.

Bug: 20136477
Change-Id: Iabbb76dce8d1efc607c1f107911d7ddab598a481
diff --git a/api/current.txt b/api/current.txt
index cfb9398..ec56ce0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -17,7 +17,6 @@
     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 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 BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
@@ -86,7 +85,6 @@
     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 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_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
     field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -145,7 +143,6 @@
     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 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_SIP = "android.permission.USE_SIP";
     field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
diff --git a/api/system-current.txt b/api/system-current.txt
index 6035ef2..5ec8a62 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -24,7 +24,6 @@
     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 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 BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
@@ -117,7 +116,6 @@
     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 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_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
     field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
@@ -217,7 +215,6 @@
     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 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_SIP = "android.permission.USE_SIP";
     field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 31e129b..993b53d 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -51,10 +51,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import static android.Manifest.permission.AUTHENTICATE_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
@@ -319,14 +316,12 @@
      *
      * <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#AUTHENTICATE_ACCOUNTS}
-     * and to have the same UID as the account's authenticator.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that owns the specified account.
      *
-     * @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
      */
-    @RequiresPermission(AUTHENTICATE_ACCOUNTS)
     public String getPassword(final Account account) {
         if (account == null) throw new IllegalArgumentException("account is null");
         try {
@@ -345,14 +340,12 @@
      *
      * <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#AUTHENTICATE_ACCOUNTS}
-     * and to have the same UID as the account's authenticator.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that owns the specified account.
      *
      * @param account The account to query for user data
      * @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) {
         if (account == null) throw new IllegalArgumentException("account is null");
         if (key == null) throw new IllegalArgumentException("key is null");
@@ -662,10 +655,8 @@
      * wizards associated with authenticators, not directly by applications.
      *
      * <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#AUTHENTICATE_ACCOUNTS}
-     * and to have the same UID as the added account's authenticator.
+     * <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
@@ -673,7 +664,6 @@
      * @return True if the account was successfully added, false if the account
      *     already exists, the account is null, or another error occurs.
      */
-    @RequiresPermission(AUTHENTICATE_ACCOUNTS)
     public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
         if (account == null) throw new IllegalArgumentException("account is null");
         try {
@@ -692,14 +682,13 @@
      * <p>
      * It is not safe to call this method from the main thread. As such, call it
      * from another thread.
-     * <p>
-     * This method requires the caller to hold the permission
-     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and should be
-     * called from the account's authenticator.
+     * <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 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) {
         if (account == null)
             throw new IllegalArgumentException("account is null");
@@ -717,9 +706,8 @@
      *
      * <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#AUTHENTICATE_ACCOUNTS}
-     * and have the same UID as the account's authenticator.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that manages the specified account.
      *
      * @param account The {@link Account} to rename
      * @param newName String name to be associated with the account.
@@ -731,7 +719,6 @@
      *     after the name change. If successful the account's name will be the
      *     specified new name.
      */
-    @RequiresPermission(AUTHENTICATE_ACCOUNTS)
     public AccountManagerFuture<Account> renameAccount(
             final Account account,
             @Size(min = 1) final String newName,
@@ -783,11 +770,8 @@
      * The authenticator may have its own policies preventing account
      * deletion, in which case the account will not be deleted.
      *
-     * <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#MANAGE_ACCOUNTS}.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that manages the specified account.
      *
      * @param account The {@link Account} to remove
      * @param callback Callback to invoke when the request completes,
@@ -800,15 +784,16 @@
      *     {@link #removeAccount(Account, Activity, AccountManagerCallback, Handler)}
      *     instead
      */
-    @RequiresPermission(MANAGE_ACCOUNTS)
     @Deprecated
     public AccountManagerFuture<Boolean> removeAccount(final Account account,
             AccountManagerCallback<Boolean> callback, Handler handler) {
         if (account == null) throw new IllegalArgumentException("account is null");
         return new Future2Task<Boolean>(handler, callback) {
+            @Override
             public void doWork() throws RemoteException {
                 mService.removeAccount(mResponse, account, false);
             }
+            @Override
             public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
                 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
                     throw new AuthenticatorException("no result in response");
@@ -827,8 +812,8 @@
      * <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#MANAGE_ACCOUNTS}.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that manages the specified account.
      *
      * @param account The {@link Account} to remove
      * @param activity The {@link Activity} context to use for launching a new
@@ -855,11 +840,11 @@
      *      adding accounts (of this type) has been disabled by policy
      * </ul>
      */
-    @RequiresPermission(MANAGE_ACCOUNTS)
     public AccountManagerFuture<Bundle> removeAccount(final Account account,
             final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
         if (account == null) throw new IllegalArgumentException("account is null");
         return new AmsTask(activity, handler, callback) {
+            @Override
             public void doWork() throws RemoteException {
                 mService.removeAccount(mResponse, account, activity != null);
             }
@@ -880,9 +865,11 @@
         if (account == null) throw new IllegalArgumentException("account is null");
         if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
         return new Future2Task<Boolean>(handler, callback) {
+            @Override
             public void doWork() throws RemoteException {
                 mService.removeAccountAsUser(mResponse, account, false, userHandle.getIdentifier());
             }
+            @Override
             public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
                 if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
                     throw new AuthenticatorException("no result in response");
@@ -918,17 +905,14 @@
      * in which case the account will not be deleted.
      * <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#AUTHENTICATE_ACCOUNTS} and to have the
-     * same UID or signature as the account's authenticator.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that manages the specified account.
      *
      * @param account The {@link Account} to delete.
      * @return True if the account was successfully deleted, false if the
      *         account did not exist, the account is null, or another error
      *         occurs.
      */
-    @RequiresPermission(AUTHENTICATE_ACCOUNTS)
     public boolean removeAccountExplicitly(Account account) {
         if (account == null) throw new IllegalArgumentException("account is null");
         try {
@@ -948,14 +932,9 @@
      *
      * <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 authToken The auth token to invalidate, may be null
      */
-    @RequiresPermission(anyOf = {MANAGE_ACCOUNTS, USE_CREDENTIALS})
     public void invalidateAuthToken(final String accountType, final String authToken) {
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         try {
@@ -976,16 +955,15 @@
      *
      * <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#AUTHENTICATE_ACCOUNTS}
-     * and to have the same UID as the account's authenticator.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that manages the specified account.
      *
-     * @param account The account to fetch an auth token for
-     * @param authTokenType The type of auth token to fetch, see {#getAuthToken}
+     * @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. Cannot be {@code null}. 
      * @return The cached auth token for this account and type, or null if
      *     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) {
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -1005,14 +983,12 @@
      *
      * <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#AUTHENTICATE_ACCOUNTS}
-     * and have the same UID as the account's authenticator.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that manages the specified account.
      *
-     * @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
      */
-    @RequiresPermission(AUTHENTICATE_ACCOUNTS)
     public void setPassword(final Account account, final String password) {
         if (account == null) throw new IllegalArgumentException("account is null");
         try {
@@ -1030,14 +1006,14 @@
      * permissions, and may be used by applications or management interfaces
      * 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
-     * {@link android.Manifest.permission#MANAGE_ACCOUNTS}
+     * <p>It is safe to call this method from the main thread.
      *
      * @param account The account whose password to clear
      */
-    @RequiresPermission(MANAGE_ACCOUNTS)
     public void clearPassword(final Account account) {
         if (account == null) throw new IllegalArgumentException("account is null");
         try {
@@ -1055,15 +1031,13 @@
      *
      * <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#AUTHENTICATE_ACCOUNTS}
-     * and to have the same UID as the account's authenticator.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that manages the specified account.
      *
-     * @param account The account to set the userdata for
-     * @param key The userdata key to set.  Must not be null
-     * @param value The value to set, null to clear this userdata key
+     * @param account Account whose user data is to be set. Must not be {@code null}.
+     * @param key String user data key to set.  Must not be null
+     * @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) {
         if (account == null) throw new IllegalArgumentException("account is null");
         if (key == null) throw new IllegalArgumentException("key is null");
@@ -1083,15 +1057,13 @@
      *
      * <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#AUTHENTICATE_ACCOUNTS}
-     * and to have the same UID as the account's authenticator.
+     * <p>This method requires the caller to have a signature match with the
+     * authenticator that manages the specified account.
      *
      * @param account The account to set an auth token for
      * @param authTokenType The type of the auth token, see {#getAuthToken}
      * @param authToken The auth token to add to the cache
      */
-    @RequiresPermission(AUTHENTICATE_ACCOUNTS)
     public void setAuthToken(Account account, final String authTokenType, final String authToken) {
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -1110,9 +1082,6 @@
      * <p>This method may block while a network request completes, and must
      * 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 authTokenType The auth token type, see {@link #getAuthToken getAuthToken()}
      * @param notifyAuthFailure If true, display a notification and return null
@@ -1126,7 +1095,6 @@
      * @throws java.io.IOException if the authenticator experienced an I/O problem
      *     creating a new auth token, usually because of network trouble
      */
-    @RequiresPermission(USE_CREDENTIALS)
     public String blockingGetAuthToken(Account account, String authTokenType,
             boolean notifyAuthFailure)
             throws OperationCanceledException, IOException, AuthenticatorException {
@@ -1165,9 +1133,6 @@
      * <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#USE_CREDENTIALS}.
-     *
      * @param account The account to fetch an auth token for
      * @param authTokenType The auth token type, an authenticator-dependent
      *     string token, must not be null
@@ -1201,7 +1166,6 @@
      * authenticator-dependent.  The caller should verify the validity of the
      * account before requesting an auth token.
      */
-    @RequiresPermission(USE_CREDENTIALS)
     public AccountManagerFuture<Bundle> getAuthToken(
             final Account account, final String authTokenType, final Bundle options,
             final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
@@ -1253,9 +1217,6 @@
      * <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#USE_CREDENTIALS}.
-     *
      * @param account The account to fetch an auth token for
      * @param authTokenType The auth token type, an authenticator-dependent
      *     string token, must not be null
@@ -1292,7 +1253,6 @@
      * boolean, AccountManagerCallback, android.os.Handler)} instead
      */
     @Deprecated
-    @RequiresPermission(USE_CREDENTIALS)
     public AccountManagerFuture<Bundle> getAuthToken(
             final Account account, final String authTokenType,
             final boolean notifyAuthFailure,
@@ -1333,9 +1293,6 @@
      * <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#USE_CREDENTIALS}.
-     *
      * @param account The account to fetch an auth token for
      * @param authTokenType The auth token type, an authenticator-dependent
      *     string token, must not be null
@@ -1371,7 +1328,6 @@
      * authenticator-dependent.  The caller should verify the validity of the
      * account before requesting an auth token.
      */
-    @RequiresPermission(USE_CREDENTIALS)
     public AccountManagerFuture<Bundle> getAuthToken(
             final Account account, final String authTokenType, final Bundle options,
             final boolean notifyAuthFailure,
@@ -1401,9 +1357,6 @@
      * <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#MANAGE_ACCOUNTS}.
-     *
      * @param accountType The type of account to add; must not be null
      * @param authTokenType The type of auth token (see {@link #getAuthToken})
      *     this account will need to be able to generate, null for none
@@ -1441,7 +1394,6 @@
      *      creating a new account, usually because of network trouble
      * </ul>
      */
-    @RequiresPermission(MANAGE_ACCOUNTS)
     public AccountManagerFuture<Bundle> addAccount(final String accountType,
             final String authTokenType, final String[] requiredFeatures,
             final Bundle addAccountOptions,
@@ -1586,9 +1538,6 @@
      * <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#MANAGE_ACCOUNTS}.
-     *
      * @param account The account to confirm password knowledge for
      * @param options Authenticator-specific options for the request;
      *     if the {@link #KEY_PASSWORD} string field is present, the
@@ -1615,11 +1564,11 @@
      * If no activity or password was specified, the returned Bundle contains
      * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
      * password prompt.
-     * 
+     *
      * <p>Also the returning Bundle may contain {@link
      * #KEY_LAST_AUTHENTICATED_TIME} indicating the last time the
      * credential was validated/created.
-     * 
+     *
      * If an error occurred,{@link AccountManagerFuture#getResult()} throws:
      * <ul>
      * <li> {@link AuthenticatorException} if the authenticator failed to respond
@@ -1629,7 +1578,6 @@
      *      verifying the password, usually because of network trouble
      * </ul>
      */
-    @RequiresPermission(MANAGE_ACCOUNTS)
     public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
             final Bundle options,
             final Activity activity,
@@ -1668,9 +1616,6 @@
      * <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#MANAGE_ACCOUNTS}.
-     *
      * @param account The account to update credentials for
      * @param authTokenType The credentials entered must allow an auth token
      *     of this type to be created (but no actual auth token is returned);
@@ -1706,7 +1651,6 @@
      *      verifying the password, usually because of network trouble
      * </ul>
      */
-    @RequiresPermission(MANAGE_ACCOUNTS)
     public AccountManagerFuture<Bundle> updateCredentials(final Account account,
             final String authTokenType,
             final Bundle options, final Activity activity,
@@ -1729,8 +1673,8 @@
      * <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#MANAGE_ACCOUNTS}.
+     * <p>This method requires the caller to have the same signature as the
+     * authenticator associated with the specified account type.
      *
      * @param accountType The account type associated with the authenticator
      *     to adjust
@@ -1758,7 +1702,6 @@
      *      updating settings, usually because of network trouble
      * </ul>
      */
-    @RequiresPermission(MANAGE_ACCOUNTS)
     public AccountManagerFuture<Bundle> editProperties(final String accountType,
             final Activity activity, final AccountManagerCallback<Bundle> callback,
             final Handler handler) {
@@ -2253,9 +2196,6 @@
      * <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#MANAGE_ACCOUNTS}.
-     *
      * @param accountType The account type required
      *     (see {@link #getAccountsByType}), must not be null
      * @param authTokenType The desired auth token type
@@ -2292,7 +2232,6 @@
      *      updating settings, usually because of network trouble
      * </ul>
      */
-    @RequiresPermission(MANAGE_ACCOUNTS)
     public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
             final String accountType, final String authTokenType, final String[] features,
             final Activity activity, final Bundle addAccountOptions,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 482ca13..5526a3c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -980,33 +980,11 @@
 
     <!-- Allows access to the list of accounts in the Accounts Service -->
     <permission android:name="android.permission.GET_ACCOUNTS"
-        android:permissionGroup="android.permission-group.ACCOUNTS"
+        android:permissionGroup="android.permission-group.CONTACTS"
         android:protectionLevel="normal"
         android:description="@string/permdesc_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.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.ACCOUNT_MANAGER"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 28274ae..3b866d7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1113,27 +1113,6 @@
       the list of accounts known by the phone.  This may include any accounts
       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. -->
     <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. -->
diff --git a/docs/html/training/sync-adapters/creating-sync-adapter.jd b/docs/html/training/sync-adapters/creating-sync-adapter.jd
index b13ce07..9bd17ba 100644
--- a/docs/html/training/sync-adapters/creating-sync-adapter.jd
+++ b/docs/html/training/sync-adapters/creating-sync-adapter.jd
@@ -583,13 +583,6 @@
         running the sync adapter, see <a href="running-sync-adapter.html"
         >Running A Sync Adapter</a>.
     </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>
 <p>
     The following snippet shows how to add the permissions:
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 21f96c9..49d9988 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -87,11 +87,8 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.sql.Timestamp;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -100,7 +97,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -526,14 +522,20 @@
 
     @Override
     public String getPassword(Account account) {
+        int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "getPassword: " + account
                     + ", caller's uid " + Binder.getCallingUid()
                     + ", pid " + Binder.getCallingPid());
         }
         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();
         long identityToken = clearCallingIdentity();
         try {
@@ -617,15 +619,21 @@
 
     @Override
     public String getUserData(Account account, String key) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "getUserData: " + account
-                    + ", key " + key
-                    + ", caller's uid " + Binder.getCallingUid()
-                    + ", pid " + Binder.getCallingPid());
+            String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
+                    account, key, callingUid, Binder.getCallingPid());
+            Log.v(TAG, msg);
         }
         if (account == null) throw new IllegalArgumentException("account 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();
         long identityToken = clearCallingIdentity();
         try {
@@ -676,13 +684,20 @@
 
     @Override
     public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "addAccountExplicitly: " + account
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         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
          * shared by the parent profile can be added to child profile.
@@ -758,10 +773,24 @@
 
     @Override
     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) {
             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();
         if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
             return false;
@@ -1007,16 +1036,21 @@
     @Override
     public void renameAccount(
             IAccountManagerResponse response, Account accountToRename, String newName) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "renameAccount: " + accountToRename + " -> " + newName
-                + ", caller's uid " + Binder.getCallingUid()
+                + ", caller's uid " + callingUid
                 + ", pid " + Binder.getCallingPid());
         }
         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();
-
-        int callingUid = getCallingUid();
         long identityToken = clearCallingIdentity();
         try {
             Account resultingAccount = renameAccountInternal(accounts, accountToRename, newName,
@@ -1125,65 +1159,21 @@
     @Override
     public void removeAccount(IAccountManagerResponse response, Account account,
             boolean expectActivityLaunch) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "removeAccount: " + account
-                    + ", response " + response
-                    + ", caller's uid " + Binder.getCallingUid()
-                    + ", pid " + Binder.getCallingPid());
-        }
-        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);
-        }
+        removeAccountAsUser(
+                response,
+                account,
+                expectActivityLaunch,
+                UserHandle.getCallingUserId());
     }
 
     @Override
     public void removeAccountAsUser(IAccountManagerResponse response, Account account,
             boolean expectActivityLaunch, int userId) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "removeAccount: " + account
                     + ", response " + response
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid()
                     + ", for user id " + userId);
         }
@@ -1193,7 +1183,18 @@
         // Only allow the system process to modify accounts of other users
         enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
                     + " 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);
         if (!canUserModifyAccounts(userId)) {
@@ -1238,13 +1239,26 @@
 
     @Override
     public boolean removeAccountExplicitly(Account account) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "removeAccountExplicitly: " + account
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
-        if (account == null) throw new IllegalArgumentException("account is null");
-        checkAuthenticateAccountsPermission(account);
+        if (account == null) {
+            /*
+             * 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();
         int userId = Binder.getCallingUserHandle().getIdentifier();
@@ -1357,7 +1371,6 @@
         }
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         if (authToken == null) throw new IllegalArgumentException("authToken is null");
-        checkManageAccountsOrUseCredentialsPermissions();
         UserAccounts accounts = getUserAccountsForCaller();
         long identityToken = clearCallingIdentity();
         try {
@@ -1490,15 +1503,22 @@
 
     @Override
     public String peekAuthToken(Account account, String authTokenType) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "peekAuthToken: " + account
                     + ", authTokenType " + authTokenType
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         if (account == null) throw new IllegalArgumentException("account 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();
         long identityToken = clearCallingIdentity();
         try {
@@ -1510,15 +1530,22 @@
 
     @Override
     public void setAuthToken(Account account, String authTokenType, String authToken) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setAuthToken: " + account
                     + ", authTokenType " + authTokenType
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         if (account == null) throw new IllegalArgumentException("account 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();
         long identityToken = clearCallingIdentity();
         try {
@@ -1530,15 +1557,21 @@
 
     @Override
     public void setPassword(Account account, String password) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setAuthToken: " + account
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         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();
-        int callingUid = getCallingUid();
         long identityToken = clearCallingIdentity();
         try {
             setPasswordInternal(accounts, account, password, callingUid);
@@ -1594,16 +1627,21 @@
 
     @Override
     public void clearPassword(Account account) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "clearPassword: " + account
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         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();
-
-        int callingUid = getCallingUid();
         long identityToken = clearCallingIdentity();
         try {
             setPasswordInternal(accounts, account, null, callingUid);
@@ -1614,15 +1652,22 @@
 
     @Override
     public void setUserData(Account account, String key, String value) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "setUserData: " + account
                     + ", key " + key
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         if (key == null) throw new IllegalArgumentException("key 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();
         long identityToken = clearCallingIdentity();
         try {
@@ -1769,7 +1814,6 @@
             return;
         }
 
-        checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
         final UserAccounts accounts = getUserAccountsForCaller();
         final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
         authenticatorInfo = mAuthenticatorCache.getServiceInfo(
@@ -2047,7 +2091,6 @@
         }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
-        checkManageAccountsPermission();
 
         // Is user disallowed from modifying accounts?
         int userId = Binder.getCallingUserHandle().getIdentifier();
@@ -2122,7 +2165,6 @@
         }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
-        checkManageAccountsPermission();
 
         // Only allow the system process to add accounts of other users
         enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
@@ -2213,7 +2255,6 @@
         }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
-        checkManageAccountsPermission();
         UserAccounts accounts = getUserAccounts(userId);
         long identityToken = clearCallingIdentity();
         try {
@@ -2250,7 +2291,6 @@
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
         if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
-        checkManageAccountsPermission();
         UserAccounts accounts = getUserAccountsForCaller();
         long identityToken = clearCallingIdentity();
         try {
@@ -2278,16 +2318,23 @@
     @Override
     public void editProperties(IAccountManagerResponse response, final String accountType,
             final boolean expectActivityLaunch) {
+        final int callingUid = Binder.getCallingUid();
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "editProperties: accountType " + accountType
                     + ", response " + response
                     + ", expectActivityLaunch " + expectActivityLaunch
-                    + ", caller's uid " + Binder.getCallingUid()
+                    + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
         if (response == null) throw new IllegalArgumentException("response 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();
         long identityToken = clearCallingIdentity();
         try {
@@ -3588,7 +3635,7 @@
     private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
         final boolean isPrivileged = isPrivileged(callerUid);
         final boolean fromAuthenticator = account != null
-                && hasAuthenticatorUid(account.type, callerUid);
+                && isAccountManagedByCaller(account.type, callerUid);
         final boolean hasExplicitGrants = account != null
                 && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -3600,14 +3647,17 @@
         return fromAuthenticator || hasExplicitGrants || isPrivileged;
     }
 
-    private boolean hasAuthenticatorUid(String accountType, int callingUid) {
+    private boolean isAccountManagedByCaller(String accountType, int callingUid) {
         final int callingUserId = UserHandle.getUserId(callingUid);
         for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
                 mAuthenticatorCache.getAllServices(callingUserId)) {
             if (serviceInfo.type.type.equals(accountType)) {
-                return (serviceInfo.uid == callingUid) ||
-                        (mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
-                                == PackageManager.SIGNATURE_MATCH);
+                /*
+                 * We can't simply compare uids because uids can be recycled before the
+                 * authenticator cache is updated.
+                 */
+                final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid);
+                return sigChk == PackageManager.SIGNATURE_MATCH;
             }
         }
         return false;
@@ -3648,36 +3698,49 @@
         }
     }
 
-    private void checkCallingUidAgainstAuthenticator(Account account) {
-        final int uid = Binder.getCallingUid();
-        if (account == null || !hasAuthenticatorUid(account.type, uid)) {
-            String msg = "caller uid " + uid + " is different than the authenticator's uid";
-            Log.w(TAG, msg);
-            throw new SecurityException(msg);
+    private boolean isSystemUid(int callingUid) {
+        String[] packages = null;
+        long ident = Binder.clearCallingIdentity();
+        try {
+            packages = mPackageManager.getPackagesForUid(callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
+        if (packages != null) {
+            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) {
-        checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS);
-        checkCallingUidAgainstAuthenticator(account);
+    private boolean isAccountOwnedByCallingUid(String accountType, int callingUid) {
+        if (!isAccountManagedByCaller(accountType, callingUid)) {
+            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() {
         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) {
         if (getUserManager().getUserRestrictions(new UserHandle(userId))
                 .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {