1/* 2 * Copyright (C) 2016 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 17import java.io.File; 18import java.io.FileInputStream; 19import java.io.FileOutputStream; 20import java.io.IOException; 21import java.lang.reflect.Method; 22import java.lang.reflect.Constructor; 23import java.util.HashMap; 24 25public class Main { 26 27 private static final String PROFILE_NAME = "primary.prof"; 28 private static final String APP_DIR_PREFIX = "app_dir_"; 29 private static final String FOREIGN_DEX_PROFILE_DIR = "foreign-dex"; 30 private static final String TEMP_FILE_NAME_PREFIX = "dummy"; 31 private static final String TEMP_FILE_NAME_SUFFIX = "-file"; 32 33 public static void main(String[] args) throws Exception { 34 File tmpFile = null; 35 File appDir = null; 36 File profileFile = null; 37 File foreignDexProfileDir = null; 38 39 try { 40 // Create the necessary files layout. 41 tmpFile = createTempFile(); 42 appDir = new File(tmpFile.getParent(), APP_DIR_PREFIX + tmpFile.getName()); 43 appDir.mkdir(); 44 foreignDexProfileDir = new File(tmpFile.getParent(), FOREIGN_DEX_PROFILE_DIR); 45 foreignDexProfileDir.mkdir(); 46 profileFile = createTempFile(); 47 48 String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar"; 49 50 // Register the app with the runtime 51 VMRuntime.registerAppInfo(profileFile.getPath(), appDir.getPath(), 52 new String[] { codePath }, foreignDexProfileDir.getPath()); 53 54 testMarkerForForeignDex(foreignDexProfileDir); 55 testMarkerForCodePath(foreignDexProfileDir); 56 testMarkerForApplicationDexFile(foreignDexProfileDir, appDir); 57 } finally { 58 if (tmpFile != null) { 59 tmpFile.delete(); 60 } 61 if (profileFile != null) { 62 profileFile.delete(); 63 } 64 if (foreignDexProfileDir != null) { 65 foreignDexProfileDir.delete(); 66 } 67 if (appDir != null) { 68 appDir.delete(); 69 } 70 } 71 } 72 73 // Verify we actually create a marker on disk for foreign dex files. 74 private static void testMarkerForForeignDex(File foreignDexProfileDir) throws Exception { 75 String foreignDex = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar"; 76 loadDexFile(foreignDex); 77 checkMarker(foreignDexProfileDir, foreignDex, /* exists */ true); 78 } 79 80 // Verify we do not create a marker on disk for dex files path of the code path. 81 private static void testMarkerForCodePath(File foreignDexProfileDir) throws Exception { 82 String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar"; 83 loadDexFile(codePath); 84 checkMarker(foreignDexProfileDir, codePath, /* exists */ false); 85 } 86 87 private static void testMarkerForApplicationDexFile(File foreignDexProfileDir, File appDir) 88 throws Exception { 89 // Copy the -ex jar to the application directory and load it from there. 90 // This will record duplicate class conflicts but we don't care for this use case. 91 File foreignDex = new File(System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar"); 92 File appDex = new File(appDir, "appDex.jar"); 93 try { 94 copyFile(foreignDex, appDex); 95 96 loadDexFile(appDex.getAbsolutePath()); 97 checkMarker(foreignDexProfileDir, appDex.getAbsolutePath(), /* exists */ false); 98 } finally { 99 if (appDex != null) { 100 appDex.delete(); 101 } 102 } 103 } 104 105 private static void checkMarker(File foreignDexProfileDir, String dexFile, boolean exists) { 106 File marker = new File(foreignDexProfileDir, dexFile.replace('/', '@')); 107 boolean result_ok = exists ? marker.exists() : !marker.exists(); 108 if (!result_ok) { 109 throw new RuntimeException("Marker test failed for:" + marker.getPath()); 110 } 111 } 112 113 private static void loadDexFile(String dexFile) throws Exception { 114 Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); 115 if (pathClassLoader == null) { 116 throw new RuntimeException("Couldn't find path class loader class"); 117 } 118 Constructor constructor = 119 pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); 120 constructor.newInstance( 121 dexFile, ClassLoader.getSystemClassLoader()); 122 } 123 124 private static class VMRuntime { 125 private static final Method registerAppInfoMethod; 126 static { 127 try { 128 Class c = Class.forName("dalvik.system.VMRuntime"); 129 registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo", 130 String.class, String.class, String[].class, String.class); 131 } catch (Exception e) { 132 throw new RuntimeException(e); 133 } 134 } 135 136 public static void registerAppInfo(String pkgName, String appDir, 137 String[] codePath, String foreignDexProfileDir) throws Exception { 138 registerAppInfoMethod.invoke(null, pkgName, appDir, codePath, foreignDexProfileDir); 139 } 140 } 141 142 private static void copyFile(File fromFile, File toFile) throws Exception { 143 FileInputStream in = new FileInputStream(fromFile); 144 FileOutputStream out = new FileOutputStream(toFile); 145 try { 146 byte[] buffer = new byte[4096]; 147 int bytesRead; 148 while ((bytesRead = in.read(buffer)) >= 0) { 149 out.write(buffer, 0, bytesRead); 150 } 151 } finally { 152 out.flush(); 153 try { 154 out.getFD().sync(); 155 } catch (IOException e) { 156 } 157 out.close(); 158 in.close(); 159 } 160 } 161 162 private static File createTempFile() throws Exception { 163 try { 164 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 165 } catch (IOException e) { 166 System.setProperty("java.io.tmpdir", "/data/local/tmp"); 167 try { 168 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 169 } catch (IOException e2) { 170 System.setProperty("java.io.tmpdir", "/sdcard"); 171 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); 172 } 173 } 174 } 175} 176