diff options
author | Jeff Sharkey <jsharkey@android.com> | 2012-10-09 23:47:00 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-10-09 23:47:01 -0700 |
commit | c85e16cafb777607e6c38e1843c5fa194b8086b3 (patch) | |
tree | 0f6c8bc32607a0305ac89a805736681c3b52b6b3 /core | |
parent | 0aaffb14c8e243433bab1a1bd5773005f0f17699 (diff) | |
parent | 6ab72d74b86e5f4ec3c3909366fd46c225a66bd7 (diff) | |
download | frameworks_base-c85e16cafb777607e6c38e1843c5fa194b8086b3.zip frameworks_base-c85e16cafb777607e6c38e1843c5fa194b8086b3.tar.gz frameworks_base-c85e16cafb777607e6c38e1843c5fa194b8086b3.tar.bz2 |
Merge "Make RegisteredServicesCache multi-user aware." into jb-mr1-dev
Diffstat (limited to 'core')
9 files changed, 379 insertions, 333 deletions
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java index 7214c50..f937cde 100644 --- a/core/java/android/accounts/AccountAuthenticatorCache.java +++ b/core/java/android/accounts/AccountAuthenticatorCache.java @@ -16,17 +16,18 @@ package android.accounts; +import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.RegisteredServicesCache; import android.content.pm.XmlSerializerAndParser; import android.content.res.Resources; import android.content.res.TypedArray; -import android.content.Context; -import android.util.AttributeSet; import android.text.TextUtils; +import android.util.AttributeSet; + import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; import java.io.IOException; diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index fc569e0..cc90b86 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -18,7 +18,7 @@ package android.accounts; import android.Manifest; import android.app.ActivityManager; -import android.app.AppGlobals; +import android.app.ActivityManagerNative; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -32,10 +32,9 @@ import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.RegisteredServicesCache; import android.content.pm.RegisteredServicesCacheListener; -import android.content.pm.UserInfo; -import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; @@ -59,6 +58,8 @@ import android.util.SparseArray; import com.android.internal.R; import com.android.internal.util.IndentingPrintWriter; +import com.google.android.collect.Lists; +import com.google.android.collect.Sets; import java.io.File; import java.io.FileDescriptor; @@ -67,8 +68,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -243,8 +244,7 @@ public class AccountManagerService } public void systemReady() { - mAuthenticatorCache.generateServicesMap(); - initUser(0); + initUser(UserHandle.USER_OWNER); } private UserManager getUserManager() { @@ -300,6 +300,14 @@ public class AccountManagerService } private void validateAccountsAndPopulateCache(UserAccounts accounts) { + mAuthenticatorCache.invalidateCache(accounts.userId); + + final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet(); + for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service : + mAuthenticatorCache.getAllServices(accounts.userId)) { + knownAuth.add(service.type); + } + synchronized (accounts.cacheLock) { final SQLiteDatabase db = accounts.openHelper.getWritableDatabase(); boolean accountDeleted = false; @@ -314,8 +322,8 @@ public class AccountManagerService final long accountId = cursor.getLong(0); final String accountType = cursor.getString(1); final String accountName = cursor.getString(2); - if (mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(accountType)) == null) { + + if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) { Log.d(TAG, "deleting account " + accountName + " because type " + accountType + " no longer has a registered authenticator"); db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null); @@ -390,20 +398,9 @@ public class AccountManagerService } } - private List<UserInfo> getAllUsers() { - return getUserManager().getUsers(); - } - - public void onServiceChanged(AuthenticatorDescription desc, boolean removed) { - // Validate accounts for all users - List<UserInfo> users = getAllUsers(); - if (users == null) { - validateAccountsAndPopulateCache(getUserAccountsForCaller()); - } else { - for (UserInfo user : users) { - validateAccountsAndPopulateCache(getUserAccounts(user.id)); - } - } + @Override + public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) { + validateAccountsAndPopulateCache(getUserAccounts(userId)); } public String getPassword(Account account) { @@ -470,10 +467,11 @@ public class AccountManagerService + "caller's uid " + Binder.getCallingUid() + ", pid " + Binder.getCallingPid()); } - long identityToken = clearCallingIdentity(); + final int userId = UserHandle.getCallingUserId(); + final long identityToken = clearCallingIdentity(); try { Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> - authenticatorCollection = mAuthenticatorCache.getAllServices(); + authenticatorCollection = mAuthenticatorCache.getAllServices(userId); AuthenticatorDescription[] types = new AuthenticatorDescription[authenticatorCollection.size()]; int i = 0; @@ -1055,9 +1053,9 @@ public class AccountManagerService if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); checkBinderPermission(Manifest.permission.USE_CREDENTIALS); final UserAccounts accounts = getUserAccountsForCaller(); - AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = - mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(account.type)); + final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; + authenticatorInfo = mAuthenticatorCache.getServiceInfo( + AuthenticatorDescription.newKey(account.type), accounts.userId); final boolean customTokens = authenticatorInfo != null && authenticatorInfo.type.customTokens; @@ -1074,7 +1072,7 @@ public class AccountManagerService if (notifyOnAuthFailure) { loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); } - + long identityToken = clearCallingIdentity(); try { // if the caller has permission, do the peek. otherwise go the more expensive @@ -1183,28 +1181,6 @@ public class AccountManagerService account, authTokenType, uid), n, user); } - String getAccountLabel(String accountType) { - RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo = - mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(accountType)); - if (serviceInfo == null) { - throw new IllegalArgumentException("unknown account type: " + accountType); - } - - final Context authContext; - try { - authContext = mContext.createPackageContext( - serviceInfo.type.packageName, 0); - } catch (PackageManager.NameNotFoundException e) { - throw new IllegalArgumentException("unknown account type: " + accountType); - } - try { - return authContext.getString(serviceInfo.type.labelId); - } catch (Resources.NotFoundException e) { - throw new IllegalArgumentException("unknown account type: " + accountType); - } - } - private Intent newGrantCredentialsPermissionIntent(Account account, int uid, AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) { @@ -1506,28 +1482,35 @@ public class AccountManagerService } /** - * Returns all the accounts qualified by user. + * Returns accounts for all running users. + * * @hide */ - public AccountAndUser[] getAllAccounts() { - ArrayList<AccountAndUser> allAccounts = new ArrayList<AccountAndUser>(); - List<UserInfo> users = getAllUsers(); - if (users == null) return new AccountAndUser[0]; - - synchronized(mUsers) { - for (UserInfo user : users) { - UserAccounts userAccounts = getUserAccounts(user.id); + public AccountAndUser[] getRunningAccounts() { + final int[] runningUserIds; + try { + runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds(); + } catch (RemoteException e) { + // Running in system_server; should never happen + throw new RuntimeException(e); + } + + final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList(); + synchronized (mUsers) { + for (int userId : runningUserIds) { + UserAccounts userAccounts = getUserAccounts(userId); if (userAccounts == null) continue; synchronized (userAccounts.cacheLock) { Account[] accounts = getAccountsFromCacheLocked(userAccounts, null); for (int a = 0; a < accounts.length; a++) { - allAccounts.add(new AccountAndUser(accounts[a], user.id)); + runningAccounts.add(new AccountAndUser(accounts[a], userId)); } } } } - AccountAndUser[] accountsArray = new AccountAndUser[allAccounts.size()]; - return allAccounts.toArray(accountsArray); + + AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()]; + return runningAccounts.toArray(accountsArray); } public Account[] getAccounts(String type) { @@ -1836,9 +1819,9 @@ public class AccountManagerService * if no authenticator or the bind fails then return false, otherwise return true */ private boolean bindToAuthenticator(String authenticatorType) { - AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = - mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(authenticatorType)); + final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo; + authenticatorInfo = mAuthenticatorCache.getServiceInfo( + AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId); if (authenticatorInfo == null) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "there is no authenticator for " + authenticatorType @@ -2083,7 +2066,7 @@ public class AccountManagerService } fout.println(); - mAuthenticatorCache.dump(fd, fout, args); + mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId); } } } @@ -2154,11 +2137,21 @@ public class AccountManagerService throw new SecurityException(msg); } - private boolean inSystemImage(int callerUid) { - String[] packages = mPackageManager.getPackagesForUid(callerUid); + private boolean inSystemImage(int callingUid) { + final int callingUserId = UserHandle.getUserId(callingUid); + + final PackageManager userPackageManager; + try { + userPackageManager = mContext.createPackageContextAsUser( + "android", 0, new UserHandle(callingUserId)).getPackageManager(); + } catch (NameNotFoundException e) { + return false; + } + + String[] packages = userPackageManager.getPackagesForUid(callingUid); for (String name : packages) { try { - PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */); + PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */); if (packageInfo != null && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { return true; @@ -2186,8 +2179,9 @@ public class AccountManagerService } private boolean hasAuthenticatorUid(String accountType, int callingUid) { + final int callingUserId = UserHandle.getUserId(callingUid); for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : - mAuthenticatorCache.getAllServices()) { + mAuthenticatorCache.getAllServices(callingUserId)) { if (serviceInfo.type.type.equals(accountType)) { return (serviceInfo.uid == callingUid) || (mPackageManager.checkSignatures(serviceInfo.uid, callingUid) diff --git a/core/java/android/accounts/IAccountAuthenticatorCache.java b/core/java/android/accounts/IAccountAuthenticatorCache.java index 20dd585..06c2106 100644 --- a/core/java/android/accounts/IAccountAuthenticatorCache.java +++ b/core/java/android/accounts/IAccountAuthenticatorCache.java @@ -39,18 +39,19 @@ public interface IAccountAuthenticatorCache { * matches the account type or null if none is present */ RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> getServiceInfo( - AuthenticatorDescription type); + AuthenticatorDescription type, int userId); /** * @return A copy of a Collection of all the current Authenticators. */ - Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices(); + Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices( + int userId); /** * Dumps the state of the cache. See * {@link android.os.Binder#dump(java.io.FileDescriptor, java.io.PrintWriter, String[])} */ - void dump(FileDescriptor fd, PrintWriter fout, String[] args); + void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId); /** * Sets a listener that will be notified whenever the authenticator set changes @@ -61,8 +62,5 @@ public interface IAccountAuthenticatorCache { void setListener(RegisteredServicesCacheListener<AuthenticatorDescription> listener, Handler handler); - /** - * Refreshes the authenticator cache. - */ - void generateServicesMap(); -}
\ No newline at end of file + void invalidateCache(int userId); +} diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java index 0f6488a..4512e82 100644 --- a/core/java/android/content/ContentService.java +++ b/core/java/android/content/ContentService.java @@ -345,10 +345,11 @@ public final class ContentService extends IContentService.Stub { public SyncAdapterType[] getSyncAdapterTypes() { // This makes it so that future permission checks will be in the context of this // process rather than the caller's process. We will restore this before returning. - long identityToken = clearCallingIdentity(); + final int userId = UserHandle.getCallingUserId(); + final long identityToken = clearCallingIdentity(); try { SyncManager syncManager = getSyncManager(); - return syncManager.getSyncAdapterTypes(); + return syncManager.getSyncAdapterTypes(userId); } finally { restoreCallingIdentity(identityToken); } diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 564a804..053cc6f 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -20,8 +20,8 @@ import android.accounts.Account; import android.accounts.AccountAndUser; import android.accounts.AccountManager; import android.accounts.AccountManagerService; -import android.accounts.OnAccountsUpdateListener; import android.app.ActivityManager; +import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; @@ -58,6 +58,7 @@ import android.util.Log; import android.util.Pair; import com.android.internal.R; +import com.android.internal.util.IndentingPrintWriter; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import com.google.android.collect.Sets; @@ -81,7 +82,7 @@ import java.util.concurrent.CountDownLatch; /** * @hide */ -public class SyncManager implements OnAccountsUpdateListener { +public class SyncManager { private static final String TAG = "SyncManager"; /** Delay a sync due to local changes this long. In milliseconds */ @@ -141,7 +142,8 @@ public class SyncManager implements OnAccountsUpdateListener { private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0]; - private volatile AccountAndUser[] mAccounts = INITIAL_ACCOUNTS_ARRAY; + // TODO: add better locking around mRunningAccounts + private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY; volatile private PowerManager.WakeLock mHandleAlarmWakeLock; volatile private PowerManager.WakeLock mSyncManagerWakeLock; @@ -205,7 +207,10 @@ public class SyncManager implements OnAccountsUpdateListener { private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { - onAccountsUpdated(null); + updateRunningAccounts(); + + // Kick off sync for everyone, since this was a radical account change + scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */, false); } }; @@ -242,33 +247,14 @@ public class SyncManager implements OnAccountsUpdateListener { return found; } - public void onAccountsUpdated(Account[] accounts) { - // remember if this was the first time this was called after an update - final boolean justBootedUp = mAccounts == INITIAL_ACCOUNTS_ARRAY; - - List<UserInfo> users = getAllUsers(); - if (users == null) return; - - int count = 0; - - // Get accounts from AccountManager for all the users on the system - // TODO: Limit this to active users, when such a concept exists. - AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts(); - for (UserInfo user : users) { - if (mBootCompleted) { - Account[] accountsForUser = - AccountManagerService.getSingleton().getAccounts(user.id); - mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id); - } - } - - mAccounts = allAccounts; + public void updateRunningAccounts() { + mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts(); for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { - if (!containsAccountAndUser(allAccounts, + if (!containsAccountAndUser(mRunningAccounts, currentSyncContext.mSyncOperation.account, currentSyncContext.mSyncOperation.userId)) { - Log.d(TAG, "canceling sync since the account has been removed"); + Log.d(TAG, "canceling sync since the account is no longer running"); sendSyncFinishedOrCanceledMessage(currentSyncContext, null /* no result since this is a cancel */); } @@ -277,26 +263,6 @@ public class SyncManager implements OnAccountsUpdateListener { // we must do this since we don't bother scheduling alarms when // the accounts are not set yet sendCheckAlarmsMessage(); - - if (allAccounts.length > 0) { - // If this is the first time this was called after a bootup then - // the accounts haven't really changed, instead they were just loaded - // from the AccountManager. Otherwise at least one of the accounts - // has a change. - // - // If there was a real account change then force a sync of all accounts. - // This is a bit of overkill, but at least it will end up retrying syncs - // that failed due to an authentication failure and thus will recover if the - // account change was a password update. - // - // If this was the bootup case then don't sync everything, instead only - // sync those that have an unknown syncable state, which will give them - // a chance to set their syncable state. - - boolean onlyThoseWithUnkownSyncableState = justBootedUp; - scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */, - onlyThoseWithUnkownSyncableState); - } } private BroadcastReceiver mConnectivityIntentReceiver = @@ -336,19 +302,18 @@ public class SyncManager implements OnAccountsUpdateListener { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId == UserHandle.USER_NULL) return; + if (Intent.ACTION_USER_REMOVED.equals(action)) { - Log.i(TAG, "User removed - cleanup: u" + userId); - onUserRemoved(intent); - } else if (Intent.ACTION_USER_STARTED.equals(action)) { - Log.i(TAG, "User started - check alarms: u" + userId); - sendCheckAlarmsMessage(); - } else if (Intent.ACTION_USER_STOPPED.equals(action)) { - Log.i(TAG, "User stopped - stop syncs: u" + userId); - cancelActiveSync( - null /* any account */, - userId, - null /* any authority */); + Log.i(TAG, "User removed: u" + userId); + onUserRemoved(userId); + } else if (Intent.ACTION_USER_STARTING.equals(action)) { + Log.i(TAG, "User starting: u" + userId); + onUserStarting(userId); + } else if (Intent.ACTION_USER_STOPPING.equals(action)) { + Log.i(TAG, "User stopping: u" + userId); + onUserStopping(userId); } } }; @@ -390,7 +355,8 @@ public class SyncManager implements OnAccountsUpdateListener { mSyncHandler = new SyncHandler(syncThread.getLooper()); mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() { - public void onServiceChanged(SyncAdapterType type, boolean removed) { + @Override + public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) { if (!removed) { scheduleSync(null, UserHandle.USER_ALL, type.authority, null, 0 /* no delay */, false /* onlyThoseWithUnkownSyncableState */); @@ -422,7 +388,8 @@ public class SyncManager implements OnAccountsUpdateListener { intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_REMOVED); - intentFilter.addAction(Intent.ACTION_USER_STARTED); + intentFilter.addAction(Intent.ACTION_USER_STARTING); + intentFilter.addAction(Intent.ACTION_USER_STOPPING); mContext.registerReceiverAsUser( mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); @@ -467,8 +434,9 @@ public class SyncManager implements OnAccountsUpdateListener { UserHandle.ALL, new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION), null, null); + // do this synchronously to ensure we have the accounts before this call returns - onAccountsUpdated(null); + onUserStarting(UserHandle.USER_OWNER); } // Pick a random second in a day to seed all periodic syncs @@ -548,7 +516,7 @@ public class SyncManager implements OnAccountsUpdateListener { } else { // if the accounts aren't configured yet then we can't support an account-less // sync request - accounts = mAccounts; + accounts = mRunningAccounts; if (accounts.length == 0) { if (isLoggable) { Log.v(TAG, "scheduleSync: no accounts configured, dropping"); @@ -579,32 +547,33 @@ public class SyncManager implements OnAccountsUpdateListener { source = SyncStorageEngine.SOURCE_SERVER; } - // Compile a list of authorities that have sync adapters. - // For each authority sync each account that matches a sync adapter. - final HashSet<String> syncableAuthorities = new HashSet<String>(); - for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : - mSyncAdapters.getAllServices()) { - syncableAuthorities.add(syncAdapter.type.authority); - } + for (AccountAndUser account : accounts) { + // Compile a list of authorities that have sync adapters. + // For each authority sync each account that matches a sync adapter. + final HashSet<String> syncableAuthorities = new HashSet<String>(); + for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter : + mSyncAdapters.getAllServices(account.userId)) { + syncableAuthorities.add(syncAdapter.type.authority); + } - // if the url was specified then replace the list of authorities with just this authority - // or clear it if this authority isn't syncable - if (requestedAuthority != null) { - final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); - syncableAuthorities.clear(); - if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); - } + // if the url was specified then replace the list of authorities + // with just this authority or clear it if this authority isn't + // syncable + if (requestedAuthority != null) { + final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority); + syncableAuthorities.clear(); + if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority); + } - for (String authority : syncableAuthorities) { - for (AccountAndUser account : accounts) { + for (String authority : syncableAuthorities) { int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId, authority); if (isSyncable == 0) { continue; } - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = - mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(authority, account.account.type)); + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(authority, account.account.type), account.userId); if (syncAdapterInfo == null) { continue; } @@ -681,10 +650,9 @@ public class SyncManager implements OnAccountsUpdateListener { false /* onlyThoseWithUnkownSyncableState */); } - public SyncAdapterType[] getSyncAdapterTypes() { - final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> - serviceInfos = - mSyncAdapters.getAllServices(); + public SyncAdapterType[] getSyncAdapterTypes(int userId) { + final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos; + serviceInfos = mSyncAdapters.getAllServices(userId); SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()]; int i = 0; for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) { @@ -920,16 +888,39 @@ public class SyncManager implements OnAccountsUpdateListener { } } - private void onUserRemoved(Intent intent) { - int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (userId == -1) return; - removeUser(userId); + private void onUserStarting(int userId) { + mSyncAdapters.invalidateCache(userId); + + updateRunningAccounts(); + + final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId); + mSyncStorageEngine.doDatabaseCleanup(accounts, userId); + + mSyncQueue.addPendingOperations(userId); + + // Schedule sync for any accounts under started user + for (Account account : accounts) { + scheduleSync(account, userId, null, null, 0 /* no delay */, + true /* onlyThoseWithUnknownSyncableState */); + } + + sendCheckAlarmsMessage(); + } + + private void onUserStopping(int userId) { + updateRunningAccounts(); + + cancelActiveSync( + null /* any account */, + userId, + null /* any authority */); } - private void removeUser(int userId) { + private void onUserRemoved(int userId) { + updateRunningAccounts(); + // Clean up the storage engine database mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId); - onAccountsUpdated(null); synchronized (mSyncQueue) { mSyncQueue.removeUser(userId); } @@ -1062,14 +1053,10 @@ public class SyncManager implements OnAccountsUpdateListener { } protected void dump(FileDescriptor fd, PrintWriter pw) { - dumpSyncState(pw); - dumpSyncHistory(pw); - - pw.println(); - pw.println("SyncAdapters:"); - for (RegisteredServicesCache.ServiceInfo info : mSyncAdapters.getAllServices()) { - pw.println(" " + info); - } + final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + dumpSyncState(ipw); + dumpSyncHistory(ipw); + dumpSyncAdapters(ipw); } static String formatTime(long time) { @@ -1085,15 +1072,15 @@ public class SyncManager implements OnAccountsUpdateListener { if (users != null) { for (UserInfo user : users) { pw.print("u" + user.id + "=" - + mSyncStorageEngine.getMasterSyncAutomatically(user.id)); + + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " "); } pw.println(); } pw.print("memory low: "); pw.println(mStorageIsLow); - final AccountAndUser[] accounts = mAccounts; + final AccountAndUser[] accounts = mRunningAccounts; - pw.print("accounts: "); + pw.print("running accounts: "); if (accounts != INITIAL_ACCOUNTS_ARRAY) { pw.println(accounts.length); } else { @@ -1153,7 +1140,7 @@ public class SyncManager implements OnAccountsUpdateListener { pw.print(" "); pw.print(account.account.type); pw.println(":"); for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType : - mSyncAdapters.getAllServices()) { + mSyncAdapters.getAllServices(account.userId)) { if (!syncAdapterType.type.accountType.equals(account.account.type)) { continue; } @@ -1530,6 +1517,23 @@ public class SyncManager implements OnAccountsUpdateListener { } } + private void dumpSyncAdapters(IndentingPrintWriter pw) { + pw.println(); + final List<UserInfo> users = getAllUsers(); + if (users != null) { + for (UserInfo user : users) { + pw.println("Sync adapters for " + user + ":"); + pw.increaseIndent(); + for (RegisteredServicesCache.ServiceInfo<?> info : + mSyncAdapters.getAllServices(user.id)) { + pw.println(info); + } + pw.decreaseIndent(); + pw.println(); + } + } + } + private static class AuthoritySyncStats { String name; long elapsedTime; @@ -1613,18 +1617,10 @@ public class SyncManager implements OnAccountsUpdateListener { Maps.newHashMap(); private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1); + public void onBootCompleted() { mBootCompleted = true; - // TODO: Handle bootcompleted event for specific user. Now let's just iterate through - // all the users. - List<UserInfo> users = getAllUsers(); - if (users != null) { - for (UserInfo user : users) { - mSyncStorageEngine.doDatabaseCleanup( - AccountManagerService.getSingleton().getAccounts(user.id), - user.id); - } - } + if (mReadyToRunLatch != null) { mReadyToRunLatch.countDown(); } @@ -1814,7 +1810,7 @@ public class SyncManager implements OnAccountsUpdateListener { return earliestFuturePollTime; } - AccountAndUser[] accounts = mAccounts; + AccountAndUser[] accounts = mRunningAccounts; final long nowAbsolute = System.currentTimeMillis(); final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis) @@ -1869,9 +1865,10 @@ public class SyncManager implements OnAccountsUpdateListener { // Sync now final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( info.account, info.userId, info.authority); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = - mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(info.authority, info.account.type)); + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(info.authority, info.account.type), + info.userId); if (syncAdapterInfo == null) { continue; } @@ -1927,7 +1924,7 @@ public class SyncManager implements OnAccountsUpdateListener { // If the accounts aren't known yet then we aren't ready to run. We will be kicked // when the account lookup request does complete. - AccountAndUser[] accounts = mAccounts; + AccountAndUser[] accounts = mRunningAccounts; if (accounts == INITIAL_ACCOUNTS_ARRAY) { if (isLoggable) { Log.v(TAG, "maybeStartNextSync: accounts not known, skipping"); @@ -1998,7 +1995,7 @@ public class SyncManager implements OnAccountsUpdateListener { final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; syncAdapterInfo = mSyncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type)); + SyncAdapterType.newKey(op.authority, op.account.type), op.userId); // only proceed if network is connected for requesting UID final boolean uidNetworkConnected; @@ -2030,7 +2027,7 @@ public class SyncManager implements OnAccountsUpdateListener { for (Integer user : removedUsers) { // if it's still removed if (mUserManager.getUserInfo(user) == null) { - removeUser(user); + onUserRemoved(user); } } } @@ -2167,8 +2164,8 @@ public class SyncManager implements OnAccountsUpdateListener { // connect to the sync adapter SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type); - RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = - mSyncAdapters.getServiceInfo(syncAdapterType); + final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo; + syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId); if (syncAdapterInfo == null) { Log.d(TAG, "can't find a sync adapter for " + syncAdapterType + ", removing settings for it"); diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java index c18c86b..395658c 100644 --- a/core/java/android/content/SyncQueue.java +++ b/core/java/android/content/SyncQueue.java @@ -16,14 +16,15 @@ package android.content; -import com.google.android.collect.Maps; - -import android.content.pm.RegisteredServicesCache; +import android.accounts.Account; +import android.content.pm.RegisteredServicesCache.ServiceInfo; import android.os.SystemClock; +import android.os.UserHandle; import android.text.format.DateUtils; -import android.util.Pair; import android.util.Log; -import android.accounts.Account; +import android.util.Pair; + +import com.google.android.collect.Maps; import java.util.ArrayList; import java.util.HashMap; @@ -36,7 +37,9 @@ import java.util.Map; */ public class SyncQueue { private static final String TAG = "SyncManager"; - private SyncStorageEngine mSyncStorageEngine; + + private final SyncStorageEngine mSyncStorageEngine; + private final SyncAdaptersCache mSyncAdapters; // A Map of SyncOperations operationKey -> SyncOperation that is designed for // quick lookup of an enqueued SyncOperation. @@ -44,23 +47,28 @@ public class SyncQueue { public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) { mSyncStorageEngine = syncStorageEngine; - ArrayList<SyncStorageEngine.PendingOperation> ops - = mSyncStorageEngine.getPendingOperations(); - final int N = ops.size(); - for (int i=0; i<N; i++) { - SyncStorageEngine.PendingOperation op = ops.get(i); - final Pair<Long, Long> backoff = - syncStorageEngine.getBackoff(op.account, op.userId, op.authority); - final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo = - syncAdapters.getServiceInfo( - SyncAdapterType.newKey(op.authority, op.account.type)); + mSyncAdapters = syncAdapters; + + addPendingOperations(UserHandle.USER_OWNER); + } + + public void addPendingOperations(int userId) { + for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) { + if (op.userId != userId) continue; + + final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff( + op.account, op.userId, op.authority); + final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo( + SyncAdapterType.newKey(op.authority, op.account.type), op.userId); if (syncAdapterInfo == null) { + Log.w(TAG, "Missing sync adapter info for authority " + op.authority + ", userId " + + op.userId); continue; } SyncOperation syncOperation = new SyncOperation( op.account, op.userId, op.syncSource, op.authority, op.extras, 0 /* delay */, backoff != null ? backoff.first : 0, - syncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), + mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority), syncAdapterInfo.type.allowParallelSyncs()); syncOperation.expedited = op.expedited; syncOperation.pendingOperation = op; diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 7642670..0b91786 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -16,49 +16,54 @@ package android.content.pm; -import android.content.Context; import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.ComponentName; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.os.Environment; import android.os.Handler; +import android.os.UserHandle; import android.util.AtomicFile; -import android.util.Log; import android.util.AttributeSet; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; import android.util.Xml; -import java.util.Map; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicReference; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.io.IOException; -import java.io.FileInputStream; - import com.android.internal.util.FastXmlSerializer; - -import com.google.android.collect.Maps; import com.google.android.collect.Lists; +import com.google.android.collect.Maps; -import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + /** - * A cache of registered services. This cache - * is built by interrogating the {@link PackageManager} and is updated as packages are added, - * removed and changed. The services are referred to by type V and - * are made available via the {@link #getServiceInfo} method. + * Cache of registered services. This cache is lazily built by interrogating + * {@link PackageManager} on a per-user basis. It's updated as packages are + * added, removed and changed. Users are responsible for calling + * {@link #invalidateCache(int)} when a user is started, since + * {@link PackageManager} broadcasts aren't sent for stopped users. + * <p> + * The services are referred to by type V and are made available via the + * {@link #getServiceInfo} method. + * * @hide */ public abstract class RegisteredServicesCache<V> { @@ -69,15 +74,29 @@ public abstract class RegisteredServicesCache<V> { private final String mMetaDataName; private final String mAttributesName; private final XmlSerializerAndParser<V> mSerializerAndParser; - private final AtomicReference<BroadcastReceiver> mReceiver; private final Object mServicesLock = new Object(); - // synchronized on mServicesLock - private HashMap<V, Integer> mPersistentServices; - // synchronized on mServicesLock - private Map<V, ServiceInfo<V>> mServices; - // synchronized on mServicesLock + + // @GuardedBy("mServicesLock") private boolean mPersistentServicesFileDidNotExist; + // @GuardedBy("mServicesLock") + private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(); + + private static class UserServices<V> { + // @GuardedBy("mServicesLock") + public final Map<V, Integer> persistentServices = Maps.newHashMap(); + // @GuardedBy("mServicesLock") + public Map<V, ServiceInfo<V>> services = null; + } + + private UserServices<V> findOrCreateUserLocked(int userId) { + UserServices<V> services = mUserServices.get(userId); + if (services == null) { + services = new UserServices<V>(); + mUserServices.put(userId, services); + } + return services; + } /** * This file contains the list of known services. We would like to maintain this forever @@ -102,36 +121,59 @@ public abstract class RegisteredServicesCache<V> { File syncDir = new File(systemDir, "registered_services"); mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml")); - generateServicesMap(); + // Load persisted services from disk + readPersistentServicesLocked(); - final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context1, Intent intent) { - generateServicesMap(); - } - }; - mReceiver = new AtomicReference<BroadcastReceiver>(receiver); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); - mContext.registerReceiver(receiver, intentFilter); + mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null); + // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - mContext.registerReceiver(receiver, sdFilter); + mContext.registerReceiver(mExternalReceiver, sdFilter); } - public void dump(FileDescriptor fd, PrintWriter fout, String[] args) { - Map<V, ServiceInfo<V>> services; + private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (uid != -1) { + generateServicesMap(UserHandle.getUserId(uid)); + } + } + }; + + private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // External apps can't coexist with multi-user, so scan owner + generateServicesMap(UserHandle.USER_OWNER); + } + }; + + public void invalidateCache(int userId) { synchronized (mServicesLock) { - services = mServices; + final UserServices<V> user = findOrCreateUserLocked(userId); + user.services = null; } - fout.println("RegisteredServicesCache: " + services.size() + " services"); - for (ServiceInfo info : services.values()) { - fout.println(" " + info); + } + + public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) { + synchronized (mServicesLock) { + final UserServices<V> user = findOrCreateUserLocked(userId); + if (user.services != null) { + fout.println("RegisteredServicesCache: " + user.services.size() + " services"); + for (ServiceInfo<?> info : user.services.values()) { + fout.println(" " + info); + } + } else { + fout.println("RegisteredServicesCache: services not loaded"); + } } } @@ -151,7 +193,7 @@ public abstract class RegisteredServicesCache<V> { } } - private void notifyListener(final V type, final boolean removed) { + private void notifyListener(final V type, final int userId, final boolean removed) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added")); } @@ -168,7 +210,7 @@ public abstract class RegisteredServicesCache<V> { final RegisteredServicesCacheListener<V> listener2 = listener; handler.post(new Runnable() { public void run() { - listener2.onServiceChanged(type, removed); + listener2.onServiceChanged(type, userId, removed); } }); } @@ -200,9 +242,14 @@ public abstract class RegisteredServicesCache<V> { * @param type the account type of the authenticator * @return the AuthenticatorInfo that matches the account type or null if none is present */ - public ServiceInfo<V> getServiceInfo(V type) { + public ServiceInfo<V> getServiceInfo(V type, int userId) { synchronized (mServicesLock) { - return mServices.get(type); + // Find user and lazily populate cache + final UserServices<V> user = findOrCreateUserLocked(userId); + if (user.services == null) { + generateServicesMap(userId); + } + return user.services.get(type); } } @@ -210,29 +257,15 @@ public abstract class RegisteredServicesCache<V> { * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all * registered authenticators. */ - public Collection<ServiceInfo<V>> getAllServices() { + public Collection<ServiceInfo<V>> getAllServices(int userId) { synchronized (mServicesLock) { - return Collections.unmodifiableCollection(mServices.values()); - } - } - - /** - * Stops the monitoring of package additions, removals and changes. - */ - public void close() { - final BroadcastReceiver receiver = mReceiver.getAndSet(null); - if (receiver != null) { - mContext.unregisterReceiver(receiver); - } - } - - @Override - protected void finalize() throws Throwable { - if (mReceiver.get() != null) { - Log.e(TAG, "RegisteredServicesCache finalized without being closed"); + // Find user and lazily populate cache + final UserServices<V> user = findOrCreateUserLocked(userId); + if (user.services == null) { + generateServicesMap(userId); + } + return Collections.unmodifiableCollection(user.services.values()); } - close(); - super.finalize(); } private boolean inSystemImage(int callerUid) { @@ -251,11 +284,17 @@ public abstract class RegisteredServicesCache<V> { return false; } - public void generateServicesMap() { - PackageManager pm = mContext.getPackageManager(); - ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>(); - List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName), - PackageManager.GET_META_DATA); + /** + * Populate {@link UserServices#services} by scanning installed packages for + * given {@link UserHandle}. + */ + private void generateServicesMap(int userId) { + Slog.d(TAG, "generateServicesMap() for " + userId); + + final PackageManager pm = mContext.getPackageManager(); + final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>(); + final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser( + new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId); for (ResolveInfo resolveInfo : resolveInfos) { try { ServiceInfo<V> info = parseServiceInfo(resolveInfo); @@ -272,10 +311,14 @@ public abstract class RegisteredServicesCache<V> { } synchronized (mServicesLock) { - if (mPersistentServices == null) { - readPersistentServicesLocked(); + final UserServices<V> user = findOrCreateUserLocked(userId); + final boolean firstScan = user.services == null; + if (firstScan) { + user.services = Maps.newHashMap(); + } else { + user.services.clear(); } - mServices = Maps.newHashMap(); + StringBuilder changes = new StringBuilder(); for (ServiceInfo<V> info : serviceInfos) { // four cases: @@ -287,19 +330,19 @@ public abstract class RegisteredServicesCache<V> { // - ignore // - exists, the UID is different, and the new one is a system package // - add, notify user that it was added - Integer previousUid = mPersistentServices.get(info.type); + Integer previousUid = user.persistentServices.get(info.type); if (previousUid == null) { changes.append(" New service added: ").append(info).append("\n"); - mServices.put(info.type, info); - mPersistentServices.put(info.type, info.uid); - if (!mPersistentServicesFileDidNotExist) { - notifyListener(info.type, false /* removed */); + user.services.put(info.type, info); + user.persistentServices.put(info.type, info.uid); + if (!(mPersistentServicesFileDidNotExist && firstScan)) { + notifyListener(info.type, userId, false /* removed */); } } else if (previousUid == info.uid) { if (Log.isLoggable(TAG, Log.VERBOSE)) { changes.append(" Existing service (nop): ").append(info).append("\n"); } - mServices.put(info.type, info); + user.services.put(info.type, info); } else if (inSystemImage(info.uid) || !containsTypeAndUid(serviceInfos, info.type, previousUid)) { if (inSystemImage(info.uid)) { @@ -309,9 +352,9 @@ public abstract class RegisteredServicesCache<V> { changes.append(" Existing service replacing a removed service: ") .append(info).append("\n"); } - mServices.put(info.type, info); - mPersistentServices.put(info.type, info.uid); - notifyListener(info.type, false /* removed */); + user.services.put(info.type, info); + user.persistentServices.put(info.type, info.uid); + notifyListener(info.type, userId, false /* removed */); } else { // ignore changes.append(" Existing service with new uid ignored: ").append(info) @@ -320,15 +363,15 @@ public abstract class RegisteredServicesCache<V> { } ArrayList<V> toBeRemoved = Lists.newArrayList(); - for (V v1 : mPersistentServices.keySet()) { + for (V v1 : user.persistentServices.keySet()) { if (!containsType(serviceInfos, v1)) { toBeRemoved.add(v1); } } for (V v1 : toBeRemoved) { - mPersistentServices.remove(v1); + user.persistentServices.remove(v1); changes.append(" Service removed: ").append(v1).append("\n"); - notifyListener(v1, true /* removed */); + notifyListener(v1, userId, true /* removed */); } if (changes.length() > 0) { if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -342,7 +385,6 @@ public abstract class RegisteredServicesCache<V> { serviceInfos.size() + " services unchanged"); } } - mPersistentServicesFileDidNotExist = false; } } @@ -415,7 +457,7 @@ public abstract class RegisteredServicesCache<V> { * Read all sync status back in to the initial engine state. */ private void readPersistentServicesLocked() { - mPersistentServices = Maps.newHashMap(); + mUserServices.clear(); if (mSerializerAndParser == null) { return; } @@ -444,8 +486,10 @@ public abstract class RegisteredServicesCache<V> { break; } String uidString = parser.getAttributeValue(null, "uid"); - int uid = Integer.parseInt(uidString); - mPersistentServices.put(service, uid); + final int uid = Integer.parseInt(uidString); + final int userId = UserHandle.getUserId(uid); + final UserServices<V> user = findOrCreateUserLocked(userId); + user.persistentServices.put(service, uid); } } eventType = parser.next(); @@ -478,11 +522,14 @@ public abstract class RegisteredServicesCache<V> { out.startDocument(null, true); out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "services"); - for (Map.Entry<V, Integer> service : mPersistentServices.entrySet()) { - out.startTag(null, "service"); - out.attribute(null, "uid", Integer.toString(service.getValue())); - mSerializerAndParser.writeAsXml(service.getKey(), out); - out.endTag(null, "service"); + for (int i = 0; i < mUserServices.size(); i++) { + final UserServices<V> user = mUserServices.valueAt(i); + for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) { + out.startTag(null, "service"); + out.attribute(null, "uid", Integer.toString(service.getValue())); + mSerializerAndParser.writeAsXml(service.getKey(), out); + out.endTag(null, "service"); + } } out.endTag(null, "services"); out.endDocument(); diff --git a/core/java/android/content/pm/RegisteredServicesCacheListener.java b/core/java/android/content/pm/RegisteredServicesCacheListener.java index 7095229..df79544 100644 --- a/core/java/android/content/pm/RegisteredServicesCacheListener.java +++ b/core/java/android/content/pm/RegisteredServicesCacheListener.java @@ -16,8 +16,6 @@ package android.content.pm; -import android.os.Parcelable; - /** * Listener for changes to the set of registered services managed by a RegisteredServicesCache. * @hide @@ -28,5 +26,5 @@ public interface RegisteredServicesCacheListener<V> { * @param type the type of registered service * @param removed true if the service was removed */ - void onServiceChanged(V type, boolean removed); + void onServiceChanged(V type, int userId, boolean removed); } diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java index 1d7576f..fd4454d 100644 --- a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java +++ b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java @@ -197,7 +197,9 @@ public class AccountManagerServiceTest extends AndroidTestCase { mServices.add(new ServiceInfo<AuthenticatorDescription>(d2, null, 0)); } - public ServiceInfo<AuthenticatorDescription> getServiceInfo(AuthenticatorDescription type) { + @Override + public ServiceInfo<AuthenticatorDescription> getServiceInfo( + AuthenticatorDescription type, int userId) { for (ServiceInfo<AuthenticatorDescription> service : mServices) { if (service.type.equals(type)) { return service; @@ -206,21 +208,21 @@ public class AccountManagerServiceTest extends AndroidTestCase { return null; } - public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices() { + @Override + public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices(int userId) { return mServices; } - public void dump(final FileDescriptor fd, final PrintWriter fout, final String[] args) { + @Override + public void dump( + final FileDescriptor fd, final PrintWriter fout, final String[] args, int userId) { } + @Override public void setListener( final RegisteredServicesCacheListener<AuthenticatorDescription> listener, final Handler handler) { } - - @Override - public void generateServicesMap() { - } } static public class MyMockContext extends MockContext { |