blob: d3d1f22af3cb5747acd6c92fe14edbaa738a552c [file] [log] [blame]
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package android.content.pm;
18
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080019import android.content.res.Resources;
20import android.os.FileUtils;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.os.UserHandle;
Aurimas Liutikasbdbde552017-12-19 13:21:10 -080024import android.support.test.filters.LargeTest;
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080025import android.test.AndroidTestCase;
26import android.util.AttributeSet;
27import android.util.SparseArray;
28
29import org.xmlpull.v1.XmlPullParser;
30import org.xmlpull.v1.XmlPullParserException;
31import org.xmlpull.v1.XmlSerializer;
32
Fyodor Kupolov259e7612015-02-11 14:13:34 -080033import java.io.ByteArrayInputStream;
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080034import java.io.File;
35import java.io.IOException;
36import java.util.ArrayList;
37import java.util.Collection;
38import java.util.HashMap;
39import java.util.HashSet;
40import java.util.List;
41import java.util.Map;
42import java.util.Set;
43
44/**
45 * Tests for {@link android.content.pm.RegisteredServicesCache}
46 */
Aurimas Liutikasbdbde552017-12-19 13:21:10 -080047@LargeTest
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080048public class RegisteredServicesCacheTest extends AndroidTestCase {
Fyodor Kupolov259e7612015-02-11 14:13:34 -080049 private static final int U0 = 0;
50 private static final int U1 = 1;
51 private static final int UID1 = 1;
52 private static final int UID2 = 2;
53 // Represents UID of a system image process
54 private static final int SYSTEM_IMAGE_UID = 20;
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080055
56 private final ResolveInfo r1 = new ResolveInfo();
57 private final ResolveInfo r2 = new ResolveInfo();
58 private final TestServiceType t1 = new TestServiceType("t1", "value1");
59 private final TestServiceType t2 = new TestServiceType("t2", "value2");
60 private File mDataDir;
61 private File mSyncDir;
Fyodor Kupolov259e7612015-02-11 14:13:34 -080062 private List<UserInfo> mUsers;
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080063
64 @Override
65 protected void setUp() throws Exception {
66 super.setUp();
67 File cacheDir = mContext.getCacheDir();
68 mDataDir = new File(cacheDir, "testServicesCache");
69 FileUtils.deleteContents(mDataDir);
Fyodor Kupolov259e7612015-02-11 14:13:34 -080070 mSyncDir = new File(mDataDir, "system/"+RegisteredServicesCache.REGISTERED_SERVICES_DIR);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080071 mSyncDir.mkdirs();
Fyodor Kupolov259e7612015-02-11 14:13:34 -080072 mUsers = new ArrayList<>();
73 mUsers.add(new UserInfo(0, "Owner", UserInfo.FLAG_ADMIN));
74 mUsers.add(new UserInfo(1, "User1", 0));
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080075 }
76
77 public void testGetAllServicesHappyPath() {
Fyodor Kupolov259e7612015-02-11 14:13:34 -080078 TestServicesCache cache = new TestServicesCache();
79 cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
80 cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
81 assertEquals(2, cache.getAllServicesSize(U0));
82 assertEquals(2, cache.getPersistentServicesSize(U0));
83 assertNotEmptyFileCreated(cache, U0);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080084 // Make sure all services can be loaded from xml
Fyodor Kupolov259e7612015-02-11 14:13:34 -080085 cache = new TestServicesCache();
86 assertEquals(2, cache.getPersistentServicesSize(U0));
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080087 }
88
89 public void testGetAllServicesReplaceUid() {
Fyodor Kupolov259e7612015-02-11 14:13:34 -080090 TestServicesCache cache = new TestServicesCache();
91 cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
92 cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
93 cache.getAllServices(U0);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080094 // Invalidate cache and clear update query results
Fyodor Kupolov259e7612015-02-11 14:13:34 -080095 cache.invalidateCache(U0);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080096 cache.clearServicesForQuerying();
Fyodor Kupolov259e7612015-02-11 14:13:34 -080097 cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
98 cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, SYSTEM_IMAGE_UID));
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -080099 Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> allServices = cache
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800100 .getAllServices(U0);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800101 assertEquals(2, allServices.size());
102 Set<Integer> uids = new HashSet<>();
103 for (RegisteredServicesCache.ServiceInfo<TestServiceType> srv : allServices) {
104 uids.add(srv.uid);
105 }
106 assertTrue("UID must be updated to the new value",
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800107 uids.contains(SYSTEM_IMAGE_UID));
108 assertFalse("UID must be updated to the new value", uids.contains(UID2));
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800109 }
110
111 public void testGetAllServicesServiceRemoved() {
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800112 TestServicesCache cache = new TestServicesCache();
113 cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
114 cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
115 assertEquals(2, cache.getAllServicesSize(U0));
116 assertEquals(2, cache.getPersistentServicesSize(U0));
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800117 // Re-read data from disk and verify services were saved
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800118 cache = new TestServicesCache();
119 assertEquals(2, cache.getPersistentServicesSize(U0));
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800120 // Now register only one service and verify that another one is removed
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800121 cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
122 assertEquals(1, cache.getAllServicesSize(U0));
123 assertEquals(1, cache.getPersistentServicesSize(U0));
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800124 }
125
126 public void testGetAllServicesMultiUser() {
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800127 TestServicesCache cache = new TestServicesCache();
128 cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
129 int u1uid = UserHandle.getUid(U1, 0);
130 cache.addServiceForQuerying(U1, r2, newServiceInfo(t2, u1uid));
131 assertEquals(1, cache.getAllServicesSize(U0));
132 assertEquals(1, cache.getPersistentServicesSize(U0));
133 assertEquals(1, cache.getAllServicesSize(U1));
134 assertEquals(1, cache.getPersistentServicesSize(U1));
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800135 assertEquals("No services should be available for user 3", 0, cache.getAllServicesSize(3));
136 // Re-read data from disk and verify services were saved
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800137 cache = new TestServicesCache();
138 assertEquals(1, cache.getPersistentServicesSize(U0));
139 assertEquals(1, cache.getPersistentServicesSize(U1));
140 assertNotEmptyFileCreated(cache, U0);
141 assertNotEmptyFileCreated(cache, U1);
142 }
143
144 public void testOnRemove() {
145 TestServicesCache cache = new TestServicesCache();
146 cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
147 int u1uid = UserHandle.getUid(U1, 0);
148 cache.addServiceForQuerying(U1, r2, newServiceInfo(t2, u1uid));
149 assertEquals(1, cache.getAllServicesSize(U0));
150 assertEquals(1, cache.getAllServicesSize(U1));
151 // Simulate ACTION_USER_REMOVED
152 cache.onUserRemoved(U1);
153 // Make queryIntentServices(u1) return no results for U1
154 cache.clearServicesForQuerying();
155 assertEquals(1, cache.getAllServicesSize(U0));
156 assertEquals(0, cache.getAllServicesSize(U1));
157 }
158
159 public void testMigration() {
160 // Prepare "old" file for testing
161 String oldFile = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
162 + "<services>\n"
163 + " <service uid=\"1\" type=\"type1\" value=\"value1\" />\n"
164 + " <service uid=\"100002\" type=\"type2\" value=\"value2\" />\n"
165 + "<services>\n";
166
167 File file = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml");
168 FileUtils.copyToFile(new ByteArrayInputStream(oldFile.getBytes()), file);
169
170 int u0 = 0;
171 int u1 = 1;
172 TestServicesCache cache = new TestServicesCache();
173 assertEquals(1, cache.getPersistentServicesSize(u0));
174 assertEquals(1, cache.getPersistentServicesSize(u1));
175 assertNotEmptyFileCreated(cache, u0);
176 assertNotEmptyFileCreated(cache, u1);
177 // Check that marker was created
178 File markerFile = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml.migrated");
179 assertTrue("Marker file should be created at " + markerFile, markerFile.exists());
180 // Now introduce 2 service types for u0: t1, t2. type1 will be removed
181 cache.addServiceForQuerying(0, r1, newServiceInfo(t1, 1));
182 cache.addServiceForQuerying(0, r2, newServiceInfo(t2, 2));
183 assertEquals(2, cache.getAllServicesSize(u0));
184 assertEquals(0, cache.getAllServicesSize(u1));
185 // Re-read data from disk. Verify that services were saved and old file was ignored
186 cache = new TestServicesCache();
187 assertEquals(2, cache.getPersistentServicesSize(u0));
188 assertEquals(0, cache.getPersistentServicesSize(u1));
189 }
190
191 private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
192 TestServiceType type, int uid) {
Jeff Sharkey9d8a1042015-12-03 17:56:20 -0700193 final ComponentInfo info = new ComponentInfo();
194 info.applicationInfo = new ApplicationInfo();
195 info.applicationInfo.uid = uid;
196 return new RegisteredServicesCache.ServiceInfo<>(type, info, null);
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800197 }
198
199 private void assertNotEmptyFileCreated(TestServicesCache cache, int userId) {
200 File dir = new File(cache.getUserSystemDirectory(userId),
201 RegisteredServicesCache.REGISTERED_SERVICES_DIR);
202 File file = new File(dir, TestServicesCache.SERVICE_INTERFACE+".xml");
203 assertTrue("File should be created at " + file, file.length() > 0);
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800204 }
205
206 /**
207 * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
208 */
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800209 private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800210 static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
211 static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
212 static final String ATTRIBUTES_NAME = "test";
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800213 private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices
214 = new SparseArray<>();
215
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800216 public TestServicesCache() {
217 super(RegisteredServicesCacheTest.this.mContext,
218 SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, new TestSerializer());
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800219 }
220
221 @Override
222 public TestServiceType parseServiceAttributes(Resources res, String packageName,
223 AttributeSet attrs) {
224 return null;
225 }
226
227 @Override
228 protected List<ResolveInfo> queryIntentServices(int userId) {
229 Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices
230 .get(userId, new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
231 return new ArrayList<>(map.keySet());
232 }
233
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800234 @Override
235 protected File getUserSystemDirectory(int userId) {
236 File dir = new File(mDataDir, "users/" + userId);
237 dir.mkdirs();
238 return dir;
239 }
240
241 @Override
242 protected List<UserInfo> getUsers() {
243 return mUsers;
244 }
245
246 @Override
247 protected UserInfo getUser(int userId) {
248 for (UserInfo user : getUsers()) {
249 if (user.id == userId) {
250 return user;
251 }
252 }
253 return null;
254 }
255
256 @Override
257 protected File getDataDirectory() {
258 return mDataDir;
259 }
260
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800261 void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
262 ServiceInfo<TestServiceType> serviceInfo) {
263 Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
264 if (map == null) {
265 map = new HashMap<>();
266 mServices.put(userId, map);
267 }
268 map.put(resolveInfo, serviceInfo);
269 }
270
271 void clearServicesForQuerying() {
272 mServices.clear();
273 }
274
275 int getPersistentServicesSize(int user) {
276 return getPersistentServices(user).size();
277 }
278
279 int getAllServicesSize(int user) {
280 return getAllServices(user).size();
281 }
282
283 @Override
284 protected boolean inSystemImage(int callerUid) {
285 return callerUid == SYSTEM_IMAGE_UID;
286 }
287
288 @Override
289 protected ServiceInfo<TestServiceType> parseServiceInfo(
290 ResolveInfo resolveInfo) throws XmlPullParserException, IOException {
291 int size = mServices.size();
292 for (int i = 0; i < size; i++) {
293 Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
294 ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
295 if (serviceInfo != null) {
296 return serviceInfo;
297 }
298 }
299 throw new IllegalArgumentException("Unexpected service " + resolveInfo);
300 }
Fyodor Kupolov259e7612015-02-11 14:13:34 -0800301
302 @Override
303 public void onUserRemoved(int userId) {
304 super.onUserRemoved(userId);
305 }
Fyodor Kupolov9e0d81e2015-02-10 10:45:55 -0800306 }
307
308 static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
309
310 public void writeAsXml(TestServiceType item, XmlSerializer out) throws IOException {
311 out.attribute(null, "type", item.type);
312 out.attribute(null, "value", item.value);
313 }
314
315 public TestServiceType createFromXml(XmlPullParser parser)
316 throws IOException, XmlPullParserException {
317 final String type = parser.getAttributeValue(null, "type");
318 final String value = parser.getAttributeValue(null, "value");
319 return new TestServiceType(type, value);
320 }
321 }
322
323 static class TestServiceType implements Parcelable {
324 final String type;
325 final String value;
326
327 public TestServiceType(String type, String value) {
328 this.type = type;
329 this.value = value;
330 }
331
332 @Override
333 public boolean equals(Object o) {
334 if (this == o) {
335 return true;
336 }
337 if (o == null || getClass() != o.getClass()) {
338 return false;
339 }
340
341 TestServiceType that = (TestServiceType) o;
342
343 return type.equals(that.type) && value.equals(that.value);
344 }
345
346 @Override
347 public int hashCode() {
348 return 31 * type.hashCode() + value.hashCode();
349 }
350
351 @Override
352 public String toString() {
353 return "TestServiceType{" +
354 "type='" + type + '\'' +
355 ", value='" + value + '\'' +
356 '}';
357 }
358
359 public int describeContents() {
360 return 0;
361 }
362
363 public void writeToParcel(Parcel dest, int flags) {
364 dest.writeString(type);
365 dest.writeString(value);
366 }
367
368 public TestServiceType(Parcel source) {
369 this(source.readString(), source.readString());
370 }
371
372 public static final Creator<TestServiceType> CREATOR = new Creator<TestServiceType>() {
373 public TestServiceType createFromParcel(Parcel source) {
374 return new TestServiceType(source);
375 }
376
377 public TestServiceType[] newArray(int size) {
378 return new TestServiceType[size];
379 }
380 };
381 }
382}