1 /* 2 * Copyright (C) 2019 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.InMemoryDexClassLoader; 18 import java.lang.reflect.Method; 19 import java.io.File; 20 import java.nio.ByteBuffer; 21 import java.util.Base64; 22 23 public class Main { main(String[] args)24 public static void main(String[] args) throws Exception { 25 System.loadLibrary(args[0]); 26 27 // Feature only enabled for target SDK version Q and later. 28 setTargetSdkVersion(/* Q */ 29); 29 30 if (isDebuggable()) { 31 // Background verification is disabled in debuggable mode. This test makes 32 // no sense then. 33 return; 34 } 35 36 if (!hasOatFile()) { 37 // We only generate vdex files if the oat directories are created. 38 return; 39 } 40 41 setProcessDataDir(DEX_LOCATION); 42 43 final int maxCacheSize = getVdexCacheSize(); 44 final int numDexFiles = DEX_BYTES_CHECKSUMS.length; 45 if (numDexFiles <= maxCacheSize) { 46 throw new IllegalStateException("Not enough dex files to test cache eviction"); 47 } 48 49 // Simply load each dex file one by one. 50 check(0, getCurrentCacheSize(), "There should be no vdex files in the beginning"); 51 for (int i = 0; i < numDexFiles; ++i) { 52 ClassLoader loader = loadDex(i); 53 waitForVerifier(); 54 check(true, hasVdexFile(loader), "Loading dex file should have produced a vdex"); 55 check(Math.min(i + 1, maxCacheSize), getCurrentCacheSize(), 56 "Unexpected number of cache entries"); 57 } 58 59 // More complicated pattern where some dex files get reused. 60 for (int s = 1; s < numDexFiles; ++s) { 61 for (int i = 0; i < maxCacheSize; ++i) { 62 ClassLoader loader = loadDex(i); 63 waitForVerifier(); 64 check(true, hasVdexFile(loader), "Loading dex file should have produced a vdex"); 65 check(maxCacheSize, getCurrentCacheSize(), "Unexpected number of cache entries"); 66 } 67 } 68 } 69 isDebuggable()70 private static native boolean isDebuggable(); hasOatFile()71 private static native boolean hasOatFile(); setTargetSdkVersion(int version)72 private static native int setTargetSdkVersion(int version); setProcessDataDir(String path)73 private static native void setProcessDataDir(String path); waitForVerifier()74 private static native void waitForVerifier(); hasVdexFile(ClassLoader loader)75 private static native boolean hasVdexFile(ClassLoader loader); getVdexCacheSize()76 private static native int getVdexCacheSize(); isAnonymousVdexBasename(String basename)77 private static native boolean isAnonymousVdexBasename(String basename); 78 check(T expected, T actual, String message)79 private static <T> void check(T expected, T actual, String message) { 80 if (!expected.equals(actual)) { 81 System.err.println("ERROR: " + message + " (expected=" + expected.toString() + 82 ", actual=" + actual.toString() + ")"); 83 } 84 } 85 getCurrentCacheSize()86 private static int getCurrentCacheSize() { 87 int count = 0; 88 File folder = new File(DEX_LOCATION, "oat"); 89 File[] subfolders = folder.listFiles(); 90 if (subfolders.length != 1) { 91 throw new IllegalStateException("Expect only one subfolder - isa"); 92 } 93 folder = subfolders[0]; 94 for (File f : folder.listFiles()) { 95 if (f.isFile() && isAnonymousVdexBasename(f.getName())) { 96 count++; 97 } 98 } 99 return count; 100 } 101 createDex(int index)102 private static byte[] createDex(int index) { 103 if (index >= 100) { 104 throw new IllegalArgumentException("Not more than two decimals"); 105 } 106 107 // Clone the base dex file. This is the dex file for index 0 (class ID "01"). 108 byte[] dex = DEX_BYTES_BASE.clone(); 109 110 // Overwrite the checksum and sha1 signature. 111 System.arraycopy(DEX_BYTES_CHECKSUMS[index], 0, dex, DEX_BYTES_CHECKSUM_OFFSET, 112 DEX_BYTES_CHECKSUM_SIZE); 113 114 // Check that the class ID offsets match expectations - they should contains "01". 115 if (dex[DEX_BYTES_CLASS_ID_OFFSET1 + 0] != 0x30 || 116 dex[DEX_BYTES_CLASS_ID_OFFSET1 + 1] != 0x31 || 117 dex[DEX_BYTES_CLASS_ID_OFFSET2 + 0] != 0x30 || 118 dex[DEX_BYTES_CLASS_ID_OFFSET2 + 1] != 0x31) { 119 throw new IllegalStateException("Wrong class name values"); 120 } 121 122 // Overwrite class ID. 123 byte str_id1 = (byte) (0x30 + ((index + 1) / 10)); 124 byte str_id2 = (byte) (0x30 + ((index + 1) % 10)); 125 dex[DEX_BYTES_CLASS_ID_OFFSET1 + 0] = str_id1; 126 dex[DEX_BYTES_CLASS_ID_OFFSET1 + 1] = str_id2; 127 dex[DEX_BYTES_CLASS_ID_OFFSET2 + 0] = str_id1; 128 dex[DEX_BYTES_CLASS_ID_OFFSET2 + 1] = str_id2; 129 130 return dex; 131 } 132 loadDex(int index)133 private static ClassLoader loadDex(int index) { 134 return new InMemoryDexClassLoader(ByteBuffer.wrap(createDex(index)), /*parent*/ null); 135 } 136 137 private static final String DEX_LOCATION = System.getenv("DEX_LOCATION"); 138 139 private static final int DEX_BYTES_CLASS_ID_OFFSET1 = 0xfd; 140 private static final int DEX_BYTES_CLASS_ID_OFFSET2 = 0x11d; 141 private static final int DEX_BYTES_CHECKSUM_OFFSET = 8; 142 private static final int DEX_BYTES_CHECKSUM_SIZE = 24; 143 144 // Dex file for: "public class MyClass01 {}". 145 private static final byte[] DEX_BYTES_BASE = Base64.getDecoder().decode( 146 "ZGV4CjAzNQBHVjDjQ9WQ2TSezZ0exFH00hvlJrenqvNEAgAAcAAAAHhWNBIAAAAAAAAAALABAAAG" + 147 "AAAAcAAAAAMAAACIAAAAAQAAAJQAAAAAAAAAAAAAAAIAAACgAAAAAQAAALAAAAB0AQAA0AAAAOwA" + 148 "AAD0AAAAAQEAABUBAAAlAQAAKAEAAAEAAAACAAAABAAAAAQAAAACAAAAAAAAAAAAAAAAAAAAAQAA" + 149 "AAAAAAAAAAAAAQAAAAEAAAAAAAAAAwAAAAAAAACfAQAAAAAAAAEAAQABAAAA6AAAAAQAAABwEAEA" + 150 "AAAOAAEADgAGPGluaXQ+AAtMTXlDbGFzczAxOwASTGphdmEvbGFuZy9PYmplY3Q7AA5NeUNsYXNz" + 151 "MDEuamF2YQABVgB1fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwi" + 152 "c2hhLTEiOiI4ZjI5NTlkMDExNmMyYjdmZTZlMDUxNWQ3MTQxZTRmMGY0ZTczYzBiIiwidmVyc2lv" + 153 "biI6IjEuNS41LWRldiJ9AAAAAQAAgYAE0AEAAAAAAAAADAAAAAAAAAABAAAAAAAAAAEAAAAGAAAA" + 154 "cAAAAAIAAAADAAAAiAAAAAMAAAABAAAAlAAAAAUAAAACAAAAoAAAAAYAAAABAAAAsAAAAAEgAAAB" + 155 "AAAA0AAAAAMgAAABAAAA6AAAAAIgAAAGAAAA7AAAAAAgAAABAAAAnwEAAAMQAAABAAAArAEAAAAQ" + 156 "AAABAAAAsAEAAA=="); 157 158 // Checksum/SHA1 signature diff for classes MyClass01 - MyClassXX. 159 // This is just a convenient way of storing many similar dex files. 160 private static final byte[][] DEX_BYTES_CHECKSUMS = new byte[][] { 161 Base64.getDecoder().decode("R1Yw40PVkNk0ns2dHsRR9NIb5Sa3p6rz"), 162 Base64.getDecoder().decode("i1V1U3C8nexVk4uw185lXZd9kzd82iaA"), 163 Base64.getDecoder().decode("tFPbVPdpzuoDWqH71Ak5HpltBHg0frMU"), 164 Base64.getDecoder().decode("eFSc7dENiK8nxviKBmd/O2s7h/NAj+l/"), 165 Base64.getDecoder().decode("DlUfNQ3cuVrCHRyw/cOFhqEe+0r6wlUP"), 166 Base64.getDecoder().decode("KVaBmdG8Y8kx8ltEPXWyi9OCdL14yeiW"), 167 Base64.getDecoder().decode("K1bioDTHtPwmrPXkvZ0XYCiripH6KsC2"), 168 Base64.getDecoder().decode("oVHctdpHG3YTNeQlVCshTkFKVra9TG4k"), 169 Base64.getDecoder().decode("eVWMFHRY+w4lpn9Uo9jn+eNAmaRK4HEw"), 170 Base64.getDecoder().decode("/lW3Q3U4ot5A2qkhiv4Aj+s8zv7984MA"), 171 Base64.getDecoder().decode("BFRB+4HwRbuD164DB3sVy28dc+Ea5YVQ"), 172 Base64.getDecoder().decode("klQBLEXyr0cviHDHlqFyWPGKaQQnqMiD"), 173 Base64.getDecoder().decode("jlTcJAkpnbDI/E4msuvMyWqKxNMTN0YU"), 174 Base64.getDecoder().decode("vlUOrp4aN0PxcaqQrQmm597P+Ymu5Adt"), 175 Base64.getDecoder().decode("HlXyT1GoJk1m33O8OMaYxqy3K1Byyf1S"), 176 Base64.getDecoder().decode("d1O5toIKjTXNZkgP3p9RiiafhuKw4gUH"), 177 Base64.getDecoder().decode("11RsuG9UrFHPipOj9zjuGU9obctMJbq6"), 178 Base64.getDecoder().decode("dlSW5egObqheoHSRthlR2c2jVKLGQ3QL"), 179 Base64.getDecoder().decode("ulMgQEhC0XMhmKxHtgdURY6B6JEqNb3E"), 180 Base64.getDecoder().decode("YFV08vrcs49xYr1OBhrza5H8Ha86FODz"), 181 Base64.getDecoder().decode("jFKPxTFd3kn6K0p6n8YEPgm0hiozXW1p"), 182 Base64.getDecoder().decode("LlUZdlCXwAn4qksYL6Urw+bZC/fYuJ1T"), 183 Base64.getDecoder().decode("K1SuRt9xZX5lAVtbpMauOWLVXs2KooUA"), 184 Base64.getDecoder().decode("2FJAWIk0JS9EdvkgHjquLL9qdcLeHaRJ"), 185 Base64.getDecoder().decode("YVResABr9IvZLV8eeIhM3TXfGC+Y6/x1"), 186 Base64.getDecoder().decode("UVTrkVGIh8u7FBHgcbS9flI0CY5g2E3m"), 187 Base64.getDecoder().decode("oVIu6RsrT6HgnbPzNGiYZSpKS0cqNi+a"), 188 Base64.getDecoder().decode("2FR/slWq9YC6kJRDEw21RVGmJhr3/uKZ"), 189 Base64.getDecoder().decode("CFbaSi70ZVaumL7zsXWlD/ernHxCZPx6"), 190 Base64.getDecoder().decode("7FTY+T1/qevWQM6Yoe+OwNcUdgcCUomJ"), 191 }; 192 } 193