Ads per-user APIs to manage accounts through the AccountManager
Bug: 16056552 Bug: 14642886 Change-Id: I17ff6c2515285e63c84cecf2f861d10666c393c5
This commit is contained in:
@@ -155,6 +155,8 @@ public class AccountManager {
|
||||
|
||||
/** @hide */
|
||||
public static final int ERROR_CODE_USER_RESTRICTED = 100;
|
||||
/** @hide */
|
||||
public static final int ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE = 101;
|
||||
|
||||
/**
|
||||
* Bundle key used for the {@link String} account name in results
|
||||
@@ -678,8 +680,7 @@ public class AccountManager {
|
||||
* @param handler {@link Handler} identifying the callback thread,
|
||||
* null for the main thread
|
||||
* @return An {@link AccountManagerFuture} which resolves to a Boolean,
|
||||
* true if the account has been successfully removed,
|
||||
* false if the authenticator forbids deleting this account.
|
||||
* true if the account has been successfully removed
|
||||
*/
|
||||
public AccountManagerFuture<Boolean> removeAccount(final Account account,
|
||||
AccountManagerCallback<Boolean> callback, Handler handler) {
|
||||
@@ -697,6 +698,28 @@ public class AccountManager {
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #removeAccount(Account, AccountManagerCallback, Handler)
|
||||
* @hide
|
||||
*/
|
||||
public AccountManagerFuture<Boolean> removeAccountAsUser(final Account account,
|
||||
AccountManagerCallback<Boolean> callback, Handler handler,
|
||||
final UserHandle userHandle) {
|
||||
if (account == null) throw new IllegalArgumentException("account is null");
|
||||
if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
|
||||
return new Future2Task<Boolean>(handler, callback) {
|
||||
public void doWork() throws RemoteException {
|
||||
mService.removeAccountAsUser(mResponse, account, userHandle.getIdentifier());
|
||||
}
|
||||
public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
|
||||
if (!bundle.containsKey(KEY_BOOLEAN_RESULT)) {
|
||||
throw new AuthenticatorException("no result in response");
|
||||
}
|
||||
return bundle.getBoolean(KEY_BOOLEAN_RESULT);
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an auth token from the AccountManager's cache. Does nothing if
|
||||
* the auth token is not currently in the cache. Applications must call this
|
||||
@@ -1183,7 +1206,8 @@ public class AccountManager {
|
||||
* <li> {@link AuthenticatorException} if no authenticator was registered for
|
||||
* this account type or the authenticator failed to respond
|
||||
* <li> {@link OperationCanceledException} if the operation was canceled for
|
||||
* any reason, including the user canceling the creation process
|
||||
* any reason, including the user canceling the creation process or adding accounts
|
||||
* (of this type) has been disabled by policy
|
||||
* <li> {@link IOException} if the authenticator experienced an I/O problem
|
||||
* creating a new account, usually because of network trouble
|
||||
* </ul>
|
||||
@@ -1207,6 +1231,30 @@ public class AccountManager {
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback, Handler)
|
||||
* @hide
|
||||
*/
|
||||
public AccountManagerFuture<Bundle> addAccountAsUser(final String accountType,
|
||||
final String authTokenType, final String[] requiredFeatures,
|
||||
final Bundle addAccountOptions, final Activity activity,
|
||||
AccountManagerCallback<Bundle> callback, Handler handler, final UserHandle userHandle) {
|
||||
if (accountType == null) throw new IllegalArgumentException("accountType is null");
|
||||
if (userHandle == null) throw new IllegalArgumentException("userHandle is null");
|
||||
final Bundle optionsIn = new Bundle();
|
||||
if (addAccountOptions != null) {
|
||||
optionsIn.putAll(addAccountOptions);
|
||||
}
|
||||
optionsIn.putString(KEY_ANDROID_PACKAGE_NAME, mContext.getPackageName());
|
||||
|
||||
return new AmsTask(activity, handler, callback) {
|
||||
public void doWork() throws RemoteException {
|
||||
mService.addAccountAsUser(mResponse, accountType, authTokenType,
|
||||
requiredFeatures, activity != null, optionsIn, userHandle.getIdentifier());
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a shared account from the primary user to a secondary user. Adding the shared account
|
||||
* doesn't take effect immediately. When the target user starts up, any pending shared accounts
|
||||
@@ -1608,8 +1656,10 @@ public class AccountManager {
|
||||
}
|
||||
|
||||
public void onError(int code, String message) {
|
||||
if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED) {
|
||||
// the authenticator indicated that this request was canceled, do so now
|
||||
if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
|
||||
|| code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
|
||||
// the authenticator indicated that this request was canceled or we were
|
||||
// forbidden to fulfill; cancel now
|
||||
cancel(true /* mayInterruptIfRunning */);
|
||||
return;
|
||||
}
|
||||
@@ -1668,7 +1718,10 @@ public class AccountManager {
|
||||
}
|
||||
|
||||
public void onError(int code, String message) {
|
||||
if (code == ERROR_CODE_CANCELED) {
|
||||
if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED
|
||||
|| code == ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE) {
|
||||
// the authenticator indicated that this request was canceled or we were
|
||||
// forbidden to fulfill; cancel now
|
||||
cancel(true /* mayInterruptIfRunning */);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,8 @@ public interface AccountManagerFuture<V> {
|
||||
* will be thrown rather than the call returning normally.
|
||||
* @return the actual result
|
||||
* @throws android.accounts.OperationCanceledException if the request was canceled for any
|
||||
* reason
|
||||
* reason (including if it is forbidden
|
||||
* by policy to modify an account (of that type))
|
||||
* @throws android.accounts.AuthenticatorException if there was an error communicating with
|
||||
* the authenticator or if the authenticator returned an invalid response
|
||||
* @throws java.io.IOException if the authenticator returned an error response that indicates
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.accounts;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
@@ -27,11 +28,26 @@ import com.android.internal.R;
|
||||
* Just shows an error message about the account restrictions for the limited user.
|
||||
*/
|
||||
public class CantAddAccountActivity extends Activity {
|
||||
public static final String EXTRA_ERROR_CODE = "android.accounts.extra.ERROR_CODE";
|
||||
public static final int MISSING = -1;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.app_not_authorized);
|
||||
|
||||
int errorCode = getIntent().getIntExtra(EXTRA_ERROR_CODE, MISSING);
|
||||
if (errorCode != MISSING) {
|
||||
TextView errorText = (TextView) findViewById(R.id.description);
|
||||
switch (errorCode) {
|
||||
case AccountManager.ERROR_CODE_USER_RESTRICTED:
|
||||
errorText.setText(R.string.app_no_restricted_accounts);
|
||||
break;
|
||||
default:
|
||||
// TODO: Get better message. See: http://b/14642886
|
||||
errorText.setText(R.string.error_message_title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onCancelButtonClicked(View view) {
|
||||
|
||||
@@ -38,6 +38,7 @@ interface IAccountManager {
|
||||
void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
|
||||
boolean addAccountExplicitly(in Account account, String password, in Bundle extras);
|
||||
void removeAccount(in IAccountManagerResponse response, in Account account);
|
||||
void removeAccountAsUser(in IAccountManagerResponse response, in Account account, int userId);
|
||||
void invalidateAuthToken(String accountType, String authToken);
|
||||
String peekAuthToken(in Account account, String authTokenType);
|
||||
void setAuthToken(in Account account, String authTokenType, String authToken);
|
||||
@@ -52,6 +53,9 @@ interface IAccountManager {
|
||||
void addAccount(in IAccountManagerResponse response, String accountType,
|
||||
String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
|
||||
in Bundle options);
|
||||
void addAccountAsUser(in IAccountManagerResponse response, String accountType,
|
||||
String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
|
||||
in Bundle options, int userId);
|
||||
void updateCredentials(in IAccountManagerResponse response, in Account account,
|
||||
String authTokenType, boolean expectActivityLaunch, in Bundle options);
|
||||
void editProperties(in IAccountManagerResponse response, String accountType,
|
||||
|
||||
@@ -2598,9 +2598,17 @@ public class DevicePolicyManager {
|
||||
* @see #setAccountManagementDisabled
|
||||
*/
|
||||
public String[] getAccountTypesWithManagementDisabled() {
|
||||
return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getAccountTypesWithManagementDisabled()
|
||||
* @hide
|
||||
*/
|
||||
public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
return mService.getAccountTypesWithManagementDisabled();
|
||||
return mService.getAccountTypesWithManagementDisabledAsUser(userId);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed talking with device policy service", e);
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ interface IDevicePolicyManager {
|
||||
|
||||
void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
|
||||
String[] getAccountTypesWithManagementDisabled();
|
||||
String[] getAccountTypesWithManagementDisabledAsUser(int userId);
|
||||
|
||||
void setLockTaskPackages(in String[] packages);
|
||||
String[] getLockTaskPackages();
|
||||
|
||||
@@ -876,6 +876,7 @@
|
||||
<java-symbol type="string" name="config_customResolverActivity" />
|
||||
<java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
|
||||
<java-symbol type="string" name="error_message_title" />
|
||||
<java-symbol type="string" name="app_no_restricted_accounts" />
|
||||
<java-symbol type="string" name="action_bar_home_description_format" />
|
||||
<java-symbol type="string" name="action_bar_home_subtitle_description_format" />
|
||||
<java-symbol type="string" name="wireless_display_route_description" />
|
||||
|
||||
@@ -871,17 +871,84 @@ public class AccountManagerService
|
||||
checkManageAccountsPermission();
|
||||
UserHandle user = Binder.getCallingUserHandle();
|
||||
UserAccounts accounts = getUserAccountsForCaller();
|
||||
if (!canUserModifyAccounts(Binder.getCallingUid(), account.type)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
new RemoveAccountSession(accounts, response, account).bind();
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAccountAsUser(IAccountManagerResponse response, Account account,
|
||||
int userId) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "removeAccount: " + account
|
||||
+ ", response " + response
|
||||
+ ", caller's uid " + Binder.getCallingUid()
|
||||
+ ", pid " + Binder.getCallingPid()
|
||||
+ ", for user id " + userId);
|
||||
}
|
||||
if (response == null) throw new IllegalArgumentException("response is null");
|
||||
if (account == null) throw new IllegalArgumentException("account is null");
|
||||
|
||||
// Only allow the system process to modify accounts of other users
|
||||
enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
|
||||
+ " trying to remove account for " + userId);
|
||||
checkManageAccountsPermission();
|
||||
|
||||
UserAccounts accounts = getUserAccounts(userId);
|
||||
if (!canUserModifyAccounts(userId)) {
|
||||
try {
|
||||
response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
|
||||
"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;
|
||||
}
|
||||
|
||||
UserHandle user = new UserHandle(userId);
|
||||
long identityToken = clearCallingIdentity();
|
||||
|
||||
cancelNotification(getSigninRequiredNotificationId(accounts, account), user);
|
||||
synchronized(accounts.credentialsPermissionNotificationIds) {
|
||||
for (Pair<Pair<Account, String>, Integer> pair:
|
||||
@@ -1526,20 +1593,23 @@ public class AccountManagerService
|
||||
checkManageAccountsPermission();
|
||||
|
||||
// Is user disallowed from modifying accounts?
|
||||
if (!canUserModifyAccounts(Binder.getCallingUid(), accountType)) {
|
||||
int userId = Binder.getCallingUserHandle().getIdentifier();
|
||||
if (!canUserModifyAccounts(userId)) {
|
||||
try {
|
||||
response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
|
||||
"User is not allowed to add an account!");
|
||||
} catch (RemoteException re) {
|
||||
}
|
||||
Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
|
||||
cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
long identityToken = clearCallingIdentity();
|
||||
showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED);
|
||||
return;
|
||||
}
|
||||
if (!canUserModifyAccountsForType(userId, accountType)) {
|
||||
try {
|
||||
mContext.startActivityAsUser(cantAddAccount, UserHandle.CURRENT);
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
|
||||
"User cannot modify accounts of this type (policy).");
|
||||
} catch (RemoteException re) {
|
||||
}
|
||||
showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1575,6 +1645,92 @@ public class AccountManagerService
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAccountAsUser(final IAccountManagerResponse response, final String accountType,
|
||||
final String authTokenType, final String[] requiredFeatures,
|
||||
final boolean expectActivityLaunch, final Bundle optionsIn, int userId) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "addAccount: accountType " + accountType
|
||||
+ ", response " + response
|
||||
+ ", authTokenType " + authTokenType
|
||||
+ ", requiredFeatures " + stringArrayToString(requiredFeatures)
|
||||
+ ", expectActivityLaunch " + expectActivityLaunch
|
||||
+ ", caller's uid " + Binder.getCallingUid()
|
||||
+ ", pid " + Binder.getCallingPid()
|
||||
+ ", for user id " + userId);
|
||||
}
|
||||
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()
|
||||
+ " trying to add account for " + userId);
|
||||
|
||||
// Is user disallowed from modifying accounts?
|
||||
if (!canUserModifyAccounts(userId)) {
|
||||
try {
|
||||
response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
|
||||
"User is not allowed to add an account!");
|
||||
} catch (RemoteException re) {
|
||||
}
|
||||
showCantAddAccount(AccountManager.ERROR_CODE_USER_RESTRICTED);
|
||||
return;
|
||||
}
|
||||
if (!canUserModifyAccountsForType(userId, accountType)) {
|
||||
try {
|
||||
response.onError(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE,
|
||||
"User cannot modify accounts of this type (policy).");
|
||||
} catch (RemoteException re) {
|
||||
}
|
||||
showCantAddAccount(AccountManager.ERROR_CODE_MANAGEMENT_DISABLED_FOR_ACCOUNT_TYPE);
|
||||
return;
|
||||
}
|
||||
|
||||
UserAccounts accounts = getUserAccounts(userId);
|
||||
final int pid = Binder.getCallingPid();
|
||||
final int uid = Binder.getCallingUid();
|
||||
final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
|
||||
options.putInt(AccountManager.KEY_CALLER_UID, uid);
|
||||
options.putInt(AccountManager.KEY_CALLER_PID, pid);
|
||||
|
||||
long identityToken = clearCallingIdentity();
|
||||
try {
|
||||
new Session(accounts, response, accountType, expectActivityLaunch,
|
||||
true /* stripAuthTokenFromResult */) {
|
||||
@Override
|
||||
public void run() throws RemoteException {
|
||||
mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toDebugString(long now) {
|
||||
return super.toDebugString(now) + ", addAccount"
|
||||
+ ", accountType " + accountType
|
||||
+ ", requiredFeatures "
|
||||
+ (requiredFeatures != null
|
||||
? TextUtils.join(",", requiredFeatures)
|
||||
: null);
|
||||
}
|
||||
}.bind();
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void showCantAddAccount(int errorCode) {
|
||||
Intent cantAddAccount = new Intent(mContext, CantAddAccountActivity.class);
|
||||
cantAddAccount.putExtra(CantAddAccountActivity.EXTRA_ERROR_CODE, errorCode);
|
||||
cantAddAccount.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
long identityToken = clearCallingIdentity();
|
||||
try {
|
||||
mContext.startActivity(cantAddAccount);
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void confirmCredentialsAsUser(IAccountManagerResponse response,
|
||||
final Account account, final Bundle options, final boolean expectActivityLaunch,
|
||||
@@ -2766,18 +2922,18 @@ public class AccountManagerService
|
||||
Manifest.permission.USE_CREDENTIALS);
|
||||
}
|
||||
|
||||
private boolean canUserModifyAccounts(int callingUid, String accountType) {
|
||||
if (callingUid != Process.myUid()) {
|
||||
if (getUserManager().getUserRestrictions(
|
||||
new UserHandle(UserHandle.getUserId(callingUid)))
|
||||
.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
|
||||
return false;
|
||||
}
|
||||
private boolean canUserModifyAccounts(int userId) {
|
||||
if (getUserManager().getUserRestrictions(new UserHandle(userId))
|
||||
.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean canUserModifyAccountsForType(int userId, String accountType) {
|
||||
DevicePolicyManager dpm = (DevicePolicyManager) mContext
|
||||
.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
String[] typesArray = dpm.getAccountTypesWithManagementDisabled();
|
||||
String[] typesArray = dpm.getAccountTypesWithManagementDisabledAsUser(userId);
|
||||
for (String forbiddenType : typesArray) {
|
||||
if (forbiddenType.equals(accountType)) {
|
||||
return false;
|
||||
|
||||
@@ -3888,11 +3888,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
|
||||
@Override
|
||||
public String[] getAccountTypesWithManagementDisabled() {
|
||||
return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
|
||||
enforceCrossUserPermission(userId);
|
||||
if (!mHasFeature) {
|
||||
return null;
|
||||
}
|
||||
synchronized (this) {
|
||||
DevicePolicyData policy = getUserData(UserHandle.getCallingUserId());
|
||||
DevicePolicyData policy = getUserData(userId);
|
||||
final int N = policy.mAdminList.size();
|
||||
HashSet<String> resultSet = new HashSet<String>();
|
||||
for (int i = 0; i < N; i++) {
|
||||
|
||||
Reference in New Issue
Block a user