Home | History | Annotate | Download | only in dex
      1 /*
      2  * Copyright (C) 2017 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 com.android.server.pm.PackageDexOptimizer;
     20 
     21 import static com.android.server.pm.PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
     22 import static org.junit.Assert.assertEquals;
     23 import static org.junit.Assert.assertNotNull;
     24 import static org.junit.Assert.assertNull;
     25 import static org.junit.Assert.assertTrue;
     26 import static org.junit.Assert.fail;
     27 
     28 import android.content.pm.ApplicationInfo;
     29 import android.support.test.filters.SmallTest;
     30 import android.support.test.runner.AndroidJUnit4;
     31 import android.util.SparseArray;
     32 
     33 import dalvik.system.DelegateLastClassLoader;
     34 import dalvik.system.DexClassLoader;
     35 import dalvik.system.PathClassLoader;
     36 
     37 import org.junit.Test;
     38 import org.junit.runner.RunWith;
     39 
     40 import java.io.File;
     41 import java.util.Arrays;
     42 import java.util.Collections;
     43 import java.util.List;
     44 
     45 @RunWith(AndroidJUnit4.class)
     46 @SmallTest
     47 public class DexoptUtilsTest {
     48     private static final String DEX_CLASS_LOADER_NAME = DexClassLoader.class.getName();
     49     private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
     50     private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
     51             DelegateLastClassLoader.class.getName();
     52 
     53     private static class TestData {
     54         ApplicationInfo info;
     55         boolean[] pathsWithCode;
     56     }
     57 
     58     private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits,
     59             boolean addSplitDependencies) {
     60         ApplicationInfo ai = new ApplicationInfo();
     61         String codeDir = "/data/app/mock.android.com";
     62         ai.setBaseCodePath(codeDir + "/base.dex");
     63         ai.classLoaderName = baseClassLoader;
     64         ai.privateFlags = ai.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
     65         boolean[] pathsWithCode;
     66         if (!addSplits) {
     67             pathsWithCode = new boolean[] {true};
     68         } else {
     69             pathsWithCode = new boolean[9];
     70             Arrays.fill(pathsWithCode, true);
     71             pathsWithCode[7] = false;  // config split
     72 
     73             ai.setSplitCodePaths(new String[]{
     74                     codeDir + "/base-1.dex",
     75                     codeDir + "/base-2.dex",
     76                     codeDir + "/base-3.dex",
     77                     codeDir + "/base-4.dex",
     78                     codeDir + "/base-5.dex",
     79                     codeDir + "/base-6.dex",
     80                     codeDir + "/config-split-7.dex",
     81                     codeDir + "/feature-no-deps.dex"});
     82 
     83             ai.splitClassLoaderNames = new String[]{
     84                     DELEGATE_LAST_CLASS_LOADER_NAME,
     85                     DELEGATE_LAST_CLASS_LOADER_NAME,
     86                     PATH_CLASS_LOADER_NAME,
     87                     DEX_CLASS_LOADER_NAME,
     88                     PATH_CLASS_LOADER_NAME,
     89                     null,   // A null class loader name should default to PathClassLoader.
     90                     null,   // The config split gets a null class loader.
     91                     null};  // The feature split with no dependency and no specified class loader.
     92             if (addSplitDependencies) {
     93                 ai.splitDependencies = new SparseArray<>(ai.splitClassLoaderNames.length + 1);
     94                 ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency
     95                 ai.splitDependencies.put(1, new int[] {2}); // split 1 depends on 2
     96                 ai.splitDependencies.put(2, new int[] {4}); // split 2 depends on 4
     97                 ai.splitDependencies.put(3, new int[] {4}); // split 3 depends on 4
     98                 ai.splitDependencies.put(4, new int[] {0}); // split 4 depends on base
     99                 ai.splitDependencies.put(5, new int[] {0}); // split 5 depends on base
    100                 ai.splitDependencies.put(6, new int[] {5}); // split 6 depends on 5
    101                 // Do not add the config split to the dependency list.
    102                 // Do not add the feature split with no dependency to the dependency list.
    103             }
    104         }
    105         TestData data = new TestData();
    106         data.info = ai;
    107         data.pathsWithCode = pathsWithCode;
    108         return data;
    109     }
    110 
    111     @Test
    112     public void testSplitChain() {
    113         TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
    114         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
    115         String[] contexts = DexoptUtils.getClassLoaderContexts(
    116                 data.info, sharedLibrary, data.pathsWithCode);
    117 
    118         assertEquals(9, contexts.length);
    119         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
    120         assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]",
    121                 contexts[1]);
    122         assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
    123         assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
    124         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
    125         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
    126         assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
    127         assertEquals(null, contexts[7]);  // config split
    128         assertEquals("PCL[]", contexts[8]);  // feature split with no dependency
    129     }
    130 
    131     @Test
    132     public void testSplitChainNoSplitDependencies() {
    133         TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
    134         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
    135         String[] contexts = DexoptUtils.getClassLoaderContexts(
    136                 data.info, sharedLibrary, data.pathsWithCode);
    137 
    138         assertEquals(9, contexts.length);
    139         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
    140         assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]);
    141         assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]);
    142         assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex]", contexts[3]);
    143         assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]);
    144         assertEquals(
    145                 "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]",
    146                 contexts[5]);
    147         assertEquals(
    148                 "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
    149                 contexts[6]);
    150         assertEquals(null, contexts[7]);  // config split
    151         assertEquals(
    152                 "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
    153                 contexts[8]);  // feature split with no dependency
    154     }
    155 
    156     @Test
    157     public void testSplitChainNoIsolationNoSharedLibrary() {
    158         TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
    159         data.info.privateFlags = data.info.privateFlags
    160                 & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
    161         String[] contexts = DexoptUtils.getClassLoaderContexts(
    162                 data.info, null, data.pathsWithCode);
    163 
    164         assertEquals(9, contexts.length);
    165         assertEquals("PCL[]", contexts[0]);
    166         assertEquals("PCL[base.dex]", contexts[1]);
    167         assertEquals("PCL[base.dex:base-1.dex]", contexts[2]);
    168         assertEquals("PCL[base.dex:base-1.dex:base-2.dex]", contexts[3]);
    169         assertEquals("PCL[base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]);
    170         assertEquals("PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]", contexts[5]);
    171         assertEquals(
    172                 "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
    173                 contexts[6]);
    174         assertEquals(null, contexts[7]);  // config split
    175         assertEquals(
    176                 "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
    177                 contexts[8]);  // feature split with no dependency
    178     }
    179 
    180     @Test
    181     public void testSplitChainNoSharedLibraries() {
    182         TestData data = createMockApplicationInfo(
    183                 DELEGATE_LAST_CLASS_LOADER_NAME, true, true);
    184         String[] contexts = DexoptUtils.getClassLoaderContexts(
    185                 data.info, null, data.pathsWithCode);
    186 
    187         assertEquals(9, contexts.length);
    188         assertEquals("DLC[]", contexts[0]);
    189         assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];DLC[base.dex]", contexts[1]);
    190         assertEquals("DLC[];PCL[base-4.dex];DLC[base.dex]", contexts[2]);
    191         assertEquals("PCL[];PCL[base-4.dex];DLC[base.dex]", contexts[3]);
    192         assertEquals("PCL[];DLC[base.dex]", contexts[4]);
    193         assertEquals("PCL[];DLC[base.dex]", contexts[5]);
    194         assertEquals("PCL[];PCL[base-5.dex];DLC[base.dex]", contexts[6]);
    195         assertEquals(null, contexts[7]);  // config split
    196         assertEquals("PCL[]", contexts[8]);  // feature split with no dependency
    197     }
    198 
    199     @Test
    200     public void testSplitChainWithNullPrimaryClassLoader() {
    201         // A null classLoaderName should mean PathClassLoader.
    202         TestData data = createMockApplicationInfo(null, true, true);
    203         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
    204         String[] contexts = DexoptUtils.getClassLoaderContexts(
    205                 data.info, sharedLibrary, data.pathsWithCode);
    206 
    207         assertEquals(9, contexts.length);
    208         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
    209         assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
    210         assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
    211         assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
    212         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
    213         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
    214         assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
    215         assertEquals(null, contexts[7]);  // config split
    216         assertEquals("PCL[]", contexts[8]);  // feature split with no dependency
    217     }
    218 
    219     @Test
    220     public void tesNoSplits() {
    221         TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
    222         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
    223         String[] contexts = DexoptUtils.getClassLoaderContexts(
    224                 data.info, sharedLibrary, data.pathsWithCode);
    225 
    226         assertEquals(1, contexts.length);
    227         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
    228     }
    229 
    230     @Test
    231     public void tesNoSplitsNullClassLoaderName() {
    232         TestData data = createMockApplicationInfo(null, false, false);
    233         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
    234         String[] contexts = DexoptUtils.getClassLoaderContexts(
    235                 data.info, sharedLibrary, data.pathsWithCode);
    236 
    237         assertEquals(1, contexts.length);
    238         assertEquals("PCL[a.dex:b.dex]", contexts[0]);
    239     }
    240 
    241     @Test
    242     public void tesNoSplitDelegateLast() {
    243         TestData data = createMockApplicationInfo(
    244                 DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
    245         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
    246         String[] contexts = DexoptUtils.getClassLoaderContexts(
    247                 data.info, sharedLibrary, data.pathsWithCode);
    248 
    249         assertEquals(1, contexts.length);
    250         assertEquals("DLC[a.dex:b.dex]", contexts[0]);
    251     }
    252 
    253     @Test
    254     public void tesNoSplitsNoSharedLibraries() {
    255         TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
    256         String[] contexts = DexoptUtils.getClassLoaderContexts(
    257                 data.info, null, data.pathsWithCode);
    258 
    259         assertEquals(1, contexts.length);
    260         assertEquals("PCL[]", contexts[0]);
    261     }
    262 
    263     @Test
    264     public void tesNoSplitDelegateLastNoSharedLibraries() {
    265         TestData data = createMockApplicationInfo(
    266                 DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
    267         String[] contexts = DexoptUtils.getClassLoaderContexts(
    268                 data.info, null, data.pathsWithCode);
    269 
    270         assertEquals(1, contexts.length);
    271         assertEquals("DLC[]", contexts[0]);
    272     }
    273 
    274     @Test
    275     public void testContextWithNoCode() {
    276         TestData data = createMockApplicationInfo(null, true, false);
    277         Arrays.fill(data.pathsWithCode, false);
    278 
    279         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
    280         String[] contexts = DexoptUtils.getClassLoaderContexts(
    281                 data.info, sharedLibrary, data.pathsWithCode);
    282 
    283         assertEquals(9, contexts.length);
    284         assertEquals(null, contexts[0]);
    285         assertEquals(null, contexts[1]);
    286         assertEquals(null, contexts[2]);
    287         assertEquals(null, contexts[3]);
    288         assertEquals(null, contexts[4]);
    289         assertEquals(null, contexts[5]);
    290         assertEquals(null, contexts[6]);
    291         assertEquals(null, contexts[7]);
    292     }
    293 
    294     @Test
    295     public void testContextBaseNoCode() {
    296         TestData data = createMockApplicationInfo(null, true, true);
    297         data.pathsWithCode[0] = false;
    298         String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
    299         String[] contexts = DexoptUtils.getClassLoaderContexts(
    300                 data.info, sharedLibrary, data.pathsWithCode);
    301 
    302         assertEquals(9, contexts.length);
    303         assertEquals(null, contexts[0]);
    304         assertEquals("DLC[];DLC[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
    305         assertEquals("DLC[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
    306         assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
    307         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
    308         assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
    309         assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
    310         assertEquals(null, contexts[7]);
    311     }
    312 
    313     @Test
    314     public void testProcessContextForDexLoad() {
    315         List<String> classLoaders = Arrays.asList(
    316                 DELEGATE_LAST_CLASS_LOADER_NAME,
    317                 PATH_CLASS_LOADER_NAME,
    318                 PATH_CLASS_LOADER_NAME);
    319         List<String> classPaths = Arrays.asList(
    320                 String.join(File.pathSeparator, "foo.dex", "bar.dex"),
    321                 String.join(File.pathSeparator, "parent1.dex"),
    322                 String.join(File.pathSeparator, "parent2.dex", "parent3.dex"));
    323         String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
    324         assertNotNull(context);
    325         assertEquals(2, context.length);
    326         assertEquals("DLC[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]);
    327         assertEquals("DLC[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]);
    328     }
    329 
    330     @Test
    331     public void testProcessContextForDexLoadSingleElement() {
    332         List<String> classLoaders = Arrays.asList(PATH_CLASS_LOADER_NAME);
    333         List<String> classPaths = Arrays.asList(
    334                 String.join(File.pathSeparator, "foo.dex", "bar.dex", "zoo.dex"));
    335         String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
    336         assertNotNull(context);
    337         assertEquals(3, context.length);
    338         assertEquals("PCL[]", context[0]);
    339         assertEquals("PCL[foo.dex]", context[1]);
    340         assertEquals("PCL[foo.dex:bar.dex]", context[2]);
    341     }
    342 
    343     @Test
    344     public void testProcessContextForDexLoadUnsupported() {
    345         List<String> classLoaders = Arrays.asList(
    346                 DELEGATE_LAST_CLASS_LOADER_NAME,
    347                 "unsupported.class.loader");
    348         List<String> classPaths = Arrays.asList(
    349                 String.join(File.pathSeparator, "foo.dex", "bar.dex"),
    350                 String.join(File.pathSeparator, "parent1.dex"));
    351         String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
    352         assertNull(context);
    353     }
    354 
    355     @Test
    356     public void testProcessContextForDexLoadIllegalCallEmptyList() {
    357         boolean gotException = false;
    358         try {
    359             DexoptUtils.processContextForDexLoad(Collections.emptyList(), Collections.emptyList());
    360         } catch (IllegalArgumentException ignore) {
    361             gotException = true;
    362         }
    363         assertTrue(gotException);
    364     }
    365 
    366     @Test
    367     public void testProcessContextForDexLoadIllegalCallDifferentSize() {
    368         boolean gotException = false;
    369         try {
    370             DexoptUtils.processContextForDexLoad(Collections.emptyList(), Arrays.asList("a"));
    371         } catch (IllegalArgumentException ignore) {
    372             gotException = true;
    373         }
    374         assertTrue(gotException);
    375     }
    376 
    377     @Test
    378     public void testEncodeClassLoader() {
    379         assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
    380                 SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.PathClassLoader"));
    381         assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
    382                 SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DexClassLoader"));
    383         assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
    384                 SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DelegateLastClassLoader"));
    385         assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
    386                 "dalvik.system.PathClassLoader"));
    387         assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
    388                 "dalvik.system.DexClassLoader"));
    389         assertEquals("DLC[xyz]", DexoptUtils.encodeClassLoader("xyz",
    390                 "dalvik.system.DelegateLastClassLoader"));
    391         assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz", null));
    392         assertEquals("abc[xyz]", DexoptUtils.encodeClassLoader("xyz", "abc"));
    393 
    394         try {
    395             DexoptUtils.encodeClassLoader(null, "abc");
    396             fail(); // Exception should be caught.
    397         } catch (NullPointerException expected) {}
    398     }
    399 
    400     @Test
    401     public void testEncodeClassLoaderChain() {
    402         assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain(
    403                 SKIP_SHARED_LIBRARY_CHECK, "PCL[a]"));
    404         assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
    405                 SKIP_SHARED_LIBRARY_CHECK));
    406         assertEquals("PCL[a];DLC[b]", DexoptUtils.encodeClassLoaderChain("PCL[a]",
    407                 "DLC[b]"));
    408         assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
    409                 SKIP_SHARED_LIBRARY_CHECK));
    410 
    411         try {
    412             DexoptUtils.encodeClassLoaderChain("a", null);
    413             fail(); // exception is expected
    414         } catch (NullPointerException expected) {}
    415 
    416         try {
    417             DexoptUtils.encodeClassLoaderChain(null, "b");
    418             fail(); // exception is expected
    419         } catch (NullPointerException expected) {}
    420     }
    421 }
    422