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.build.proto.BuildInformation; 22 import com.android.tradefed.config.ConfigurationDescriptor; 23 import com.android.tradefed.config.proto.ConfigurationDescription.Metadata; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.device.ITestDevice.RecoveryMode; 26 import com.android.tradefed.invoker.proto.InvocationContext.Context; 27 import com.android.tradefed.log.LogUtil.CLog; 28 import com.android.tradefed.testtype.suite.ITestSuite; 29 import com.android.tradefed.util.MultiMap; 30 import com.android.tradefed.util.UniqueMultiMap; 31 32 import java.io.IOException; 33 import java.io.ObjectInputStream; 34 import java.util.ArrayList; 35 import java.util.LinkedHashMap; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Map.Entry; 39 40 /** 41 * Generic implementation of a {@link IInvocationContext}. 42 */ 43 public class InvocationContext implements IInvocationContext { 44 private static final long serialVersionUID = BuildSerializedVersion.VERSION; 45 46 // Transient field are not serialized 47 private transient Map<ITestDevice, IBuildInfo> mAllocatedDeviceAndBuildMap; 48 /** Map of the configuration device name and the actual {@link ITestDevice} * */ 49 private transient Map<String, ITestDevice> mNameAndDeviceMap; 50 private Map<String, IBuildInfo> mNameAndBuildinfoMap; 51 private final UniqueMultiMap<String, String> mInvocationAttributes = 52 new UniqueMultiMap<String, String>(); 53 private Map<IInvocationContext.TimingEvent, Long> mInvocationTimingMetrics; 54 /** Invocation test-tag **/ 55 private String mTestTag; 56 /** configuration descriptor */ 57 private ConfigurationDescriptor mConfigurationDescriptor; 58 /** module invocation context (when running as part of a {@link ITestSuite} */ 59 private IInvocationContext mModuleContext; 60 /** 61 * List of map the device serials involved in the sharded invocation, empty if not a sharded 62 * invocation. 63 */ 64 private Map<Integer, List<String>> mShardSerials; 65 66 private boolean mLocked; 67 68 /** 69 * Creates a {@link BuildInfo} using default attribute values. 70 */ InvocationContext()71 public InvocationContext() { 72 mInvocationTimingMetrics = new LinkedHashMap<>(); 73 mAllocatedDeviceAndBuildMap = new LinkedHashMap<ITestDevice, IBuildInfo>(); 74 // Use LinkedHashMap to ensure key ordering by insertion order 75 mNameAndDeviceMap = new LinkedHashMap<String, ITestDevice>(); 76 mNameAndBuildinfoMap = new LinkedHashMap<String, IBuildInfo>(); 77 mShardSerials = new LinkedHashMap<Integer, List<String>>(); 78 } 79 80 @Override getInvocationId()81 public String getInvocationId() { 82 List<String> values = mInvocationAttributes.get(INVOCATION_ID); 83 return values == null || values.isEmpty() ? null : values.get(0); 84 } 85 86 /** 87 * {@inheritDoc} 88 */ 89 @Override getNumDevicesAllocated()90 public int getNumDevicesAllocated() { 91 return mAllocatedDeviceAndBuildMap.size(); 92 } 93 94 /** 95 * {@inheritDoc} 96 */ 97 @Override addAllocatedDevice(String devicename, ITestDevice testDevice)98 public void addAllocatedDevice(String devicename, ITestDevice testDevice) { 99 mNameAndDeviceMap.put(devicename, testDevice); 100 // back fill the information if possible 101 if (mNameAndBuildinfoMap.get(devicename) != null) { 102 mAllocatedDeviceAndBuildMap.put(testDevice, mNameAndBuildinfoMap.get(devicename)); 103 } 104 } 105 106 /** 107 * {@inheritDoc} 108 */ 109 @Override addAllocatedDevice(Map<String, ITestDevice> deviceWithName)110 public void addAllocatedDevice(Map<String, ITestDevice> deviceWithName) { 111 mNameAndDeviceMap.putAll(deviceWithName); 112 // back fill the information if possible 113 for (Entry<String, ITestDevice> entry : deviceWithName.entrySet()) { 114 if (mNameAndBuildinfoMap.get(entry.getKey()) != null) { 115 mAllocatedDeviceAndBuildMap.put( 116 entry.getValue(), mNameAndBuildinfoMap.get(entry.getKey())); 117 } 118 } 119 } 120 121 /** 122 * {@inheritDoc} 123 */ 124 @Override getDeviceBuildMap()125 public Map<ITestDevice, IBuildInfo> getDeviceBuildMap() { 126 return mAllocatedDeviceAndBuildMap; 127 } 128 129 /** 130 * {@inheritDoc} 131 */ 132 @Override getDevices()133 public List<ITestDevice> getDevices() { 134 return new ArrayList<ITestDevice>(mNameAndDeviceMap.values()); 135 } 136 137 /** 138 * {@inheritDoc} 139 */ 140 @Override getBuildInfos()141 public List<IBuildInfo> getBuildInfos() { 142 return new ArrayList<IBuildInfo>(mNameAndBuildinfoMap.values()); 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 @Override getSerials()149 public List<String> getSerials() { 150 List<String> listSerials = new ArrayList<String>(); 151 for (ITestDevice testDevice : mNameAndDeviceMap.values()) { 152 listSerials.add(testDevice.getSerialNumber()); 153 } 154 return listSerials; 155 } 156 157 /** 158 * {@inheritDoc} 159 */ 160 @Override getDeviceConfigNames()161 public List<String> getDeviceConfigNames() { 162 List<String> listNames = new ArrayList<String>(); 163 listNames.addAll(mNameAndDeviceMap.keySet()); 164 return listNames; 165 } 166 167 /** 168 * {@inheritDoc} 169 */ 170 @Override getDevice(String deviceName)171 public ITestDevice getDevice(String deviceName) { 172 return mNameAndDeviceMap.get(deviceName); 173 } 174 175 /** 176 * {@inheritDoc} 177 */ 178 @Override getBuildInfo(String deviceName)179 public IBuildInfo getBuildInfo(String deviceName) { 180 return mNameAndBuildinfoMap.get(deviceName); 181 } 182 183 /** 184 * {@inheritDoc} 185 */ 186 @Override addDeviceBuildInfo(String deviceName, IBuildInfo buildinfo)187 public void addDeviceBuildInfo(String deviceName, IBuildInfo buildinfo) { 188 mNameAndBuildinfoMap.put(deviceName, buildinfo); 189 mAllocatedDeviceAndBuildMap.put(getDevice(deviceName), buildinfo); 190 } 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override getBuildInfo(ITestDevice testDevice)196 public IBuildInfo getBuildInfo(ITestDevice testDevice) { 197 return mAllocatedDeviceAndBuildMap.get(testDevice); 198 } 199 200 /** 201 * {@inheritDoc} 202 */ 203 @Override addInvocationAttribute(String attributeName, String attributeValue)204 public void addInvocationAttribute(String attributeName, String attributeValue) { 205 if (mLocked) { 206 throw new IllegalStateException( 207 "Attempting to add invocation attribute during a test."); 208 } 209 mInvocationAttributes.put(attributeName, attributeValue); 210 } 211 212 /** {@inheritDoc} */ 213 @Override addInvocationAttributes(MultiMap<String, String> attributesMap)214 public void addInvocationAttributes(MultiMap<String, String> attributesMap) { 215 if (mLocked) { 216 throw new IllegalStateException( 217 "Attempting to add invocation attribute during a test."); 218 } 219 mInvocationAttributes.putAll(attributesMap); 220 } 221 222 /** {@inheritDoc} */ 223 @Override getAttributes()224 public MultiMap<String, String> getAttributes() { 225 // Return a copy of the map to avoid unwanted modifications. 226 UniqueMultiMap<String, String> copy = new UniqueMultiMap<>(); 227 copy.putAll(mInvocationAttributes); 228 return copy; 229 } 230 231 232 /** {@inheritDoc} */ 233 @Override getInvocationTimingMetrics()234 public Map<IInvocationContext.TimingEvent, Long> getInvocationTimingMetrics() { 235 return mInvocationTimingMetrics; 236 } 237 238 /** 239 * {@inheritDoc} 240 */ 241 @Override addInvocationTimingMetric(IInvocationContext.TimingEvent timingEvent, Long durationMillis)242 public void addInvocationTimingMetric(IInvocationContext.TimingEvent timingEvent, 243 Long durationMillis) { 244 mInvocationTimingMetrics.put(timingEvent, durationMillis); 245 } 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override getDeviceBySerial(String serial)251 public ITestDevice getDeviceBySerial(String serial) { 252 for (ITestDevice testDevice : mNameAndDeviceMap.values()) { 253 if (testDevice.getSerialNumber().equals(serial)) { 254 return testDevice; 255 } 256 } 257 CLog.d("Device with serial '%s', not found in the metadata", serial); 258 return null; 259 } 260 261 /** {@inheritDoc} */ 262 @Override getDeviceName(ITestDevice device)263 public String getDeviceName(ITestDevice device) { 264 for (String name : mNameAndDeviceMap.keySet()) { 265 if (device.equals(getDevice(name))) { 266 return name; 267 } 268 } 269 CLog.d( 270 "Device with serial '%s' doesn't match a name in the metadata", 271 device.getSerialNumber()); 272 return null; 273 } 274 275 /** {@inheritDoc} */ 276 @Override getBuildInfoName(IBuildInfo info)277 public String getBuildInfoName(IBuildInfo info) { 278 for (String name : mNameAndBuildinfoMap.keySet()) { 279 if (info.equals(getBuildInfo(name))) { 280 return name; 281 } 282 } 283 CLog.d("Build info doesn't match a name in the metadata"); 284 return null; 285 } 286 287 /** {@inheritDoc} */ 288 @Override getTestTag()289 public String getTestTag() { 290 return mTestTag; 291 } 292 293 /** 294 * {@inheritDoc} 295 */ 296 @Override setTestTag(String testTag)297 public void setTestTag(String testTag) { 298 mTestTag = testTag; 299 } 300 301 /** 302 * {@inheritDoc} 303 */ 304 @Override setRecoveryModeForAllDevices(RecoveryMode mode)305 public void setRecoveryModeForAllDevices(RecoveryMode mode) { 306 for (ITestDevice device : getDevices()) { 307 device.setRecoveryMode(mode); 308 } 309 } 310 311 /** {@inheritDoc} */ 312 @Override setConfigurationDescriptor(ConfigurationDescriptor configurationDescriptor)313 public void setConfigurationDescriptor(ConfigurationDescriptor configurationDescriptor) { 314 mConfigurationDescriptor = configurationDescriptor; 315 } 316 317 /** {@inheritDoc} */ 318 @Override getConfigurationDescriptor()319 public ConfigurationDescriptor getConfigurationDescriptor() { 320 return mConfigurationDescriptor; 321 } 322 323 /** {@inheritDoc} */ 324 @Override setModuleInvocationContext(IInvocationContext invocationContext)325 public void setModuleInvocationContext(IInvocationContext invocationContext) { 326 mModuleContext = invocationContext; 327 } 328 329 /** {@inheritDoc} */ 330 @Override getModuleInvocationContext()331 public IInvocationContext getModuleInvocationContext() { 332 return mModuleContext; 333 } 334 335 /** Lock the context to prevent more invocation attributes to be added. */ lockAttributes()336 public void lockAttributes() { 337 mLocked = true; 338 } 339 340 /** Private method to unlock the attributes. Used for sandbox test mode only. */ 341 @SuppressWarnings("unused") unlock()342 private void unlock() { 343 mLocked = false; 344 } 345 346 /** {@inheritDoc} */ 347 @Override addSerialsFromShard(Integer index, List<String> serials)348 public void addSerialsFromShard(Integer index, List<String> serials) { 349 if (mLocked) { 350 throw new IllegalStateException( 351 "Attempting to add serial from shard attribute during a test."); 352 } 353 mShardSerials.put(index, serials); 354 } 355 356 /** {@inheritDoc} */ 357 @Override getShardsSerials()358 public Map<Integer, List<String>> getShardsSerials() { 359 return new LinkedHashMap<>(mShardSerials); 360 } 361 362 /** Special java method that allows for custom deserialization. */ readObject(ObjectInputStream in)363 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 364 // our "pseudo-constructor" 365 in.defaultReadObject(); 366 // now we are a "live" object again, so let's init the transient field 367 mAllocatedDeviceAndBuildMap = new LinkedHashMap<ITestDevice, IBuildInfo>(); 368 mNameAndDeviceMap = new LinkedHashMap<String, ITestDevice>(); 369 // For compatibility, when parent TF does not have the invocation timing yet. 370 if (mInvocationTimingMetrics == null) { 371 mInvocationTimingMetrics = new LinkedHashMap<>(); 372 } 373 } 374 375 /** {@inheritDoc} */ 376 @Override toProto()377 public Context toProto() { 378 Context.Builder contextBuilder = Context.newBuilder(); 379 // The invocation test tag. 380 if (mTestTag != null) { 381 contextBuilder.setTestTag(mTestTag); 382 } 383 // Map name to build info 384 Map<String, BuildInformation.BuildInfo> mapBuild = new LinkedHashMap<>(); 385 for (String name : mNameAndBuildinfoMap.keySet()) { 386 mapBuild.put(name, mNameAndBuildinfoMap.get(name).toProto()); 387 } 388 contextBuilder.putAllNameBuildInfo(mapBuild); 389 // Metadata 390 List<Metadata> metadatas = new ArrayList<>(); 391 for (String key : mInvocationAttributes.keySet()) { 392 Metadata value = 393 Metadata.newBuilder() 394 .setKey(key) 395 .addAllValue(mInvocationAttributes.get(key)) 396 .build(); 397 metadatas.add(value); 398 } 399 contextBuilder.addAllMetadata(metadatas); 400 // Configuration Description 401 contextBuilder.setConfigurationDescription(mConfigurationDescriptor.toProto()); 402 // Module Context if it exists 403 if (mModuleContext != null) { 404 contextBuilder.setModuleContext(mModuleContext.toProto()); 405 } 406 return contextBuilder.build(); 407 } 408 409 /** Inverse operation to {@link InvocationContext#toProto()} to get the instance back. */ fromProto(Context protoContext)410 public static InvocationContext fromProto(Context protoContext) { 411 InvocationContext context = new InvocationContext(); 412 // Test Tag. 413 context.mTestTag = protoContext.getTestTag(); 414 // Map Build Info 415 for (String key : protoContext.getNameBuildInfo().keySet()) { 416 context.mNameAndBuildinfoMap.put( 417 key, BuildInfo.fromProto(protoContext.getNameBuildInfo().get(key))); 418 } 419 // Metadata 420 for (Metadata meta : protoContext.getMetadataList()) { 421 for (String value : meta.getValueList()) { 422 context.mInvocationAttributes.put(meta.getKey(), value); 423 } 424 } 425 // Configuration Description 426 context.mConfigurationDescriptor = 427 ConfigurationDescriptor.fromProto(protoContext.getConfigurationDescription()); 428 // Module Context - context module will have some property set: module-id at the minimum 429 if (protoContext.hasModuleContext()) { 430 // TODO: Check explicitly for module-id 431 context.mModuleContext = InvocationContext.fromProto(protoContext.getModuleContext()); 432 } 433 return context; 434 } 435 } 436