/* * Copyright (C) 2016 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 java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.util.HashMap; public class Main { private static final String PROFILE_NAME = "primary.prof"; private static final String APP_DIR_PREFIX = "app_dir_"; private static final String FOREIGN_DEX_PROFILE_DIR = "foreign-dex"; private static final String TEMP_FILE_NAME_PREFIX = "dummy"; private static final String TEMP_FILE_NAME_SUFFIX = "-file"; public static void main(String[] args) throws Exception { File tmpFile = null; File appDir = null; File profileFile = null; File foreignDexProfileDir = null; try { // Create the necessary files layout. tmpFile = createTempFile(); appDir = new File(tmpFile.getParent(), APP_DIR_PREFIX + tmpFile.getName()); appDir.mkdir(); foreignDexProfileDir = new File(tmpFile.getParent(), FOREIGN_DEX_PROFILE_DIR); foreignDexProfileDir.mkdir(); profileFile = createTempFile(); String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar"; // Register the app with the runtime VMRuntime.registerAppInfo(profileFile.getPath(), appDir.getPath(), new String[] { codePath }, foreignDexProfileDir.getPath()); testMarkerForForeignDex(foreignDexProfileDir); testMarkerForCodePath(foreignDexProfileDir); testMarkerForApplicationDexFile(foreignDexProfileDir, appDir); } finally { if (tmpFile != null) { tmpFile.delete(); } if (profileFile != null) { profileFile.delete(); } if (foreignDexProfileDir != null) { foreignDexProfileDir.delete(); } if (appDir != null) { appDir.delete(); } } } // Verify we actually create a marker on disk for foreign dex files. private static void testMarkerForForeignDex(File foreignDexProfileDir) throws Exception { String foreignDex = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar"; loadDexFile(foreignDex); checkMarker(foreignDexProfileDir, foreignDex, /* exists */ true); } // Verify we do not create a marker on disk for dex files path of the code path. private static void testMarkerForCodePath(File foreignDexProfileDir) throws Exception { String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar"; loadDexFile(codePath); checkMarker(foreignDexProfileDir, codePath, /* exists */ false); } private static void testMarkerForApplicationDexFile(File foreignDexProfileDir, File appDir) throws Exception { // Copy the -ex jar to the application directory and load it from there. // This will record duplicate class conflicts but we don't care for this use case. File foreignDex = new File(System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar"); File appDex = new File(appDir, "appDex.jar"); try { copyFile(foreignDex, appDex); loadDexFile(appDex.getAbsolutePath()); checkMarker(foreignDexProfileDir, appDex.getAbsolutePath(), /* exists */ false); } finally { if (appDex != null) { appDex.delete(); } } } private static void checkMarker(File foreignDexProfileDir, String dexFile, boolean exists) { File marker = new File(foreignDexProfileDir, dexFile.replace('/', '@')); boolean result_ok = exists ? marker.exists() : !marker.exists(); if (!result_ok) { throw new RuntimeException("Marker test failed for:" + marker.getPath()); } } private static void loadDexFile(String dexFile) throws Exception { Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader"); if (pathClassLoader == null) { throw new RuntimeException("Couldn't find path class loader class"); } Constructor constructor = pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class); constructor.newInstance( dexFile, ClassLoader.getSystemClassLoader()); } private static class VMRuntime { private static final Method registerAppInfoMethod; static { try { Class c = Class.forName("dalvik.system.VMRuntime"); registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo", String.class, String.class, String[].class, String.class); } catch (Exception e) { throw new RuntimeException(e); } } public static void registerAppInfo(String pkgName, String appDir, String[] codePath, String foreignDexProfileDir) throws Exception { registerAppInfoMethod.invoke(null, pkgName, appDir, codePath, foreignDexProfileDir); } } private static void copyFile(File fromFile, File toFile) throws Exception { FileInputStream in = new FileInputStream(fromFile); FileOutputStream out = new FileOutputStream(toFile); try { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { out.write(buffer, 0, bytesRead); } } finally { out.flush(); try { out.getFD().sync(); } catch (IOException e) { } out.close(); in.close(); } } private static File createTempFile() throws Exception { try { return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); } catch (IOException e) { System.setProperty("java.io.tmpdir", "/data/local/tmp"); try { return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); } catch (IOException e2) { System.setProperty("java.io.tmpdir", "/sdcard"); return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); } } } }