1 /*
2  * Copyright (C) 2017 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 package com.android.tradefed.util;
18 
19 import com.android.tradefed.build.IBuildInfo;
20 import com.android.tradefed.build.IDeviceBuildInfo;
21 import com.android.tradefed.command.CommandOptions;
22 import com.android.tradefed.config.filter.CommandOptionsGetter;
23 import com.android.tradefed.log.LogUtil.CLog;
24 import com.android.tradefed.service.TradefedFeatureClient;
25 
26 import com.google.common.annotations.VisibleForTesting;
27 import com.proto.tradefed.feature.FeatureResponse;
28 
29 import java.io.File;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 
36 
37 /** Utility class for making system calls. */
38 public class SystemUtil {
39 
40     @VisibleForTesting static SystemUtil singleton = new SystemUtil();
41 
42     // Environment variables for the test cases directory in target out directory and host out
43     // directory.
44     public enum EnvVariable {
45         ANDROID_TARGET_OUT_TESTCASES,
46         ANDROID_HOST_OUT_TESTCASES,
47     }
48 
49     public static final String REMOTE_VM_VARIABLE = "REMOTE_VM_ENV";
50 
51     private static final String HOST_TESTCASES = "host/testcases";
52     private static final String TARGET_TESTCASES = "target/testcases";
53 
54     private static final String LOCAL_AUTH_VARIABLE = "LOCAL_AUTH";
55     private static final String LOCAL_MODE = "LOCAL_MODE";
56 
57     /** Keep track of the mapping of the variables to the subpath it takes in the tests dir. */
58     public static final Map<EnvVariable, String> ENV_VARIABLE_PATHS_IN_TESTS_DIR = new HashMap<>();
59 
60     static {
ENV_VARIABLE_PATHS_IN_TESTS_DIR.put( EnvVariable.ANDROID_TARGET_OUT_TESTCASES, TARGET_TESTCASES)61         ENV_VARIABLE_PATHS_IN_TESTS_DIR.put(
62                 EnvVariable.ANDROID_TARGET_OUT_TESTCASES, TARGET_TESTCASES);
ENV_VARIABLE_PATHS_IN_TESTS_DIR.put(EnvVariable.ANDROID_HOST_OUT_TESTCASES, HOST_TESTCASES)63         ENV_VARIABLE_PATHS_IN_TESTS_DIR.put(EnvVariable.ANDROID_HOST_OUT_TESTCASES, HOST_TESTCASES);
64     }
65 
66     static final String ENV_ANDROID_PRODUCT_OUT = "ANDROID_PRODUCT_OUT";
67 
68 
69     /**
70      * Get the value of an environment variable.
71      *
72      * <p>The wrapper function is created for mock in unit test.
73      *
74      * @param name the name of the environment variable.
75      * @return {@link String} value of the given environment variable.
76      */
77     @VisibleForTesting
getEnv(String name)78     String getEnv(String name) {
79         return System.getenv(name);
80     }
81 
82     /** Get a list of {@link File} pointing to tests directories external to Tradefed. */
getExternalTestCasesDirs()83     public static List<File> getExternalTestCasesDirs() {
84         List<File> testCasesDirs = new ArrayList<File>();
85         // TODO(b/36782030): Support running both HOST and TARGET tests.
86         List<String> testCasesDirNames =
87                 // List order matters. ConfigurationFactory caller uses first dir with test config.
88                 Arrays.asList(
89                         singleton.getEnv(EnvVariable.ANDROID_TARGET_OUT_TESTCASES.name()),
90                         singleton.getEnv(EnvVariable.ANDROID_HOST_OUT_TESTCASES.name()));
91         for (String testCasesDirName : testCasesDirNames) {
92             if (testCasesDirName != null) {
93                 File dir = new File(testCasesDirName);
94                 if (dir.exists() && dir.isDirectory()) {
95                     CLog.d("Found test case dir: %s", testCasesDirName);
96                     testCasesDirs.add(dir);
97                 } else {
98                     CLog.w(
99                             "Path %s for test cases directory does not exist or it's not a "
100                                     + "directory.",
101                             testCasesDirName);
102                 }
103             }
104         }
105         return testCasesDirs;
106     }
107 
108     /**
109      * Get the file associated with the env. variable.
110      *
111      * @param envVariable ANDROID_TARGET_OUT_TESTCASES or ANDROID_HOST_OUT_TESTCASES
112      * @return The directory associated.
113      */
getExternalTestCasesDir(EnvVariable envVariable)114     public static File getExternalTestCasesDir(EnvVariable envVariable) {
115         String var = System.getenv(envVariable.name());
116         if (var == null) {
117             return null;
118         }
119         File dir = new File(var);
120         if (dir.exists() && dir.isDirectory()) {
121             CLog.d("Found test case dir: %s for %s.", dir.getAbsolutePath(), envVariable.name());
122             return dir;
123         }
124         return null;
125     }
126 
127     /**
128      * Get a list of {@link File} of the test cases directories
129      *
130      * @param buildInfo the build artifact information. Set it to null if build info is not
131      *     available or there is no need to get test cases directories from build info.
132      * @return a list of {@link File} of directories of the test cases folder of build output, based
133      *     on the value of environment variables and the given build info.
134      */
getTestCasesDirs(IBuildInfo buildInfo)135     public static List<File> getTestCasesDirs(IBuildInfo buildInfo) {
136         List<File> testCasesDirs = new ArrayList<File>();
137         testCasesDirs.addAll(getExternalTestCasesDirs());
138 
139         // TODO: Remove this logic after Versioned TF V2 is implemented, in which staging build
140         // artifact will be done by the parent process, and the test cases dirs will be set by
141         // environment variables.
142         // Add tests dir from build info.
143         if (buildInfo instanceof IDeviceBuildInfo) {
144             IDeviceBuildInfo deviceBuildInfo = (IDeviceBuildInfo) buildInfo;
145             File testsDir = deviceBuildInfo.getTestsDir();
146             // Add all possible paths to the testcases directory list.
147             if (testsDir != null) {
148                 testCasesDirs.addAll(
149                         Arrays.asList(
150                                 testsDir,
151                                 FileUtil.getFileForPath(testsDir, HOST_TESTCASES),
152                                 FileUtil.getFileForPath(testsDir, TARGET_TESTCASES)));
153             }
154         }
155 
156         return testCasesDirs;
157     }
158 
159     /**
160      * Gets the product specific output dir from an Android build tree. Typically this location
161      * contains images for various device partitions, bootloader, radio and so on.
162      *
163      * <p>Note: the method does not guarantee that this path exists.
164      *
165      * @return the location of the output dir or <code>null</code> if the current build is not
166      */
getProductOutputDir()167     public static File getProductOutputDir() {
168         String path = singleton.getEnv(ENV_ANDROID_PRODUCT_OUT);
169         if (path == null) {
170             return null;
171         } else {
172             return new File(path);
173         }
174     }
175 
176     /** Return true if we are currently running in a remote environment. */
isRemoteEnvironment()177     public static boolean isRemoteEnvironment() {
178         if ("1".equals(System.getenv(REMOTE_VM_VARIABLE))) {
179             return true;
180         }
181         return false;
182     }
183 
184     /**
185      * Returns true if Tradefed is running in local mode and should automate some actions for user.
186      */
isLocalMode()187     public static boolean isLocalMode() {
188         if (System.getenv(LOCAL_MODE) != null) {
189             return true;
190         }
191         if (System.getenv(LOCAL_AUTH_VARIABLE) != null) {
192             return true;
193         }
194         return false;
195     }
196 
197     /** Returns the path to the Java binary that current test harness is running in */
getRunningJavaBinaryPath()198     public static File getRunningJavaBinaryPath() {
199         return getRunningJavaBinaryPath(false);
200     }
201 
202     /**
203      * This version with explicit feature server is only for special situation such as noisy dry
204      * run.
205      */
getRunningJavaBinaryPath(boolean skipJavaCheck)206     public static File getRunningJavaBinaryPath(boolean skipJavaCheck) {
207         String javaHome = System.getProperty("java.home");
208         if (!skipJavaCheck) {
209             try (TradefedFeatureClient client = new TradefedFeatureClient()) {
210                 Map<String, String> args = new HashMap<String, String>();
211                 args.put(CommandOptionsGetter.OPTION_NAME, CommandOptions.JDK_FOLDER_OPTION_NAME);
212                 FeatureResponse rep =
213                         client.triggerFeature(CommandOptionsGetter.COMMAND_OPTIONS_GETTER, args);
214                 if (!rep.hasErrorInfo()) {
215                     javaHome = rep.getResponse();
216                     CLog.d("Using jdk: %s", javaHome);
217                 }
218             }
219         }
220         if (javaHome == null) {
221             throw new RuntimeException("System property \"java.home\" is not set.");
222         }
223         // this only works on *nix systems, but it's not like we officially support any others
224         File javaBinary = new File(String.format("%s/bin/java", javaHome));
225         if (!javaBinary.exists()) {
226             throw new RuntimeException(
227                     String.format("Computed Java binary path %s does not exist", javaBinary));
228         }
229         return javaBinary;
230     }
231 }
232