Calin Juravle | c90bc92 | 2016-02-24 10:13:09 +0000 | [diff] [blame] | 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 | |
| 17 | import java.io.File; |
| 18 | import java.io.FileInputStream; |
| 19 | import java.io.FileOutputStream; |
| 20 | import java.io.IOException; |
| 21 | import java.lang.reflect.Method; |
| 22 | import java.lang.reflect.Constructor; |
| 23 | import java.util.HashMap; |
| 24 | |
| 25 | public 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 { |
Andreas Gampe | 166aaee | 2016-07-18 08:27:23 -0700 | [diff] [blame] | 114 | Class<?> pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); |
Calin Juravle | c90bc92 | 2016-02-24 10:13:09 +0000 | [diff] [blame] | 115 | if (pathClassLoader == null) { |
| 116 | throw new RuntimeException("Couldn't find path class loader class"); |
| 117 | } |
Andreas Gampe | 166aaee | 2016-07-18 08:27:23 -0700 | [diff] [blame] | 118 | Constructor<?> constructor = |
Calin Juravle | c90bc92 | 2016-02-24 10:13:09 +0000 | [diff] [blame] | 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 { |
Andreas Gampe | 166aaee | 2016-07-18 08:27:23 -0700 | [diff] [blame] | 128 | Class<?> c = Class.forName("dalvik.system.VMRuntime"); |
Calin Juravle | c90bc92 | 2016-02-24 10:13:09 +0000 | [diff] [blame] | 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 | } |