summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFyodor Kupolov <fkupolov@google.com>2015-02-13 17:05:10 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2015-02-13 17:05:11 +0000
commitbc26d2eafd6185a197a968b2dffde5871791cf0b (patch)
treed613c16057d307ae8451ca7207567a167f14f8b1
parentb25334ca3c2c4f792254bf8e628788d0b7c4775a (diff)
parent259e76153255e4d7c6d6c14af77936fe060e7038 (diff)
downloadframeworks_base-bc26d2eafd6185a197a968b2dffde5871791cf0b.zip
frameworks_base-bc26d2eafd6185a197a968b2dffde5871791cf0b.tar.gz
frameworks_base-bc26d2eafd6185a197a968b2dffde5871791cf0b.tar.bz2
Merge "RegisteredServicesCache now saves files to a user-specific dir"
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java243
-rw-r--r--core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java202
2 files changed, 315 insertions, 130 deletions
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index a386097..c2d2f65 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -27,6 +27,7 @@ import android.content.res.XmlResourceParser;
import android.os.Environment;
import android.os.Handler;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.AtomicFile;
import android.util.AttributeSet;
import android.util.Log;
@@ -47,9 +48,9 @@ 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.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
@@ -74,6 +75,7 @@ import libcore.io.IoUtils;
public abstract class RegisteredServicesCache<V> {
private static final String TAG = "PackageManager";
private static final boolean DEBUG = false;
+ protected static final String REGISTERED_SERVICES_DIR = "registered_services";
public final Context mContext;
private final String mInterfaceName;
@@ -84,57 +86,67 @@ public abstract class RegisteredServicesCache<V> {
private final Object mServicesLock = new Object();
@GuardedBy("mServicesLock")
- private boolean mPersistentServicesFileDidNotExist;
- @GuardedBy("mServicesLock")
private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
private static class UserServices<V> {
@GuardedBy("mServicesLock")
- public final Map<V, Integer> persistentServices = Maps.newHashMap();
+ final Map<V, Integer> persistentServices = Maps.newHashMap();
+ @GuardedBy("mServicesLock")
+ Map<V, ServiceInfo<V>> services = null;
@GuardedBy("mServicesLock")
- public Map<V, ServiceInfo<V>> services = null;
+ boolean mPersistentServicesFileDidNotExist = true;
}
+ @GuardedBy("mServicesLock")
private UserServices<V> findOrCreateUserLocked(int userId) {
+ return findOrCreateUserLocked(userId, true);
+ }
+
+ @GuardedBy("mServicesLock")
+ private UserServices<V> findOrCreateUserLocked(int userId, boolean loadFromFileIfNew) {
UserServices<V> services = mUserServices.get(userId);
if (services == null) {
services = new UserServices<V>();
mUserServices.put(userId, services);
+ if (loadFromFileIfNew && mSerializerAndParser != null) {
+ // Check if user exists and try loading data from file
+ // clear existing data if there was an error during migration
+ UserInfo user = getUser(userId);
+ if (user != null) {
+ AtomicFile file = createFileForUser(user.id);
+ if (file.getBaseFile().exists()) {
+ if (DEBUG) {
+ Slog.i(TAG, String.format("Loading u%s data from %s", user.id, file));
+ }
+ InputStream is = null;
+ try {
+ is = file.openRead();
+ readPersistentServicesLocked(is);
+ } catch (Exception e) {
+ Log.w(TAG, "Error reading persistent services for user " + user.id, e);
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ }
+ }
+ }
}
return services;
}
- /**
- * This file contains the list of known services. We would like to maintain this forever
- * so we store it as an XML file.
- */
- private final AtomicFile mPersistentServicesFile;
-
// the listener and handler are synchronized on "this" and must be updated together
private RegisteredServicesCacheListener<V> mListener;
private Handler mHandler;
public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
String attributeName, XmlSerializerAndParser<V> serializerAndParser) {
- this(context, interfaceName, metaDataName, attributeName, serializerAndParser,
- Environment.getDataDirectory());
- }
-
- @VisibleForTesting
- protected RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
- String attributeName, XmlSerializerAndParser<V> serializerAndParser, File dataDir) {
mContext = context;
mInterfaceName = interfaceName;
mMetaDataName = metaDataName;
mAttributesName = attributeName;
mSerializerAndParser = serializerAndParser;
- File systemDir = new File(dataDir, "system");
- File syncDir = new File(systemDir, "registered_services");
- mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
-
- // Load persisted services from disk
- readPersistentServicesLocked();
+ migrateIfNecessaryLocked();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -148,6 +160,11 @@ public abstract class RegisteredServicesCache<V> {
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mExternalReceiver, sdFilter);
+
+ // Register for user-related events
+ IntentFilter userFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mContext.registerReceiver(mUserRemovedReceiver, userFilter);
}
private final void handlePackageEvent(Intent intent, int userId) {
@@ -199,6 +216,17 @@ public abstract class RegisteredServicesCache<V> {
}
};
+ private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (DEBUG) {
+ Slog.d(TAG, "u" + userId + " removed - cleaning up");
+ }
+ onUserRemoved(userId);
+ }
+ };
+
public void invalidateCache(int userId) {
synchronized (mServicesLock) {
final UserServices<V> user = findOrCreateUserLocked(userId);
@@ -390,7 +418,7 @@ public abstract class RegisteredServicesCache<V> {
changed = true;
user.services.put(info.type, info);
user.persistentServices.put(info.type, info.uid);
- if (!(mPersistentServicesFileDidNotExist && firstScan)) {
+ if (!(user.mPersistentServicesFileDidNotExist && firstScan)) {
notifyListener(info.type, userId, false /* removed */);
}
} else if (previousUid == info.uid) {
@@ -460,7 +488,7 @@ public abstract class RegisteredServicesCache<V> {
}
}
if (changed) {
- writePersistentServicesLocked();
+ writePersistentServicesLocked(user, userId);
}
}
}
@@ -542,89 +570,152 @@ public abstract class RegisteredServicesCache<V> {
/**
* Read all sync status back in to the initial engine state.
*/
- private void readPersistentServicesLocked() {
- mUserServices.clear();
+ private void readPersistentServicesLocked(InputStream is)
+ throws XmlPullParserException, IOException {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(is, null);
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.START_TAG
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ eventType = parser.next();
+ }
+ String tagName = parser.getName();
+ if ("services".equals(tagName)) {
+ eventType = parser.next();
+ do {
+ if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
+ tagName = parser.getName();
+ if ("service".equals(tagName)) {
+ V service = mSerializerAndParser.createFromXml(parser);
+ if (service == null) {
+ break;
+ }
+ String uidString = parser.getAttributeValue(null, "uid");
+ final int uid = Integer.parseInt(uidString);
+ final int userId = UserHandle.getUserId(uid);
+ final UserServices<V> user = findOrCreateUserLocked(userId,
+ false /*loadFromFileIfNew*/) ;
+ user.persistentServices.put(service, uid);
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+ }
+ }
+
+ private void migrateIfNecessaryLocked() {
if (mSerializerAndParser == null) {
return;
}
- FileInputStream fis = null;
- try {
- mPersistentServicesFileDidNotExist = !mPersistentServicesFile.getBaseFile().exists();
- if (mPersistentServicesFileDidNotExist) {
- return;
- }
- fis = mPersistentServicesFile.openRead();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, null);
- int eventType = parser.getEventType();
- while (eventType != XmlPullParser.START_TAG
- && eventType != XmlPullParser.END_DOCUMENT) {
- eventType = parser.next();
- }
- String tagName = parser.getName();
- if ("services".equals(tagName)) {
- eventType = parser.next();
- do {
- if (eventType == XmlPullParser.START_TAG && parser.getDepth() == 2) {
- tagName = parser.getName();
- if ("service".equals(tagName)) {
- V service = mSerializerAndParser.createFromXml(parser);
- if (service == null) {
- break;
+ File systemDir = new File(getDataDirectory(), "system");
+ File syncDir = new File(systemDir, REGISTERED_SERVICES_DIR);
+ AtomicFile oldFile = new AtomicFile(new File(syncDir, mInterfaceName + ".xml"));
+ boolean oldFileExists = oldFile.getBaseFile().exists();
+
+ if (oldFileExists) {
+ File marker = new File(syncDir, mInterfaceName + ".xml.migrated");
+ // if not migrated, perform the migration and add a marker
+ if (!marker.exists()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Marker file " + marker + " does not exist - running migration");
+ }
+ InputStream is = null;
+ try {
+ is = oldFile.openRead();
+ mUserServices.clear();
+ readPersistentServicesLocked(is);
+ } catch (Exception e) {
+ Log.w(TAG, "Error reading persistent services, starting from scratch", e);
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ try {
+ for (UserInfo user : getUsers()) {
+ UserServices<V> userServices = mUserServices.get(user.id);
+ if (userServices != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "Migrating u" + user.id + " services "
+ + userServices.persistentServices);
}
- String uidString = parser.getAttributeValue(null, "uid");
- final int uid = Integer.parseInt(uidString);
- final int userId = UserHandle.getUserId(uid);
- final UserServices<V> user = findOrCreateUserLocked(userId);
- user.persistentServices.put(service, uid);
+ writePersistentServicesLocked(userServices, user.id);
}
}
- eventType = parser.next();
- } while (eventType != XmlPullParser.END_DOCUMENT);
+ marker.createNewFile();
+ } catch (Exception e) {
+ Log.w(TAG, "Migration failed", e);
+ }
+ // Migration is complete and we don't need to keep data for all users anymore,
+ // It will be loaded from a new location when requested
+ mUserServices.clear();
}
- } catch (Exception e) {
- Log.w(TAG, "Error reading persistent services, starting from scratch", e);
- } finally {
- IoUtils.closeQuietly(fis);
}
}
/**
- * Write all sync status to the sync status file.
+ * Writes services of a specified user to the file.
*/
- private void writePersistentServicesLocked() {
+ private void writePersistentServicesLocked(UserServices<V> user, int userId) {
if (mSerializerAndParser == null) {
return;
}
+ AtomicFile atomicFile = createFileForUser(userId);
FileOutputStream fos = null;
try {
- fos = mPersistentServicesFile.startWrite();
+ fos = atomicFile.startWrite();
XmlSerializer out = new FastXmlSerializer();
out.setOutput(fos, "utf-8");
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "services");
- 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");
- }
+ 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();
- mPersistentServicesFile.finishWrite(fos);
+ atomicFile.finishWrite(fos);
} catch (IOException e1) {
Log.w(TAG, "Error writing accounts", e1);
if (fos != null) {
- mPersistentServicesFile.failWrite(fos);
+ atomicFile.failWrite(fos);
}
}
}
@VisibleForTesting
+ protected void onUserRemoved(int userId) {
+ mUserServices.remove(userId);
+ }
+
+ @VisibleForTesting
+ protected List<UserInfo> getUsers() {
+ return UserManager.get(mContext).getUsers(true);
+ }
+
+ @VisibleForTesting
+ protected UserInfo getUser(int userId) {
+ return UserManager.get(mContext).getUserInfo(userId);
+ }
+
+ private AtomicFile createFileForUser(int userId) {
+ File userDir = getUserSystemDirectory(userId);
+ File userFile = new File(userDir, REGISTERED_SERVICES_DIR + "/" + mInterfaceName + ".xml");
+ return new AtomicFile(userFile);
+ }
+
+ @VisibleForTesting
+ protected File getUserSystemDirectory(int userId) {
+ return Environment.getUserSystemDirectory(userId);
+ }
+
+ @VisibleForTesting
+ protected File getDataDirectory() {
+ return Environment.getDataDirectory();
+ }
+
+ @VisibleForTesting
protected Map<V, Integer> getPersistentServices(int userId) {
return findOrCreateUserLocked(userId).persistentServices;
}
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index f9b69a0..32b4557 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -16,7 +16,6 @@
package android.content.pm;
-import android.content.Context;
import android.content.res.Resources;
import android.os.FileUtils;
import android.os.Parcel;
@@ -30,6 +29,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -44,6 +44,12 @@ import java.util.Set;
* Tests for {@link android.content.pm.RegisteredServicesCache}
*/
public class RegisteredServicesCacheTest extends AndroidTestCase {
+ private static final int U0 = 0;
+ private static final int U1 = 1;
+ private static final int UID1 = 1;
+ private static final int UID2 = 2;
+ // Represents UID of a system image process
+ private static final int SYSTEM_IMAGE_UID = 20;
private final ResolveInfo r1 = new ResolveInfo();
private final ResolveInfo r2 = new ResolveInfo();
@@ -51,6 +57,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
private final TestServiceType t2 = new TestServiceType("t2", "value2");
private File mDataDir;
private File mSyncDir;
+ private List<UserInfo> mUsers;
@Override
protected void setUp() throws Exception {
@@ -58,97 +65,152 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
File cacheDir = mContext.getCacheDir();
mDataDir = new File(cacheDir, "testServicesCache");
FileUtils.deleteContents(mDataDir);
- mSyncDir = new File(mDataDir, "system/registered_services");
+ mSyncDir = new File(mDataDir, "system/"+RegisteredServicesCache.REGISTERED_SERVICES_DIR);
mSyncDir.mkdirs();
+ mUsers = new ArrayList<>();
+ mUsers.add(new UserInfo(0, "Owner", UserInfo.FLAG_ADMIN));
+ mUsers.add(new UserInfo(1, "User1", 0));
}
public void testGetAllServicesHappyPath() {
- TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
- cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
- cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
- assertEquals(2, cache.getAllServicesSize(0));
- assertEquals(2, cache.getPersistentServicesSize(0));
- File file = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml");
- assertTrue("File should be created at " + file, file.length() > 0);
+ TestServicesCache cache = new TestServicesCache();
+ cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
+ cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
+ assertEquals(2, cache.getAllServicesSize(U0));
+ assertEquals(2, cache.getPersistentServicesSize(U0));
+ assertNotEmptyFileCreated(cache, U0);
// Make sure all services can be loaded from xml
- cache = new TestServicesCache(mContext, mDataDir);
- assertEquals(2, cache.getPersistentServicesSize(0));
+ cache = new TestServicesCache();
+ assertEquals(2, cache.getPersistentServicesSize(U0));
}
public void testGetAllServicesReplaceUid() {
- TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
- cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
- cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
- cache.getAllServices(0);
+ TestServicesCache cache = new TestServicesCache();
+ cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
+ cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
+ cache.getAllServices(U0);
// Invalidate cache and clear update query results
- cache.invalidateCache(0);
+ cache.invalidateCache(U0);
cache.clearServicesForQuerying();
- cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
- cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null,
- TestServicesCache.SYSTEM_IMAGE_UID));
+ cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
+ cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, SYSTEM_IMAGE_UID));
Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> allServices = cache
- .getAllServices(0);
+ .getAllServices(U0);
assertEquals(2, allServices.size());
Set<Integer> uids = new HashSet<>();
for (RegisteredServicesCache.ServiceInfo<TestServiceType> srv : allServices) {
uids.add(srv.uid);
}
assertTrue("UID must be updated to the new value",
- uids.contains(TestServicesCache.SYSTEM_IMAGE_UID));
- assertFalse("UID must be updated to the new value", uids.contains(2));
+ uids.contains(SYSTEM_IMAGE_UID));
+ assertFalse("UID must be updated to the new value", uids.contains(UID2));
}
public void testGetAllServicesServiceRemoved() {
- TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
- cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
- cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
- assertEquals(2, cache.getAllServicesSize(0));
- assertEquals(2, cache.getPersistentServicesSize(0));
+ TestServicesCache cache = new TestServicesCache();
+ cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
+ cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
+ assertEquals(2, cache.getAllServicesSize(U0));
+ assertEquals(2, cache.getPersistentServicesSize(U0));
// Re-read data from disk and verify services were saved
- cache = new TestServicesCache(mContext, mDataDir);
- assertEquals(2, cache.getPersistentServicesSize(0));
+ cache = new TestServicesCache();
+ assertEquals(2, cache.getPersistentServicesSize(U0));
// Now register only one service and verify that another one is removed
- cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
- assertEquals(1, cache.getAllServicesSize(0));
- assertEquals(1, cache.getPersistentServicesSize(0));
+ cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
+ assertEquals(1, cache.getAllServicesSize(U0));
+ assertEquals(1, cache.getPersistentServicesSize(U0));
}
public void testGetAllServicesMultiUser() {
- TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
- int u0 = 0;
- int u1 = 1;
- int pid1 = 1;
- cache.addServiceForQuerying(u0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null,
- pid1));
- int u1uid = UserHandle.getUid(u1, 0);
- cache.addServiceForQuerying(u1, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null,
- u1uid));
- assertEquals(u1, cache.getAllServicesSize(u0));
- assertEquals(u1, cache.getPersistentServicesSize(u0));
- assertEquals(u1, cache.getAllServicesSize(u1));
- assertEquals(u1, cache.getPersistentServicesSize(u1));
+ TestServicesCache cache = new TestServicesCache();
+ cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
+ int u1uid = UserHandle.getUid(U1, 0);
+ cache.addServiceForQuerying(U1, r2, newServiceInfo(t2, u1uid));
+ assertEquals(1, cache.getAllServicesSize(U0));
+ assertEquals(1, cache.getPersistentServicesSize(U0));
+ assertEquals(1, cache.getAllServicesSize(U1));
+ assertEquals(1, cache.getPersistentServicesSize(U1));
assertEquals("No services should be available for user 3", 0, cache.getAllServicesSize(3));
// Re-read data from disk and verify services were saved
- cache = new TestServicesCache(mContext, mDataDir);
- assertEquals(u1, cache.getPersistentServicesSize(u0));
- assertEquals(u1, cache.getPersistentServicesSize(u1));
+ cache = new TestServicesCache();
+ assertEquals(1, cache.getPersistentServicesSize(U0));
+ assertEquals(1, cache.getPersistentServicesSize(U1));
+ assertNotEmptyFileCreated(cache, U0);
+ assertNotEmptyFileCreated(cache, U1);
+ }
+
+ public void testOnRemove() {
+ TestServicesCache cache = new TestServicesCache();
+ cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
+ int u1uid = UserHandle.getUid(U1, 0);
+ cache.addServiceForQuerying(U1, r2, newServiceInfo(t2, u1uid));
+ assertEquals(1, cache.getAllServicesSize(U0));
+ assertEquals(1, cache.getAllServicesSize(U1));
+ // Simulate ACTION_USER_REMOVED
+ cache.onUserRemoved(U1);
+ // Make queryIntentServices(u1) return no results for U1
+ cache.clearServicesForQuerying();
+ assertEquals(1, cache.getAllServicesSize(U0));
+ assertEquals(0, cache.getAllServicesSize(U1));
+ }
+
+ public void testMigration() {
+ // Prepare "old" file for testing
+ String oldFile = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<services>\n"
+ + " <service uid=\"1\" type=\"type1\" value=\"value1\" />\n"
+ + " <service uid=\"100002\" type=\"type2\" value=\"value2\" />\n"
+ + "<services>\n";
+
+ File file = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml");
+ FileUtils.copyToFile(new ByteArrayInputStream(oldFile.getBytes()), file);
+
+ int u0 = 0;
+ int u1 = 1;
+ TestServicesCache cache = new TestServicesCache();
+ assertEquals(1, cache.getPersistentServicesSize(u0));
+ assertEquals(1, cache.getPersistentServicesSize(u1));
+ assertNotEmptyFileCreated(cache, u0);
+ assertNotEmptyFileCreated(cache, u1);
+ // Check that marker was created
+ File markerFile = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml.migrated");
+ assertTrue("Marker file should be created at " + markerFile, markerFile.exists());
+ // Now introduce 2 service types for u0: t1, t2. type1 will be removed
+ cache.addServiceForQuerying(0, r1, newServiceInfo(t1, 1));
+ cache.addServiceForQuerying(0, r2, newServiceInfo(t2, 2));
+ assertEquals(2, cache.getAllServicesSize(u0));
+ assertEquals(0, cache.getAllServicesSize(u1));
+ // Re-read data from disk. Verify that services were saved and old file was ignored
+ cache = new TestServicesCache();
+ assertEquals(2, cache.getPersistentServicesSize(u0));
+ assertEquals(0, cache.getPersistentServicesSize(u1));
+ }
+
+ private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
+ TestServiceType type, int uid) {
+ return new RegisteredServicesCache.ServiceInfo<>(type, null, uid);
+ }
+
+ private void assertNotEmptyFileCreated(TestServicesCache cache, int userId) {
+ File dir = new File(cache.getUserSystemDirectory(userId),
+ RegisteredServicesCache.REGISTERED_SERVICES_DIR);
+ File file = new File(dir, TestServicesCache.SERVICE_INTERFACE+".xml");
+ assertTrue("File should be created at " + file, file.length() > 0);
}
/**
* Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
*/
- private static class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
+ private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
static final String ATTRIBUTES_NAME = "test";
- // Represents UID of a system image process
- static final int SYSTEM_IMAGE_UID = 20;
private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices
= new SparseArray<>();
- public TestServicesCache(Context context, File dir) {
- super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
- new TestSerializer(), dir);
+ public TestServicesCache() {
+ super(RegisteredServicesCacheTest.this.mContext,
+ SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, new TestSerializer());
}
@Override
@@ -164,6 +226,33 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
return new ArrayList<>(map.keySet());
}
+ @Override
+ protected File getUserSystemDirectory(int userId) {
+ File dir = new File(mDataDir, "users/" + userId);
+ dir.mkdirs();
+ return dir;
+ }
+
+ @Override
+ protected List<UserInfo> getUsers() {
+ return mUsers;
+ }
+
+ @Override
+ protected UserInfo getUser(int userId) {
+ for (UserInfo user : getUsers()) {
+ if (user.id == userId) {
+ return user;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected File getDataDirectory() {
+ return mDataDir;
+ }
+
void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
ServiceInfo<TestServiceType> serviceInfo) {
Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
@@ -204,6 +293,11 @@ public class RegisteredServicesCacheTest extends AndroidTestCase {
}
throw new IllegalArgumentException("Unexpected service " + resolveInfo);
}
+
+ @Override
+ public void onUserRemoved(int userId) {
+ super.onUserRemoved(userId);
+ }
}
static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {