DexManagerTests.java revision 1aa5f88e35734383e66ecd65e82e83d788e18ccb
1/*
2 * Copyright (C) 2016 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 com.android.server.pm.dex;
18
19import android.os.Build;
20import android.content.pm.ApplicationInfo;
21import android.content.pm.PackageInfo;
22import android.support.test.filters.SmallTest;
23import android.support.test.runner.AndroidJUnit4;
24
25import dalvik.system.VMRuntime;
26
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.HashMap;
30import java.util.List;
31import java.util.Map;
32
33import org.junit.Before;
34import org.junit.Test;
35import org.junit.runner.RunWith;
36
37import static org.junit.Assert.assertEquals;
38import static org.junit.Assert.assertFalse;
39import static org.junit.Assert.assertNotNull;
40import static org.junit.Assert.assertNull;
41import static org.junit.Assert.assertTrue;
42import static org.junit.Assert.fail;
43
44import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
45import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
46
47@RunWith(AndroidJUnit4.class)
48@SmallTest
49public class DexManagerTests {
50    private DexManager mDexManager;
51
52    private TestData mFooUser0;
53    private TestData mBarUser0;
54    private TestData mBarUser1;
55    private TestData mInvalidIsa;
56    private TestData mDoesNotExist;
57
58    private int mUser0;
59    private int mUser1;
60    @Before
61    public void setup() {
62
63        mUser0 = 0;
64        mUser1 = 1;
65
66        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
67        String foo = "foo";
68        String bar = "bar";
69
70        mFooUser0 = new TestData(foo, isa, mUser0);
71        mBarUser0 = new TestData(bar, isa, mUser0);
72        mBarUser1 = new TestData(bar, isa, mUser1);
73        mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
74        mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
75
76        mDexManager = new DexManager(null, null, null, null);
77
78        // Foo and Bar are available to user0.
79        // Only Bar is available to user1;
80        Map<Integer, List<PackageInfo>> existingPackages = new HashMap<>();
81        existingPackages.put(mUser0, Arrays.asList(mFooUser0.mPackageInfo, mBarUser0.mPackageInfo));
82        existingPackages.put(mUser1, Arrays.asList(mBarUser1.mPackageInfo));
83        mDexManager.load(existingPackages);
84    }
85
86    @Test
87    public void testNotifyPrimaryUse() {
88        // The main dex file and splits are re-loaded by the app.
89        notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
90
91        // Package is not used by others, so we should get nothing back.
92        assertNull(getPackageUseInfo(mFooUser0));
93    }
94
95    @Test
96    public void testNotifyPrimaryForeignUse() {
97        // Foo loads Bar main apks.
98        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
99
100        // Bar is used by others now and should be in our records
101        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
102        assertNotNull(pui);
103        assertTrue(pui.isUsedByOtherApps());
104        assertTrue(pui.getDexUseInfoMap().isEmpty());
105    }
106
107    @Test
108    public void testNotifySecondary() {
109        // Foo loads its own secondary files.
110        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
111        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
112
113        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
114        assertNotNull(pui);
115        assertFalse(pui.isUsedByOtherApps());
116        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
117        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
118    }
119
120    @Test
121    public void testNotifySecondaryForeign() {
122        // Foo loads bar secondary files.
123        List<String> barSecondaries = mBarUser0.getSecondaryDexPaths();
124        notifyDexLoad(mFooUser0, barSecondaries, mUser0);
125
126        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
127        assertNotNull(pui);
128        assertFalse(pui.isUsedByOtherApps());
129        assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
130        assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
131    }
132
133    @Test
134    public void testNotifySequence() {
135        // Foo loads its own secondary files.
136        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
137        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
138        // Foo loads Bar own secondary files.
139        List<String> barSecondaries = mBarUser0.getSecondaryDexPaths();
140        notifyDexLoad(mFooUser0, barSecondaries, mUser0);
141        // Foo loads Bar primary files.
142        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
143        // Bar loads its own secondary files.
144        notifyDexLoad(mBarUser0, barSecondaries, mUser0);
145        // Bar loads some own secondary files which foo didn't load.
146        List<String> barSecondariesForOwnUse = mBarUser0.getSecondaryDexPathsForOwnUse();
147        notifyDexLoad(mBarUser0, barSecondariesForOwnUse, mUser0);
148
149        // Check bar usage. Should be used by other app (for primary and barSecondaries).
150        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
151        assertNotNull(pui);
152        assertTrue(pui.isUsedByOtherApps());
153        assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
154                pui.getDexUseInfoMap().size());
155
156        assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
157        assertSecondaryUse(mFooUser0, pui, barSecondariesForOwnUse,
158                /*isUsedByOtherApps*/false, mUser0);
159
160        // Check foo usage. Should not be used by other app.
161        pui = getPackageUseInfo(mFooUser0);
162        assertNotNull(pui);
163        assertFalse(pui.isUsedByOtherApps());
164        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
165        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
166    }
167
168    @Test
169    public void testPackageUseInfoNotFound() {
170        // Assert we don't get back data we did not previously record.
171        assertNull(getPackageUseInfo(mFooUser0));
172    }
173
174    @Test
175    public void testInvalidIsa() {
176        // Notifying with an invalid ISA should be ignored.
177        notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
178        assertNull(getPackageUseInfo(mInvalidIsa));
179    }
180
181    @Test
182    public void testNotExistingPackate() {
183        // Notifying about the load of a package which was previously not
184        // register in DexManager#load should be ignored.
185        notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
186        assertNull(getPackageUseInfo(mDoesNotExist));
187    }
188
189    @Test
190    public void testCrossUserAttempt() {
191        // Bar from User1 tries to load secondary dex files from User0 Bar.
192        // Request should be ignored.
193        notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
194        assertNull(getPackageUseInfo(mBarUser1));
195    }
196
197    @Test
198    public void testPackageNotInstalledForUser() {
199        // User1 tries to load Foo which is installed for User0 but not for User1.
200        // Note that the PackageManagerService already filters this out but we
201        // still check that nothing goes unexpected in DexManager.
202        notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
203        assertNull(getPackageUseInfo(mBarUser1));
204    }
205
206    private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
207            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
208        for (String dex : secondaries) {
209            DexUseInfo dui = pui.getDexUseInfoMap().get(dex);
210            assertNotNull(dui);
211            assertEquals(isUsedByOtherApps, dui.isUsedByOtherApps());
212            assertEquals(ownerUserId, dui.getOwnerUserId());
213            assertEquals(1, dui.getLoaderIsas().size());
214            assertTrue(dui.getLoaderIsas().contains(testData.mLoaderIsa));
215        }
216    }
217
218    private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
219        mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, dexPaths,
220                testData.mLoaderIsa, loaderUserId);
221    }
222
223    private PackageUseInfo getPackageUseInfo(TestData testData) {
224        return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
225    }
226
227    private static PackageInfo getMockPackageInfo(String packageName, int userId) {
228        PackageInfo pi = new PackageInfo();
229        pi.packageName = packageName;
230        pi.applicationInfo = getMockApplicationInfo(packageName, userId);
231        return pi;
232    }
233
234    private static ApplicationInfo getMockApplicationInfo(String packageName, int userId) {
235        ApplicationInfo ai = new ApplicationInfo();
236        String codeDir = "/data/app/" + packageName;
237        ai.setBaseCodePath(codeDir + "/base.dex");
238        ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"});
239        ai.dataDir = "/data/user/" + userId + "/" + packageName;
240        ai.packageName = packageName;
241        return ai;
242    }
243
244    private static class TestData {
245        private final PackageInfo mPackageInfo;
246        private final String mLoaderIsa;
247
248        private TestData(String  packageName, String loaderIsa, int userId) {
249            mPackageInfo = getMockPackageInfo(packageName, userId);
250            mLoaderIsa = loaderIsa;
251        }
252
253        private String getPackageName() {
254            return mPackageInfo.packageName;
255        }
256
257        List<String> getSecondaryDexPaths() {
258            List<String> paths = new ArrayList<>();
259            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary1.dex");
260            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary2.dex");
261            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary3.dex");
262            return paths;
263        }
264
265        List<String> getSecondaryDexPathsForOwnUse() {
266            List<String> paths = new ArrayList<>();
267            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary4.dex");
268            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary5.dex");
269            return paths;
270        }
271
272        List<String> getBaseAndSplitDexPaths() {
273            List<String> paths = new ArrayList<>();
274            paths.add(mPackageInfo.applicationInfo.sourceDir);
275            for (String split : mPackageInfo.applicationInfo.splitSourceDirs) {
276                paths.add(split);
277            }
278            return paths;
279        }
280    }
281}
282