Home | History | Annotate | Download | only in pm
      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 
     17 package android.content.pm;
     18 
     19 import android.content.res.Resources;
     20 import android.os.FileUtils;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 import android.os.UserHandle;
     24 import android.support.test.filters.LargeTest;
     25 import android.test.AndroidTestCase;
     26 import android.util.AttributeSet;
     27 import android.util.SparseArray;
     28 
     29 import org.xmlpull.v1.XmlPullParser;
     30 import org.xmlpull.v1.XmlPullParserException;
     31 import org.xmlpull.v1.XmlSerializer;
     32 
     33 import java.io.ByteArrayInputStream;
     34 import java.io.File;
     35 import java.io.IOException;
     36 import java.util.ArrayList;
     37 import java.util.Collection;
     38 import java.util.HashMap;
     39 import java.util.HashSet;
     40 import java.util.List;
     41 import java.util.Map;
     42 import java.util.Set;
     43 
     44 /**
     45  * Tests for {@link android.content.pm.RegisteredServicesCache}
     46  */
     47 @LargeTest
     48 public class RegisteredServicesCacheTest extends AndroidTestCase {
     49     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;
     55 
     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;
     62     private List<UserInfo> mUsers;
     63 
     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);
     70         mSyncDir = new File(mDataDir, "system/"+RegisteredServicesCache.REGISTERED_SERVICES_DIR);
     71         mSyncDir.mkdirs();
     72         mUsers = new ArrayList<>();
     73         mUsers.add(new UserInfo(0, "Owner", UserInfo.FLAG_ADMIN));
     74         mUsers.add(new UserInfo(1, "User1", 0));
     75     }
     76 
     77     public void testGetAllServicesHappyPath() {
     78         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);
     84         // Make sure all services can be loaded from xml
     85         cache = new TestServicesCache();
     86         assertEquals(2, cache.getPersistentServicesSize(U0));
     87     }
     88 
     89     public void testGetAllServicesReplaceUid() {
     90         TestServicesCache cache = new TestServicesCache();
     91         cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
     92         cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, UID2));
     93         cache.getAllServices(U0);
     94         // Invalidate cache and clear update query results
     95         cache.invalidateCache(U0);
     96         cache.clearServicesForQuerying();
     97         cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
     98         cache.addServiceForQuerying(U0, r2, newServiceInfo(t2, SYSTEM_IMAGE_UID));
     99         Collection<RegisteredServicesCache.ServiceInfo<TestServiceType>> allServices = cache
    100                 .getAllServices(U0);
    101         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",
    107                 uids.contains(SYSTEM_IMAGE_UID));
    108         assertFalse("UID must be updated to the new value", uids.contains(UID2));
    109     }
    110 
    111     public void testGetAllServicesServiceRemoved() {
    112         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));
    117         // Re-read data from disk and verify services were saved
    118         cache = new TestServicesCache();
    119         assertEquals(2, cache.getPersistentServicesSize(U0));
    120         // Now register only one service and verify that another one is removed
    121         cache.addServiceForQuerying(U0, r1, newServiceInfo(t1, UID1));
    122         assertEquals(1, cache.getAllServicesSize(U0));
    123         assertEquals(1, cache.getPersistentServicesSize(U0));
    124     }
    125 
    126     public void testGetAllServicesMultiUser() {
    127         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));
    135         assertEquals("No services should be available for user 3", 0, cache.getAllServicesSize(3));
    136         // Re-read data from disk and verify services were saved
    137         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) {
    193         final ComponentInfo info = new ComponentInfo();
    194         info.applicationInfo = new ApplicationInfo();
    195         info.applicationInfo.uid = uid;
    196         return new RegisteredServicesCache.ServiceInfo<>(type, info, null);
    197     }
    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);
    204     }
    205 
    206     /**
    207      * Mock implementation of {@link android.content.pm.RegisteredServicesCache} for testing
    208      */
    209     private class TestServicesCache extends RegisteredServicesCache<TestServiceType> {
    210         static final String SERVICE_INTERFACE = "RegisteredServicesCacheTest";
    211         static final String SERVICE_META_DATA = "RegisteredServicesCacheTest";
    212         static final String ATTRIBUTES_NAME = "test";
    213         private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices
    214                 = new SparseArray<>();
    215 
    216         public TestServicesCache() {
    217             super(RegisteredServicesCacheTest.this.mContext,
    218                     SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, new TestSerializer());
    219         }
    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 
    234         @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 
    261         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         }
    301 
    302         @Override
    303         public void onUserRemoved(int userId) {
    304             super.onUserRemoved(userId);
    305         }
    306     }
    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 }
    383