* commit '45ed828ae2f8030186da8aa9848764f4a9f32224': Make RegisteredServicesCache multi-user aware.
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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];
|
||||
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);
|
||||
}
|
||||
|
||||
synchronized(mUsers) {
|
||||
for (UserInfo user : users) {
|
||||
UserAccounts userAccounts = getUserAccounts(user.id);
|
||||
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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
void invalidateCache(int userId);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 removeUser(int userId) {
|
||||
private void onUserStopping(int userId) {
|
||||
updateRunningAccounts();
|
||||
|
||||
cancelActiveSync(
|
||||
null /* any account */,
|
||||
userId,
|
||||
null /* any authority */);
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
import android.content.pm.RegisteredServicesCache;
|
||||
import android.os.SystemClock;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Pair;
|
||||
import android.util.Log;
|
||||
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.Log;
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
synchronized (mServicesLock) {
|
||||
services = mServices;
|
||||
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));
|
||||
}
|
||||
}
|
||||
fout.println("RegisteredServicesCache: " + services.size() + " services");
|
||||
for (ServiceInfo info : services.values()) {
|
||||
fout.println(" " + info);
|
||||
};
|
||||
|
||||
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) {
|
||||
final UserServices<V> user = findOrCreateUserLocked(userId);
|
||||
user.services = null;
|
||||
}
|
||||
}
|
||||
|
||||
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,31 +257,17 @@ 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());
|
||||
// Find user and lazily populate cache
|
||||
final UserServices<V> user = findOrCreateUserLocked(userId);
|
||||
if (user.services == null) {
|
||||
generateServicesMap(userId);
|
||||
}
|
||||
return Collections.unmodifiableCollection(user.services.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");
|
||||
}
|
||||
close();
|
||||
super.finalize();
|
||||
}
|
||||
|
||||
private boolean inSystemImage(int callerUid) {
|
||||
String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
|
||||
for (String name : packages) {
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,20 +208,20 @@ 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) {
|
||||
}
|
||||
|
||||
public void setListener(
|
||||
final RegisteredServicesCacheListener<AuthenticatorDescription> listener,
|
||||
final Handler handler) {
|
||||
@Override
|
||||
public void dump(
|
||||
final FileDescriptor fd, final PrintWriter fout, final String[] args, int userId) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateServicesMap() {
|
||||
public void setListener(
|
||||
final RegisteredServicesCacheListener<AuthenticatorDescription> listener,
|
||||
final Handler handler) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user