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 
37 /**
38  * A {@link IDeviceBuildProvider} that bootstraps build info from the test device
39  *
40  * <p>
41  * This is typically used for devices with an externally supplied build, i.e. not generated by
42  * in-house build system. Certain information, specifically the branch, is not actually available
43  * from the device, therefore it's artificially generated.
44  *
45  * <p>All build meta data info comes from various ro.* property fields on device
46  *
47  * <p>Currently this build provider generates meta data as follows:
48  * <ul>
49  * <li>branch:
50  * $(ro.product.brand)-$(ro.product.name)-$(ro.product.device)-$(ro.build.version.release),
51  * for example:
52  * <ul>
53  *   <li>for Google Play edition Samsung S4 running Android 4.2: samsung-jgedlteue-jgedlte-4.2
54  *   <li>for Nexus 7 running Android 4.2: google-nakasi-grouper-4.2
55  * </ul>
56  * <li>build flavor: as provided by {@link ITestDevice#getBuildFlavor()}
57  * <li>build alias: as provided by {@link ITestDevice#getBuildAlias()}
58  * <li>build id: as provided by {@link ITestDevice#getBuildId()}
59  */
60 @OptionClass(alias = "bootstrap-build")
61 public class BootstrapBuildProvider implements IDeviceBuildProvider {
62 
63     @Option(name="build-target", description="build target name to supply.")
64     private String mBuildTargetName = "bootstrapped";
65 
66     @Option(name="branch", description="build branch name to supply.")
67     private String mBranch = null;
68 
69     @Option(
70         name = "build-id",
71         description = "Specify the build id to report instead of the one from the device."
72     )
73     private String mBuildId = null;
74 
75     @Option(name="shell-available-timeout",
76             description="Time to wait in seconds for device shell to become available. " +
77             "Default to 300 seconds.")
78     private long mShellAvailableTimeout = 5 * 60;
79 
80     @Option(name="tests-dir", description="Path to top directory of expanded tests zip")
81     private File mTestsDir = null;
82 
83     @Override
getBuild()84     public IBuildInfo getBuild() throws BuildRetrievalError {
85         throw new UnsupportedOperationException("Call getBuild(ITestDevice)");
86     }
87 
88     @Override
cleanUp(IBuildInfo info)89     public void cleanUp(IBuildInfo info) {
90     }
91 
92     @Override
getBuild(ITestDevice device)93     public IBuildInfo getBuild(ITestDevice device) throws BuildRetrievalError,
94             DeviceNotAvailableException {
95         IBuildInfo info = new DeviceBuildInfo(mBuildId, mBuildTargetName);
96         info.setProperties(BuildInfoProperties.DO_NOT_COPY_ON_SHARDING);
97         if (!(device.getIDevice() instanceof StubDevice)) {
98             if (!device.waitForDeviceShell(mShellAvailableTimeout * 1000)) {
99                 throw new DeviceNotAvailableException(
100                         String.format(
101                                 "Shell did not become available in %d seconds",
102                                 mShellAvailableTimeout),
103                         device.getSerialNumber());
104             }
105         } else {
106             // In order to avoid issue with a null branch, use a placeholder stub for StubDevice.
107             mBranch = "stub";
108         }
109         BuildInfoUtil.bootstrapDeviceBuildAttributes(
110                 info,
111                 device,
112                 mBuildId,
113                 null /* override build flavor */,
114                 mBranch,
115                 null /* override build alias */);
116         if (mTestsDir != null && mTestsDir.isDirectory()) {
117             info.setFile("testsdir", mTestsDir, info.getBuildId());
118         }
119         // Avoid tests dir being null, by creating a temporary dir.
120         boolean createdTestDir = false;
121         if (mTestsDir == null) {
122             createdTestDir = true;
123             try {
124                 mTestsDir =
125                         FileUtil.createTempDir(
126                                 "bootstrap-test-dir",
127                                 CurrentInvocation.getInfo(InvocationInfo.WORK_FOLDER));
128             } catch (IOException e) {
129                 throw new BuildRetrievalError(
130                         e.getMessage(), e, InfraErrorIdentifier.FAIL_TO_CREATE_FILE);
131             }
132             ((IDeviceBuildInfo) info).setTestsDir(mTestsDir, "1");
133         }
134         if (getInvocationFiles() != null) {
135             getInvocationFiles()
136                     .put(
137                             FilesKey.TESTS_DIRECTORY,
138                             mTestsDir,
139                             !createdTestDir /* shouldNotDelete */);
140         }
141         return info;
142     }
143 
144     @VisibleForTesting
getInvocationFiles()145     ExecutionFiles getInvocationFiles() {
146         return CurrentInvocation.getInvocationFiles();
147     }
148 
getTestsDir()149     public final File getTestsDir() {
150         return mTestsDir;
151     }
152 }
153