DexManagerTests.java revision 99dd37b3c5262910150ef955d16a33d32da264dd
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.content.pm.ApplicationInfo;
20import android.content.pm.PackageInfo;
21import android.os.Build;
22import android.os.UserHandle;
23import android.support.test.filters.SmallTest;
24import android.support.test.runner.AndroidJUnit4;
25
26import dalvik.system.VMRuntime;
27
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33
34import org.junit.Before;
35import org.junit.Test;
36import org.junit.runner.RunWith;
37
38import static org.junit.Assert.assertEquals;
39import static org.junit.Assert.assertFalse;
40import static org.junit.Assert.assertNotNull;
41import static org.junit.Assert.assertNull;
42import static org.junit.Assert.assertTrue;
43import static org.junit.Assert.fail;
44
45import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
46import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
47
48@RunWith(AndroidJUnit4.class)
49@SmallTest
50public class DexManagerTests {
51    private DexManager mDexManager;
52
53    private TestData mFooUser0;
54    private TestData mBarUser0;
55    private TestData mBarUser1;
56    private TestData mInvalidIsa;
57    private TestData mDoesNotExist;
58
59    private int mUser0;
60    private int mUser1;
61
62    @Before
63    public void setup() {
64
65        mUser0 = 0;
66        mUser1 = 1;
67
68        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
69        String foo = "foo";
70        String bar = "bar";
71
72        mFooUser0 = new TestData(foo, isa, mUser0);
73        mBarUser0 = new TestData(bar, isa, mUser0);
74        mBarUser1 = new TestData(bar, isa, mUser1);
75        mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
76        mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
77
78        mDexManager = new DexManager(null, null, null, null);
79
80        // Foo and Bar are available to user0.
81        // Only Bar is available to user1;
82        Map<Integer, List<PackageInfo>> existingPackages = new HashMap<>();
83        existingPackages.put(mUser0, Arrays.asList(mFooUser0.mPackageInfo, mBarUser0.mPackageInfo));
84        existingPackages.put(mUser1, Arrays.asList(mBarUser1.mPackageInfo));
85        mDexManager.load(existingPackages);
86    }
87
88    @Test
89    public void testNotifyPrimaryUse() {
90        // The main dex file and splits are re-loaded by the app.
91        notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
92
93        // Package is not used by others, so we should get nothing back.
94        assertNull(getPackageUseInfo(mFooUser0));
95    }
96
97    @Test
98    public void testNotifyPrimaryForeignUse() {
99        // Foo loads Bar main apks.
100        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
101
102        // Bar is used by others now and should be in our records
103        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
104        assertNotNull(pui);
105        assertTrue(pui.isUsedByOtherApps());
106        assertTrue(pui.getDexUseInfoMap().isEmpty());
107    }
108
109    @Test
110    public void testNotifySecondary() {
111        // Foo loads its own secondary files.
112        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
113        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
114
115        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
116        assertNotNull(pui);
117        assertFalse(pui.isUsedByOtherApps());
118        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
119        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
120    }
121
122    @Test
123    public void testNotifySecondaryForeign() {
124        // Foo loads bar secondary files.
125        List<String> barSecondaries = mBarUser0.getSecondaryDexPaths();
126        notifyDexLoad(mFooUser0, barSecondaries, mUser0);
127
128        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
129        assertNotNull(pui);
130        assertFalse(pui.isUsedByOtherApps());
131        assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
132        assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
133    }
134
135    @Test
136    public void testNotifySequence() {
137        // Foo loads its own secondary files.
138        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
139        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
140        // Foo loads Bar own secondary files.
141        List<String> barSecondaries = mBarUser0.getSecondaryDexPaths();
142        notifyDexLoad(mFooUser0, barSecondaries, mUser0);
143        // Foo loads Bar primary files.
144        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
145        // Bar loads its own secondary files.
146        notifyDexLoad(mBarUser0, barSecondaries, mUser0);
147        // Bar loads some own secondary files which foo didn't load.
148        List<String> barSecondariesForOwnUse = mBarUser0.getSecondaryDexPathsForOwnUse();
149        notifyDexLoad(mBarUser0, barSecondariesForOwnUse, mUser0);
150
151        // Check bar usage. Should be used by other app (for primary and barSecondaries).
152        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
153        assertNotNull(pui);
154        assertTrue(pui.isUsedByOtherApps());
155        assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
156                pui.getDexUseInfoMap().size());
157
158        assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
159        assertSecondaryUse(mFooUser0, pui, barSecondariesForOwnUse,
160                /*isUsedByOtherApps*/false, mUser0);
161
162        // Check foo usage. Should not be used by other app.
163        pui = getPackageUseInfo(mFooUser0);
164        assertNotNull(pui);
165        assertFalse(pui.isUsedByOtherApps());
166        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
167        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
168    }
169
170    @Test
171    public void testPackageUseInfoNotFound() {
172        // Assert we don't get back data we did not previously record.
173        assertNull(getPackageUseInfo(mFooUser0));
174    }
175
176    @Test
177    public void testInvalidIsa() {
178        // Notifying with an invalid ISA should be ignored.
179        notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
180        assertNull(getPackageUseInfo(mInvalidIsa));
181    }
182
183    @Test
184    public void testNotExistingPackate() {
185        // Notifying about the load of a package which was previously not
186        // register in DexManager#load should be ignored.
187        notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
188        assertNull(getPackageUseInfo(mDoesNotExist));
189    }
190
191    @Test
192    public void testCrossUserAttempt() {
193        // Bar from User1 tries to load secondary dex files from User0 Bar.
194        // Request should be ignored.
195        notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
196        assertNull(getPackageUseInfo(mBarUser1));
197    }
198
199    @Test
200    public void testPackageNotInstalledForUser() {
201        // User1 tries to load Foo which is installed for User0 but not for User1.
202        // Note that the PackageManagerService already filters this out but we
203        // still check that nothing goes unexpected in DexManager.
204        notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
205        assertNull(getPackageUseInfo(mBarUser1));
206    }
207
208    @Test
209    public void testNotifyPackageInstallUsedByOther() {
210        TestData newPackage = new TestData("newPackage",
211                VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
212
213        List<String> newSecondaries = newPackage.getSecondaryDexPaths();
214        // Before we notify about the installation of the newPackage if mFoo
215        // is trying to load something from it we should not find it.
216        notifyDexLoad(mFooUser0, newSecondaries, mUser0);
217        assertNull(getPackageUseInfo(newPackage));
218
219        // Notify about newPackage install and let mFoo load its dexes.
220        mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
221        notifyDexLoad(mFooUser0, newSecondaries, mUser0);
222
223        // We should get back the right info.
224        PackageUseInfo pui = getPackageUseInfo(newPackage);
225        assertNotNull(pui);
226        assertFalse(pui.isUsedByOtherApps());
227        assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
228        assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
229    }
230
231    @Test
232    public void testNotifyPackageInstallSelfUse() {
233        TestData newPackage = new TestData("newPackage",
234                VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
235
236        List<String> newSecondaries = newPackage.getSecondaryDexPaths();
237        // Packages should be able to find their own dex files even if the notification about
238        // their installation is delayed.
239        notifyDexLoad(newPackage, newSecondaries, mUser0);
240
241        PackageUseInfo pui = getPackageUseInfo(newPackage);
242        assertNotNull(pui);
243        assertFalse(pui.isUsedByOtherApps());
244        assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
245        assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
246    }
247
248    @Test
249    public void testNotifyPackageUpdated() {
250        // Foo loads Bar main apks.
251        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
252
253        // Bar is used by others now and should be in our records.
254        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
255        assertNotNull(pui);
256        assertTrue(pui.isUsedByOtherApps());
257        assertTrue(pui.getDexUseInfoMap().isEmpty());
258
259        // Notify that bar is updated.
260        mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
261                mBarUser0.mPackageInfo.applicationInfo.sourceDir,
262                mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
263
264        // The usedByOtherApps flag should be clear now.
265        pui = getPackageUseInfo(mBarUser0);
266        assertNotNull(pui);
267        assertFalse(pui.isUsedByOtherApps());
268    }
269
270    @Test
271    public void testNotifyPackageUpdatedCodeLocations() {
272        // Simulate a split update.
273        String newSplit = mBarUser0.replaceLastSplit();
274        List<String> newSplits = new ArrayList<>();
275        newSplits.add(newSplit);
276
277        // We shouldn't find yet the new split as we didn't notify the package update.
278        notifyDexLoad(mFooUser0, newSplits, mUser0);
279        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
280        assertNull(pui);
281
282        // Notify that bar is updated. splitSourceDirs will contain the updated path.
283        mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
284                mBarUser0.mPackageInfo.applicationInfo.sourceDir,
285                mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
286
287        // Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
288        notifyDexLoad(mFooUser0, newSplits, mUser0);
289        pui = getPackageUseInfo(mBarUser0);
290        assertNotNull(pui);
291        assertTrue(pui.isUsedByOtherApps());
292    }
293
294    @Test
295    public void testNotifyPackageDataDestroyForOne() {
296        // Bar loads its own secondary files.
297        notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
298        notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
299
300        mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), mUser0);
301
302        // Bar should not be around since it was removed for all users.
303        PackageUseInfo pui = getPackageUseInfo(mBarUser1);
304        assertNotNull(pui);
305        assertSecondaryUse(mBarUser1, pui, mBarUser1.getSecondaryDexPaths(),
306                /*isUsedByOtherApps*/false, mUser1);
307    }
308
309    @Test
310    public void testNotifyPackageDataDestroyForeignUse() {
311        // Foo loads its own secondary files.
312        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
313        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
314
315        // Bar loads Foo main apks.
316        notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
317
318        mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
319
320        // Foo should still be around since it's used by other apps but with no
321        // secondary dex info.
322        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
323        assertNotNull(pui);
324        assertTrue(pui.isUsedByOtherApps());
325        assertTrue(pui.getDexUseInfoMap().isEmpty());
326    }
327
328    @Test
329    public void testNotifyPackageDataDestroyComplete() {
330        // Foo loads its own secondary files.
331        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
332        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
333
334        mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
335
336        // Foo should not be around since all its secondary dex info were deleted
337        // and it is not used by other apps.
338        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
339        assertNull(pui);
340    }
341
342    @Test
343    public void testNotifyPackageDataDestroyForAll() {
344        // Foo loads its own secondary files.
345        notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
346        notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
347
348        mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
349
350        // Bar should not be around since it was removed for all users.
351        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
352        assertNull(pui);
353    }
354
355    private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
356            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
357        for (String dex : secondaries) {
358            DexUseInfo dui = pui.getDexUseInfoMap().get(dex);
359            assertNotNull(dui);
360            assertEquals(isUsedByOtherApps, dui.isUsedByOtherApps());
361            assertEquals(ownerUserId, dui.getOwnerUserId());
362            assertEquals(1, dui.getLoaderIsas().size());
363            assertTrue(dui.getLoaderIsas().contains(testData.mLoaderIsa));
364        }
365    }
366
367    private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
368        mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, dexPaths,
369                testData.mLoaderIsa, loaderUserId);
370    }
371
372    private PackageUseInfo getPackageUseInfo(TestData testData) {
373        return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
374    }
375
376    private static PackageInfo getMockPackageInfo(String packageName, int userId) {
377        PackageInfo pi = new PackageInfo();
378        pi.packageName = packageName;
379        pi.applicationInfo = getMockApplicationInfo(packageName, userId);
380        return pi;
381    }
382
383    private static ApplicationInfo getMockApplicationInfo(String packageName, int userId) {
384        ApplicationInfo ai = new ApplicationInfo();
385        String codeDir = "/data/app/" + packageName;
386        ai.setBaseCodePath(codeDir + "/base.dex");
387        ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"});
388        ai.dataDir = "/data/user/" + userId + "/" + packageName;
389        ai.packageName = packageName;
390        return ai;
391    }
392
393    private static class TestData {
394        private final PackageInfo mPackageInfo;
395        private final String mLoaderIsa;
396
397        private TestData(String  packageName, String loaderIsa, int userId) {
398            mPackageInfo = getMockPackageInfo(packageName, userId);
399            mLoaderIsa = loaderIsa;
400        }
401
402        private String getPackageName() {
403            return mPackageInfo.packageName;
404        }
405
406        List<String> getSecondaryDexPaths() {
407            List<String> paths = new ArrayList<>();
408            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary1.dex");
409            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary2.dex");
410            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary3.dex");
411            return paths;
412        }
413
414        List<String> getSecondaryDexPathsForOwnUse() {
415            List<String> paths = new ArrayList<>();
416            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary4.dex");
417            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary5.dex");
418            return paths;
419        }
420
421        List<String> getBaseAndSplitDexPaths() {
422            List<String> paths = new ArrayList<>();
423            paths.add(mPackageInfo.applicationInfo.sourceDir);
424            for (String split : mPackageInfo.applicationInfo.splitSourceDirs) {
425                paths.add(split);
426            }
427            return paths;
428        }
429
430        String replaceLastSplit() {
431            int length = mPackageInfo.applicationInfo.splitSourceDirs.length;
432            // Add an extra bogus dex extension to simulate a new split name.
433            mPackageInfo.applicationInfo.splitSourceDirs[length - 1] += ".dex";
434            return mPackageInfo.applicationInfo.splitSourceDirs[length - 1];
435        }
436    }
437}
438