1 /*
2  * Copyright (C) 2016 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.invoker;
17 
18 import com.android.tradefed.build.BuildInfo;
19 import com.android.tradefed.build.BuildSerializedVersion;
20 import com.android.tradefed.build.IBuildInfo;
21 import com.android.tradefed.config.ConfigurationDescriptor;
22 import com.android.tradefed.device.ITestDevice;
23 import com.android.tradefed.device.ITestDevice.RecoveryMode;
24 import com.android.tradefed.log.LogUtil.CLog;
25 import com.android.tradefed.testtype.suite.ITestSuite;
26 import com.android.tradefed.util.MultiMap;
27 import com.android.tradefed.util.UniqueMultiMap;
28 
29 import java.io.IOException;
30 import java.io.ObjectInputStream;
31 import java.util.ArrayList;
32 import java.util.LinkedHashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 
37 /**
38  * Generic implementation of a {@link IInvocationContext}.
39  */
40 public class InvocationContext implements IInvocationContext {
41     private static final long serialVersionUID = BuildSerializedVersion.VERSION;
42 
43     // Transient field are not serialized
44     private transient Map<ITestDevice, IBuildInfo> mAllocatedDeviceAndBuildMap;
45     /** Map of the configuration device name and the actual {@link ITestDevice} * */
46     private transient Map<String, ITestDevice> mNameAndDeviceMap;
47     private Map<String, IBuildInfo> mNameAndBuildinfoMap;
48     private final UniqueMultiMap<String, String> mInvocationAttributes =
49             new UniqueMultiMap<String, String>();
50     private Map<IInvocationContext.TimingEvent, Long> mInvocationTimingMetrics;
51     /** Invocation test-tag **/
52     private String mTestTag;
53     /** configuration descriptor */
54     private ConfigurationDescriptor mConfigurationDescriptor;
55     /** module invocation context (when running as part of a {@link ITestSuite} */
56     private IInvocationContext mModuleContext;
57     /**
58      * List of map the device serials involved in the sharded invocation, empty if not a sharded
59      * invocation.
60      */
61     private Map<Integer, List<String>> mShardSerials;
62 
63     private boolean mLocked;
64 
65     /**
66      * Creates a {@link BuildInfo} using default attribute values.
67      */
InvocationContext()68     public InvocationContext() {
69         mInvocationTimingMetrics = new LinkedHashMap<>();
70         mAllocatedDeviceAndBuildMap = new LinkedHashMap<ITestDevice, IBuildInfo>();
71         // Use LinkedHashMap to ensure key ordering by insertion order
72         mNameAndDeviceMap = new LinkedHashMap<String, ITestDevice>();
73         mNameAndBuildinfoMap = new LinkedHashMap<String, IBuildInfo>();
74         mShardSerials = new LinkedHashMap<Integer, List<String>>();
75     }
76 
77     /**
78      * {@inheritDoc}
79      */
80     @Override
getNumDevicesAllocated()81     public int getNumDevicesAllocated() {
82         return mAllocatedDeviceAndBuildMap.size();
83     }
84 
85     /**
86      * {@inheritDoc}
87      */
88     @Override
addAllocatedDevice(String devicename, ITestDevice testDevice)89     public void addAllocatedDevice(String devicename, ITestDevice testDevice) {
90         mNameAndDeviceMap.put(devicename, testDevice);
91         // back fill the information if possible
92         if (mNameAndBuildinfoMap.get(devicename) != null) {
93             mAllocatedDeviceAndBuildMap.put(testDevice, mNameAndBuildinfoMap.get(devicename));
94         }
95     }
96 
97     /**
98      * {@inheritDoc}
99      */
100     @Override
addAllocatedDevice(Map<String, ITestDevice> deviceWithName)101     public void addAllocatedDevice(Map<String, ITestDevice> deviceWithName) {
102         mNameAndDeviceMap.putAll(deviceWithName);
103         // back fill the information if possible
104         for (Entry<String, ITestDevice> entry : deviceWithName.entrySet()) {
105             if (mNameAndBuildinfoMap.get(entry.getKey()) != null) {
106                 mAllocatedDeviceAndBuildMap.put(
107                         entry.getValue(), mNameAndBuildinfoMap.get(entry.getKey()));
108             }
109         }
110     }
111 
112     /**
113      * {@inheritDoc}
114      */
115     @Override
getDeviceBuildMap()116     public Map<ITestDevice, IBuildInfo> getDeviceBuildMap() {
117         return mAllocatedDeviceAndBuildMap;
118     }
119 
120     /**
121      * {@inheritDoc}
122      */
123     @Override
getDevices()124     public List<ITestDevice> getDevices() {
125         return new ArrayList<ITestDevice>(mNameAndDeviceMap.values());
126     }
127 
128     /**
129      * {@inheritDoc}
130      */
131     @Override
getBuildInfos()132     public List<IBuildInfo> getBuildInfos() {
133         return new ArrayList<IBuildInfo>(mNameAndBuildinfoMap.values());
134     }
135 
136     /**
137      * {@inheritDoc}
138      */
139     @Override
getSerials()140     public List<String> getSerials() {
141         List<String> listSerials = new ArrayList<String>();
142         for (ITestDevice testDevice : mNameAndDeviceMap.values()) {
143             listSerials.add(testDevice.getSerialNumber());
144         }
145         return listSerials;
146     }
147 
148     /**
149      * {@inheritDoc}
150      */
151     @Override
getDeviceConfigNames()152     public List<String> getDeviceConfigNames() {
153         List<String> listNames = new ArrayList<String>();
154         listNames.addAll(mNameAndDeviceMap.keySet());
155         return listNames;
156     }
157 
158     /**
159      * {@inheritDoc}
160      */
161     @Override
getDevice(String deviceName)162     public ITestDevice getDevice(String deviceName) {
163         return mNameAndDeviceMap.get(deviceName);
164     }
165 
166     /**
167      * {@inheritDoc}
168      */
169     @Override
getBuildInfo(String deviceName)170     public IBuildInfo getBuildInfo(String deviceName) {
171         return mNameAndBuildinfoMap.get(deviceName);
172     }
173 
174     /**
175      * {@inheritDoc}
176      */
177     @Override
addDeviceBuildInfo(String deviceName, IBuildInfo buildinfo)178     public void addDeviceBuildInfo(String deviceName, IBuildInfo buildinfo) {
179         mNameAndBuildinfoMap.put(deviceName, buildinfo);
180         mAllocatedDeviceAndBuildMap.put(getDevice(deviceName), buildinfo);
181     }
182 
183     /**
184      * {@inheritDoc}
185      */
186     @Override
getBuildInfo(ITestDevice testDevice)187     public IBuildInfo getBuildInfo(ITestDevice testDevice) {
188         return mAllocatedDeviceAndBuildMap.get(testDevice);
189     }
190 
191     /**
192      * {@inheritDoc}
193      */
194     @Override
addInvocationAttribute(String attributeName, String attributeValue)195     public void addInvocationAttribute(String attributeName, String attributeValue) {
196         if (mLocked) {
197             throw new IllegalStateException(
198                     "Attempting to add invocation attribute during a test.");
199         }
200         mInvocationAttributes.put(attributeName, attributeValue);
201     }
202 
203     /** {@inheritDoc} */
204     @Override
addInvocationAttributes(UniqueMultiMap<String, String> attributesMap)205     public void addInvocationAttributes(UniqueMultiMap<String, String> attributesMap) {
206         if (mLocked) {
207             throw new IllegalStateException(
208                     "Attempting to add invocation attribute during a test.");
209         }
210         mInvocationAttributes.putAll(attributesMap);
211     }
212 
213     /** {@inheritDoc} */
214     @Override
getAttributes()215     public MultiMap<String, String> getAttributes() {
216         // Return a copy of the map to avoid unwanted modifications.
217         UniqueMultiMap<String, String> copy = new UniqueMultiMap<>();
218         copy.putAll(mInvocationAttributes);
219         return copy;
220     }
221 
222 
223     /** {@inheritDoc} */
224     @Override
getInvocationTimingMetrics()225     public Map<IInvocationContext.TimingEvent, Long> getInvocationTimingMetrics() {
226         return mInvocationTimingMetrics;
227     }
228 
229     /**
230      * {@inheritDoc}
231      */
232     @Override
addInvocationTimingMetric(IInvocationContext.TimingEvent timingEvent, Long durationMillis)233     public void addInvocationTimingMetric(IInvocationContext.TimingEvent timingEvent,
234             Long durationMillis) {
235         mInvocationTimingMetrics.put(timingEvent, durationMillis);
236     }
237 
238     /**
239      * {@inheritDoc}
240      */
241     @Override
getDeviceBySerial(String serial)242     public ITestDevice getDeviceBySerial(String serial) {
243         for (ITestDevice testDevice : mNameAndDeviceMap.values()) {
244             if (testDevice.getSerialNumber().equals(serial)) {
245                 return testDevice;
246             }
247         }
248         CLog.d("Device with serial '%s', not found in the metadata", serial);
249         return null;
250     }
251 
252     /** {@inheritDoc} */
253     @Override
getDeviceName(ITestDevice device)254     public String getDeviceName(ITestDevice device) {
255         for (String name : mNameAndDeviceMap.keySet()) {
256             if (device.equals(getDevice(name))) {
257                 return name;
258             }
259         }
260         CLog.d(
261                 "Device with serial '%s' doesn't match a name in the metadata",
262                 device.getSerialNumber());
263         return null;
264     }
265 
266     /** {@inheritDoc} */
267     @Override
getTestTag()268     public String getTestTag() {
269         return mTestTag;
270     }
271 
272     /**
273      * {@inheritDoc}
274      */
275     @Override
setTestTag(String testTag)276     public void setTestTag(String testTag) {
277         mTestTag = testTag;
278     }
279 
280     /**
281      * {@inheritDoc}
282      */
283     @Override
setRecoveryModeForAllDevices(RecoveryMode mode)284     public void setRecoveryModeForAllDevices(RecoveryMode mode) {
285         for (ITestDevice device : getDevices()) {
286             device.setRecoveryMode(mode);
287         }
288     }
289 
290     /** {@inheritDoc} */
291     @Override
setConfigurationDescriptor(ConfigurationDescriptor configurationDescriptor)292     public void setConfigurationDescriptor(ConfigurationDescriptor configurationDescriptor) {
293         mConfigurationDescriptor = configurationDescriptor;
294     }
295 
296     /** {@inheritDoc} */
297     @Override
getConfigurationDescriptor()298     public ConfigurationDescriptor getConfigurationDescriptor() {
299         return mConfigurationDescriptor;
300     }
301 
302     /** {@inheritDoc} */
303     @Override
setModuleInvocationContext(IInvocationContext invocationContext)304     public void setModuleInvocationContext(IInvocationContext invocationContext) {
305         mModuleContext = invocationContext;
306     }
307 
308     /** {@inheritDoc} */
309     @Override
getModuleInvocationContext()310     public IInvocationContext getModuleInvocationContext() {
311         return mModuleContext;
312     }
313 
314     /** Lock the context to prevent more invocation attributes to be added. */
lockAttributes()315     public void lockAttributes() {
316         mLocked = true;
317     }
318 
319     /** {@inheritDoc} */
320     @Override
addSerialsFromShard(Integer index, List<String> serials)321     public void addSerialsFromShard(Integer index, List<String> serials) {
322         if (mLocked) {
323             throw new IllegalStateException(
324                     "Attempting to add serial from shard attribute during a test.");
325         }
326         mShardSerials.put(index, serials);
327     }
328 
329     /** {@inheritDoc} */
330     @Override
getShardsSerials()331     public Map<Integer, List<String>> getShardsSerials() {
332         return new LinkedHashMap<>(mShardSerials);
333     }
334 
335     /** Special java method that allows for custom deserialization. */
readObject(ObjectInputStream in)336     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
337         // our "pseudo-constructor"
338         in.defaultReadObject();
339         // now we are a "live" object again, so let's init the transient field
340         mAllocatedDeviceAndBuildMap = new LinkedHashMap<ITestDevice, IBuildInfo>();
341         mNameAndDeviceMap = new LinkedHashMap<String, ITestDevice>();
342         // For compatibility, when parent TF does not have the invocation timing yet.
343         if (mInvocationTimingMetrics == null) {
344             mInvocationTimingMetrics = new LinkedHashMap<>();
345         }
346     }
347 }
348