diff options
Diffstat (limited to 'services/java')
3 files changed, 296 insertions, 46 deletions
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java index 2a62c17..49295f5 100644 --- a/services/java/com/android/server/accounts/AccountManagerService.java +++ b/services/java/com/android/server/accounts/AccountManagerService.java @@ -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) { @@ -1606,6 +1750,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); diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 7fb8902..cfb0f3f 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -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()); } } } diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index c3f4256..1414cbd 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -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(); |