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