RegisteredServicesCacheTest.java revision 9e0d81e8439089845b7ddee21676f3f427a34cca
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.Context;
209e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.content.res.Resources;
219e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.os.FileUtils;
229e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.os.Parcel;
239e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.os.Parcelable;
249e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.os.UserHandle;
259e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.test.AndroidTestCase;
269e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.util.AttributeSet;
279e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport android.util.SparseArray;
289e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
299e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport org.xmlpull.v1.XmlPullParser;
309e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport org.xmlpull.v1.XmlPullParserException;
319e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolovimport org.xmlpull.v1.XmlSerializer;
329e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
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 {
479e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
489e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private final ResolveInfo r1 = new ResolveInfo();
499e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private final ResolveInfo r2 = new ResolveInfo();
509e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private final TestServiceType t1 = new TestServiceType("t1", "value1");
519e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private final TestServiceType t2 = new TestServiceType("t2", "value2");
529e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private File mDataDir;
539e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private File mSyncDir;
549e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
559e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    @Override
569e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    protected void setUp() throws Exception {
579e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        super.setUp();
589e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        File cacheDir = mContext.getCacheDir();
599e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        mDataDir = new File(cacheDir, "testServicesCache");
609e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        FileUtils.deleteContents(mDataDir);
619e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        mSyncDir = new File(mDataDir, "system/registered_services");
629e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        mSyncDir.mkdirs();
639e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
649e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
659e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    public void testGetAllServicesHappyPath() {
669e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
679e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
689e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
699e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(2, cache.getAllServicesSize(0));
709e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(0));
719e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        File file = new File(mSyncDir, TestServicesCache.SERVICE_INTERFACE + ".xml");
729e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertTrue("File should be created at " + file, file.length() > 0);
739e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Make sure all services can be loaded from xml
749e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache = new TestServicesCache(mContext, mDataDir);
759e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(0));
769e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
779e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
789e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    public void testGetAllServicesReplaceUid() {
799e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
809e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
819e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
829e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.getAllServices(0);
839e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Invalidate cache and clear update query results
849e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.invalidateCache(0);
859e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.clearServicesForQuerying();
869e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
879e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null,
889e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                TestServicesCache.SYSTEM_IMAGE_UID));
899e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> allServices = cache
909e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                .getAllServices(0);
919e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(2, allServices.size());
929e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        Set<Integer> uids = new HashSet<>();
939e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        for (RegisteredServicesCache.ServiceInfo<TestServiceType> srv : allServices) {
949e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            uids.add(srv.uid);
959e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
969e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertTrue("UID must be updated to the new value",
979e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                uids.contains(TestServicesCache.SYSTEM_IMAGE_UID));
989e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertFalse("UID must be updated to the new value", uids.contains(2));
999e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
1009e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1019e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    public void testGetAllServicesServiceRemoved() {
1029e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
1039e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
1049e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null, 2));
1059e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(2, cache.getAllServicesSize(0));
1069e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(0));
1079e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Re-read data from disk and verify services were saved
1089e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache = new TestServicesCache(mContext, mDataDir);
1099e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(2, cache.getPersistentServicesSize(0));
1109e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Now register only one service and verify that another one is removed
1119e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null, 1));
1129e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(1, cache.getAllServicesSize(0));
1139e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(1, cache.getPersistentServicesSize(0));
1149e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
1159e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1169e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    public void testGetAllServicesMultiUser() {
1179e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        TestServicesCache cache = new TestServicesCache(mContext, mDataDir);
1189e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        int u0 = 0;
1199e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        int u1 = 1;
1209e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        int pid1 = 1;
1219e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(u0, r1, new RegisteredServicesCache.ServiceInfo<>(t1, null,
1229e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                pid1));
1239e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        int u1uid = UserHandle.getUid(u1, 0);
1249e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache.addServiceForQuerying(u1, r2, new RegisteredServicesCache.ServiceInfo<>(t2, null,
1259e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                u1uid));
1269e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(u1, cache.getAllServicesSize(u0));
1279e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(u1, cache.getPersistentServicesSize(u0));
1289e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(u1, cache.getAllServicesSize(u1));
1299e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(u1, cache.getPersistentServicesSize(u1));
1309e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals("No services should be available for user 3", 0, cache.getAllServicesSize(3));
1319e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Re-read data from disk and verify services were saved
1329e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        cache = new TestServicesCache(mContext, mDataDir);
1339e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(u1, cache.getPersistentServicesSize(u0));
1349e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        assertEquals(u1, cache.getPersistentServicesSize(u1));
1359e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
1369e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1379e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    /**
1389e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov     * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
1399e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov     */
1409e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    private static class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
1419e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
1429e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
1439e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        static final String ATTRIBUTES_NAME = "test";
1449e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        // Represents UID of a system image process
1459e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        static final int SYSTEM_IMAGE_UID = 20;
1469e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices
1479e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                = new SparseArray<>();
1489e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1499e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServicesCache(Context context, File dir) {
1509e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME,
1519e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    new TestSerializer(), dir);
1529e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1539e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1549e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
1559e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServiceType parseServiceAttributes(Resources res, String packageName,
1569e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                AttributeSet attrs) {
1579e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return null;
1589e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1599e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1609e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
1619e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        protected List<ResolveInfo> queryIntentServices(int userId) {
1629e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices
1639e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    .get(userId, new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
1649e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return new ArrayList<>(map.keySet());
1659e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1669e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1679e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
1689e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                ServiceInfo<TestServiceType> serviceInfo) {
1699e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
1709e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            if (map == null) {
1719e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                map = new HashMap<>();
1729e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                mServices.put(userId, map);
1739e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
1749e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            map.put(resolveInfo, serviceInfo);
1759e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1769e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1779e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        void clearServicesForQuerying() {
1789e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            mServices.clear();
1799e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1809e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1819e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        int getPersistentServicesSize(int user) {
1829e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return getPersistentServices(user).size();
1839e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1849e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1859e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        int getAllServicesSize(int user) {
1869e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return getAllServices(user).size();
1879e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1889e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1899e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
1909e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        protected boolean inSystemImage(int callerUid) {
1919e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return callerUid == SYSTEM_IMAGE_UID;
1929e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
1939e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
1949e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
1959e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        protected ServiceInfo<TestServiceType> parseServiceInfo(
1969e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                ResolveInfo resolveInfo) throws XmlPullParserException, IOException {
1979e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            int size = mServices.size();
1989e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            for (int i = 0; i < size; i++) {
1999e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
2009e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
2019e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                if (serviceInfo != null) {
2029e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    return serviceInfo;
2039e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                }
2049e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
2059e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            throw new IllegalArgumentException("Unexpected service " + resolveInfo);
2069e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2079e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
2089e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2099e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    static class TestSerializer implements XmlSerializerAndParser<TestServiceType> {
2109e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2119e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public void writeAsXml(TestServiceType item, XmlSerializer out) throws IOException {
2129e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            out.attribute(null, "type", item.type);
2139e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            out.attribute(null, "value", item.value);
2149e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2159e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2169e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServiceType createFromXml(XmlPullParser parser)
2179e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                throws IOException, XmlPullParserException {
2189e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            final String type = parser.getAttributeValue(null, "type");
2199e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            final String value = parser.getAttributeValue(null, "value");
2209e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return new TestServiceType(type, value);
2219e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2229e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
2239e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2249e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    static class TestServiceType implements Parcelable {
2259e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        final String type;
2269e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        final String value;
2279e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2289e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServiceType(String type, String value) {
2299e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            this.type = type;
2309e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            this.value = value;
2319e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2329e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2339e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
2349e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public boolean equals(Object o) {
2359e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            if (this == o) {
2369e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                return true;
2379e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
2389e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            if (o == null || getClass() != o.getClass()) {
2399e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                return false;
2409e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
2419e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2429e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            TestServiceType that = (TestServiceType) o;
2439e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2449e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return type.equals(that.type) && value.equals(that.value);
2459e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2469e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2479e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
2489e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public int hashCode() {
2499e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return 31 * type.hashCode() + value.hashCode();
2509e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2519e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2529e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        @Override
2539e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public String toString() {
2549e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return "TestServiceType{" +
2559e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    "type='" + type + '\'' +
2569e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    ", value='" + value + '\'' +
2579e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                    '}';
2589e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2599e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2609e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public int describeContents() {
2619e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            return 0;
2629e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2639e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2649e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public void writeToParcel(Parcel dest, int flags) {
2659e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            dest.writeString(type);
2669e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            dest.writeString(value);
2679e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2689e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2699e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public TestServiceType(Parcel source) {
2709e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            this(source.readString(), source.readString());
2719e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        }
2729e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2739e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        public static final Creator<TestServiceType> CREATOR = new Creator<TestServiceType>() {
2749e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            public TestServiceType createFromParcel(Parcel source) {
2759e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                return new TestServiceType(source);
2769e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
2779e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov
2789e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            public TestServiceType[] newArray(int size) {
2799e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov                return new TestServiceType[size];
2809e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov            }
2819e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov        };
2829e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov    }
2839e0d81e8439089845b7ddee21676f3f427a34ccaFyodor Kupolov}
284