1 /* 2 * Copyright (C) 2015 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 package com.android.tradefed.util; 17 18 import com.android.tradefed.build.IBuildInfo; 19 import com.android.tradefed.build.IDeviceBuildInfo; 20 import com.android.tradefed.invoker.logger.InvocationMetricLogger; 21 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey; 22 import com.android.tradefed.targetprep.AltDirBehavior; 23 24 import java.io.File; 25 import java.io.IOException; 26 import java.net.URL; 27 import java.util.ArrayList; 28 import java.util.Collections; 29 import java.util.List; 30 31 /** 32 * A helper class for operations related to tests zip generated by Android build system 33 */ 34 public class BuildTestsZipUtils { 35 36 /** 37 * Resolve the actual apk path based on testing artifact information inside build info. 38 * 39 * @param buildInfo build artifact information 40 * @param apkFileName filename of the apk to install 41 * @param altDirs alternative search paths, in addition to path inside {@code buildInfo} 42 * @param altDirBehavior how alternative search paths should be used against path inside 43 * {@code buildInfo}: as fallback, or as override; if unspecified, fallback will be used 44 * @param lookupInResource if the file should be looked up in test harness resources as a final 45 * fallback mechanism 46 * @param deviceSigningKey 47 * @return a {@link File} representing the physical apk file on host or {@code null} if the 48 * file does not exist. 49 */ getApkFile(IBuildInfo buildInfo, String apkFileName, List<File> altDirs, AltDirBehavior altDirBehavior, boolean lookupInResource, String deviceSigningKey)50 public static File getApkFile(IBuildInfo buildInfo, String apkFileName, 51 List<File> altDirs, AltDirBehavior altDirBehavior, 52 boolean lookupInResource, String deviceSigningKey) throws IOException { 53 String apkBase = apkFileName.split("\\.")[0]; 54 55 List<File> dirs = new ArrayList<>(); 56 if (altDirs != null) { 57 for (File dir : altDirs) { 58 dirs.add(dir); 59 // Files in tests zip file will be in DATA/app/, 60 // DATA/app/apk_name or DATA/priv-app/apk_name 61 dirs.add(FileUtil.getFileForPath(dir, "DATA", "app")); 62 dirs.add(FileUtil.getFileForPath(dir, "DATA", "app", apkBase)); 63 dirs.add(FileUtil.getFileForPath(dir, "DATA", "priv-app", apkBase)); 64 // Files in out dir will be in data/app/apk_name 65 dirs.add(FileUtil.getFileForPath(dir, "data", "app", apkBase)); 66 } 67 } 68 // reverse the order so ones provided via command line last can be searched first 69 Collections.reverse(dirs); 70 71 List<File> expandedTestDirs = new ArrayList<>(); 72 File testsDir = null; 73 if (buildInfo != null && buildInfo instanceof IDeviceBuildInfo) { 74 testsDir = ((IDeviceBuildInfo) buildInfo).getTestsDir(); 75 if (testsDir != null && testsDir.exists()) { 76 expandedTestDirs.add(FileUtil.getFileForPath(testsDir, "DATA", "app")); 77 expandedTestDirs.add(FileUtil.getFileForPath(testsDir, "DATA", "app", apkBase)); 78 expandedTestDirs.add( 79 FileUtil.getFileForPath(testsDir, "DATA", "priv-app", apkBase)); 80 expandedTestDirs.add(FileUtil.getFileForPath(testsDir, apkBase)); 81 82 // Files in testcases directory imported from env. variable can have a folder 83 // hierarchy, so we search for folder. 84 File testcasesSubDir = FileUtil.findFile(testsDir, apkBase); 85 if (testcasesSubDir != null) { 86 expandedTestDirs.add(testcasesSubDir); 87 } else { 88 // If there doesn't exist a directory named after apkBase, it's possible that 89 // the apk is built output to a different module directory. Therefore, try to 90 // search entire testsDir to locate the apk. 91 // TODO(dshi): Find a better way to locate apk. Ideally we should start with the 92 // test module's directory to avoid false-positive result. 93 expandedTestDirs.add(testsDir); 94 } 95 } 96 } 97 if (altDirBehavior == null) { 98 altDirBehavior = AltDirBehavior.FALLBACK; 99 } 100 if (altDirBehavior == AltDirBehavior.FALLBACK) { 101 // alt dirs are appended after build artifact dirs 102 expandedTestDirs.addAll(dirs); 103 dirs = expandedTestDirs; 104 } else if (altDirBehavior == AltDirBehavior.OVERRIDE) { 105 dirs.addAll(expandedTestDirs); 106 } else { 107 throw new IOException("Missing handler for alt-dir-behavior: " + altDirBehavior); 108 } 109 if (dirs.isEmpty() && !lookupInResource) { 110 throw new IOException( 111 "Provided buildInfo does not contain a valid tests directory and no " + 112 "fallback options were provided"); 113 } 114 115 for (File dir : dirs) { 116 // Recursively search each folder 117 File testAppFile = FileUtil.findFile(dir, apkFileName); 118 if (testAppFile != null && testAppFile.exists()) { 119 return testAppFile; 120 } 121 } 122 if (lookupInResource) { 123 List<String> resourceLookup = new ArrayList<>(); 124 if (deviceSigningKey != null) { 125 resourceLookup.add(String.format("/apks/%s-%s.apk", apkBase, deviceSigningKey)); 126 } 127 resourceLookup.add(String.format("/apks/%s", apkFileName)); 128 File apkTempFile = FileUtil.createTempFile(apkFileName, ".apk"); 129 URL apkUrl = null; 130 for (String path : resourceLookup) { 131 apkUrl = BuildTestsZipUtils.class.getResource(path); 132 if (apkUrl != null) { 133 break; 134 } 135 } 136 if (apkUrl != null) { 137 FileUtil.writeToFile(apkUrl.openStream(), apkTempFile); 138 // since we don't know when the file would be no longer needed, we set the file to 139 // be deleted on VM termination 140 apkTempFile.deleteOnExit(); 141 return apkTempFile; 142 } 143 // If we couldn't find a resource, we delete the tmp file 144 FileUtil.deleteFile(apkTempFile); 145 } 146 147 // Try to stage the files from remote zip files. 148 if (testsDir != null) { 149 File apkFile = buildInfo.stageRemoteFile(apkFileName, testsDir); 150 if (apkFile != null) { 151 InvocationMetricLogger.addInvocationMetrics( 152 InvocationMetricKey.STAGE_UNDEFINED_DEPENDENCY, apkFileName); 153 return apkFile; 154 } 155 } 156 return null; 157 } 158 } 159