diff options
-rw-r--r-- | api/current.txt | 3 | ||||
-rw-r--r-- | api/removed.txt | 9 | ||||
-rw-r--r-- | api/system-current.txt | 3 | ||||
-rw-r--r-- | api/system-removed.txt | 9 | ||||
-rw-r--r-- | core/java/android/accounts/AccountManager.java | 155 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 24 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 21 | ||||
-rw-r--r-- | docs/html/training/sync-adapters/creating-sync-adapter.jd | 7 | ||||
-rw-r--r-- | services/core/java/com/android/server/accounts/AccountManagerService.java | 301 |
9 files changed, 230 insertions, 302 deletions
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 @@ package android { 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 @@ package android { 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 @@ package android { 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/removed.txt b/api/removed.txt index 0046a70..f6ad27b 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -1,12 +1,3 @@ -package android { - - public static final class Manifest.permission { - field public static final java.lang.String SUBSCRIBED_FEEDS_READ = "android.permission.SUBSCRIBED_FEEDS_READ"; - field public static final java.lang.String SUBSCRIBED_FEEDS_WRITE = "android.permission.SUBSCRIBED_FEEDS_WRITE"; - } - -} - package android.content.pm { public class PackageInfo implements android.os.Parcelable { 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 @@ package android { 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 @@ package android { 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 @@ 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_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/api/system-removed.txt b/api/system-removed.txt index 0046a70..f6ad27b 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -1,12 +1,3 @@ -package android { - - public static final class Manifest.permission { - field public static final java.lang.String SUBSCRIBED_FEEDS_READ = "android.permission.SUBSCRIBED_FEEDS_READ"; - field public static final java.lang.String SUBSCRIBED_FEEDS_WRITE = "android.permission.SUBSCRIBED_FEEDS_WRITE"; - } - -} - package android.content.pm { public class PackageInfo implements android.os.Parcelable { 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.FutureTask; 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 @@ public class AccountManager { * * <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 @@ public class AccountManager { * * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * @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 @@ public class AccountManager { * <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 @@ public class AccountManager { * * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * 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 @@ public class AccountManager { * {@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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { 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 @@ public class AccountManager { * 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 @@ public class AccountManager { * * <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 @@ public class AccountManager { * * <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 @@ public class AccountManager { * * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * * <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 @@ public class AccountManager { * * <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 @@ public class AccountManager { * <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 @@ public class AccountManager { * @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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * 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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 @@ public class AccountManager { * <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 @@ public class AccountManager { * 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 54123f5..a16a2f4 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 675d3e2..f6af19c 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 @@ public class MainActivity extends FragmentActivity { 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.File; 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.Date; 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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService // 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 @@ public class AccountManagerService @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 @@ public class AccountManagerService } 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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService @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 @@ public class AccountManagerService return; } - checkBinderPermission(Manifest.permission.USE_CREDENTIALS); final UserAccounts accounts = getUserAccountsForCaller(); final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; authenticatorInfo = mAuthenticatorCache.getServiceInfo( @@ -2047,7 +2091,6 @@ public class AccountManagerService } 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 @@ public class AccountManagerService } 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 @@ public class AccountManagerService } 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 @@ public class AccountManagerService 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 @@ public class AccountManagerService @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 @@ public class AccountManagerService 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 @@ public class AccountManagerService 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 @@ public class AccountManagerService } } - 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)) { |