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