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.IPackageManager;
21import android.content.pm.PackageInfo;
22import android.os.Build;
23import android.os.UserHandle;
24import android.support.test.filters.SmallTest;
25import android.support.test.runner.AndroidJUnit4;
26
27import com.android.server.pm.Installer;
28
29import dalvik.system.DelegateLastClassLoader;
30import dalvik.system.PathClassLoader;
31import dalvik.system.VMRuntime;
32
33import java.io.File;
34import java.util.ArrayList;
35import java.util.Arrays;
36import java.util.Collections;
37import java.util.HashMap;
38import java.util.List;
39import java.util.Map;
40
41import org.junit.Before;
42import org.junit.Rule;
43import org.junit.Test;
44import org.junit.runner.RunWith;
45import org.mockito.Mock;
46import org.mockito.junit.MockitoJUnit;
47import org.mockito.junit.MockitoRule;
48import org.mockito.quality.Strictness;
49
50import static org.junit.Assert.assertEquals;
51import static org.junit.Assert.assertFalse;
52import static org.junit.Assert.assertNotNull;
53import static org.junit.Assert.assertNull;
54import static org.junit.Assert.assertTrue;
55import static org.junit.Assert.fail;
56import static org.mockito.ArgumentMatchers.any;
57import static org.mockito.ArgumentMatchers.anyInt;
58import static org.mockito.ArgumentMatchers.anyString;
59import static org.mockito.Mockito.times;
60import static org.mockito.Mockito.verify;
61import static org.mockito.Mockito.when;
62
63import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
64import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
65
66@RunWith(AndroidJUnit4.class)
67@SmallTest
68public class DexManagerTests {
69    private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
70    private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
71            DelegateLastClassLoader.class.getName();
72
73    @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
74    @Mock Installer mInstaller;
75    @Mock IPackageManager mPM;
76    private final Object mInstallLock = new Object();
77    @Mock DexManager.Listener mListener;
78
79    private DexManager mDexManager;
80
81    private TestData mFooUser0;
82    private TestData mBarUser0;
83    private TestData mBarUser1;
84    private TestData mInvalidIsa;
85    private TestData mDoesNotExist;
86
87    private TestData mBarUser0UnsupportedClassLoader;
88    private TestData mBarUser0DelegateLastClassLoader;
89
90    private int mUser0;
91    private int mUser1;
92
93    @Before
94    public void setup() {
95        mUser0 = 0;
96        mUser1 = 1;
97
98        String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
99        String foo = "foo";
100        String bar = "bar";
101
102        mFooUser0 = new TestData(foo, isa, mUser0, PATH_CLASS_LOADER_NAME);
103        mBarUser0 = new TestData(bar, isa, mUser0, PATH_CLASS_LOADER_NAME);
104        mBarUser1 = new TestData(bar, isa, mUser1, PATH_CLASS_LOADER_NAME);
105        mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
106        mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
107
108        mBarUser0UnsupportedClassLoader = new TestData(bar, isa, mUser0,
109                "unsupported.class_loader");
110        mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
111                DELEGATE_LAST_CLASS_LOADER_NAME);
112
113        mDexManager = new DexManager(
114            /*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock,
115            mListener);
116
117        // Foo and Bar are available to user0.
118        // Only Bar is available to user1;
119        Map<Integer, List<PackageInfo>> existingPackages = new HashMap<>();
120        existingPackages.put(mUser0, Arrays.asList(mFooUser0.mPackageInfo, mBarUser0.mPackageInfo));
121        existingPackages.put(mUser1, Arrays.asList(mBarUser1.mPackageInfo));
122        mDexManager.load(existingPackages);
123    }
124
125    @Test
126    public void testNotifyPrimaryUse() {
127        // The main dex file and splits are re-loaded by the app.
128        notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
129
130        // Package is not used by others, so we should get nothing back.
131        assertNoUseInfo(mFooUser0);
132    }
133
134    @Test
135    public void testNotifyPrimaryForeignUse() {
136        // Foo loads Bar main apks.
137        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
138
139        // Bar is used by others now and should be in our records
140        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
141        assertIsUsedByOtherApps(mBarUser0, pui, true);
142        assertTrue(pui.getDexUseInfoMap().isEmpty());
143    }
144
145    @Test
146    public void testNotifySecondary() {
147        // Foo loads its own secondary files.
148        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
149        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
150
151        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
152        assertIsUsedByOtherApps(mFooUser0, pui, false);
153        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
154        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
155    }
156
157    @Test
158    public void testNotifySecondaryForeign() {
159        // Foo loads bar secondary files.
160        List<String> barSecondaries = mBarUser0.getSecondaryDexPaths();
161        notifyDexLoad(mFooUser0, barSecondaries, mUser0);
162
163        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
164        assertIsUsedByOtherApps(mBarUser0, pui, false);
165        assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
166        assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
167    }
168
169    @Test
170    public void testNotifySequence() {
171        // Foo loads its own secondary files.
172        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
173        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
174        // Foo loads Bar own secondary files.
175        List<String> barSecondaries = mBarUser0.getSecondaryDexPaths();
176        notifyDexLoad(mFooUser0, barSecondaries, mUser0);
177        // Foo loads Bar primary files.
178        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
179        // Bar loads its own secondary files.
180        notifyDexLoad(mBarUser0, barSecondaries, mUser0);
181        // Bar loads some own secondary files which foo didn't load.
182        List<String> barSecondariesForOwnUse = mBarUser0.getSecondaryDexPathsForOwnUse();
183        notifyDexLoad(mBarUser0, barSecondariesForOwnUse, mUser0);
184
185        // Check bar usage. Should be used by other app (for primary and barSecondaries).
186        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
187        assertIsUsedByOtherApps(mBarUser0, pui, true);
188        assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
189                pui.getDexUseInfoMap().size());
190
191        assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
192        assertSecondaryUse(mFooUser0, pui, barSecondariesForOwnUse,
193                /*isUsedByOtherApps*/false, mUser0);
194
195        // Check foo usage. Should not be used by other app.
196        pui = getPackageUseInfo(mFooUser0);
197        assertIsUsedByOtherApps(mFooUser0, pui, false);
198        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
199        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
200    }
201
202    @Test
203    public void testPackageUseInfoNotFound() {
204        // Assert we don't get back data we did not previously record.
205        assertNoUseInfo(mFooUser0);
206    }
207
208    @Test
209    public void testInvalidIsa() {
210        // Notifying with an invalid ISA should be ignored.
211        notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
212        assertNoUseInfo(mInvalidIsa);
213    }
214
215    @Test
216    public void testNotExistingPackage() {
217        // Notifying about the load of a package which was previously not
218        // register in DexManager#load should be ignored.
219        notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
220        assertNoUseInfo(mDoesNotExist);
221    }
222
223    @Test
224    public void testCrossUserAttempt() {
225        // Bar from User1 tries to load secondary dex files from User0 Bar.
226        // Request should be ignored.
227        notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
228        assertNoUseInfo(mBarUser1);
229    }
230
231    @Test
232    public void testPackageNotInstalledForUser() {
233        // User1 tries to load Foo which is installed for User0 but not for User1.
234        // Note that the PackageManagerService already filters this out but we
235        // still check that nothing goes unexpected in DexManager.
236        notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
237        assertNoUseInfo(mBarUser1);
238    }
239
240    @Test
241    public void testNotifyPackageInstallUsedByOther() {
242        TestData newPackage = new TestData("newPackage",
243                VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
244
245        List<String> newSecondaries = newPackage.getSecondaryDexPaths();
246        // Before we notify about the installation of the newPackage if mFoo
247        // is trying to load something from it we should not find it.
248        notifyDexLoad(mFooUser0, newSecondaries, mUser0);
249        assertNoUseInfo(newPackage);
250
251        // Notify about newPackage install and let mFoo load its dexes.
252        mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
253        notifyDexLoad(mFooUser0, newSecondaries, mUser0);
254
255        // We should get back the right info.
256        PackageUseInfo pui = getPackageUseInfo(newPackage);
257        assertIsUsedByOtherApps(newPackage, pui, false);
258        assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
259        assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
260    }
261
262    @Test
263    public void testNotifyPackageInstallSelfUse() {
264        TestData newPackage = new TestData("newPackage",
265                VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
266
267        List<String> newSecondaries = newPackage.getSecondaryDexPaths();
268        // Packages should be able to find their own dex files even if the notification about
269        // their installation is delayed.
270        notifyDexLoad(newPackage, newSecondaries, mUser0);
271
272        PackageUseInfo pui = getPackageUseInfo(newPackage);
273        assertIsUsedByOtherApps(newPackage, pui, false);
274        assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
275        assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
276    }
277
278    @Test
279    public void testNotifyPackageUpdated() {
280        // Foo loads Bar main apks.
281        notifyDexLoad(mFooUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0);
282
283        // Bar is used by others now and should be in our records.
284        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
285        assertIsUsedByOtherApps(mBarUser0, pui, true);
286        assertTrue(pui.getDexUseInfoMap().isEmpty());
287
288        // Notify that bar is updated.
289        mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
290                mBarUser0.mPackageInfo.applicationInfo.sourceDir,
291                mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
292
293        // The usedByOtherApps flag should be clear now.
294        pui = getPackageUseInfo(mBarUser0);
295        assertIsUsedByOtherApps(mBarUser0, pui, false);
296    }
297
298    @Test
299    public void testNotifyPackageUpdatedCodeLocations() {
300        // Simulate a split update.
301        String newSplit = mBarUser0.replaceLastSplit();
302        List<String> newSplits = new ArrayList<>();
303        newSplits.add(newSplit);
304
305        // We shouldn't find yet the new split as we didn't notify the package update.
306        notifyDexLoad(mFooUser0, newSplits, mUser0);
307        assertNoUseInfo(mBarUser0);
308
309        // Notify that bar is updated. splitSourceDirs will contain the updated path.
310        mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
311                mBarUser0.mPackageInfo.applicationInfo.sourceDir,
312                mBarUser0.mPackageInfo.applicationInfo.splitSourceDirs);
313
314        // Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
315        notifyDexLoad(mFooUser0, newSplits, mUser0);
316        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
317        assertNotNull(pui);
318        assertIsUsedByOtherApps(newSplits, pui, true);
319    }
320
321    @Test
322    public void testNotifyPackageDataDestroyForOne() {
323        // Bar loads its own secondary files.
324        notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
325        notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
326
327        mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), mUser0);
328
329        // Bar should not be around since it was removed for all users.
330        PackageUseInfo pui = getPackageUseInfo(mBarUser1);
331        assertNotNull(pui);
332        assertSecondaryUse(mBarUser1, pui, mBarUser1.getSecondaryDexPaths(),
333                /*isUsedByOtherApps*/false, mUser1);
334    }
335
336    @Test
337    public void testNotifyPackageDataDestroyForeignUse() {
338        // Foo loads its own secondary files.
339        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
340        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
341
342        // Bar loads Foo main apks.
343        notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
344
345        mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
346
347        // Foo should still be around since it's used by other apps but with no
348        // secondary dex info.
349        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
350        assertIsUsedByOtherApps(mFooUser0, pui, true);
351        assertTrue(pui.getDexUseInfoMap().isEmpty());
352    }
353
354    @Test
355    public void testNotifyPackageDataDestroyComplete() {
356        // Foo loads its own secondary files.
357        List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
358        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
359
360        mDexManager.notifyPackageDataDestroyed(mFooUser0.getPackageName(), mUser0);
361
362        // Foo should not be around since all its secondary dex info were deleted
363        // and it is not used by other apps.
364        assertNoUseInfo(mFooUser0);
365    }
366
367    @Test
368    public void testNotifyPackageDataDestroyForAll() {
369        // Foo loads its own secondary files.
370        notifyDexLoad(mBarUser0, mBarUser0.getSecondaryDexPaths(), mUser0);
371        notifyDexLoad(mBarUser1, mBarUser1.getSecondaryDexPaths(), mUser1);
372
373        mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
374
375        // Bar should not be around since it was removed for all users.
376        assertNoUseInfo(mBarUser0);
377    }
378
379    @Test
380    public void testNotifyFrameworkLoad() {
381        String frameworkDex = "/system/framework/com.android.location.provider.jar";
382        // Load a dex file from framework.
383        notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
384        // The dex file should not be recognized as a package.
385        assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
386    }
387
388    @Test
389    public void testNotifySecondaryFromProtected() {
390        // Foo loads its own secondary files.
391        List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
392        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
393
394        PackageUseInfo pui = getPackageUseInfo(mFooUser0);
395        assertIsUsedByOtherApps(mFooUser0, pui, false);
396        assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
397        assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
398    }
399
400    @Test
401    public void testNotifyUnsupportedClassLoader() {
402        List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
403        notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
404
405        PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
406        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
407        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
408        // We expect that all the contexts are unsupported.
409        String[] expectedContexts =
410                Collections.nCopies(secondaries.size(),
411                        PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
412        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
413                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
414    }
415
416    @Test
417    public void testNotifyVariableClassLoader() {
418        // Record bar secondaries with the default PathClassLoader.
419        List<String> secondaries = mBarUser0.getSecondaryDexPaths();
420
421        notifyDexLoad(mBarUser0, secondaries, mUser0);
422        PackageUseInfo pui = getPackageUseInfo(mBarUser0);
423        assertIsUsedByOtherApps(mBarUser0, pui, false);
424        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
425        assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
426
427        // Record bar secondaries again with a different class loader. This will change the context.
428        notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
429
430        pui = getPackageUseInfo(mBarUser0);
431        assertIsUsedByOtherApps(mBarUser0, pui, false);
432        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
433        // We expect that all the contexts to be changed to variable now.
434        String[] expectedContexts =
435                Collections.nCopies(secondaries.size(),
436                        PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT).toArray(new String[0]);
437        assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0,
438                expectedContexts);
439    }
440
441    @Test
442    public void testNotifyUnsupportedClassLoaderDoesNotChange() {
443        List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
444        notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
445
446        PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
447        assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
448        assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
449        // We expect that all the contexts are unsupported.
450        String[] expectedContexts =
451                Collections.nCopies(secondaries.size(),
452                        PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
453        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
454                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
455
456        // Record bar secondaries again with a different class loader. This will change the context.
457        // However, because the context was already marked as unsupported we should not chage it.
458        notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
459        pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
460        assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
461                /*isUsedByOtherApps*/false, mUser0, expectedContexts);
462
463    }
464
465    @Test
466    public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
467        List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
468        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
469
470        when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
471                .thenReturn(mFooUser0.mPackageInfo);
472
473        mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
474
475        verify(mListener, times(fooSecondaries.size()))
476                .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
477                        any(DexUseInfo.class), anyString(), anyInt());
478    }
479
480    private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
481            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
482            String[] expectedContexts) {
483        assertNotNull(expectedContexts);
484        assertEquals(expectedContexts.length, secondaries.size());
485        int index = 0;
486        for (String dex : secondaries) {
487            DexUseInfo dui = pui.getDexUseInfoMap().get(dex);
488            assertNotNull(dui);
489            assertEquals(isUsedByOtherApps, dui.isUsedByOtherApps());
490            assertEquals(ownerUserId, dui.getOwnerUserId());
491            assertEquals(1, dui.getLoaderIsas().size());
492            assertTrue(dui.getLoaderIsas().contains(testData.mLoaderIsa));
493            assertEquals(expectedContexts[index++], dui.getClassLoaderContext());
494        }
495    }
496    private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
497            List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
498        String[] expectedContexts = DexoptUtils.processContextForDexLoad(
499                Arrays.asList(testData.mClassLoader),
500                Arrays.asList(String.join(File.pathSeparator, secondaries)));
501        assertSecondaryUse(testData, pui, secondaries, isUsedByOtherApps, ownerUserId,
502                expectedContexts);
503    }
504
505    private void assertIsUsedByOtherApps(TestData testData, PackageUseInfo pui,
506            boolean isUsedByOtherApps) {
507        assertIsUsedByOtherApps(testData.getBaseAndSplitDexPaths(), pui, isUsedByOtherApps);
508    }
509
510    private void assertIsUsedByOtherApps(List<String> codePaths, PackageUseInfo pui,
511            boolean isUsedByOtherApps) {
512        for (String codePath : codePaths) {
513            assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath));
514        }
515    }
516    private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
517        // By default, assume a single class loader in the chain.
518        // This makes writing tests much easier.
519        List<String> classLoaders = Arrays.asList(testData.mClassLoader);
520        List<String> classPaths = Arrays.asList(String.join(File.pathSeparator, dexPaths));
521        notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
522    }
523
524    private void notifyDexLoad(TestData testData, List<String> classLoader, List<String> classPaths,
525            int loaderUserId) {
526        mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, classLoader, classPaths,
527                testData.mLoaderIsa, loaderUserId);
528    }
529
530    private PackageUseInfo getPackageUseInfo(TestData testData) {
531        assertTrue(mDexManager.hasInfoOnPackage(testData.getPackageName()));
532        return mDexManager.getPackageUseInfoOrDefault(testData.getPackageName());
533    }
534
535    private void assertNoUseInfo(TestData testData) {
536        assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
537    }
538
539    private static PackageInfo getMockPackageInfo(String packageName, int userId) {
540        PackageInfo pi = new PackageInfo();
541        pi.packageName = packageName;
542        pi.applicationInfo = getMockApplicationInfo(packageName, userId);
543        return pi;
544    }
545
546    private static ApplicationInfo getMockApplicationInfo(String packageName, int userId) {
547        ApplicationInfo ai = new ApplicationInfo();
548        String codeDir = "/data/app/" + packageName;
549        ai.setBaseCodePath(codeDir + "/base.dex");
550        ai.setSplitCodePaths(new String[] {codeDir + "/split-1.dex", codeDir + "/split-2.dex"});
551        ai.dataDir = "/data/user/" + userId + "/" + packageName;
552        ai.deviceProtectedDataDir = "/data/user_de/" + userId + "/" + packageName;
553        ai.credentialProtectedDataDir = "/data/user_ce/" + userId + "/" + packageName;
554        ai.packageName = packageName;
555        return ai;
556    }
557
558    private static class TestData {
559        private final PackageInfo mPackageInfo;
560        private final String mLoaderIsa;
561        private final String mClassLoader;
562
563        private TestData(String packageName, String loaderIsa, int userId, String classLoader) {
564            mPackageInfo = getMockPackageInfo(packageName, userId);
565            mLoaderIsa = loaderIsa;
566            mClassLoader = classLoader;
567        }
568
569        private TestData(String packageName, String loaderIsa, int userId) {
570            this(packageName, loaderIsa, userId, PATH_CLASS_LOADER_NAME);
571        }
572
573        private String getPackageName() {
574            return mPackageInfo.packageName;
575        }
576
577        List<String> getSecondaryDexPaths() {
578            List<String> paths = new ArrayList<>();
579            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary1.dex");
580            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary2.dex");
581            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary3.dex");
582            return paths;
583        }
584
585        List<String> getSecondaryDexPathsForOwnUse() {
586            List<String> paths = new ArrayList<>();
587            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary4.dex");
588            paths.add(mPackageInfo.applicationInfo.dataDir + "/secondary5.dex");
589            return paths;
590        }
591
592        List<String> getSecondaryDexPathsFromProtectedDirs() {
593            List<String> paths = new ArrayList<>();
594            paths.add(mPackageInfo.applicationInfo.deviceProtectedDataDir + "/secondary6.dex");
595            paths.add(mPackageInfo.applicationInfo.credentialProtectedDataDir + "/secondary7.dex");
596            return paths;
597        }
598
599        List<String> getBaseAndSplitDexPaths() {
600            List<String> paths = new ArrayList<>();
601            paths.add(mPackageInfo.applicationInfo.sourceDir);
602            for (String split : mPackageInfo.applicationInfo.splitSourceDirs) {
603                paths.add(split);
604            }
605            return paths;
606        }
607
608        String replaceLastSplit() {
609            int length = mPackageInfo.applicationInfo.splitSourceDirs.length;
610            // Add an extra bogus dex extension to simulate a new split name.
611            mPackageInfo.applicationInfo.splitSourceDirs[length - 1] += ".dex";
612            return mPackageInfo.applicationInfo.splitSourceDirs[length - 1];
613        }
614    }
615}
616