DexManagerTests.java revision 5a9094c1b6e5797327674d4f12fa1ac4ade7275c
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
77        mDexManager = new DexManager();
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    private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
208            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
209        for (String dex : secondaries) {
210            DexUseInfo dui = pui.getDexUseInfoMap().get(dex);
211            assertNotNull(dui);
212            assertEquals(isUsedByOtherApps, dui.isUsedByOtherApps());
213            assertEquals(ownerUserId, dui.getOwnerUserId());
214            assertEquals(1, dui.getLoaderIsas().size());
215            assertTrue(dui.getLoaderIsas().contains(testData.mLoaderIsa));
216        }
217    }
218
219    private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
220        mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, dexPaths,
221                testData.mLoaderIsa, loaderUserId);
222    }
223
224    private PackageUseInfo getPackageUseInfo(TestData testData) {
225        return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
226    }
227
228    private static PackageInfo getMockPackageInfo(String packageName, int userId) {
229        PackageInfo pi = new PackageInfo();
230        pi.packageName = packageName;
231        pi.applicationInfo = getMockApplicationInfo(packageName, userId);
232        return pi;
233    }
234
235    private static ApplicationInfo getMockApplicationInfo(String packageName, int userId) {
236        ApplicationInfo ai = new ApplicationInfo();
237        String codeDir = "/data/app/" + packageName;
238        ai.setBaseCodePath(codeDir + "/base.dex");
239        ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"});
240        ai.dataDir = "/data/user/" + userId + "/" + packageName;
241        ai.packageName = packageName;
242        return ai;
243    }
244
245    private static class TestData {
246        private final PackageInfo mPackageInfo;
247        private final String mLoaderIsa;
248
249        private TestData(String  packageName, String loaderIsa, int userId) {
250            mPackageInfo = getMockPackageInfo(packageName, userId);
251            mLoaderIsa = loaderIsa;
252        }
253
254        private String getPackageName() {
255            return mPackageInfo.packageName;
256        }
257
258        List<String> getSecondaryDexPaths() {
259            List<String> paths = new ArrayList<>();
260            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary1.dex");
261            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary2.dex");
262            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary3.dex");
263            return paths;
264        }
265
266        List<String> getSecondaryDexPathsForOwnUse() {
267            List<String> paths = new ArrayList<>();
268            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary4.dex");
269            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary5.dex");
270            return paths;
271        }
272
273        List<String> getBaseAndSplitDexPaths() {
274            List<String> paths = new ArrayList<>();
275            paths.add(mPackageInfo.applicationInfo.sourceDir);
276            for (String split : mPackageInfo.applicationInfo.splitSourceDirs) {
277                paths.add(split);
278            }
279            return paths;
280        }
281    }
282}
283