Home | History | Annotate | Download | only in dex
      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 
     17 package com.android.server.pm.dex;
     18 
     19 import android.content.pm.ApplicationInfo;
     20 import android.content.pm.IPackageManager;
     21 import android.content.pm.PackageInfo;
     22 import android.os.Build;
     23 import android.os.UserHandle;
     24 import android.support.test.filters.SmallTest;
     25 import android.support.test.runner.AndroidJUnit4;
     26 
     27 import com.android.server.pm.Installer;
     28 
     29 import dalvik.system.DelegateLastClassLoader;
     30 import dalvik.system.PathClassLoader;
     31 import dalvik.system.VMRuntime;
     32 
     33 import java.io.File;
     34 import java.util.ArrayList;
     35 import java.util.Arrays;
     36 import java.util.Collections;
     37 import java.util.HashMap;
     38 import java.util.List;
     39 import java.util.Map;
     40 
     41 import org.junit.Before;
     42 import org.junit.Rule;
     43 import org.junit.Test;
     44 import org.junit.runner.RunWith;
     45 import org.mockito.Mock;
     46 import org.mockito.junit.MockitoJUnit;
     47 import org.mockito.junit.MockitoRule;
     48 import org.mockito.quality.Strictness;
     49 
     50 import static org.junit.Assert.assertEquals;
     51 import static org.junit.Assert.assertFalse;
     52 import static org.junit.Assert.assertNotNull;
     53 import static org.junit.Assert.assertNull;
     54 import static org.junit.Assert.assertTrue;
     55 import static org.junit.Assert.fail;
     56 import static org.mockito.ArgumentMatchers.any;
     57 import static org.mockito.ArgumentMatchers.anyInt;
     58 import static org.mockito.ArgumentMatchers.anyString;
     59 import static org.mockito.Mockito.times;
     60 import static org.mockito.Mockito.verify;
     61 import static org.mockito.Mockito.when;
     62 
     63 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
     64 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
     65 
     66 @RunWith(AndroidJUnit4.class)
     67 @SmallTest
     68 public 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