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 import dalvik.system.PathClassLoader; 18 import java.io.File; 19 import java.lang.reflect.Constructor; 20 import java.lang.reflect.Method; 21 import java.nio.file.Files; 22 import java.util.Arrays; 23 24 public class Main { 25 // This needs to be kept in sync with DexDomain in ChildClass. 26 enum DexDomain { 27 CorePlatform, 28 Platform, 29 Application 30 } 31 main(String[] args)32 public static void main(String[] args) throws Exception { 33 System.loadLibrary(args[0]); 34 prepareNativeLibFileName(args[0]); 35 36 // Enable hidden API checks in case they are disabled by default. 37 init(); 38 39 // TODO there are sequential depencies between these test cases, and bugs 40 // in the production code may lead to subsequent tests to erroneously pass, 41 // or test the wrong thing. We rely on not deduping hidden API warnings 42 // here for the same reasons), meaning the code under test and production 43 // code are running in different configurations. Each test should be run in 44 // a fresh process to ensure that they are working correctly and not 45 // accidentally interfering with each other. 46 // As a side effect, we also cannot test Platform->Platform and later 47 // Platform->CorePlatform as the former succeeds in verifying linkage usages 48 // that should fail in the latter. 49 // We also cannot use InMemoryDexClassLoader because it runs verification in 50 // a background thread and being able to dynamically change the configuration 51 // (like list of exemptions) would require proper thread synchronization. 52 53 // Run test with both parent and child dex files loaded with class loaders. 54 // The expectation is that hidden members in parent should be visible to 55 // the child. 56 doTest(DexDomain.Application, DexDomain.Application, false); 57 doUnloading(); 58 59 // Now append parent dex file to boot class path and run again. This time 60 // the child dex file should not be able to access private APIs of the 61 // parent. 62 int parentIdx = appendToBootClassLoader(DEX_PARENT_BOOT, /* isCorePlatform */ false); 63 doTest(DexDomain.Platform, DexDomain.Application, false); 64 doUnloading(); 65 66 // Now run the same test again, but with the blacklist exmemptions list set 67 // to "L" which matches everything. 68 doTest(DexDomain.Platform, DexDomain.Application, true); 69 doUnloading(); 70 71 // Repeat the two tests above, only with parent being a core-platform dex file. 72 setDexDomain(parentIdx, /* isCorePlatform */ true); 73 doTest(DexDomain.CorePlatform, DexDomain.Application, false); 74 doUnloading(); 75 doTest(DexDomain.CorePlatform, DexDomain.Application, true); 76 doUnloading(); 77 78 // Append child to boot class path, first as a platform dex file. 79 // It should not be allowed to access non-public, non-core platform API members. 80 int childIdx = appendToBootClassLoader(DEX_CHILD, /* isCorePlatform */ false); 81 doTest(DexDomain.CorePlatform, DexDomain.Platform, false); 82 doUnloading(); 83 84 // And finally change child to core-platform dex. With both in the boot classpath 85 // and both core-platform, access should be granted. 86 setDexDomain(childIdx, /* isCorePlatform */ true); 87 doTest(DexDomain.CorePlatform, DexDomain.CorePlatform, false); 88 doUnloading(); 89 } 90 doTest(DexDomain parentDomain, DexDomain childDomain, boolean whitelistAllApis)91 private static void doTest(DexDomain parentDomain, DexDomain childDomain, 92 boolean whitelistAllApis) throws Exception { 93 // Load parent dex if it is not in boot class path. 94 ClassLoader parentLoader = null; 95 if (parentDomain == DexDomain.Application) { 96 parentLoader = new PathClassLoader(DEX_PARENT, ClassLoader.getSystemClassLoader()); 97 } else { 98 parentLoader = BOOT_CLASS_LOADER; 99 } 100 101 // Load child dex if it is not in boot class path. 102 ClassLoader childLoader = null; 103 if (childDomain == DexDomain.Application) { 104 childLoader = new PathClassLoader(DEX_CHILD, parentLoader); 105 } else { 106 if (parentLoader != BOOT_CLASS_LOADER) { 107 throw new IllegalStateException( 108 "DeclaringClass must be in parent class loader of CallingClass"); 109 } 110 childLoader = BOOT_CLASS_LOADER; 111 } 112 113 // Create a unique copy of the native library. Each shared library can only 114 // be loaded once, but for some reason even classes from a class loader 115 // cannot register their native methods against symbols in a shared library 116 // loaded by their parent class loader. 117 String nativeLibCopy = createNativeLibCopy(parentDomain, childDomain, whitelistAllApis); 118 119 // Set exemptions to "L" (matches all classes) if we are testing whitelisting. 120 setWhitelistAll(whitelistAllApis); 121 122 // Invoke ChildClass.runTest 123 Class<?> childClass = Class.forName("ChildClass", true, childLoader); 124 Method runTestMethod = childClass.getDeclaredMethod( 125 "runTest", String.class, Integer.TYPE, Integer.TYPE, Boolean.TYPE); 126 runTestMethod.invoke(null, nativeLibCopy, parentDomain.ordinal(), childDomain.ordinal(), 127 whitelistAllApis); 128 } 129 130 // Routine which tries to figure out the absolute path of our native library. prepareNativeLibFileName(String arg)131 private static void prepareNativeLibFileName(String arg) throws Exception { 132 String libName = System.mapLibraryName(arg); 133 Method libPathsMethod = Runtime.class.getDeclaredMethod("getLibPaths"); 134 libPathsMethod.setAccessible(true); 135 String[] libPaths = (String[]) libPathsMethod.invoke(Runtime.getRuntime()); 136 nativeLibFileName = null; 137 for (String p : libPaths) { 138 String candidate = p + libName; 139 if (new File(candidate).exists()) { 140 nativeLibFileName = candidate; 141 break; 142 } 143 } 144 if (nativeLibFileName == null) { 145 throw new IllegalStateException("Didn't find " + libName + " in " + 146 Arrays.toString(libPaths)); 147 } 148 } 149 150 // Copy native library to a new file with a unique name so it does not 151 // conflict with other loaded instance of the same binary file. createNativeLibCopy(DexDomain parentDomain, DexDomain childDomain, boolean whitelistAllApis)152 private static String createNativeLibCopy(DexDomain parentDomain, DexDomain childDomain, 153 boolean whitelistAllApis) throws Exception { 154 String tempFileName = System.mapLibraryName( 155 "hiddenapitest_" + (parentDomain.ordinal()) + (childDomain.ordinal()) + 156 (whitelistAllApis ? "1" : "0")); 157 File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName); 158 Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath()); 159 return tempFile.getAbsolutePath(); 160 } 161 doUnloading()162 private static void doUnloading() { 163 // Do multiple GCs to prevent rare flakiness if some other thread is 164 // keeping the classloader live. 165 for (int i = 0; i < 5; ++i) { 166 Runtime.getRuntime().gc(); 167 } 168 } 169 170 private static String nativeLibFileName; 171 172 private static final String DEX_PARENT = 173 new File(System.getenv("DEX_LOCATION"), "674-hiddenapi.jar").getAbsolutePath(); 174 private static final String DEX_PARENT_BOOT = 175 new File(new File(System.getenv("DEX_LOCATION"), "res"), "boot.jar").getAbsolutePath(); 176 private static final String DEX_CHILD = 177 new File(System.getenv("DEX_LOCATION"), "674-hiddenapi-ex.jar").getAbsolutePath(); 178 179 private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader(); 180 appendToBootClassLoader(String dexPath, boolean isCorePlatform)181 private static native int appendToBootClassLoader(String dexPath, boolean isCorePlatform); setDexDomain(int index, boolean isCorePlatform)182 private static native void setDexDomain(int index, boolean isCorePlatform); init()183 private static native void init(); setWhitelistAll(boolean value)184 private static native void setWhitelistAll(boolean value); 185 } 186