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.cts.useslibrary;
18 
19 import android.test.InstrumentationTestCase;
20 
21 import dalvik.system.BaseDexClassLoader;
22 import dalvik.system.DexFile;
23 import dalvik.system.PathClassLoader;
24 
25 import java.io.File;
26 import java.lang.reflect.Field;
27 import java.lang.reflect.Method;
28 import java.util.Arrays;
29 
30 public class UsesLibraryTest extends InstrumentationTestCase {
31     private static final String TAG = "UsesLibraryTest";
32 
33     /**
34      * Verify that the test apk is backed by an oat file in the presence of shared libraries.
35      *
36      * This essentially verifies that dex2oat was not disabled and that hopefully
37      * the shared library check did something and was successful.
38      * Note that we cannot verify the shared library oat files (if WITH_DEXPREOPT=false
39      * we do not preopt the shared libraries).
40      */
testUsesLibrary()41     public void testUsesLibrary() throws Exception {
42         ClassLoader loader = getClass().getClassLoader();
43         if (loader instanceof BaseDexClassLoader) {
44             DexFile apkDexFile = getTestDexFile((BaseDexClassLoader) loader);
45             assertTrue(isDexFileBackedByOatFile(apkDexFile));
46         }
47     }
48 
49     /**
50      * Verify that we still use an oat file (backed by a vdex-only file) when the shared
51      * libraries are missing.
52      */
testMissingLibrary()53     public void testMissingLibrary() throws Exception {
54         ClassLoader loader = getClass().getClassLoader();
55         if (loader instanceof BaseDexClassLoader) {
56             DexFile apkDexFile = getTestDexFile((BaseDexClassLoader) loader);
57 
58             PathClassLoader testLoader = new PathClassLoader(apkDexFile.getName(), null);
59             Object[] testDexElements = getDexElementsFromClassLoader(testLoader);
60             assertTrue(testDexElements != null && testDexElements.length == 1);
61 
62             DexFile testDexFile = getDexFileFromDexElement(testDexElements[0]);
63             assertTrue(isDexFileBackedByOatFile(testDexFile));
64         }
65     }
66 
67     /**
68      * Verify that we still use an oat file (backed by a vdex-only file) if the classpath generates
69      * a class collision failure.
70      */
testDuplicateLibrary()71     public void testDuplicateLibrary() throws Exception {
72         ClassLoader loader = getClass().getClassLoader();
73         if (loader instanceof BaseDexClassLoader) {
74             Object[] dexElements = getDexElementsFromClassLoader((BaseDexClassLoader) loader);
75             assertTrue(dexElements != null);
76             assertTrue(dexElements.length > 0);
77 
78             // The last dex file should be the test apk file: com.android.cts.useslibrary.
79             DexFile apkDexFile = getDexFileFromDexElement(dexElements[dexElements.length - 1]);
80             String apkPath = apkDexFile.getName();
81 
82             // In order to ensure the collision check is executed we use the apkDexFile when
83             // constructing the test class path for duplicates.
84             String testPath = apkPath + File.pathSeparator + apkPath;
85 
86             PathClassLoader testLoader = new PathClassLoader(testPath, null);
87             Object[] testDexElements = getDexElementsFromClassLoader(testLoader);
88             assertTrue(testDexElements != null);
89             assertEquals(Arrays.toString(testDexElements), 2, testDexElements.length);
90 
91             DexFile testDexFile = getDexFileFromDexElement(testDexElements[1]);
92             assertTrue(isDexFileBackedByOatFile(testDexFile));
93         }
94     }
95 
getDexElementsFromClassLoader(BaseDexClassLoader loader)96     private Object[] getDexElementsFromClassLoader(BaseDexClassLoader loader) throws Exception {
97         Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
98         pathListField.setAccessible(true);
99         // This is a DexPathList, but that class is package private.
100         Object pathList = pathListField.get(loader);
101         Field dexElementsField = pathList.getClass().getDeclaredField("dexElements");
102         dexElementsField.setAccessible(true);
103         // The objects in this array are Elements, but that class is package private.
104         return (Object[]) dexElementsField.get(pathList);
105     }
106 
107     // The argument must be a DexPathList.Element.
getDexFileFromDexElement(Object dexElement)108     private DexFile getDexFileFromDexElement(Object dexElement) throws Exception {
109         Field dexFileField = dexElement.getClass().getDeclaredField("dexFile");
110         dexFileField.setAccessible(true);
111         return (DexFile) dexFileField.get(dexElement);
112     }
113 
getTestDexFile(BaseDexClassLoader loader)114     private DexFile getTestDexFile(BaseDexClassLoader loader) throws Exception {
115         Object[] dexElements = getDexElementsFromClassLoader(loader);
116         assertTrue(dexElements != null && dexElements.length > 0);
117         // First dex files are shared libraries and the cts instrumentation library.
118         // The last dex file should be the test apk file: com.android.cts.useslibrary.
119         return getDexFileFromDexElement(dexElements[dexElements.length - 1]);
120     }
121 
isDexFileBackedByOatFile(DexFile dexFile)122     private boolean isDexFileBackedByOatFile(DexFile dexFile) throws Exception {
123         Method isBackedByOatFileMethod = DexFile.class.getDeclaredMethod("isBackedByOatFile");
124         isBackedByOatFileMethod.setAccessible(true);
125         return (boolean) isBackedByOatFileMethod.invoke(dexFile);
126     }
127 }
128