1 /* 2 * Copyright (C) 2010 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.build; 17 18 import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey; 19 import com.android.tradefed.build.proto.BuildInformation; 20 import com.android.tradefed.build.proto.BuildInformation.BuildFile; 21 import com.android.tradefed.build.proto.BuildInformation.KeyBuildFilePair; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.error.HarnessRuntimeException; 24 import com.android.tradefed.invoker.logger.InvocationMetricLogger; 25 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey; 26 import com.android.tradefed.invoker.tracing.CloseableTraceScope; 27 import com.android.tradefed.log.LogUtil.CLog; 28 import com.android.tradefed.result.error.InfraErrorIdentifier; 29 import com.android.tradefed.service.TradefedFeatureClient; 30 import com.android.tradefed.testtype.suite.ResolvePartialDownload; 31 import com.android.tradefed.util.FileUtil; 32 import com.android.tradefed.util.MultiMap; 33 import com.android.tradefed.util.UniqueMultiMap; 34 35 import com.google.common.base.MoreObjects; 36 import com.google.common.base.Objects; 37 import com.google.common.base.Strings; 38 import com.proto.tradefed.feature.FeatureResponse; 39 40 import java.io.File; 41 import java.io.IOException; 42 import java.io.ObjectInputStream; 43 import java.io.ObjectOutputStream; 44 import java.lang.reflect.InvocationTargetException; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collection; 48 import java.util.HashMap; 49 import java.util.HashSet; 50 import java.util.Hashtable; 51 import java.util.List; 52 import java.util.Map; 53 import java.util.Set; 54 import java.util.stream.Collectors; 55 56 /** 57 * Generic implementation of a {@link IBuildInfo} that should be associated 58 * with a {@link ITestDevice}. 59 */ 60 public class BuildInfo implements IBuildInfo { 61 private static final long serialVersionUID = BuildSerializedVersion.VERSION; 62 private static final String BUILD_ALIAS_KEY = "build_alias"; 63 64 private String mBuildId = UNKNOWN_BUILD_ID; 65 private String mTestTag = "stub"; 66 private String mBuildTargetName = "stub"; 67 private final UniqueMultiMap<String, String> mBuildAttributes = 68 new UniqueMultiMap<String, String>(); 69 // TODO: once deployed make non-transient 70 private Map<String, VersionedFile> mVersionedFileMap; 71 private transient MultiMap<String, VersionedFile> mVersionedFileMultiMap; 72 private String mBuildFlavor = null; 73 private String mBuildBranch = null; 74 private String mDeviceSerial = null; 75 76 /** File handling properties: Some files of the BuildInfo might requires special handling */ 77 private final Set<BuildInfoProperties> mProperties = new HashSet<>(); 78 /** Whether to stage remote files. */ 79 private boolean mStageRemoteFile = true; 80 81 private static final String[] FILE_NOT_TO_CLONE = 82 new String[] { 83 BuildInfoFileKey.TESTDIR_IMAGE.getFileKey(), 84 BuildInfoFileKey.HOST_LINKED_DIR.getFileKey(), 85 BuildInfoFileKey.TARGET_LINKED_DIR.getFileKey(), 86 }; 87 88 /** 89 * Creates a {@link BuildInfo} using default attribute values. 90 */ BuildInfo()91 public BuildInfo() { 92 mVersionedFileMap = new Hashtable<String, VersionedFile>(); 93 mVersionedFileMultiMap = new MultiMap<String, VersionedFile>(); 94 } 95 96 /** 97 * Creates a {@link BuildInfo} 98 * 99 * @param buildId the build id 100 * @param buildTargetName the build target name 101 */ BuildInfo(String buildId, String buildTargetName)102 public BuildInfo(String buildId, String buildTargetName) { 103 this(); 104 mBuildId = buildId; 105 mBuildTargetName = buildTargetName; 106 } 107 108 /** 109 * Creates a {@link BuildInfo}, populated with attributes given in another build. 110 * 111 * @param buildToCopy 112 */ BuildInfo(BuildInfo buildToCopy)113 BuildInfo(BuildInfo buildToCopy) { 114 this(buildToCopy.getBuildId(), buildToCopy.getBuildTargetName()); 115 addAllBuildAttributes(buildToCopy); 116 try { 117 addAllFiles(buildToCopy); 118 } catch (IOException e) { 119 throw new RuntimeException(e); 120 } 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override getBuildId()127 public String getBuildId() { 128 return mBuildId; 129 } 130 131 /** 132 * {@inheritDoc} 133 */ 134 @Override setBuildId(String buildId)135 public void setBuildId(String buildId) { 136 mBuildId = buildId; 137 } 138 139 /** 140 * {@inheritDoc} 141 */ 142 @Override setTestTag(String testTag)143 public void setTestTag(String testTag) { 144 mTestTag = testTag; 145 } 146 147 /** 148 * {@inheritDoc} 149 */ 150 @Override getTestTag()151 public String getTestTag() { 152 return mTestTag; 153 } 154 155 /** 156 * {@inheritDoc} 157 */ 158 @Override getDeviceSerial()159 public String getDeviceSerial() { 160 return mDeviceSerial; 161 } 162 163 /** 164 * {@inheritDoc} 165 */ 166 @Override getBuildAttributes()167 public Map<String, String> getBuildAttributes() { 168 return mBuildAttributes.getUniqueMap(); 169 } 170 171 /** {@inheritDoc} */ 172 @Override setProperties(BuildInfoProperties... properties)173 public void setProperties(BuildInfoProperties... properties) { 174 mProperties.clear(); 175 mProperties.addAll(Arrays.asList(properties)); 176 } 177 178 /** {@inheritDoc} */ 179 @Override getProperties()180 public Set<BuildInfoProperties> getProperties() { 181 return new HashSet<>(mProperties); 182 } 183 184 /** 185 * {@inheritDoc} 186 */ 187 @Override getBuildTargetName()188 public String getBuildTargetName() { 189 return mBuildTargetName; 190 } 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override addBuildAttribute(String attributeName, String attributeValue)196 public void addBuildAttribute(String attributeName, String attributeValue) { 197 if (attributeValue == null) { 198 attributeValue = ""; 199 } 200 mBuildAttributes.put(attributeName, attributeValue); 201 } 202 203 @Override removeBuildAttribute(String attributeName)204 public void removeBuildAttribute(String attributeName) { 205 mBuildAttributes.remove(attributeName); 206 } 207 208 /** {@inheritDoc} */ 209 @Override addBuildAttributes(Map<String, String> buildAttributes)210 public void addBuildAttributes(Map<String, String> buildAttributes) { 211 mBuildAttributes.putAll(buildAttributes); 212 } 213 214 /** 215 * Helper method to copy build attributes, branch, and flavor from other build. 216 */ addAllBuildAttributes(BuildInfo build)217 protected void addAllBuildAttributes(BuildInfo build) { 218 mBuildAttributes.putAll(build.getAttributesMultiMap()); 219 setBuildFlavor(build.getBuildFlavor()); 220 setBuildBranch(build.getBuildBranch()); 221 setTestTag(build.getTestTag()); 222 } 223 getAttributesMultiMap()224 protected MultiMap<String, String> getAttributesMultiMap() { 225 return mBuildAttributes; 226 } 227 228 /** 229 * Helper method to copy all files from the other build. 230 * 231 * <p>Creates new hardlinks to the files so that each build will have a unique file path to the 232 * file. 233 * 234 * @throws IOException if an exception is thrown when creating the hardlink. 235 */ addAllFiles(BuildInfo build)236 protected void addAllFiles(BuildInfo build) throws IOException { 237 for (Map.Entry<String, VersionedFile> fileEntry : build.getVersionedFileMap().entrySet()) { 238 File origFile = fileEntry.getValue().getFile(); 239 if (applyBuildProperties(fileEntry.getValue(), build, this)) { 240 continue; 241 } 242 if (fileEntry.getKey().startsWith(IBuildInfo.REMOTE_FILE_PREFIX)) { 243 setFile( 244 fileEntry.getKey(), 245 new File(fileEntry.getValue().getFile().getPath()), 246 fileEntry.getValue().getVersion()); 247 continue; 248 } 249 File copyFile; 250 if (origFile.isDirectory()) { 251 copyFile = FileUtil.createTempDir(fileEntry.getKey()); 252 FileUtil.recursiveHardlink(origFile, copyFile, false); 253 } else { 254 // Only using createTempFile to create a unique dest filename 255 copyFile = FileUtil.createTempFile(fileEntry.getKey(), 256 FileUtil.getExtension(origFile.getName())); 257 copyFile.delete(); 258 FileUtil.hardlinkFile(origFile, copyFile); 259 } 260 setFile(fileEntry.getKey(), copyFile, fileEntry.getValue().getVersion()); 261 } 262 } 263 264 /** 265 * Allow to apply some of the {@link com.android.tradefed.build.IBuildInfo.BuildInfoProperties} 266 * and possibly do a different handling. 267 * 268 * @param origFileConsidered The currently looked at {@link VersionedFile}. 269 * @param build the original build being cloned 270 * @param receiver the build receiving the information. 271 * @return True if we applied the properties and further handling should be skipped. False 272 * otherwise. 273 */ applyBuildProperties( VersionedFile origFileConsidered, IBuildInfo build, IBuildInfo receiver)274 protected boolean applyBuildProperties( 275 VersionedFile origFileConsidered, IBuildInfo build, IBuildInfo receiver) { 276 // If the no copy on sharding is set, that means the tests dir will be shared and should 277 // not be copied. 278 if (getProperties().contains(BuildInfoProperties.DO_NOT_COPY_ON_SHARDING)) { 279 for (String name : FILE_NOT_TO_CLONE) { 280 if (origFileConsidered.getFile().equals(build.getFile(name))) { 281 receiver.setFile( 282 name, origFileConsidered.getFile(), origFileConsidered.getVersion()); 283 return true; 284 } 285 } 286 } 287 if (getProperties().contains(BuildInfoProperties.DO_NOT_COPY_IMAGE_FILE)) { 288 if (origFileConsidered.equals(build.getVersionedFile(BuildInfoFileKey.DEVICE_IMAGE))) { 289 CLog.d("Skip copying of device_image."); 290 return true; 291 } 292 } 293 return false; 294 } 295 getVersionedFileMap()296 protected Map<String, VersionedFile> getVersionedFileMap() { 297 return mVersionedFileMultiMap.getUniqueMap(); 298 } 299 getVersionedFileMapFull()300 protected MultiMap<String, VersionedFile> getVersionedFileMapFull() { 301 return new MultiMap<>(mVersionedFileMultiMap); 302 } 303 304 /** {@inheritDoc} */ 305 @Override getVersionedFileKeys()306 public Set<String> getVersionedFileKeys() { 307 return mVersionedFileMultiMap.keySet(); 308 } 309 310 /** 311 * {@inheritDoc} 312 */ 313 @Override getFile(String name)314 public File getFile(String name) { 315 List<VersionedFile> fileRecords = mVersionedFileMultiMap.get(name); 316 if (fileRecords == null || fileRecords.isEmpty()) { 317 return null; 318 } 319 return fileRecords.get(0).getFile(); 320 } 321 322 /** {@inheritDoc} */ 323 @Override getFile(BuildInfoFileKey key)324 public File getFile(BuildInfoFileKey key) { 325 return getFile(key.getFileKey()); 326 } 327 328 /** {@inheritDoc} */ 329 @Override getVersionedFile(String name)330 public final VersionedFile getVersionedFile(String name) { 331 List<VersionedFile> fileRecords = mVersionedFileMultiMap.get(name); 332 if (fileRecords == null || fileRecords.isEmpty()) { 333 return null; 334 } 335 return fileRecords.get(0); 336 } 337 338 /** {@inheritDoc} */ 339 @Override getVersionedFile(BuildInfoFileKey key)340 public VersionedFile getVersionedFile(BuildInfoFileKey key) { 341 return getVersionedFile(key.getFileKey()); 342 } 343 344 /** {@inheritDoc} */ 345 @Override getVersionedFiles(BuildInfoFileKey key)346 public final List<VersionedFile> getVersionedFiles(BuildInfoFileKey key) { 347 if (!key.isList()) { 348 throw new UnsupportedOperationException( 349 String.format("Key %s does not support list of files.", key.getFileKey())); 350 } 351 return mVersionedFileMultiMap.get(key.getFileKey()); 352 } 353 354 /** 355 * {@inheritDoc} 356 */ 357 @Override getFiles()358 public Collection<VersionedFile> getFiles() { 359 return mVersionedFileMultiMap.values(); 360 } 361 362 /** 363 * {@inheritDoc} 364 */ 365 @Override getVersion(String name)366 public String getVersion(String name) { 367 List<VersionedFile> fileRecords = mVersionedFileMultiMap.get(name); 368 if (fileRecords == null || fileRecords.isEmpty()) { 369 return null; 370 } 371 return fileRecords.get(0).getVersion(); 372 } 373 374 /** {@inheritDoc} */ 375 @Override getVersion(BuildInfoFileKey key)376 public String getVersion(BuildInfoFileKey key) { 377 return getVersion(key.getFileKey()); 378 } 379 380 /** 381 * {@inheritDoc} 382 */ 383 @Override setFile(String name, File file, String version)384 public void setFile(String name, File file, String version) { 385 if (file == null) { 386 CLog.w("Tried to add to build info file name '%s' which is null.", name); 387 return; 388 } 389 if (!mVersionedFileMap.containsKey(name)) { 390 mVersionedFileMap.put(name, new VersionedFile(file, version)); 391 } 392 if (mVersionedFileMultiMap.containsKey(name)) { 393 BuildInfoFileKey key = BuildInfoFileKey.fromString(name); 394 // If the key is a list, we will add it to the map. 395 if (key == null || !key.isList()) { 396 CLog.e( 397 "Device build already contains a file for %s in thread %s", 398 name, Thread.currentThread().getName()); 399 return; 400 } 401 } 402 mVersionedFileMultiMap.put(name, new VersionedFile(file, version)); 403 } 404 405 /** {@inheritDoc} */ 406 @Override setFile(BuildInfoFileKey key, File file, String version)407 public void setFile(BuildInfoFileKey key, File file, String version) { 408 setFile(key.getFileKey(), file, version); 409 } 410 411 /** {@inheritDoc} */ 412 @Override getAppPackageFiles()413 public List<VersionedFile> getAppPackageFiles() { 414 List<VersionedFile> origList = getVersionedFiles(BuildInfoFileKey.PACKAGE_FILES); 415 List<VersionedFile> listCopy = new ArrayList<VersionedFile>(); 416 if (origList != null) { 417 listCopy.addAll(origList); 418 } 419 return listCopy; 420 } 421 422 /** {@inheritDoc} */ 423 @Override addAppPackageFile(File appPackageFile, String version)424 public void addAppPackageFile(File appPackageFile, String version) { 425 setFile(BuildInfoFileKey.PACKAGE_FILES, appPackageFile, version); 426 } 427 428 /** 429 * {@inheritDoc} 430 */ 431 @Override cleanUp()432 public void cleanUp() { 433 for (VersionedFile fileRecord : mVersionedFileMultiMap.values()) { 434 FileUtil.recursiveDelete(fileRecord.getFile()); 435 } 436 mVersionedFileMultiMap.clear(); 437 } 438 439 /** {@inheritDoc} */ 440 @Override cleanUp(List<File> doNotClean)441 public void cleanUp(List<File> doNotClean) { 442 if (doNotClean == null) { 443 cleanUp(); 444 } 445 for (VersionedFile fileRecord : mVersionedFileMultiMap.values()) { 446 if (!doNotClean.contains(fileRecord.getFile())) { 447 FileUtil.recursiveDelete(fileRecord.getFile()); 448 } 449 } 450 refreshVersionedFiles(); 451 } 452 453 /** 454 * Run through all the {@link VersionedFile} and remove from the map the one that do not exists. 455 */ refreshVersionedFiles()456 private void refreshVersionedFiles() { 457 Set<String> keys = new HashSet<>(mVersionedFileMultiMap.keySet()); 458 for (String key : keys) { 459 for (VersionedFile file : mVersionedFileMultiMap.get(key)) { 460 if (!file.getFile().exists()) { 461 mVersionedFileMultiMap.remove(key); 462 } 463 } 464 } 465 } 466 467 /** 468 * {@inheritDoc} 469 */ 470 @Override clone()471 public IBuildInfo clone() { 472 BuildInfo copy = null; 473 try { 474 copy = 475 this.getClass() 476 .getDeclaredConstructor(String.class, String.class) 477 .newInstance(getBuildId(), getBuildTargetName()); 478 } catch (InstantiationException 479 | IllegalAccessException 480 | IllegalArgumentException 481 | InvocationTargetException 482 | NoSuchMethodException 483 | SecurityException e) { 484 CLog.e("Failed to clone the build info."); 485 throw new RuntimeException(e); 486 } 487 copy.addAllBuildAttributes(this); 488 copy.setProperties(this.getProperties().toArray(new BuildInfoProperties[0])); 489 try { 490 copy.addAllFiles(this); 491 } catch (IOException e) { 492 throw new RuntimeException(e); 493 } 494 copy.setBuildBranch(mBuildBranch); 495 copy.setBuildFlavor(mBuildFlavor); 496 copy.setDeviceSerial(mDeviceSerial); 497 498 return copy; 499 } 500 501 /** 502 * {@inheritDoc} 503 */ 504 @Override getBuildFlavor()505 public String getBuildFlavor() { 506 return mBuildFlavor; 507 } 508 509 /** 510 * {@inheritDoc} 511 */ 512 @Override setBuildFlavor(String buildFlavor)513 public void setBuildFlavor(String buildFlavor) { 514 mBuildFlavor = buildFlavor; 515 } 516 517 /** 518 * {@inheritDoc} 519 */ 520 @Override getBuildBranch()521 public String getBuildBranch() { 522 return mBuildBranch; 523 } 524 525 /** 526 * {@inheritDoc} 527 */ 528 @Override setBuildBranch(String branch)529 public void setBuildBranch(String branch) { 530 mBuildBranch = branch; 531 } 532 533 /** 534 * {@inheritDoc} 535 */ 536 @Override setDeviceSerial(String serial)537 public void setDeviceSerial(String serial) { 538 mDeviceSerial = serial; 539 } 540 541 /** 542 * {@inheritDoc} 543 */ 544 @Override hashCode()545 public int hashCode() { 546 return Objects.hashCode(mBuildAttributes, mBuildBranch, mBuildFlavor, mBuildId, 547 mBuildTargetName, mTestTag, mDeviceSerial); 548 } 549 550 /** 551 * {@inheritDoc} 552 */ 553 @Override equals(Object obj)554 public boolean equals(Object obj) { 555 if (this == obj) { 556 return true; 557 } 558 if (obj == null) { 559 return false; 560 } 561 if (getClass() != obj.getClass()) { 562 return false; 563 } 564 BuildInfo other = (BuildInfo) obj; 565 return Objects.equal(mBuildAttributes, other.mBuildAttributes) 566 && Objects.equal(mBuildBranch, other.mBuildBranch) 567 && Objects.equal(mBuildFlavor, other.mBuildFlavor) 568 && Objects.equal(mBuildId, other.mBuildId) 569 && Objects.equal(mBuildTargetName, other.mBuildTargetName) 570 && Objects.equal(mTestTag, other.mTestTag) 571 && Objects.equal(mDeviceSerial, other.mDeviceSerial); 572 } 573 574 /** 575 * {@inheritDoc} 576 */ 577 @Override toString()578 public String toString() { 579 return MoreObjects.toStringHelper(this.getClass()) 580 .omitNullValues() 581 .add("build_alias", getBuildAttributes().get(BUILD_ALIAS_KEY)) 582 .add("bid", mBuildId) 583 .add("target", mBuildTargetName) 584 .add("build_flavor", mBuildFlavor) 585 .add("branch", mBuildBranch) 586 .add("serial", mDeviceSerial) 587 .toString(); 588 } 589 590 /** {@inheritDoc} */ 591 @Override toProto()592 public BuildInformation.BuildInfo toProto() { 593 BuildInformation.BuildInfo.Builder protoBuilder = BuildInformation.BuildInfo.newBuilder(); 594 if (getBuildId() != null) { 595 protoBuilder.setBuildId(getBuildId()); 596 } 597 if (getBuildFlavor() != null) { 598 protoBuilder.setBuildFlavor(getBuildFlavor()); 599 } 600 if (getBuildBranch() != null) { 601 protoBuilder.setBranch(getBuildBranch()); 602 } 603 // Attributes 604 protoBuilder.putAllAttributes(getBuildAttributes()); 605 // Populate the versioned file 606 for (String fileKey : mVersionedFileMultiMap.keySet()) { 607 KeyBuildFilePair.Builder buildFile = KeyBuildFilePair.newBuilder(); 608 buildFile.setBuildFileKey(fileKey); 609 for (VersionedFile vFile : mVersionedFileMultiMap.get(fileKey)) { 610 BuildFile.Builder fileInformation = BuildFile.newBuilder(); 611 fileInformation.setVersion(Strings.nullToEmpty(vFile.getVersion())); 612 if (fileKey.startsWith(IBuildInfo.REMOTE_FILE_PREFIX)) { 613 // Remote file doesn't exist on local cache, so don't save absolute path. 614 fileInformation.setLocalPath(vFile.getFile().toString()); 615 } else { 616 fileInformation.setLocalPath(vFile.getFile().getAbsolutePath()); 617 } 618 buildFile.addFile(fileInformation); 619 } 620 protoBuilder.addVersionedFile(buildFile); 621 } 622 protoBuilder.setBuildInfoClass(this.getClass().getCanonicalName()); 623 return protoBuilder.build(); 624 } 625 626 /** Copy all the {@link VersionedFile} from a given build to this one. */ copyAllFileFrom(BuildInfo build)627 public final void copyAllFileFrom(BuildInfo build) { 628 MultiMap<String, VersionedFile> versionedMap = build.getVersionedFileMapFull(); 629 for (String versionedFile : versionedMap.keySet()) { 630 for (VersionedFile vFile : versionedMap.get(versionedFile)) { 631 setFile(versionedFile, vFile.getFile(), vFile.getVersion()); 632 } 633 } 634 } 635 636 /** Special serialization to handle the new underlying type. */ writeObject(ObjectOutputStream outputStream)637 private void writeObject(ObjectOutputStream outputStream) throws IOException { 638 outputStream.defaultWriteObject(); 639 outputStream.writeObject(mVersionedFileMultiMap); 640 } 641 642 /** Special java method that allows for custom deserialization. */ readObject(ObjectInputStream in)643 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 644 in.defaultReadObject(); 645 try { 646 mVersionedFileMultiMap = (MultiMap<String, VersionedFile>) in.readObject(); 647 } catch (IOException | ClassNotFoundException e) { 648 mVersionedFileMultiMap = new MultiMap<>(); 649 } 650 } 651 652 /** Inverse operation to {@link #toProto()} to get the instance back. */ fromProto(BuildInformation.BuildInfo protoBuild)653 public static IBuildInfo fromProto(BuildInformation.BuildInfo protoBuild) { 654 IBuildInfo buildInfo; 655 String buildClass = protoBuild.getBuildInfoClass(); 656 if (buildClass.isEmpty()) { 657 buildInfo = new BuildInfo(); 658 } else { 659 // Restore the original type of build info. 660 try { 661 buildInfo = 662 Class.forName(buildClass) 663 .asSubclass(BuildInfo.class) 664 .getDeclaredConstructor() 665 .newInstance(); 666 } catch (InstantiationException 667 | IllegalAccessException 668 | ClassNotFoundException 669 | InvocationTargetException 670 | NoSuchMethodException e) { 671 throw new RuntimeException(e); 672 } 673 } 674 // Build id 675 if (!protoBuild.getBuildId().isEmpty()) { 676 buildInfo.setBuildId(protoBuild.getBuildId()); 677 } 678 // Build Flavor 679 if (!protoBuild.getBuildFlavor().isEmpty()) { 680 buildInfo.setBuildFlavor(protoBuild.getBuildFlavor()); 681 } 682 // Build Branch 683 if (!protoBuild.getBranch().isEmpty()) { 684 buildInfo.setBuildBranch(protoBuild.getBranch()); 685 } 686 // Attributes 687 for (String key : protoBuild.getAttributesMap().keySet()) { 688 buildInfo.addBuildAttribute(key, protoBuild.getAttributesMap().get(key)); 689 } 690 // Versioned File 691 for (KeyBuildFilePair filePair : protoBuild.getVersionedFileList()) { 692 for (BuildFile buildFile : filePair.getFileList()) { 693 buildInfo.setFile( 694 filePair.getBuildFileKey(), 695 new File(buildFile.getLocalPath()), 696 buildFile.getVersion()); 697 } 698 } 699 return buildInfo; 700 } 701 702 /** {@inheritDoc} */ 703 @Override getRemoteFiles()704 public Set<File> getRemoteFiles() { 705 Set<File> remoteFiles = new HashSet<>(); 706 for (String fileKey : mVersionedFileMultiMap.keySet()) { 707 if (fileKey.startsWith(IBuildInfo.REMOTE_FILE_PREFIX)) { 708 // Remote file is not versioned, there should be only one entry. 709 remoteFiles.add(mVersionedFileMultiMap.get(fileKey).get(0).getFile()); 710 } 711 } 712 return remoteFiles; 713 } 714 715 /** {@inheritDoc} */ 716 @Override stageRemoteFile(String fileName, File workingDir)717 public File stageRemoteFile(String fileName, File workingDir) { 718 if (!mStageRemoteFile) { 719 CLog.w("Staging remote files is disabled. Skip staging file: %s", fileName); 720 return null; 721 } 722 if (getRemoteFiles().isEmpty()) { 723 return null; 724 } 725 InvocationMetricLogger.addInvocationMetrics( 726 InvocationMetricKey.STAGE_TESTS_INDIVIDUAL_DOWNLOADS, fileName); 727 List<String> includeFilters = Arrays.asList(String.format("/%s?($|/)", fileName)); 728 729 try (CloseableTraceScope stage = new CloseableTraceScope("stageRemoteFile:" + fileName); 730 TradefedFeatureClient client = new TradefedFeatureClient()) { 731 Map<String, String> args = new HashMap<>(); 732 args.put(ResolvePartialDownload.DESTINATION_DIR, workingDir.getAbsolutePath()); 733 args.put(ResolvePartialDownload.INCLUDE_FILTERS, String.join(";", includeFilters)); 734 // TODO: Remove exclude filter when we support not specifying it. For now put a 735 // placeholder that will exclude nothing. 736 args.put(ResolvePartialDownload.EXCLUDE_FILTERS, "doesntmatch"); 737 args.put("use-cas", "false"); 738 String remotePaths = 739 getRemoteFiles().stream() 740 .map(p -> p.toString()) 741 .collect(Collectors.joining(";")); 742 args.put(ResolvePartialDownload.REMOTE_PATHS, remotePaths); 743 long startTime = System.currentTimeMillis(); 744 FeatureResponse rep = 745 client.triggerFeature( 746 ResolvePartialDownload.RESOLVE_PARTIAL_DOWNLOAD_FEATURE_NAME, args); 747 InvocationMetricLogger.addInvocationPairMetrics( 748 InvocationMetricKey.STAGE_REMOTE_TIME, startTime, System.currentTimeMillis()); 749 if (rep.hasErrorInfo()) { 750 throw new HarnessRuntimeException( 751 rep.getErrorInfo().getErrorTrace(), 752 InfraErrorIdentifier.ARTIFACT_DOWNLOAD_ERROR); 753 } 754 } 755 756 return FileUtil.findFile(workingDir, fileName); 757 } 758 759 /** {@inheritDoc} */ 760 @Override allowStagingRemoteFile(boolean stageRemoteFile)761 public void allowStagingRemoteFile(boolean stageRemoteFile) { 762 mStageRemoteFile = stageRemoteFile; 763 } 764 } 765