/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import dalvik.system.InMemoryDexClassLoader; import java.lang.reflect.Method; import java.io.File; import java.nio.ByteBuffer; import java.util.Base64; public class Main { private static void check(boolean expected, boolean actual, String message) { if (expected != actual) { System.err.println( "ERROR: " + message + " (expected=" + expected + ", actual=" + actual + ")"); } } private static ClassLoader singleLoader() { return new InMemoryDexClassLoader( new ByteBuffer[] { ByteBuffer.wrap(DEX_BYTES_A), ByteBuffer.wrap(DEX_BYTES_B) }, /*parent*/null); } private static ClassLoader[] multiLoader() { ClassLoader clA = new InMemoryDexClassLoader(ByteBuffer.wrap(DEX_BYTES_A), /*parent*/ null); ClassLoader clB = new InMemoryDexClassLoader(ByteBuffer.wrap(DEX_BYTES_B), /*parent*/ clA); return new ClassLoader[] { clA, clB }; } private static void test(ClassLoader loader, boolean expectedHasVdexFile, boolean expectedBackedByOat, boolean invokeMethod) throws Exception { // If ART created a vdex file, it must have verified all the classes. // That happens if and only if we expect a vdex at the end of the test but // do not expect it to have been loaded. boolean expectedClassesVerified = expectedHasVdexFile && !expectedBackedByOat; waitForVerifier(); check(expectedClassesVerified, areClassesVerified(loader), "areClassesVerified"); check(expectedHasVdexFile, hasVdexFile(loader), "areClassesVerified"); check(expectedBackedByOat, isBackedByOatFile(loader), "isBackedByOatFile"); check(expectedBackedByOat, areClassesPreverified(loader), "areClassesPreverified"); if (invokeMethod) { loader.loadClass("art.ClassB").getDeclaredMethod("printHello").invoke(null); if (expectedBackedByOat) { String filter = getCompilerFilter(loader.loadClass("art.ClassB")); if (!("verify".equals(filter))) { throw new Error("Expected verify, got " + filter); } } } } public static void main(String[] args) throws Exception { System.loadLibrary(args[0]); ClassLoader[] loaders = null; // Feature only enabled for target SDK version Q and later. setTargetSdkVersion(/* Q */ 29); // Feature is disabled in debuggable mode because runtime threads are not // allowed to load classes. boolean featureEnabled = !isDebuggable(); // Data directory not set. Background verification job should not have run // and vdex should not have been created. test(singleLoader(), /*hasVdex*/ false, /*backedByOat*/ false, /*invokeMethod*/ true); // Set data directory for this process. setProcessDataDir(DEX_LOCATION); // Data directory is now set. Background verification job should have run, // should have verified classes and written results to a vdex. test(singleLoader(), /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ true); test(singleLoader(), /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, /*invokeMethod*/ true); // Test loading the two dex files with separate class loaders. // Background verification task should still verify all classes. loaders = multiLoader(); test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ false); test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ true); loaders = multiLoader(); test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, /*invokeMethod*/ false); test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, /*invokeMethod*/ true); // Change boot classpath checksum. appendToBootClassLoader(DEX_EXTRA, /*isCorePlatform*/ false); loaders = multiLoader(); test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ false); test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ false, /*invokeMethod*/ true); loaders = multiLoader(); test(loaders[0], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, /*invokeMethod*/ false); test(loaders[1], /*hasVdex*/ featureEnabled, /*backedByOat*/ featureEnabled, /*invokeMethod*/ true); } private static native boolean isDebuggable(); private static native int setTargetSdkVersion(int version); private static native void setProcessDataDir(String path); private static native void waitForVerifier(); private static native boolean areClassesVerified(ClassLoader loader); private static native boolean hasVdexFile(ClassLoader loader); private static native boolean isBackedByOatFile(ClassLoader loader); private static native boolean areClassesPreverified(ClassLoader loader); private static native String getCompilerFilter(Class cls); // Defined in 674-hiddenapi. private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform); private static final String DEX_LOCATION = System.getenv("DEX_LOCATION"); private static final String DEX_EXTRA = new File(DEX_LOCATION, "692-vdex-inmem-loader-ex.jar").getAbsolutePath(); private static final byte[] DEX_BYTES_A = Base64.getDecoder().decode( "ZGV4CjAzNQBxYu/tdPfiHaRPYr5yaT6ko9V/xMinr1OwAgAAcAAAAHhWNBIAAAAAAAAAABwCAAAK" + "AAAAcAAAAAQAAACYAAAAAgAAAKgAAAAAAAAAAAAAAAMAAADAAAAAAQAAANgAAAC4AQAA+AAAADAB" + "AAA4AQAARQEAAEwBAABPAQAAXQEAAHEBAACFAQAAiAEAAJIBAAAEAAAABQAAAAYAAAAHAAAAAwAA" + "AAIAAAAAAAAABwAAAAMAAAAAAAAAAAABAAAAAAAAAAAACAAAAAEAAQAAAAAAAAAAAAEAAAABAAAA" + "AAAAAAEAAAAAAAAACQIAAAAAAAABAAAAAAAAACwBAAADAAAAGgACABEAAAABAAEAAQAAACgBAAAE" + "AAAAcBACAAAADgATAA4AFQAOAAY8aW5pdD4AC0NsYXNzQS5qYXZhAAVIZWxsbwABTAAMTGFydC9D" + "bGFzc0E7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwABVgAIZ2V0SGVs" + "bG8AdX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoi" + "OTY2MDhmZDdiYmNjZGQyMjc2Y2Y4OTI4M2QyYjgwY2JmYzRmYzgxYyIsInZlcnNpb24iOiIxLjUu" + "NC1kZXYifQAAAAIAAIGABJACAQn4AQAAAAAADAAAAAAAAAABAAAAAAAAAAEAAAAKAAAAcAAAAAIA" + "AAAEAAAAmAAAAAMAAAACAAAAqAAAAAUAAAADAAAAwAAAAAYAAAABAAAA2AAAAAEgAAACAAAA+AAA" + "AAMgAAACAAAAKAEAAAIgAAAKAAAAMAEAAAAgAAABAAAACQIAAAMQAAABAAAAGAIAAAAQAAABAAAA" + "HAIAAA=="); private static final byte[] DEX_BYTES_B = Base64.getDecoder().decode( "ZGV4CjAzNQB+hWvce73hXt7ZVNgp9RAyMLSwQzsWUjV4AwAAcAAAAHhWNBIAAAAAAAAAAMwCAAAQ" + "AAAAcAAAAAcAAACwAAAAAwAAAMwAAAABAAAA8AAAAAUAAAD4AAAAAQAAACABAAA4AgAAQAEAAI4B" + "AACWAQAAowEAAKYBAAC0AQAAwgEAANkBAADtAQAAAQIAABUCAAAYAgAAHAIAACYCAAArAgAANwIA" + "AEACAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAAAgAAAAQAAAAAAAAACQAAAAYAAAAAAAAA" + "CgAAAAYAAACIAQAABQACAAwAAAAAAAAACwAAAAEAAQAAAAAAAQABAA0AAAACAAIADgAAAAMAAQAA" + "AAAAAQAAAAEAAAADAAAAAAAAAAEAAAAAAAAAtwIAAAAAAAABAAEAAQAAAHwBAAAEAAAAcBAEAAAA" + "DgACAAAAAgAAAIABAAAKAAAAYgAAAHEAAAAAAAwBbiADABAADgATAA4AFQAOlgAAAAABAAAABAAG" + "PGluaXQ+AAtDbGFzc0IuamF2YQABTAAMTGFydC9DbGFzc0E7AAxMYXJ0L0NsYXNzQjsAFUxqYXZh" + "L2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsA" + "EkxqYXZhL2xhbmcvU3lzdGVtOwABVgACVkwACGdldEhlbGxvAANvdXQACnByaW50SGVsbG8AB3By" + "aW50bG4AdX5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIsIm1pbi1hcGkiOjEsInNoYS0x" + "IjoiOTY2MDhmZDdiYmNjZGQyMjc2Y2Y4OTI4M2QyYjgwY2JmYzRmYzgxYyIsInZlcnNpb24iOiIx" + "LjUuNC1kZXYifQAAAAIAAYGABMACAQnYAgAAAAAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABAAAABw" + "AAAAAgAAAAcAAACwAAAAAwAAAAMAAADMAAAABAAAAAEAAADwAAAABQAAAAUAAAD4AAAABgAAAAEA" + "AAAgAQAAASAAAAIAAABAAQAAAyAAAAIAAAB8AQAAARAAAAEAAACIAQAAAiAAABAAAACOAQAAACAA" + "AAEAAAC3AgAAAxAAAAEAAADIAgAAABAAAAEAAADMAgAA"); }