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