19e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov/*
29e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * Copyright (C) 2015 The Android Open Source Project
39e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov *
49e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * Licensed under the Apache License, Version 2.0 (the "License");
59e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * you may not use this file except in compliance with the License.
69e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * You may obtain a copy of the License at
79e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov *
89e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov *      http://www.apache.org/licenses/LICENSE-2.0
99e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov *
109e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * Unless required by applicable law or agreed to in writing, software
119e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * distributed under the License is distributed on an "AS IS" BASIS,
129e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * See the License for the specific language governing permissions and
149e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * limitations under the License
159e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov */
169e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
179e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovpackage android.content.pm;
189e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
199e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.content.res.Resources;
209e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.os.FileUtils;
219e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.os.Parcel;
229e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.os.Parcelable;
239e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.os.UserHandle;
249e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.test.AndroidTestCase;
259e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.util.AttributeSet;
269e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.util.SparseArray;
279e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
289e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport org.xmlpull.v1.XmlPullParser;
299e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport org.xmlpull.v1.XmlPullParserException;
309e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport org.xmlpull.v1.XmlSerializer;
319e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
32259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolovimport java.io.ByteArrayInputStream;
339e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.io.File;
349e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.io.IOException;
359e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.util.ArrayList;
369e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.util.Collection;
379e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.util.HashMap;
389e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.util.HashSet;
399e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.util.List;
409e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.util.Map;
419e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport java.util.Set;
429e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
439e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov/**
449e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov * Tests for {@link android.content.pm.RegisteredServicesCache}
459e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov */
469e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovpublic class RegisteredServicesCacheTest extends AndroidTestCase {
47259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private static final int U0 = 0;
48259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private static final int U1 = 1;
49259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private static final int UID1 = 1;
50259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private static final int UID2 = 2;
51259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    // Represents UID of a system image process
52259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private static final int SYSTEM_IMAGE_UID = 20;
539e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
549e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private final ResolveInfo r1 = new ResolveInfo();
559e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private final ResolveInfo r2 = new ResolveInfo();
569e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private final TestServiceType t1 = new TestServiceType("t1", "value1");
579e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private final TestServiceType t2 = new TestServiceType("t2", "value2");
589e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private File mDataDir;
599e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private File mSyncDir;
60259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private List<UserInfo> mUsers;
619e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
629e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    @Override
639e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    protected void setUp() throws Exception {
649e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        super.setUp();
659e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        File cacheDir = mContext.getCacheDir();
669e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        mDataDir = new File(cacheDir, "testServicesCache");
679e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        FileUtils.deleteContents(mDataDir);
68259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        mSyncDir = new File(mDataDir, "system/"+RegisteredServicesCache.REGISTERED_SERVICES_DIR);
699e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        mSyncDir.mkdirs();
70259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        mUsers = new ArrayList<>();
71259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        mUsers.add(new UserInfo(0, "Owner", UserInfo.FLAG_ADMIN));
72259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        mUsers.add(new UserInfo(1, "User1", 0));
739e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
749e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
759e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    public void testGetAllServicesHappyPath() {
76259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        TestServicesCache cache = new TestServicesCache();
77259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
78259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
79259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(2, cache.getAllServicesSize(U0));
80259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(U0));
81259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertNotEmptyFileCreated(cache, U0);
829e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Make sure all services can be loaded from xml
83259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache = new TestServicesCache();
84259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(U0));
859e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
869e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
879e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    public void testGetAllServicesReplaceUid() {
88259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        TestServicesCache cache = new TestServicesCache();
89259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
90259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
91259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.getAllServices(U0);
929e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Invalidate cache and clear update query results
93259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.invalidateCache(U0);
949e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.clearServicesForQuerying();
95259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
96259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, SYSTEM_IMAGE_UID));
979e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> allServices = cache
98259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                .getAllServices(U0);
999e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(2, allServices.size());
1009e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        Set<Integer> uids = new HashSet<>();
1019e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        for (RegisteredServicesCache.ServiceInfo<TestServiceType> srv : allServices) {
1029e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            uids.add(srv.uid);
1039e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1049e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertTrue("UID must be updated to the new value",
105259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                uids.contains(SYSTEM_IMAGE_UID));
106259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertFalse("UID must be updated to the new value", uids.contains(UID2));
1079e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
1089e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1099e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    public void testGetAllServicesServiceRemoved() {
110259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        TestServicesCache cache = new TestServicesCache();
111259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
112259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
113259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(2, cache.getAllServicesSize(U0));
114259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(U0));
1159e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Re-read data from disk and verify services were saved
116259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache = new TestServicesCache();
117259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(U0));
1189e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Now register only one service and verify that another one is removed
119259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
120259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getAllServicesSize(U0));
121259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getPersistentServicesSize(U0));
1229e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
1239e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1249e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    public void testGetAllServicesMultiUser() {
125259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        TestServicesCache cache = new TestServicesCache();
126259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
127259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        int u1uid = UserHandle.getUid(U1, 0);
128259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U1, r2, newServiceInfo(t2, u1uid));
129259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getAllServicesSize(U0));
130259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getPersistentServicesSize(U0));
131259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getAllServicesSize(U1));
132259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getPersistentServicesSize(U1));
1339e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals("No services should be available for user 3", 0, cache.getAllServicesSize(3));
1349e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Re-read data from disk and verify services were saved
135259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache = new TestServicesCache();
136259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getPersistentServicesSize(U0));
137259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getPersistentServicesSize(U1));
138259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertNotEmptyFileCreated(cache, U0);
139259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertNotEmptyFileCreated(cache, U1);
140259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    }
141259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
142259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    public void testOnRemove() {
143259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        TestServicesCache cache = new TestServicesCache();
144259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
145259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        int u1uid = UserHandle.getUid(U1, 0);
146259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(U1, r2, newServiceInfo(t2, u1uid));
147259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getAllServicesSize(U0));
148259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getAllServicesSize(U1));
149259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        // Simulate ACTION_USER_REMOVED
150259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.onUserRemoved(U1);
151259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        // Make queryIntentServices(u1) return no results for U1
152259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.clearServicesForQuerying();
153259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getAllServicesSize(U0));
154259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(0, cache.getAllServicesSize(U1));
155259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    }
156259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
157259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    public void testMigration() {
158259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        // Prepare "old" file for testing
159259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        String oldFile = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
160259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                + "<services>\n"
161259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                + "    <service uid=\"1\" type=\"type1\" value=\"value1\" />\n"
162259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                + "    <service uid=\"100002\" type=\"type2\" value=\"value2\" />\n"
163259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                + "<services>\n";
164259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
165259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        File file = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml");
166259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        FileUtils.copyToFile(new ByteArrayInputStream(oldFile.getBytes()), file);
167259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
168259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        int u0 = 0;
169259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        int u1 = 1;
170259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        TestServicesCache cache = new TestServicesCache();
171259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getPersistentServicesSize(u0));
172259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(1, cache.getPersistentServicesSize(u1));
173259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertNotEmptyFileCreated(cache, u0);
174259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertNotEmptyFileCreated(cache, u1);
175259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        // Check that marker was created
176259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        File markerFile = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml.migrated");
177259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertTrue("Marker file should be created at " + markerFile, markerFile.exists());
178259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        // Now introduce 2 service types for u0: t1, t2. type1 will be removed
179259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(0, r1, newServiceInfo(t1, 1));
180259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache.addServiceForQuerying(0, r2, newServiceInfo(t2, 2));
181259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(2, cache.getAllServicesSize(u0));
182259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(0, cache.getAllServicesSize(u1));
183259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        // Re-read data from disk. Verify that services were saved and old file was ignored
184259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        cache = new TestServicesCache();
185259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(u0));
186259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertEquals(0, cache.getPersistentServicesSize(u1));
187259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    }
188259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
189259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
190259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            TestServiceType type, int uid) {
191259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        return new RegisteredServicesCache.ServiceInfo<>(type, null, uid);
192259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    }
193259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
194259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private void assertNotEmptyFileCreated(TestServicesCache cache, int userId) {
195259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        File dir = new File(cache.getUserSystemDirectory(userId),
196259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                RegisteredServicesCache.REGISTERED_SERVICES_DIR);
197259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        File file = new File(dir, TestServicesCache.SERVICE_INTERFACE+".xml");
198259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        assertTrue("File should be created at " + file, file.length() > 0);
1999e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
2009e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2019e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    /**
2029e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov     * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
2039e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov     */
204259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov    private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
2059e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
2069e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
2079e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        static final String ATTRIBUTES_NAME = "test";
2089e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices
2099e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                = new SparseArray<>();
2109e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
211259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        public TestServicesCache() {
212259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            super(RegisteredServicesCacheTest.this.mContext,
213259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                    SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, new TestSerializer());
2149e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2159e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2169e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
2179e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServiceType parseServiceAttributes(Resources res, String packageName,
2189e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                AttributeSet attrs) {
2199e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return null;
2209e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2219e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2229e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
2239e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        protected List<ResolveInfo> queryIntentServices(int userId) {
2249e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices
2259e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    .get(userId, new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
2269e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return new ArrayList<>(map.keySet());
2279e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2289e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
229259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        @Override
230259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        protected File getUserSystemDirectory(int userId) {
231259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            File dir = new File(mDataDir, "users/" + userId);
232259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            dir.mkdirs();
233259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            return dir;
234259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        }
235259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
236259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        @Override
237259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        protected List<UserInfo> getUsers() {
238259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            return mUsers;
239259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        }
240259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
241259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        @Override
242259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        protected UserInfo getUser(int userId) {
243259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            for (UserInfo user : getUsers()) {
244259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                if (user.id == userId) {
245259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                    return user;
246259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov                }
247259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            }
248259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            return null;
249259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        }
250259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
251259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        @Override
252259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        protected File getDataDirectory() {
253259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            return mDataDir;
254259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        }
255259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
2569e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
2579e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                ServiceInfo<TestServiceType> serviceInfo) {
2589e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
2599e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            if (map == null) {
2609e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                map = new HashMap<>();
2619e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                mServices.put(userId, map);
2629e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
2639e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            map.put(resolveInfo, serviceInfo);
2649e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2659e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2669e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        void clearServicesForQuerying() {
2679e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            mServices.clear();
2689e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2699e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2709e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        int getPersistentServicesSize(int user) {
2719e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return getPersistentServices(user).size();
2729e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2739e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2749e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        int getAllServicesSize(int user) {
2759e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return getAllServices(user).size();
2769e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2779e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2789e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
2799e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        protected boolean inSystemImage(int callerUid) {
2809e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return callerUid == SYSTEM_IMAGE_UID;
2819e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2829e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2839e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
2849e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        protected ServiceInfo<TestServiceType> parseServiceInfo(
2859e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                ResolveInfo resolveInfo) throws XmlPullParserException, IOException {
2869e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            int size = mServices.size();
2879e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            for (int i = 0; i < size; i++) {
2889e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
2899e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
2909e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                if (serviceInfo != null) {
2919e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    return serviceInfo;
2929e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                }
2939e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
2949e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            throw new IllegalArgumentException("Unexpected service " + resolveInfo);
2959e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
296259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov
297259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        @Override
298259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        public void onUserRemoved(int userId) {
299259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov            super.onUserRemoved(userId);
300259e76153255e4d7c6d6c14af77936fe060e7038Fyodor Kupolov        }
3019e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
3029e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3039e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
3049e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3059e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public void writeAsXml(TestServiceType item, XmlSerializer out) throws IOException {
3069e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            out.attribute(null, "type", item.type);
3079e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            out.attribute(null, "value", item.value);
3089e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3099e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3109e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServiceType createFromXml(XmlPullParser parser)
3119e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                throws IOException, XmlPullParserException {
3129e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            final String type = parser.getAttributeValue(null, "type");
3139e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            final String value = parser.getAttributeValue(null, "value");
3149e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return new TestServiceType(type, value);
3159e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3169e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
3179e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3189e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    static class TestServiceType implements Parcelable {
3199e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        final String type;
3209e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        final String value;
3219e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3229e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServiceType(String type, String value) {
3239e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            this.type = type;
3249e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            this.value = value;
3259e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3269e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3279e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
3289e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public boolean equals(Object o) {
3299e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            if (this == o) {
3309e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                return true;
3319e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
3329e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            if (o == null || getClass() != o.getClass()) {
3339e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                return false;
3349e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
3359e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3369e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            TestServiceType that = (TestServiceType) o;
3379e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3389e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return type.equals(that.type) && value.equals(that.value);
3399e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3409e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3419e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
3429e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public int hashCode() {
3439e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return 31 * type.hashCode() + value.hashCode();
3449e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3459e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3469e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
3479e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public String toString() {
3489e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return "TestServiceType{" +
3499e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    "type='" + type + '\'' +
3509e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    ", value='" + value + '\'' +
3519e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    '}';
3529e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3539e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3549e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public int describeContents() {
3559e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return 0;
3569e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3579e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3589e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public void writeToParcel(Parcel dest, int flags) {
3599e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            dest.writeString(type);
3609e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            dest.writeString(value);
3619e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3629e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3639e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServiceType(Parcel source) {
3649e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            this(source.readString(), source.readString());
3659e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
3669e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3679e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public static final Creator<TestServiceType> CREATOR = new Creator<TestServiceType>() {
3689e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            public TestServiceType createFromParcel(Parcel source) {
3699e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                return new TestServiceType(source);
3709e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
3719e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
3729e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            public TestServiceType[] newArray(int size) {
3739e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                return new TestServiceType[size];
3749e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
3759e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        };
3769e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
3779e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov}
378