Home | History | Annotate | Download | only in dalvik
      1 /*
      2  * Copyright (C) 2015 Google Inc.
      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 package com.google.caliper.platform.dalvik;
     18 
     19 import static com.google.common.base.Preconditions.checkState;
     20 
     21 import com.google.caliper.platform.Platform;
     22 import com.google.common.base.Preconditions;
     23 import com.google.common.base.Predicate;
     24 import com.google.common.base.Predicates;
     25 import com.google.common.collect.ImmutableList;
     26 import com.google.common.collect.ImmutableSet;
     27 import com.google.common.base.Joiner;
     28 
     29 import java.io.File;
     30 import java.util.Collection;
     31 import java.util.Collections;
     32 import java.util.Map;
     33 
     34 /**
     35  * An abstraction of the Dalvik (aka Android) platform.
     36  *
     37  * <p>Although this talks about dalvik it actually works with ART too.
     38  */
     39 public final class DalvikPlatform extends Platform {
     40 
     41   // By default, use dalvikvm.
     42   // However with --vm (by calling customVmHomeDir) we can change this.
     43   private String vmExecutable = "dalvikvm";
     44   private String vmAndroidRoot = System.getenv("ANDROID_ROOT");
     45 
     46   public DalvikPlatform() {
     47     super(Type.DALVIK);
     48 
     49     if (vmAndroidRoot == null) {
     50       // Shouldn't happen unless running it on the host and someone forgot to set it.
     51       // On an actual device, ANDROID_ROOT is always set.
     52       vmAndroidRoot = "/system";
     53     }
     54   }
     55 
     56   @Override
     57   public File vmExecutable(File vmHome) {
     58     File bin = new File(vmHome, "bin");
     59     Preconditions.checkState(bin.exists() && bin.isDirectory(),
     60         "Could not find %s under android root %s", bin, vmHome);
     61     String executableName = vmExecutable;
     62     File dalvikvm = new File(bin, executableName);
     63     if (!dalvikvm.exists() || dalvikvm.isDirectory()) {
     64       throw new IllegalStateException(
     65           String.format("Cannot find %s binary in %s", executableName, bin));
     66     }
     67 
     68     return dalvikvm;
     69   }
     70 
     71   @Override
     72   public ImmutableSet<String> commonInstrumentVmArgs() {
     73     return ImmutableSet.of();
     74   }
     75 
     76   @Override
     77   public ImmutableSet<String> workerProcessArgs() {
     78     if (vmExecutable.equals("app_process")) {
     79       // app_process expects a parent_dir argument before the main class name, e.g.
     80       // app_process -cp /path/to/dex/file /system/bin foo.bar.Main
     81       return ImmutableSet.of(vmAndroidRoot + "/bin");
     82     }
     83     return ImmutableSet.of();
     84   }
     85 
     86   @Override
     87   protected String workerClassPath() {
     88     // TODO(user): Find a way to get the class path programmatically from the class loader.
     89     return System.getProperty("java.class.path");
     90   }
     91 
     92   /**
     93    * Construct the set of arguments that specify the classpath which
     94    * is passed to the worker.
     95    *
     96    * <p>
     97    * Use {@code -Djava.class.path=$classpath} for dalvik because it is supported
     98    * by dalvikvm and also by app_process (which doesn't recognize "-cp args").
     99    * </p>
    100    */
    101   @Override
    102   public ImmutableList<String> workerClassPathArgs() {
    103     String classPathArgs = String.format("-Djava.class.path=%s", workerClassPath());
    104     return ImmutableList.of(classPathArgs);
    105   }
    106 
    107   @Override
    108   public Collection<String> inputArguments() {
    109     return Collections.emptyList();
    110   }
    111 
    112   @Override
    113   public Predicate<String> vmPropertiesToRetain() {
    114     return Predicates.alwaysFalse();
    115   }
    116 
    117   @Override
    118   public void checkVmProperties(Map<String, String> options) {
    119     checkState(options.isEmpty());
    120   }
    121 
    122   @Override
    123   public File customVmHomeDir(Map<String, String> vmGroupMap, String vmConfigName) {
    124     // Support a handful of specific commands:
    125     switch (vmConfigName) {
    126       case "app_process":   // run with Android framework code already initialized.
    127       case "dalvikvm":      // same as not using --vm (selects 64-bit on 64-bit, 32-bit on 32-bit)
    128       case "dalvikvm32":    // 32-bit specific dalvikvm (e.g. if running on 64-bit device)
    129       case "dalvikvm64":    // 64-bit specific dalvikvm (which is already default on 64-bit)
    130       case "art":           // similar to dalvikvm but goes through a script with better settings.
    131       {
    132         // Usually passed as vmHome to vmExecutable, but we don't get that passed here.
    133         String vmHome = vmAndroidRoot;
    134 
    135         // Do not return the binary here. We return the new vmHome used by #vmExecutable.
    136         // Remember that the home directory was changed, and accordingly update the executable name.
    137         vmExecutable = vmConfigName;
    138 
    139         File androidRootFile = new File(vmHome);
    140 
    141         if (!androidRootFile.exists()) {
    142           throw new IllegalStateException(String.format("%s does not exist", androidRootFile));
    143         } else if (!androidRootFile.isDirectory()) {
    144           throw new IllegalStateException(String.format("%s is not a directory", androidRootFile));
    145         }
    146 
    147         return androidRootFile;
    148       }
    149     }
    150 
    151     // Unknown vm, throw an exception.
    152 
    153     Joiner.MapJoiner mapJoiner = Joiner.on(',').withKeyValueSeparator("=");
    154     String mapString = (vmGroupMap == null) ? "<null>" : mapJoiner.join(vmGroupMap);
    155 
    156     if (vmConfigName == null) {
    157       vmConfigName = "<null>";
    158     }
    159 
    160     throw new UnsupportedOperationException(
    161             "Running with a custom Dalvik VM is not currently supported (group map = '" + mapString
    162             + "', vmConfigName = '" + vmConfigName + "')");
    163   }
    164 }
    165