1 /* 2 * Copyright (C) 2013 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.build; 18 19 import com.android.annotations.VisibleForTesting; 20 import com.android.tradefed.build.IBuildInfo.BuildInfoProperties; 21 import com.android.tradefed.config.Option; 22 import com.android.tradefed.config.OptionClass; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.device.StubDevice; 26 import com.android.tradefed.invoker.ExecutionFiles; 27 import com.android.tradefed.invoker.ExecutionFiles.FilesKey; 28 import com.android.tradefed.invoker.logger.CurrentInvocation; 29 import com.android.tradefed.invoker.logger.CurrentInvocation.InvocationInfo; 30 import com.android.tradefed.result.error.InfraErrorIdentifier; 31 import com.android.tradefed.util.BuildInfoUtil; 32 import com.android.tradefed.util.FileUtil; 33 34 import java.io.File; 35 import java.io.IOException; 36 import java.util.LinkedHashMap; 37 import java.util.Map; 38 import java.util.Map.Entry; 39 40 /** 41 * A {@link IDeviceBuildProvider} that bootstraps build info from the test device 42 * 43 * <p> 44 * This is typically used for devices with an externally supplied build, i.e. not generated by 45 * in-house build system. Certain information, specifically the branch, is not actually available 46 * from the device, therefore it's artificially generated. 47 * 48 * <p>All build meta data info comes from various ro.* property fields on device 49 * 50 * <p>Currently this build provider generates meta data as follows: 51 * <ul> 52 * <li>branch: 53 * $(ro.product.brand)-$(ro.product.name)-$(ro.product.device)-$(ro.build.version.release), 54 * for example: 55 * <ul> 56 * <li>for Google Play edition Samsung S4 running Android 4.2: samsung-jgedlteue-jgedlte-4.2 57 * <li>for Nexus 7 running Android 4.2: google-nakasi-grouper-4.2 58 * </ul> 59 * <li>build flavor: as provided by {@link ITestDevice#getBuildFlavor()} 60 * <li>build alias: as provided by {@link ITestDevice#getBuildAlias()} 61 * <li>build id: as provided by {@link ITestDevice#getBuildId()} 62 */ 63 @OptionClass(alias = "bootstrap-build") 64 public class BootstrapBuildProvider implements IDeviceBuildProvider { 65 66 @Option(name="build-target", description="build target name to supply.") 67 private String mBuildTargetName = "bootstrapped"; 68 69 @Option(name="branch", description="build branch name to supply.") 70 private String mBranch = null; 71 72 @Option( 73 name = "build-id", 74 description = "Specify the build id to report instead of the one from the device." 75 ) 76 private String mBuildId = null; 77 78 @Option(name="shell-available-timeout", 79 description="Time to wait in seconds for device shell to become available. " + 80 "Default to 300 seconds.") 81 private long mShellAvailableTimeout = 5 * 60; 82 83 @Option(name="tests-dir", description="Path to top directory of expanded tests zip") 84 private File mTestsDir = null; 85 86 @Option( 87 name = "extra-file", 88 description = 89 "The extra file to be added to the Build Provider. " 90 + "Can be repeated. For example --extra-file file_key_1=/path/to/file") 91 private Map<String, File> mExtraFiles = new LinkedHashMap<>(); 92 93 @Override getBuild()94 public IBuildInfo getBuild() throws BuildRetrievalError { 95 throw new UnsupportedOperationException("Call getBuild(ITestDevice)"); 96 } 97 98 @Override cleanUp(IBuildInfo info)99 public void cleanUp(IBuildInfo info) { 100 } 101 102 @Override getBuild(ITestDevice device)103 public IBuildInfo getBuild(ITestDevice device) throws BuildRetrievalError, 104 DeviceNotAvailableException { 105 IBuildInfo info = new DeviceBuildInfo(mBuildId, mBuildTargetName); 106 addFiles(info, mExtraFiles); 107 info.setProperties(BuildInfoProperties.DO_NOT_COPY_ON_SHARDING); 108 if (!(device.getIDevice() instanceof StubDevice)) { 109 if (!device.waitForDeviceShell(mShellAvailableTimeout * 1000)) { 110 throw new DeviceNotAvailableException( 111 String.format( 112 "Shell did not become available in %d seconds", 113 mShellAvailableTimeout), 114 device.getSerialNumber()); 115 } 116 } else { 117 // In order to avoid issue with a null branch, use a placeholder stub for StubDevice. 118 mBranch = "stub"; 119 } 120 BuildInfoUtil.bootstrapDeviceBuildAttributes( 121 info, 122 device, 123 mBuildId, 124 null /* override build flavor */, 125 mBranch, 126 null /* override build alias */); 127 if (mTestsDir != null && mTestsDir.isDirectory()) { 128 info.setFile("testsdir", mTestsDir, info.getBuildId()); 129 } 130 // Avoid tests dir being null, by creating a temporary dir. 131 boolean createdTestDir = false; 132 if (mTestsDir == null) { 133 createdTestDir = true; 134 try { 135 mTestsDir = 136 FileUtil.createTempDir( 137 "bootstrap-test-dir", 138 CurrentInvocation.getInfo(InvocationInfo.WORK_FOLDER)); 139 } catch (IOException e) { 140 throw new BuildRetrievalError( 141 e.getMessage(), e, InfraErrorIdentifier.FAIL_TO_CREATE_FILE); 142 } 143 ((IDeviceBuildInfo) info).setTestsDir(mTestsDir, "1"); 144 } 145 if (getInvocationFiles() != null) { 146 getInvocationFiles() 147 .put( 148 FilesKey.TESTS_DIRECTORY, 149 mTestsDir, 150 !createdTestDir /* shouldNotDelete */); 151 } 152 return info; 153 } 154 155 /** 156 * Add file to build info. 157 * 158 * @param buildInfo the {@link IBuildInfo} the build info 159 * @param fileMaps the {@link Map} of file_key and file object to be added to the buildInfo 160 */ addFiles(IBuildInfo buildInfo, Map<String, File> fileMaps)161 private void addFiles(IBuildInfo buildInfo, Map<String, File> fileMaps) { 162 for (final Entry<String, File> entry : fileMaps.entrySet()) { 163 buildInfo.setFile(entry.getKey(), entry.getValue(), "0"); 164 } 165 } 166 167 @VisibleForTesting getInvocationFiles()168 ExecutionFiles getInvocationFiles() { 169 return CurrentInvocation.getInvocationFiles(); 170 } 171 getTestsDir()172 public final File getTestsDir() { 173 return mTestsDir; 174 } 175 } 176