Merge "RegisteredServicesCache now saves files to a user-specific dir"

This commit is contained in:
Fyodor Kupolov
2015-02-13 17:05:10 +00:00
committed by Android (Google) Code Review
2 changed files with 315 additions and 130 deletions

View File

@@ -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;
@@ -83,58 +85,68 @@ 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")
public Map<V, ServiceInfo<V>> services = null;
Map<V, ServiceInfo<V>> services = null;
@GuardedBy("mServicesLock")
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,88 +570,151 @@ 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;

View File

@@ -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> {