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