am 67df64b3
: Shared accounts and sharing of apps
* commit '67df64b3a48a8157d08a98fa90135d0ac0ee621c': Shared accounts and sharing of apps
This commit is contained in:
@ -1094,7 +1094,7 @@ public final class Pm {
|
||||
private boolean deletePackage(String pkg, int unInstallFlags) {
|
||||
PackageDeleteObserver obs = new PackageDeleteObserver();
|
||||
try {
|
||||
mPm.deletePackage(pkg, obs, unInstallFlags);
|
||||
mPm.deletePackageAsUser(pkg, obs, UserHandle.USER_OWNER, unInstallFlags);
|
||||
|
||||
synchronized (obs) {
|
||||
while (!obs.finished) {
|
||||
|
@ -275,6 +275,38 @@ public abstract class AbstractAccountAuthenticator {
|
||||
handleException(response, "getAccountRemovalAllowed", account.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void getAccountCredentialsForCloning(IAccountAuthenticatorResponse response,
|
||||
Account account) throws RemoteException {
|
||||
checkBinderPermission();
|
||||
try {
|
||||
final Bundle result =
|
||||
AbstractAccountAuthenticator.this.getAccountCredentialsForCloning(
|
||||
new AccountAuthenticatorResponse(response), account);
|
||||
if (result != null) {
|
||||
response.onResult(result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException(response, "getAccountCredentialsForCloning", account.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAccountFromCredentials(IAccountAuthenticatorResponse response,
|
||||
Account account,
|
||||
Bundle accountCredentials) throws RemoteException {
|
||||
checkBinderPermission();
|
||||
try {
|
||||
final Bundle result =
|
||||
AbstractAccountAuthenticator.this.addAccountFromCredentials(
|
||||
new AccountAuthenticatorResponse(response), account,
|
||||
accountCredentials);
|
||||
if (result != null) {
|
||||
response.onResult(result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
handleException(response, "addAccountFromCredentials", account.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleException(IAccountAuthenticatorResponse response, String method,
|
||||
@ -471,4 +503,54 @@ public abstract class AbstractAccountAuthenticator {
|
||||
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Returns a Bundle that contains whatever is required to clone the account on a different
|
||||
* user. The Bundle is passed to the authenticator instance in the target user via
|
||||
* {@link #addAccountFromCredentials(AccountAuthenticatorResponse, Account, Bundle)}.
|
||||
* The default implementation returns null, indicating that cloning is not supported.
|
||||
* @param response to send the result back to the AccountManager, will never be null
|
||||
* @param account the account to clone, will never be null
|
||||
* @return a Bundle result or null if the result is to be returned via the response.
|
||||
* @throws NetworkErrorException
|
||||
* @see {@link #addAccountFromCredentials(AccountAuthenticatorResponse, Account, Bundle)}
|
||||
*/
|
||||
public Bundle getAccountCredentialsForCloning(final AccountAuthenticatorResponse response,
|
||||
final Account account) throws NetworkErrorException {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
|
||||
response.onResult(result);
|
||||
}
|
||||
}).start();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Creates an account based on credentials provided by the authenticator instance of another
|
||||
* user on the device, who has chosen to share the account with this user.
|
||||
* @param response to send the result back to the AccountManager, will never be null
|
||||
* @param account the account to clone, will never be null
|
||||
* @param accountCredentials the Bundle containing the required credentials to create the
|
||||
* account. Contents of the Bundle are only meaningful to the authenticator. This Bundle is
|
||||
* provided by {@link #getAccountCredentialsForCloning(AccountAuthenticatorResponse, Account)}.
|
||||
* @return a Bundle result or null if the result is to be returned via the response.
|
||||
* @throws NetworkErrorException
|
||||
* @see {@link #getAccountCredentialsForCloning(AccountAuthenticatorResponse, Account)}
|
||||
*/
|
||||
public Bundle addAccountFromCredentials(final AccountAuthenticatorResponse response,
|
||||
Account account,
|
||||
Bundle accountCredentials) throws NetworkErrorException {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
|
||||
response.onResult(result);
|
||||
}
|
||||
}).start();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1122,6 +1122,57 @@ public class AccountManager {
|
||||
}.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
|
||||
* are attempted to be copied to the target user from the primary via calls to the
|
||||
* authenticator.
|
||||
* @param account the account to share
|
||||
* @param user the target user
|
||||
* @return
|
||||
* @hide
|
||||
*/
|
||||
public boolean addSharedAccount(final Account account, UserHandle user) {
|
||||
try {
|
||||
boolean val = mService.addSharedAccountAsUser(account, user.getIdentifier());
|
||||
return val;
|
||||
} catch (RemoteException re) {
|
||||
// won't ever happen
|
||||
throw new RuntimeException(re);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Removes the shared account.
|
||||
* @param account the account to remove
|
||||
* @param user the user to remove the account from
|
||||
* @return
|
||||
*/
|
||||
public boolean removeSharedAccount(final Account account, UserHandle user) {
|
||||
try {
|
||||
boolean val = mService.removeSharedAccountAsUser(account, user.getIdentifier());
|
||||
return val;
|
||||
} catch (RemoteException re) {
|
||||
// won't ever happen
|
||||
throw new RuntimeException(re);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
public Account[] getSharedAccounts(UserHandle user) {
|
||||
try {
|
||||
return mService.getSharedAccountsAsUser(user.getIdentifier());
|
||||
} catch (RemoteException re) {
|
||||
// won't ever happen
|
||||
throw new RuntimeException(re);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the user knows the password for an account to make extra
|
||||
* sure they are the owner of the account. The user-entered password can
|
||||
|
@ -70,4 +70,17 @@ oneway interface IAccountAuthenticator {
|
||||
* Gets whether or not the account is allowed to be removed.
|
||||
*/
|
||||
void getAccountRemovalAllowed(in IAccountAuthenticatorResponse response, in Account account);
|
||||
|
||||
/**
|
||||
* Returns a Bundle containing the required credentials to copy the account across users.
|
||||
*/
|
||||
void getAccountCredentialsForCloning(in IAccountAuthenticatorResponse response,
|
||||
in Account account);
|
||||
|
||||
/**
|
||||
* Uses the Bundle containing credentials from another instance of the authenticator to create
|
||||
* a copy of the account on this user.
|
||||
*/
|
||||
void addAccountFromCredentials(in IAccountAuthenticatorResponse response, in Account account,
|
||||
in Bundle accountCredentials);
|
||||
}
|
||||
|
@ -58,4 +58,9 @@ interface IAccountManager {
|
||||
in Bundle options, boolean expectActivityLaunch, int userId);
|
||||
void getAuthTokenLabel(in IAccountManagerResponse response, String accountType,
|
||||
String authTokenType);
|
||||
|
||||
/* Shared accounts */
|
||||
boolean addSharedAccountAsUser(in Account account, int userId);
|
||||
Account[] getSharedAccountsAsUser(int userId);
|
||||
boolean removeSharedAccountAsUser(in Account account, int userId);
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
|
||||
@ -1064,7 +1065,7 @@ final class ApplicationPackageManager extends PackageManager {
|
||||
public int installExistingPackage(String packageName)
|
||||
throws NameNotFoundException {
|
||||
try {
|
||||
int res = mPM.installExistingPackage(packageName);
|
||||
int res = mPM.installExistingPackageAsUser(packageName, UserHandle.myUserId());
|
||||
if (res == INSTALL_FAILED_INVALID_URI) {
|
||||
throw new NameNotFoundException("Package " + packageName + " doesn't exist");
|
||||
}
|
||||
@ -1126,7 +1127,7 @@ final class ApplicationPackageManager extends PackageManager {
|
||||
@Override
|
||||
public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
|
||||
try {
|
||||
mPM.deletePackage(packageName, observer, flags);
|
||||
mPM.deletePackageAsUser(packageName, observer, UserHandle.myUserId(), flags);
|
||||
} catch (RemoteException e) {
|
||||
// Should never happen!
|
||||
}
|
||||
|
@ -194,20 +194,22 @@ interface IPackageManager {
|
||||
void setInstallerPackageName(in String targetPackage, in String installerPackageName);
|
||||
|
||||
/**
|
||||
* Delete a package.
|
||||
* Delete a package for a specific user.
|
||||
*
|
||||
* @param packageName The fully qualified name of the package to delete.
|
||||
* @param observer a callback to use to notify when the package deletion in finished.
|
||||
* @param userId the id of the user for whom to delete the package
|
||||
* @param flags - possible values: {@link #DONT_DELETE_DATA}
|
||||
*/
|
||||
void deletePackage(in String packageName, IPackageDeleteObserver observer, int flags);
|
||||
void deletePackageAsUser(in String packageName, IPackageDeleteObserver observer,
|
||||
int userId, int flags);
|
||||
|
||||
String getInstallerPackageName(in String packageName);
|
||||
|
||||
void addPackageToPreferred(String packageName);
|
||||
|
||||
|
||||
void removePackageFromPreferred(String packageName);
|
||||
|
||||
|
||||
List<PackageInfo> getPreferredPackages(int flags);
|
||||
|
||||
void resetPreferredActivities(int userId);
|
||||
@ -381,7 +383,7 @@ interface IPackageManager {
|
||||
in VerificationParams verificationParams,
|
||||
in ContainerEncryptionParams encryptionParams);
|
||||
|
||||
int installExistingPackage(String packageName);
|
||||
int installExistingPackageAsUser(String packageName, int userId);
|
||||
|
||||
void verifyPendingInstall(int id, int verificationCode);
|
||||
void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
|
||||
|
@ -97,6 +97,10 @@ public class UserInfo implements Parcelable {
|
||||
return (flags & FLAG_GUEST) == FLAG_GUEST;
|
||||
}
|
||||
|
||||
public boolean isRestricted() {
|
||||
return (flags & FLAG_RESTRICTED) == FLAG_RESTRICTED;
|
||||
}
|
||||
|
||||
public UserInfo() {
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ import android.accounts.Account;
|
||||
import android.accounts.AccountAndUser;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AccountManagerResponse;
|
||||
import android.accounts.AuthenticatorDescription;
|
||||
import android.accounts.GrantCredentialsPermissionActivity;
|
||||
import android.accounts.IAccountAuthenticator;
|
||||
@ -70,6 +69,7 @@ import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.collect.Sets;
|
||||
@ -103,7 +103,7 @@ public class AccountManagerService
|
||||
|
||||
private static final int TIMEOUT_DELAY_MS = 1000 * 60;
|
||||
private static final String DATABASE_NAME = "accounts.db";
|
||||
private static final int DATABASE_VERSION = 4;
|
||||
private static final int DATABASE_VERSION = 5;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
@ -146,6 +146,8 @@ public class AccountManagerService
|
||||
private static final String META_KEY = "key";
|
||||
private static final String META_VALUE = "value";
|
||||
|
||||
private static final String TABLE_SHARED_ACCOUNTS = "shared_accounts";
|
||||
|
||||
private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
|
||||
new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
|
||||
private static final Intent ACCOUNTS_CHANGED_INTENT;
|
||||
@ -249,12 +251,18 @@ public class AccountManagerService
|
||||
|
||||
IntentFilter userFilter = new IntentFilter();
|
||||
userFilter.addAction(Intent.ACTION_USER_REMOVED);
|
||||
mContext.registerReceiver(new BroadcastReceiver() {
|
||||
userFilter.addAction(Intent.ACTION_USER_STARTED);
|
||||
mContext.registerReceiverAsUser(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
onUserRemoved(intent);
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_USER_REMOVED.equals(action)) {
|
||||
onUserRemoved(intent);
|
||||
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
|
||||
onUserStarted(intent);
|
||||
}
|
||||
}
|
||||
}, userFilter);
|
||||
}, UserHandle.ALL, userFilter, null, null);
|
||||
}
|
||||
|
||||
public void systemReady() {
|
||||
@ -430,6 +438,21 @@ public class AccountManagerService
|
||||
}
|
||||
}
|
||||
|
||||
private void onUserStarted(Intent intent) {
|
||||
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
|
||||
if (userId < 1) return;
|
||||
|
||||
// Check if there's a shared account that needs to be created as an account
|
||||
Account[] sharedAccounts = getSharedAccountsAsUser(userId);
|
||||
if (sharedAccounts == null || sharedAccounts.length == 0) return;
|
||||
Account[] accounts = getAccountsAsUser(null, userId);
|
||||
for (Account sa : sharedAccounts) {
|
||||
if (ArrayUtils.contains(accounts, sa)) continue;
|
||||
// Account doesn't exist. Copy it now.
|
||||
copyAccountToUser(sa, UserHandle.USER_OWNER, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
|
||||
Slog.d(TAG, "onServiceChanged() for userId " + userId);
|
||||
@ -535,14 +558,120 @@ public class AccountManagerService
|
||||
// fails if the account already exists
|
||||
long identityToken = clearCallingIdentity();
|
||||
try {
|
||||
return addAccountInternal(accounts, account, password, extras);
|
||||
return addAccountInternal(accounts, account, password, extras, false);
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean copyAccountToUser(final Account account, int userFrom, int userTo) {
|
||||
final UserAccounts fromAccounts = getUserAccounts(userFrom);
|
||||
final UserAccounts toAccounts = getUserAccounts(userTo);
|
||||
if (fromAccounts == null || toAccounts == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long identityToken = clearCallingIdentity();
|
||||
try {
|
||||
new Session(fromAccounts, null, account.type, false,
|
||||
false /* stripAuthTokenFromResult */) {
|
||||
protected String toDebugString(long now) {
|
||||
return super.toDebugString(now) + ", getAccountCredentialsForClone"
|
||||
+ ", " + account.type;
|
||||
}
|
||||
|
||||
public void run() throws RemoteException {
|
||||
mAuthenticator.getAccountCredentialsForCloning(this, account);
|
||||
}
|
||||
|
||||
public void onResult(Bundle result) {
|
||||
if (result != null) {
|
||||
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
|
||||
// Create a Session for the target user and pass in the bundle
|
||||
Slog.i(TAG, "getAccountCredentialsForCloning returned success, "
|
||||
+ "sending result to target user");
|
||||
completeCloningAccount(result, account, toAccounts);
|
||||
} else {
|
||||
Slog.e(TAG, "getAccountCredentialsForCloning returned failure");
|
||||
clonePassword(fromAccounts, toAccounts, account);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
Slog.e(TAG, "getAccountCredentialsForCloning returned null");
|
||||
clonePassword(fromAccounts, toAccounts, account);
|
||||
super.onResult(result);
|
||||
}
|
||||
}
|
||||
}.bind();
|
||||
} finally {
|
||||
restoreCallingIdentity(identityToken);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Remove fallback - move to authenticator
|
||||
private void clonePassword(UserAccounts fromAccounts, UserAccounts toAccounts,
|
||||
Account account) {
|
||||
long id = clearCallingIdentity();
|
||||
try {
|
||||
String password = readPasswordInternal(fromAccounts, account);
|
||||
String extraFlags = readUserDataInternal(fromAccounts, account, "flags");
|
||||
String extraServices = readUserDataInternal(fromAccounts, account, "services");
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("flags", extraFlags);
|
||||
extras.putString("services", extraServices);
|
||||
addAccountInternal(toAccounts, account, password, extras, true);
|
||||
} finally {
|
||||
restoreCallingIdentity(id);
|
||||
}
|
||||
}
|
||||
|
||||
void completeCloningAccount(final Bundle result, final Account account,
|
||||
final UserAccounts targetUser) {
|
||||
long id = clearCallingIdentity();
|
||||
try {
|
||||
new Session(targetUser, null, account.type, false,
|
||||
false /* stripAuthTokenFromResult */) {
|
||||
protected String toDebugString(long now) {
|
||||
return super.toDebugString(now) + ", getAccountCredentialsForClone"
|
||||
+ ", " + account.type;
|
||||
}
|
||||
|
||||
public void run() throws RemoteException {
|
||||
mAuthenticator.addAccountFromCredentials(this, account, result);
|
||||
}
|
||||
|
||||
public void onResult(Bundle result) {
|
||||
if (result != null) {
|
||||
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
|
||||
// TODO: Anything?
|
||||
Slog.i(TAG, "addAccount returned success");
|
||||
} else {
|
||||
// TODO: Show error notification
|
||||
// TODO: Should we remove the shadow account to avoid retries?
|
||||
Slog.e(TAG, "addAccountFromCredentials returned failure");
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
Slog.e(TAG, "addAccountFromCredentials returned null");
|
||||
super.onResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
public void onError(int errorCode, String errorMessage) {
|
||||
super.onError(errorCode, errorMessage);
|
||||
// TODO: Show error notification to user
|
||||
// TODO: Should we remove the shadow account so that it doesn't keep trying?
|
||||
}
|
||||
|
||||
}.bind();
|
||||
} finally {
|
||||
restoreCallingIdentity(id);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
|
||||
Bundle extras) {
|
||||
Bundle extras, boolean restricted) {
|
||||
if (account == null) {
|
||||
return false;
|
||||
}
|
||||
@ -768,6 +897,21 @@ public class AccountManagerService
|
||||
removeAccountFromCacheLocked(accounts, account);
|
||||
sendAccountsChangedBroadcast(accounts.userId);
|
||||
}
|
||||
if (accounts.userId == UserHandle.USER_OWNER) {
|
||||
// Owner's account was removed, remove from any users that are sharing
|
||||
// this account.
|
||||
long id = Binder.clearCallingIdentity();
|
||||
try {
|
||||
List<UserInfo> users = mUserManager.getUsers(true);
|
||||
for (UserInfo user : users) {
|
||||
if (!user.isPrimary() && user.isRestricted()) {
|
||||
removeSharedAccountAsUser(account, user.id);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateAuthToken(String accountType, String authToken) {
|
||||
@ -1605,6 +1749,65 @@ public class AccountManagerService
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addSharedAccountAsUser(Account account, int userId) {
|
||||
userId = handleIncomingUser(userId);
|
||||
SQLiteDatabase db = getUserAccounts(userId).openHelper.getWritableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ACCOUNTS_NAME, account.name);
|
||||
values.put(ACCOUNTS_TYPE, account.type);
|
||||
db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
|
||||
new String[] {account.name, account.type});
|
||||
long accountId = db.insert(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
|
||||
if (accountId < 0) {
|
||||
Log.w(TAG, "insertAccountIntoDatabase: " + account
|
||||
+ ", skipping the DB insert failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeSharedAccountAsUser(Account account, int userId) {
|
||||
userId = handleIncomingUser(userId);
|
||||
UserAccounts accounts = getUserAccounts(userId);
|
||||
SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
|
||||
int r = db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
|
||||
new String[] {account.name, account.type});
|
||||
if (r > 0) {
|
||||
removeAccountInternal(accounts, account);
|
||||
}
|
||||
return r > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account[] getSharedAccountsAsUser(int userId) {
|
||||
userId = handleIncomingUser(userId);
|
||||
UserAccounts accounts = getUserAccounts(userId);
|
||||
ArrayList<Account> accountList = new ArrayList<Account>();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = accounts.openHelper.getReadableDatabase()
|
||||
.query(TABLE_SHARED_ACCOUNTS, new String[]{ACCOUNTS_NAME, ACCOUNTS_TYPE},
|
||||
null, null, null, null, null);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
|
||||
int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
|
||||
do {
|
||||
accountList.add(new Account(cursor.getString(nameIndex),
|
||||
cursor.getString(typeIndex)));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
Account[] accountArray = new Account[accountList.size()];
|
||||
accountList.toArray(accountArray);
|
||||
return accountArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account[] getAccounts(String type) {
|
||||
return getAccountsAsUser(type, UserHandle.getCallingUserId());
|
||||
@ -1679,7 +1882,6 @@ public class AccountManagerService
|
||||
private int mNumRequestContinued = 0;
|
||||
private int mNumErrors = 0;
|
||||
|
||||
|
||||
IAccountAuthenticator mAuthenticator = null;
|
||||
|
||||
private final boolean mStripAuthTokenFromResult;
|
||||
@ -1688,7 +1890,7 @@ public class AccountManagerService
|
||||
public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
|
||||
boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
|
||||
super();
|
||||
if (response == null) throw new IllegalArgumentException("response is null");
|
||||
//if (response == null) throw new IllegalArgumentException("response is null");
|
||||
if (accountType == null) throw new IllegalArgumentException("accountType is null");
|
||||
mAccounts = accounts;
|
||||
mStripAuthTokenFromResult = stripAuthTokenFromResult;
|
||||
@ -1699,11 +1901,13 @@ public class AccountManagerService
|
||||
synchronized (mSessions) {
|
||||
mSessions.put(toString(), this);
|
||||
}
|
||||
try {
|
||||
response.asBinder().linkToDeath(this, 0 /* flags */);
|
||||
} catch (RemoteException e) {
|
||||
mResponse = null;
|
||||
binderDied();
|
||||
if (response != null) {
|
||||
try {
|
||||
response.asBinder().linkToDeath(this, 0 /* flags */);
|
||||
} catch (RemoteException e) {
|
||||
mResponse = null;
|
||||
binderDied();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2011,9 +2215,19 @@ public class AccountManagerService
|
||||
+ META_KEY + " TEXT PRIMARY KEY NOT NULL, "
|
||||
+ META_VALUE + " TEXT)");
|
||||
|
||||
createSharedAccountsTable(db);
|
||||
|
||||
createAccountsDeletionTrigger(db);
|
||||
}
|
||||
|
||||
private void createSharedAccountsTable(SQLiteDatabase db) {
|
||||
db.execSQL("CREATE TABLE " + TABLE_SHARED_ACCOUNTS + " ( "
|
||||
+ ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ ACCOUNTS_NAME + " TEXT NOT NULL, "
|
||||
+ ACCOUNTS_TYPE + " TEXT NOT NULL, "
|
||||
+ "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
|
||||
}
|
||||
|
||||
private void createAccountsDeletionTrigger(SQLiteDatabase db) {
|
||||
db.execSQL(""
|
||||
+ " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
|
||||
@ -2058,6 +2272,15 @@ public class AccountManagerService
|
||||
" = 'com.google' WHERE " + ACCOUNTS_TYPE + " == 'com.google.GAIA'");
|
||||
oldVersion++;
|
||||
}
|
||||
|
||||
if (oldVersion == 4) {
|
||||
createSharedAccountsTable(db);
|
||||
oldVersion++;
|
||||
}
|
||||
|
||||
if (oldVersion != newVersion) {
|
||||
Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2216,6 +2439,16 @@ public class AccountManagerService
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
|
||||
private int handleIncomingUser(int userId) {
|
||||
try {
|
||||
return ActivityManagerNative.getDefault().handleIncomingUser(
|
||||
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, true, "", null);
|
||||
} catch (RemoteException re) {
|
||||
// Shouldn't happen, local.
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
private boolean inSystemImage(int callingUid) {
|
||||
final int callingUserId = UserHandle.getUserId(callingUid);
|
||||
|
||||
|
@ -5652,7 +5652,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
null);
|
||||
|
||||
final int uid = Binder.getCallingUid();
|
||||
if (!isUserAllowed(uid, UserManager.ALLOW_INSTALL_APPS)) {
|
||||
if (!isUserAllowed(UserHandle.getUserId(uid), UserManager.ALLOW_INSTALL_APPS)) {
|
||||
try {
|
||||
observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);
|
||||
} catch (RemoteException re) {
|
||||
@ -5690,13 +5690,17 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
* @hide
|
||||
*/
|
||||
@Override
|
||||
public int installExistingPackage(String packageName) {
|
||||
public int installExistingPackageAsUser(String packageName, int userId) {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
|
||||
null);
|
||||
PackageSetting pkgSetting;
|
||||
final int uid = Binder.getCallingUid();
|
||||
final int userId = UserHandle.getUserId(uid);
|
||||
if (!isUserAllowed(uid, UserManager.ALLOW_INSTALL_APPS)) {
|
||||
if (UserHandle.getUserId(uid) != userId) {
|
||||
mContext.enforceCallingPermission(
|
||||
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
|
||||
"installExistingPackage for user " + userId);
|
||||
}
|
||||
if (!isUserAllowed(userId, UserManager.ALLOW_INSTALL_APPS)) {
|
||||
return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
|
||||
}
|
||||
|
||||
@ -5730,14 +5734,11 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
return PackageManager.INSTALL_SUCCEEDED;
|
||||
}
|
||||
|
||||
private boolean isUserAllowed(int callingUid, String restrictionKey) {
|
||||
if (callingUid != android.os.Process.myUid()) {
|
||||
Bundle restrictions = sUserManager.getUserRestrictions(
|
||||
UserHandle.getUserId(callingUid));
|
||||
if (!restrictions.getBoolean(UserManager.ALLOW_INSTALL_APPS)) {
|
||||
Log.w(TAG, "User does not have permission to: " + restrictionKey);
|
||||
return false;
|
||||
}
|
||||
private boolean isUserAllowed(int userId, String restrictionKey) {
|
||||
Bundle restrictions = sUserManager.getUserRestrictions(userId);
|
||||
if (!restrictions.getBoolean(UserManager.ALLOW_INSTALL_APPS)) {
|
||||
Log.w(TAG, "User does not have permission to: " + restrictionKey);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -8090,14 +8091,19 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
return tmpPackageFile;
|
||||
}
|
||||
|
||||
public void deletePackage(final String packageName,
|
||||
final IPackageDeleteObserver observer,
|
||||
final int flags) {
|
||||
@Override
|
||||
public void deletePackageAsUser(final String packageName,
|
||||
final IPackageDeleteObserver observer,
|
||||
final int userId, final int flags) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.DELETE_PACKAGES, null);
|
||||
// Queue up an async operation since the package deletion may take a little while.
|
||||
final int uid = Binder.getCallingUid();
|
||||
if (!isUserAllowed(uid, UserManager.ALLOW_UNINSTALL_APPS)) {
|
||||
if (UserHandle.getUserId(uid) != userId) {
|
||||
mContext.enforceCallingPermission(
|
||||
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
|
||||
"deletePackage for user " + userId);
|
||||
}
|
||||
if (!isUserAllowed(userId, UserManager.ALLOW_UNINSTALL_APPS)) {
|
||||
try {
|
||||
observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_USER_RESTRICTED);
|
||||
} catch (RemoteException re) {
|
||||
@ -8105,10 +8111,11 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue up an async operation since the package deletion may take a little while.
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
mHandler.removeCallbacks(this);
|
||||
final int returnCode = deletePackageX(packageName, uid, flags);
|
||||
final int returnCode = deletePackageX(packageName, userId, flags);
|
||||
if (observer != null) {
|
||||
try {
|
||||
observer.packageDeleted(packageName, returnCode);
|
||||
@ -8134,14 +8141,14 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
* persisting settings for later use
|
||||
* sending a broadcast if necessary
|
||||
*/
|
||||
private int deletePackageX(String packageName, int uid, int flags) {
|
||||
private int deletePackageX(String packageName, int userId, int flags) {
|
||||
final PackageRemovedInfo info = new PackageRemovedInfo();
|
||||
final boolean res;
|
||||
|
||||
IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
|
||||
try {
|
||||
if (dpm != null && dpm.packageHasActiveAdmins(packageName, UserHandle.getUserId(uid))) {
|
||||
if (dpm != null && dpm.packageHasActiveAdmins(packageName, userId)) {
|
||||
Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
|
||||
return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
|
||||
}
|
||||
@ -8153,7 +8160,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
synchronized (mInstallLock) {
|
||||
res = deletePackageLI(packageName,
|
||||
(flags & PackageManager.DELETE_ALL_USERS) != 0
|
||||
? UserHandle.ALL : new UserHandle(UserHandle.getUserId(uid)),
|
||||
? UserHandle.ALL : new UserHandle(userId),
|
||||
true, flags | REMOVE_CHATTY, info, true);
|
||||
systemUpdate = info.isRemovedPackageSystemUpdate;
|
||||
if (res && !systemUpdate && mPackages.get(packageName) == null) {
|
||||
@ -8376,7 +8383,7 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
|
||||
return false;
|
||||
}
|
||||
if (!isSystemApp(ps) && user != null
|
||||
if (user != null
|
||||
&& user.getIdentifier() != UserHandle.USER_ALL) {
|
||||
// The caller is asking that the package only be deleted for a single
|
||||
// user. To do this, we just mark its uninstalled state and delete
|
||||
@ -8387,17 +8394,27 @@ public class PackageManagerService extends IPackageManager.Stub {
|
||||
true, //stopped
|
||||
true, //notLaunched
|
||||
null, null);
|
||||
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
|
||||
// Other user still have this package installed, so all
|
||||
if (!isSystemApp(ps)) {
|
||||
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
|
||||
// Other user still have this package installed, so all
|
||||
// we need to do is clear this user's data and save that
|
||||
// it is uninstalled.
|
||||
removeUser = user.getIdentifier();
|
||||
appId = ps.appId;
|
||||
mSettings.writePackageRestrictionsLPr(removeUser);
|
||||
} else {
|
||||
// We need to set it back to 'installed' so the uninstall
|
||||
// broadcasts will be sent correctly.
|
||||
ps.setInstalled(true, user.getIdentifier());
|
||||
}
|
||||
} else {
|
||||
// This is a system app, so we assume that the
|
||||
// other users still have this package installed, so all
|
||||
// we need to do is clear this user's data and save that
|
||||
// it is uninstalled.
|
||||
removeUser = user.getIdentifier();
|
||||
appId = ps.appId;
|
||||
mSettings.writePackageRestrictionsLPr(removeUser);
|
||||
} else {
|
||||
// We need to set it back to 'installed' so the uninstall
|
||||
// broadcasts will be sent correctly.
|
||||
ps.setInstalled(true, user.getIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -542,16 +542,16 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
|
||||
private void fallbackToSingleUserLocked() {
|
||||
// Create the primary user
|
||||
UserInfo primary = new UserInfo(0,
|
||||
UserInfo primary = new UserInfo(UserHandle.USER_OWNER,
|
||||
mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
|
||||
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
|
||||
mUsers.put(0, primary);
|
||||
mNextSerialNumber = MIN_USER_ID;
|
||||
|
||||
|
||||
Bundle restrictions = new Bundle();
|
||||
initRestrictionsToDefaults(restrictions);
|
||||
mUserRestrictions.append(0, restrictions);
|
||||
|
||||
mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);
|
||||
|
||||
updateUserIdsLocked();
|
||||
|
||||
writeUserListLocked();
|
||||
|
Reference in New Issue
Block a user