/*
* 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);
}
}
}
}