diff options
author | Fyodor Kupolov <fkupolov@google.com> | 2015-02-13 17:05:10 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-02-13 17:05:11 +0000 |
commit | bc26d2eafd6185a197a968b2dffde5871791cf0b (patch) | |
tree | d613c16057d307ae8451ca7207567a167f14f8b1 | |
parent | b25334ca3c2c4f792254bf8e628788d0b7c4775a (diff) | |
parent | 259e76153255e4d7c6d6c14af77936fe060e7038 (diff) | |
download | frameworks_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.java | 243 | ||||
-rw-r--r-- | core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java | 202 |
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> { |