RegisteredServicesCacheTest.java revision 9e0d81e8439089845b7ddee21676f3f427a34cca
1/*
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
19import android.content.Context;
20import android.content.res.Resources;
21import android.os.FileUtils;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.os.UserHandle;
25import 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
33import java.io.File;
34import java.io.IOException;
35import java.util.ArrayList;
36import java.util.Collection;
37import java.util.HashMap;
38import java.util.HashSet;
39import java.util.List;
40import java.util.Map;
41import java.util.Set;
42
43/**
44 * Tests for {@link android.content.pm.RegisteredServicesCache}
45 */
46public class RegisteredServicesCacheTest extends AndroidTestCase {
47
48    private final ResolveInfo r1 = new ResolveInfo();
49    private final ResolveInfo r2 = new ResolveInfo();
50    private final TestServiceType t1 = new TestServiceType("t1", "value1");
51    private final TestServiceType t2 = new TestServiceType("t2", "value2");
52    private File mDataDir;
53    private File mSyncDir;
54
55    @Override
56    protected void setUp() throws Exception {
57        super.setUp();
58        File cacheDir = mContext.getCacheDir();
59        mDataDir = new File(cacheDir, "testServicesCache");
60        FileUtils.deleteContents(mDataDir);
61        mSyncDir = new File(mDataDir, "system/registered_services");
62        mSyncDir.mkdirs();
63    }
64
65    public void testGetAllServicesHappyPath() {
66        TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
67        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
68        cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
69        assertEquals(2, cache.getAllServicesSize(0));
70        assertEquals(2, cache.getPersistentServicesSize(0));
71        File file = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml");
72        assertTrue("File should be created at " + file, file.length() > 0);
73        // Make sure all services can be loaded from xml
74        cache = new TestServicesCache(mContext, mDataDir);
75        assertEquals(2, cache.getPersistentServicesSize(0));
76    }
77
78    public void testGetAllServicesReplaceUid() {
79        TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
80        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
81        cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
82        cache.getAllServices(0);
83        // Invalidate cache and clear update query results
84        cache.invalidateCache(0);
85        cache.clearServicesForQuerying();
86        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
87        cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null,
88                TestServicesCache.SYSTEM_IMAGE_UID));
89        Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> allServices = cache
90                .getAllServices(0);
91        assertEquals(2, allServices.size());
92        Set<Integer> uids = new HashSet<>();
93        for (RegisteredServicesCache.ServiceInfo<TestServiceType> srv : allServices) {
94            uids.add(srv.uid);
95        }
96        assertTrue("UID must be updated to the new value",
97                uids.contains(TestServicesCache.SYSTEM_IMAGE_UID));
98        assertFalse("UID must be updated to the new value", uids.contains(2));
99    }
100
101    public void testGetAllServicesServiceRemoved() {
102        TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
103        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
104        cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
105        assertEquals(2, cache.getAllServicesSize(0));
106        assertEquals(2, cache.getPersistentServicesSize(0));
107        // Re-read data from disk and verify services were saved
108        cache = new TestServicesCache(mContext, mDataDir);
109        assertEquals(2, cache.getPersistentServicesSize(0));
110        // Now register only one service and verify that another one is removed
111        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
112        assertEquals(1, cache.getAllServicesSize(0));
113        assertEquals(1, cache.getPersistentServicesSize(0));
114    }
115
116    public void testGetAllServicesMultiUser() {
117        TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
118        int u0 = 0;
119        int u1 = 1;
120        int pid1 = 1;
121        cache.addServiceForQuerying(u0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null,
122                pid1));
123        int u1uid = UserHandle.getUid(u1, 0);
124        cache.addServiceForQuerying(u1, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null,
125                u1uid));
126        assertEquals(u1, cache.getAllServicesSize(u0));
127        assertEquals(u1, cache.getPersistentServicesSize(u0));
128        assertEquals(u1, cache.getAllServicesSize(u1));
129        assertEquals(u1, cache.getPersistentServicesSize(u1));
130        assertEquals("No services should be available for user 3", 0, cache.getAllServicesSize(3));
131        // Re-read data from disk and verify services were saved
132        cache = new TestServicesCache(mContext, mDataDir);
133        assertEquals(u1, cache.getPersistentServicesSize(u0));
134        assertEquals(u1, cache.getPersistentServicesSize(u1));
135    }
136
137    /**
138     * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
139     */
140    private static class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
141        static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
142        static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
143        static final String ATTRIBUTES_NAME = "test";
144        // Represents UID of a system image process
145        static final int SYSTEM_IMAGE_UID = 20;
146        private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices
147                = new SparseArray<>();
148
149        public TestServicesCache(Context context, File dir) {
150            super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
151                    new TestSerializer(), dir);
152        }
153
154        @Override
155        public TestServiceType parseServiceAttributes(Resources res, String packageName,
156                AttributeSet attrs) {
157            return null;
158        }
159
160        @Override
161        protected List<ResolveInfo> queryIntentServices(int userId) {
162            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices
163                    .get(userId, new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
164            return new ArrayList<>(map.keySet());
165        }
166
167        void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
168                ServiceInfo<TestServiceType> serviceInfo) {
169            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
170            if (map == null) {
171                map = new HashMap<>();
172                mServices.put(userId, map);
173            }
174            map.put(resolveInfo, serviceInfo);
175        }
176
177        void clearServicesForQuerying() {
178            mServices.clear();
179        }
180
181        int getPersistentServicesSize(int user) {
182            return getPersistentServices(user).size();
183        }
184
185        int getAllServicesSize(int user) {
186            return getAllServices(user).size();
187        }
188
189        @Override
190        protected boolean inSystemImage(int callerUid) {
191            return callerUid == SYSTEM_IMAGE_UID;
192        }
193
194        @Override
195        protected ServiceInfo<TestServiceType> parseServiceInfo(
196                ResolveInfo resolveInfo) throws XmlPullParserException, IOException {
197            int size = mServices.size();
198            for (int i = 0; i < size; i++) {
199                Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
200                ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
201                if (serviceInfo != null) {
202                    return serviceInfo;
203                }
204            }
205            throw new IllegalArgumentException("Unexpected service " + resolveInfo);
206        }
207    }
208
209    static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
210
211        public void writeAsXml(TestServiceType item, XmlSerializer out) throws IOException {
212            out.attribute(null, "type", item.type);
213            out.attribute(null, "value", item.value);
214        }
215
216        public TestServiceType createFromXml(XmlPullParser parser)
217                throws IOException, XmlPullParserException {
218            final String type = parser.getAttributeValue(null, "type");
219            final String value = parser.getAttributeValue(null, "value");
220            return new TestServiceType(type, value);
221        }
222    }
223
224    static class TestServiceType implements Parcelable {
225        final String type;
226        final String value;
227
228        public TestServiceType(String type, String value) {
229            this.type = type;
230            this.value = value;
231        }
232
233        @Override
234        public boolean equals(Object o) {
235            if (this == o) {
236                return true;
237            }
238            if (o == null || getClass() != o.getClass()) {
239                return false;
240            }
241
242            TestServiceType that = (TestServiceType) o;
243
244            return type.equals(that.type) && value.equals(that.value);
245        }
246
247        @Override
248        public int hashCode() {
249            return 31 * type.hashCode() + value.hashCode();
250        }
251
252        @Override
253        public String toString() {
254            return "TestServiceType{" +
255                    "type='" + type + '\'' +
256                    ", value='" + value + '\'' +
257                    '}';
258        }
259
260        public int describeContents() {
261            return 0;
262        }
263
264        public void writeToParcel(Parcel dest, int flags) {
265            dest.writeString(type);
266            dest.writeString(value);
267        }
268
269        public TestServiceType(Parcel source) {
270            this(source.readString(), source.readString());
271        }
272
273        public static final Creator<TestServiceType> CREATOR = new Creator<TestServiceType>() {
274            public TestServiceType createFromParcel(Parcel source) {
275                return new TestServiceType(source);
276            }
277
278            public TestServiceType[] newArray(int size) {
279                return new TestServiceType[size];
280            }
281        };
282    }
283}
284