Home | History | Annotate | Download | only in src
      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 {
    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