New AccountManager method to copy accounts between users.

Adding the copyAccountToUser method which copies an account
along with its credentials to a different user.

Also an extra in the public api to identify the account to migrate
during provisioning.

Bug: 17716971
Change-Id: I2f29f1765ba0d360a3894b13ef86253b7c7d3284
This commit is contained in:
Esteban Talavera
2014-10-31 15:41:12 +00:00
committed by Alexandra Gherghina
parent f39cec0b06
commit 22dc3b7ba1
3 changed files with 70 additions and 29 deletions

View File

@@ -1442,6 +1442,40 @@ public class AccountManager {
} }
} }
/**
* Copies an account from the primary user to another user.
* @param account the account to copy
* @param user the target user
* @param callback Callback to invoke when the request completes,
* null for no callback
* @param handler {@link Handler} identifying the callback thread,
* null for the main thread
* @return An {@link AccountManagerFuture} which resolves to a Boolean indicated wether it
* succeeded.
* @hide
*/
public AccountManagerFuture<Boolean> copyAccountToUser(
final Account account, final UserHandle user,
AccountManagerCallback<Boolean> callback, Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null");
if (user == null) throw new IllegalArgumentException("user is null");
return new Future2Task<Boolean>(handler, callback) {
@Override
public void doWork() throws RemoteException {
mService.copyAccountToUser(
mResponse, account, UserHandle.USER_OWNER, user.getIdentifier());
}
@Override
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();
}
/** /**
* @hide * @hide
* Removes the shared account. * Removes the shared account.

View File

@@ -42,6 +42,8 @@ interface IAccountManager {
void removeAccountAsUser(in IAccountManagerResponse response, in Account account, void removeAccountAsUser(in IAccountManagerResponse response, in Account account,
boolean expectActivityLaunch, int userId); boolean expectActivityLaunch, int userId);
boolean removeAccountExplicitly(in Account account); boolean removeAccountExplicitly(in Account account);
void copyAccountToUser(in IAccountManagerResponse response, in Account account,
int userFrom, int userTo);
void invalidateAuthToken(String accountType, String authToken); void invalidateAuthToken(String accountType, String authToken);
String peekAuthToken(in Account account, String authTokenType); String peekAuthToken(in Account account, String authTokenType);
void setAuthToken(in Account account, String authTokenType, String authToken); void setAuthToken(in Account account, String authTokenType, String authToken);

View File

@@ -486,7 +486,7 @@ public class AccountManagerService
for (Account sa : sharedAccounts) { for (Account sa : sharedAccounts) {
if (ArrayUtils.contains(accounts, sa)) continue; if (ArrayUtils.contains(accounts, sa)) continue;
// Account doesn't exist. Copy it now. // Account doesn't exist. Copy it now.
copyAccountToUser(sa, UserHandle.USER_OWNER, userId); copyAccountToUser(null /*no response*/, sa, UserHandle.USER_OWNER, userId);
} }
} }
@@ -672,16 +672,31 @@ public class AccountManagerService
} }
} }
private boolean copyAccountToUser(final Account account, int userFrom, int userTo) { @Override
public void copyAccountToUser(final IAccountManagerResponse response, final Account account,
int userFrom, int userTo) {
enforceCrossUserPermission(UserHandle.USER_ALL, "Calling copyAccountToUser requires "
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
final UserAccounts fromAccounts = getUserAccounts(userFrom); final UserAccounts fromAccounts = getUserAccounts(userFrom);
final UserAccounts toAccounts = getUserAccounts(userTo); final UserAccounts toAccounts = getUserAccounts(userTo);
if (fromAccounts == null || toAccounts == null) { if (fromAccounts == null || toAccounts == null) {
return false; if (response != null) {
Bundle result = new Bundle();
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
try {
response.onResult(result);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report error back to the client." + e);
}
}
return;
} }
Slog.d(TAG, "Copying account " + account.name
+ " from user " + userFrom + " to user " + userTo);
long identityToken = clearCallingIdentity(); long identityToken = clearCallingIdentity();
try { try {
new Session(fromAccounts, null, account.type, false, new Session(fromAccounts, response, account.type, false,
false /* stripAuthTokenFromResult */) { false /* stripAuthTokenFromResult */) {
@Override @Override
protected String toDebugString(long now) { protected String toDebugString(long now) {
@@ -696,12 +711,10 @@ public class AccountManagerService
@Override @Override
public void onResult(Bundle result) { public void onResult(Bundle result) {
if (result != null) { if (result != null
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { && result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
// Create a Session for the target user and pass in the bundle // Create a Session for the target user and pass in the bundle
completeCloningAccount(result, account, toAccounts); completeCloningAccount(response, result, account, toAccounts);
}
return;
} else { } else {
super.onResult(result); super.onResult(result);
} }
@@ -710,14 +723,13 @@ public class AccountManagerService
} finally { } finally {
restoreCallingIdentity(identityToken); restoreCallingIdentity(identityToken);
} }
return true;
} }
void completeCloningAccount(final Bundle result, final Account account, private void completeCloningAccount(IAccountManagerResponse response,
final UserAccounts targetUser) { final Bundle accountCredentials, final Account account, final UserAccounts targetUser) {
long id = clearCallingIdentity(); long id = clearCallingIdentity();
try { try {
new Session(targetUser, null, account.type, false, new Session(targetUser, response, account.type, false,
false /* stripAuthTokenFromResult */) { false /* stripAuthTokenFromResult */) {
@Override @Override
protected String toDebugString(long now) { protected String toDebugString(long now) {
@@ -730,10 +742,10 @@ public class AccountManagerService
// Confirm that the owner's account still exists before this step. // Confirm that the owner's account still exists before this step.
UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER); UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER);
synchronized (owner.cacheLock) { synchronized (owner.cacheLock) {
Account[] ownerAccounts = getAccounts(UserHandle.USER_OWNER); for (Account acc : getAccounts(UserHandle.USER_OWNER)) {
for (Account acc : ownerAccounts) {
if (acc.equals(account)) { if (acc.equals(account)) {
mAuthenticator.addAccountFromCredentials(this, account, result); mAuthenticator.addAccountFromCredentials(
this, account, accountCredentials);
break; break;
} }
} }
@@ -742,18 +754,11 @@ public class AccountManagerService
@Override @Override
public void onResult(Bundle result) { public void onResult(Bundle result) {
if (result != null) { // TODO: Anything to do if if succedded?
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) { // TODO: If it failed: Show error notification? Should we remove the shadow
// TODO: Anything? // account to avoid retries?
} else {
// TODO: Show error notification
// TODO: Should we remove the shadow account to avoid retries?
}
return;
} else {
super.onResult(result); super.onResult(result);
} }
}
@Override @Override
public void onError(int errorCode, String errorMessage) { public void onError(int errorCode, String errorMessage) {
@@ -2740,7 +2745,7 @@ public class AccountManagerService
break; break;
case MESSAGE_COPY_SHARED_ACCOUNT: case MESSAGE_COPY_SHARED_ACCOUNT:
copyAccountToUser((Account) msg.obj, msg.arg1, msg.arg2); copyAccountToUser(/*no response*/ null, (Account) msg.obj, msg.arg1, msg.arg2);
break; break;
default: default: