1 /* 2 * Copyright (C) 2008 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.cts; 17 18 import java.io.BufferedOutputStream; 19 import java.io.File; 20 import java.io.FileNotFoundException; 21 import java.io.FileOutputStream; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.OutputStream; 25 import java.security.NoSuchAlgorithmException; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.Collection; 29 import java.util.Enumeration; 30 import java.util.HashMap; 31 import java.util.List; 32 import java.util.zip.ZipEntry; 33 import java.util.zip.ZipFile; 34 35 import javax.xml.parsers.DocumentBuilderFactory; 36 import javax.xml.parsers.ParserConfigurationException; 37 38 import org.w3c.dom.Document; 39 import org.w3c.dom.Node; 40 import org.w3c.dom.NodeList; 41 import org.xml.sax.SAXException; 42 43 /** 44 * Holds CTS host configuration information, such as: 45 * <ul> 46 * <li> test case repository 47 * <li> test plan repository 48 * <li> test result repository 49 * </ul> 50 */ 51 public class HostConfig extends XMLResourceHandler { 52 public static boolean DEBUG = false; 53 54 public static final String ALL = "all"; 55 56 static final String SIGNATURE_TEST_PACKAGE_NAME = "SignatureTest"; 57 static final String DEFAULT_HOST_CONFIG_FILE_NAME = "host_config.xml"; 58 static final String FILE_SUFFIX_XML = ".xml"; 59 static final String FILE_SUFFIX_APK = ".apk"; 60 static final String FILE_SUFFIX_ZIP = ".zip"; 61 static final String FILE_SUFFIX_JAR = ".jar"; 62 static final String[] CTS_RESULT_RESOURCES = {"cts_result.xsl", "cts_result.css", 63 "logo.gif", "newrule-green.png"}; 64 65 private String mConfigRoot; 66 private String mLogRoot; 67 private CaseRepository mCaseRepos; 68 private ResultRepository mResultRepos; 69 private PlanRepository mPlanRepos; 70 71 // key: app package name 72 // value: TestPackage 73 private HashMap<String, TestPackage> mTestPackageMap; 74 75 enum Ints { 76 // Number of tests executed between reboots. A value <= 0 disables reboots. 77 maxTestCount (200), 78 // Max size [tests] for a package to be run in batch mode 79 maxTestsInBatchMode (0), 80 // Max time [ms] between test status updates for both individual and batch mode. 81 testStatusTimeoutMs (5 * 60 * 1000), 82 // Max time [ms] from start of package in batch mode and the first test status update. 83 batchStartTimeoutMs (30 * 60 * 1000), 84 // Max time [ms] from start of test in individual mode to the first test status update. 85 individualStartTimeoutMs (5 * 60 * 1000), 86 // Timeout [ms] for the signature check 87 signatureTestTimeoutMs (10 * 60 * 1000), 88 // Timeout [ms] for package installations 89 packageInstallTimeoutMs (2 * 60 * 1000), 90 // Time to wait [ms] after a package installation or removal 91 postInstallWaitMs (30 * 1000); 92 93 private int value; 94 95 Ints(int value) { 96 this.value = value; 97 } 98 99 int value() { 100 return value; 101 } 102 103 void setValue(int value) { 104 this.value = value; 105 } 106 } 107 108 private final static HostConfig sInstance = new HostConfig(); 109 110 private HostConfig() { 111 mTestPackageMap = new HashMap<String, TestPackage>(); 112 } 113 114 public static HostConfig getInstance() { 115 return sInstance; 116 } 117 118 /** 119 * Returns the max number of tests to run between reboots. A value of 0 or smaller indicates 120 * that reboots should not be used. 121 */ 122 public static int getMaxTestCount() { 123 return Ints.maxTestCount.value(); 124 } 125 126 /** 127 * Load configuration. 128 * 129 * @param configPath The configuration path. 130 * @return If succeed in loading, return true; else, return false. 131 */ 132 public boolean load(String configPath) throws SAXException, IOException, 133 ParserConfigurationException { 134 135 String fileName = null; 136 String[] subDirs = configPath.split("\\" + File.separator); 137 for (String d : subDirs) { 138 if (d.contains(FILE_SUFFIX_XML)) { 139 fileName = d; 140 } 141 } 142 143 String configFile = null; 144 if (fileName == null) { 145 //remove the possible trailing "/" of the path 146 if (File.separatorChar == configPath.charAt(configPath.length() - 1)) { 147 configPath = configPath.substring(0, configPath.length() - 1); 148 } 149 mConfigRoot = configPath; 150 fileName = DEFAULT_HOST_CONFIG_FILE_NAME; 151 } else { 152 mConfigRoot = configPath.substring(0, configPath.length() - fileName.length() - 1); 153 } 154 configFile = mConfigRoot + File.separator + fileName; 155 156 Document doc = DocumentBuilderFactory.newInstance() 157 .newDocumentBuilder().parse(new File(configFile)); 158 159 String repositoryRoot = getStringAttributeValue(doc 160 .getElementsByTagName("Repository").item(0), "root"); 161 if ((null == repositoryRoot) || (repositoryRoot.length() == 0)) { 162 repositoryRoot = mConfigRoot; 163 } 164 165 String caseCfg = getStringAttributeValue(doc, "TestCase", "path", fileName); 166 String planCfg = getStringAttributeValue(doc, "TestPlan", "path", fileName); 167 String resCfg = getStringAttributeValue(doc, "TestResult", "path", fileName); 168 if ((caseCfg == null) || (planCfg == null) || (resCfg == null)) { 169 return false; 170 } 171 172 getConfigValues(doc); 173 174 String caseRoot = repositoryRoot + File.separator + caseCfg; 175 String planRoot = repositoryRoot + File.separator + planCfg; 176 String resRoot = repositoryRoot + File.separator + resCfg; 177 178 String logCfg = getStringAttributeValueOpt(doc, "TestLog", "path", fileName); 179 if (null == logCfg) { 180 mLogRoot = mConfigRoot; 181 } else { 182 mLogRoot = repositoryRoot + File.separator + logCfg; 183 } 184 185 boolean validCase = true; 186 if (!validateDirectory(caseRoot)) { 187 validCase = new File(caseRoot).mkdirs(); 188 } 189 boolean validRes = true; 190 if (!validateDirectory(resRoot)) { 191 validRes = new File(resRoot).mkdirs(); 192 } 193 if (validRes) { 194 extractResultResources(resRoot); 195 } 196 boolean validPlan = true; 197 if (!validateDirectory(planRoot)) { 198 validPlan = new File(planRoot).mkdirs(); 199 } 200 boolean validLog = true; 201 if (!validateDirectory(mLogRoot)) { 202 validLog = new File(mLogRoot).mkdirs(); 203 } 204 205 mCaseRepos = new CaseRepository(caseRoot); 206 mResultRepos = new ResultRepository(resRoot); 207 mPlanRepos = new PlanRepository(planRoot); 208 209 return validCase && validRes && validPlan && validLog; 210 } 211 212 /** 213 * Extract the result resources into the specified directory. 214 * 215 * @param resRoot the directory to extract the resources into. 216 */ 217 public void extractResultResources(String resRoot) { 218 for (String res: CTS_RESULT_RESOURCES) { 219 extractResource(res, resRoot); 220 } 221 } 222 223 /** 224 * Get the test packages. 225 * 226 * @return The test packages. 227 */ 228 public Collection<TestPackage> getTestPackages() { 229 return mTestPackageMap.values(); 230 } 231 232 /** 233 * Get the test package by the name of the test package. 234 * 235 * @param packageName The package name. 236 * @return The test package. 237 */ 238 public TestPackage getTestPackage(final String packageName) { 239 return mTestPackageMap.get(packageName); 240 } 241 242 /** 243 * Load repositories. 244 */ 245 public void loadRepositories(boolean quick) throws NoSuchAlgorithmException { 246 loadTestPackages(); 247 if (!quick) { 248 loadTestResults(); 249 } 250 } 251 252 /** 253 * Load test results to create session accordingly. 254 */ 255 private void loadTestResults() { 256 getResultRepository().loadTestResults(); 257 } 258 259 /** 260 * Load all of the test packages. 261 */ 262 public void loadTestPackages() throws NoSuchAlgorithmException { 263 if (mTestPackageMap.size() == 0) { 264 mCaseRepos.loadTestPackages(); 265 } 266 } 267 268 /** 269 * Remove all of the test packages. 270 */ 271 public void removeTestPacakges() { 272 mTestPackageMap.clear(); 273 } 274 275 /** 276 * Get the package binary name. 277 * 278 * @param appPackageName The package name. 279 * @return The binary name of the package. 280 */ 281 public String getPackageBinaryName(String appPackageName) { 282 283 for (TestPackage pkg : mTestPackageMap.values()) { 284 if (appPackageName.equals(pkg.getAppPackageName())) { 285 return pkg.getAppBinaryName(); 286 } 287 } 288 289 return null; 290 } 291 292 /** 293 * Get the root directory of configuration. 294 * 295 * @return The root directory of configuration. 296 */ 297 public String getConfigRoot() { 298 return mConfigRoot; 299 } 300 301 /** 302 * Get the root directory of log files. 303 * 304 * @return the root directory of log files. 305 */ 306 public String getLogRoot() { 307 return mLogRoot; 308 } 309 310 /** 311 * Get string attribute value. 312 * 313 * @param doc The document. 314 * @param tagName The tag name. 315 * @param attrName The attribute name. 316 * @param fileName The file name. 317 * @return The attribute value. 318 */ 319 private String getStringAttributeValue(final Document doc, 320 final String tagName, final String attrName, final String fileName) { 321 322 String cfgStr = null; 323 try { 324 cfgStr = getStringAttributeValue(doc 325 .getElementsByTagName(tagName).item(0), attrName); 326 if ((null == cfgStr) || (cfgStr.length() == 0)) { 327 Log.e("Configure error (in " + fileName 328 + "), pls make sure <" + tagName + ">'s attribute <" 329 + attrName + ">'s value is correctly set.", null); 330 return null; 331 } 332 } catch (Exception e) { 333 Log.e("Configure error (in " + fileName 334 + "), pls make sure <" + tagName 335 + ">'s value is correctly set.", null); 336 return null; 337 } 338 339 return cfgStr; 340 } 341 342 /** 343 * Get string attribute value if it exists. 344 * 345 * @param doc The document. 346 * @param tagName The tag name. 347 * @param attrName The attribute name. 348 * @param fileName The file name. 349 * @return The attribute value. 350 */ 351 private String getStringAttributeValueOpt(final Document doc, 352 final String tagName, final String attrName, final String fileName) { 353 354 String cfgStr = null; 355 try { 356 cfgStr = getStringAttributeValue(doc 357 .getElementsByTagName(tagName).item(0), attrName); 358 } catch (Exception e) { 359 return null; 360 } 361 362 return cfgStr; 363 } 364 365 /** 366 * Load configuration values from config file. 367 * 368 * @param doc The document from which to load the values. 369 */ 370 private void getConfigValues(final Document doc) { 371 NodeList intValues = doc.getElementsByTagName("IntValue"); 372 for (int i = 0; i < intValues.getLength(); i++) { 373 Node n = intValues.item(i); 374 String name = getStringAttributeValue(n, "name"); 375 String value = getStringAttributeValue(n, "value"); 376 try { 377 Integer v = Integer.parseInt(value); 378 Ints.valueOf(name).setValue(v); 379 } catch (NumberFormatException e) { 380 Log.e("Configuration error. Illegal value for " + name, e); 381 } catch (IllegalArgumentException e) { 382 Log.e("Unknown configuration value " + name, e); 383 } 384 } 385 } 386 387 /** 388 * Validate the directory. 389 * 390 * @param path The path to be validated. 391 * @return If valid directory, return true; else, return false. 392 */ 393 private boolean validateDirectory(final String path) { 394 395 File pathFile = new File(path); 396 if ((null == pathFile) || (pathFile.exists() == false) 397 || (pathFile.isDirectory() == false)) { 398 return false; 399 } 400 401 return true; 402 } 403 404 /** 405 * Extract a resource into the given destination directory. This is used 406 * for resource files such as images and CSS. 407 * 408 * @param name The name of the resource. 409 * @param dest The directory the resource should be extracted to. 410 * @return true, if successful, or the file already existed. 411 */ 412 private boolean extractResource(String name, String dest) { 413 File file = new File(dest, name); 414 if (!file.exists()) { 415 // do not extract again if the file is already there 416 InputStream in = getClass().getResourceAsStream(File.separator + name); 417 if (in != null) { 418 try { 419 FileOutputStream fout = new FileOutputStream(file); 420 byte[] data = new byte[512]; 421 int len = in.read(data); 422 while (len > 0) { 423 fout.write(data, 0, len); 424 len = in.read(data); 425 } 426 fout.flush(); 427 fout.close(); 428 in.close(); 429 } catch (FileNotFoundException e) { 430 return false; 431 } catch (IOException e) { 432 return false; 433 } 434 } 435 } 436 return true; 437 } 438 439 /** 440 * Get the case repository. 441 * 442 * @return The case repository. 443 */ 444 public CaseRepository getCaseRepository() { 445 return mCaseRepos; 446 } 447 448 /** 449 * Get the plan repository. 450 * 451 * @return The plan repository. 452 */ 453 public PlanRepository getPlanRepository() { 454 return mPlanRepos; 455 } 456 457 /** 458 * Get the result repository. 459 * 460 * @return The result repository. 461 */ 462 public ResultRepository getResultRepository() { 463 return mResultRepos; 464 } 465 466 /** 467 * Storing the root information of some repository. 468 * 469 */ 470 class Repository { 471 protected String mRoot; 472 473 Repository(String root) { 474 mRoot = root; 475 } 476 477 /** 478 * Get the root of the repository. 479 * 480 * @return The root of the repository. 481 */ 482 public String getRoot() { 483 return mRoot; 484 } 485 486 /** 487 * Check if the specified file is a valid XML file. 488 * 489 * @param f The file to be valid. 490 * @return If valid XML file, return true; else, return false. 491 */ 492 public boolean isValidXmlFile(File f) { 493 if (f.getPath().endsWith(FILE_SUFFIX_XML)) { 494 return true; 495 } 496 497 return false; 498 } 499 } 500 501 /** 502 * Storing the information of result repository. 503 */ 504 class ResultRepository extends Repository { 505 506 ResultRepository(String root) { 507 super(root); 508 } 509 510 /** 511 * Load test results to create session accordingly. 512 */ 513 public void loadTestResults() { 514 515 for (File f : new File(mRoot).listFiles()) { 516 if (f.isDirectory()) { 517 String pathName = mRoot + File.separator + f.getName() 518 + File.separator + TestSessionLog.CTS_RESULT_FILE_NAME; 519 if (HostUtils.isFileExist(pathName)) { 520 try { 521 TestSessionLog log = 522 TestSessionLogBuilder.getInstance().build(pathName); 523 TestSession ts = TestSessionBuilder.getInstance().build(log); 524 if (ts != null) { 525 TestHost.getInstance().addSession(ts); 526 } 527 } catch (Exception e) { 528 Log.e("Error importing existing result from " + pathName, e); 529 } 530 } 531 } 532 } 533 } 534 } 535 536 /** 537 * Storing the information of case repository. 538 */ 539 class CaseRepository extends Repository { 540 CaseRepository(String root) { 541 super(root); 542 } 543 544 /** 545 * Get package names. 546 * 547 * @return The package names. 548 */ 549 public ArrayList<String> getPackageNames() { 550 ArrayList<String> packageNames = new ArrayList<String>(); 551 for (TestPackage pkg : mTestPackageMap.values()) { 552 String binaryName = pkg.getAppBinaryName(); 553 if (binaryName.equals(SIGNATURE_TEST_PACKAGE_NAME)) { 554 packageNames.add(0, binaryName); 555 } else { 556 packageNames.add(pkg.getAppPackageName()); 557 } 558 } 559 560 return packageNames; 561 } 562 563 /** 564 * Get package binary names. 565 * 566 * @return The package binary names. 567 */ 568 public ArrayList<String> getPackageBinaryNames() { 569 ArrayList<String> pkgBinaryNames = new ArrayList<String>(); 570 for (TestPackage pkg : mTestPackageMap.values()) { 571 String pkgBinaryName = pkg.getAppBinaryName(); 572 if (pkgBinaryName.equals(SIGNATURE_TEST_PACKAGE_NAME)) { 573 pkgBinaryNames.add(0, pkgBinaryName); 574 } else { 575 pkgBinaryNames.add(pkg.getAppBinaryName()); 576 } 577 } 578 579 return pkgBinaryNames; 580 } 581 582 /** 583 * Load package XML file names. 584 * 585 * @return The package XML file names. 586 */ 587 public List<String> loadPackageXmlFileNames() { 588 ArrayList<String> packageXmlFileNames = new ArrayList<String>(); 589 590 for (File f : new File(mRoot).listFiles()) { 591 if (isValidXmlFile(f)) { 592 String fileName = f.getName(); 593 String name = fileName.substring(0, fileName.lastIndexOf(".")); 594 packageXmlFileNames.add(name); 595 } 596 } 597 598 return packageXmlFileNames; 599 } 600 601 /** 602 * Load test packages. 603 */ 604 public void loadTestPackages() throws NoSuchAlgorithmException { 605 List<String> pkgXmlFileNameList = loadPackageXmlFileNames(); 606 for (String pkgXmlFileName : pkgXmlFileNameList) { 607 String xmlPath = getRoot() + File.separator 608 + pkgXmlFileName + FILE_SUFFIX_XML; 609 TestPackage pkg = loadPackage(xmlPath); 610 if (isValidPackage(pkg)) { 611 mTestPackageMap.put(pkg.getAppPackageName(), pkg); 612 } 613 } 614 } 615 616 /** 617 * Get package binary name. 618 * 619 * @param packagePath The package path. 620 * @return The package binary name. 621 */ 622 private String getPackageBinaryName(String packagePath) { 623 return packagePath.substring(packagePath.lastIndexOf(File.separator) + 1, 624 packagePath.lastIndexOf(".")); 625 } 626 627 /** 628 * Check if the specified package is a valid package. 629 * 630 * @param pkg The specified package to be checked. 631 * @return If valid package, return true; else, return false. 632 */ 633 private boolean isValidPackage(TestPackage pkg) { 634 if (pkg == null) { 635 return false; 636 } 637 638 String pkgFileName = pkg.getAppBinaryName(); 639 String apkFilePath = mRoot + File.separator + pkgFileName + FILE_SUFFIX_APK; 640 String xmlFilePath = mRoot + File.separator + pkgFileName + FILE_SUFFIX_XML; 641 File xmlFile = new File(xmlFilePath); 642 if (pkg.isHostSideOnly()) { 643 if (xmlFile.exists() && xmlFile.isFile()) { 644 return true; 645 } 646 } else { 647 File apkFile = new File(apkFilePath); 648 if (xmlFile.exists() && xmlFile.isFile() 649 && apkFile.exists() && apkFile.isFile()) { 650 return true; 651 } 652 } 653 654 return false; 655 } 656 657 /** 658 * Add package to case repository. 659 * 660 * @param packagePath The package to be added. 661 * @return If valid package and succeed in add it, return true; else, return false. 662 */ 663 public boolean addPackage(String packagePath) throws FileNotFoundException, 664 IOException, NoSuchAlgorithmException { 665 ZipFile zipFile = new ZipFile(packagePath); 666 Enumeration<? extends ZipEntry> entries = zipFile.entries(); 667 668 ArrayList<String> filePathList = new ArrayList<String>(); 669 String xmlFilePath = null; 670 while (entries.hasMoreElements()) { 671 ZipEntry entry = entries.nextElement(); 672 673 String name = entry.getName(); 674 if (name.endsWith(FILE_SUFFIX_APK) 675 || name.endsWith(FILE_SUFFIX_XML) 676 || name.endsWith(FILE_SUFFIX_JAR)) { 677 int index = name.lastIndexOf(File.separator); 678 String fileName = name; 679 if (index != -1) { 680 fileName = name.substring(index + 1); 681 } 682 String filePath = mRoot + File.separator + fileName; 683 writeToFile(zipFile.getInputStream(entry), filePath); 684 filePathList.add(filePath); 685 if (name.endsWith(FILE_SUFFIX_XML)) { 686 xmlFilePath = filePath; 687 } 688 } 689 } 690 691 String packageName = getPackageBinaryName(packagePath); 692 PackageZipFileValidator zipValidator = new PackageZipFileValidator(); 693 if (zipValidator.validate(filePathList, packageName, xmlFilePath) == false) { 694 for (String filePath : filePathList) { 695 deleteFile(filePath); 696 } 697 return false; 698 } 699 700 TestPackage pkg = loadPackage(xmlFilePath); 701 if (pkg != null) { 702 mTestPackageMap.put(pkg.getAppPackageName(), pkg); 703 } 704 705 return true; 706 } 707 708 /** 709 * Load the package from the package description XML file. 710 * 711 * @param xmlFileName The package description XML file. 712 * @return The TestPackage. 713 */ 714 private TestPackage loadPackage(String xmlFileName) throws NoSuchAlgorithmException { 715 if ((xmlFileName == null) || (xmlFileName.length() == 0)) { 716 return null; 717 } 718 719 File xmlFile = new File(xmlFileName); 720 TestSessionBuilder sessionBuilder; 721 TestPackage pkg = null; 722 try { 723 sessionBuilder = TestSessionBuilder.getInstance(); 724 pkg = sessionBuilder.loadPackage(xmlFile, null); 725 } catch (ParserConfigurationException e) { 726 } catch (SAXException e) { 727 } catch (IOException e) { 728 } 729 return pkg; 730 } 731 732 /** 733 * check if the packagePath is valid against the case repository 734 * 735 * @param packagePath the path to be checked 736 * @return if the path isn't suffixed with .zip, return false; 737 * if the package name exists in case repository, return false; 738 * for other conditions, return true; 739 */ 740 public boolean isValidPackageName(String packagePath) { 741 if (!packagePath.endsWith(FILE_SUFFIX_ZIP)) { 742 Log.e("Package error: package name " + packagePath + " is not a zip file.", null); 743 return false; 744 } 745 746 String fileName = packagePath.substring(packagePath.lastIndexOf(File.separator) + 1, 747 packagePath.length() - FILE_SUFFIX_ZIP.length()); 748 749 String path = mRoot + File.separator + fileName; 750 if (HostUtils.isFileExist(path + FILE_SUFFIX_APK) 751 || HostUtils.isFileExist(path + FILE_SUFFIX_XML)) { 752 Log.e("Package error: package name " + fileName + " exists already.", null); 753 return false; 754 } 755 756 return true; 757 } 758 759 /** 760 * Validate zipped package file against package logic 761 */ 762 class PackageZipFileValidator { 763 /** 764 * validate the package content to see if it contains enough data 765 ** 766 * @param filePathList The file path list contained in the package zip file. 767 * @param packageName The package name. 768 * @param xmlFilePath The description XML file path. 769 * @return If valid, return true; else, return false. 770 */ 771 public boolean validate(ArrayList<String> filePathList, String packageName, 772 String xmlFilePath) throws NoSuchAlgorithmException { 773 if (xmlFilePath == null) { 774 Log.e("Package error: package doesn't contain XML file: " 775 + packageName + FILE_SUFFIX_XML, null); 776 return false; 777 } else { 778 TestPackage pkg = loadPackage(xmlFilePath); 779 if (pkg == null) { 780 Log.e("Package error: the description XML file contained in : " 781 + packageName + FILE_SUFFIX_APK + " is invalid.", null); 782 return false; 783 } else { 784 if (!validateTargetApk(filePathList, pkg.getTargetBinaryName())) { 785 return false; 786 } 787 788 if (!validateHostControllerJar(filePathList, pkg.getJarPath())) { 789 return false; 790 } 791 792 String apkFilePath = mRoot + File.separator 793 + packageName + FILE_SUFFIX_APK; 794 if (!filePathList.contains(apkFilePath)) { 795 Log.e("Package error: package doesn't contain APK file: " 796 + packageName + FILE_SUFFIX_APK, null); 797 return false; 798 } 799 } 800 } 801 802 return true; 803 } 804 805 /** 806 * Validate host controller jar file described in the package description XML file. 807 * 808 * @param filePathList The files contained in the zipped package file. 809 * @param hostControllerJarPath The host controller jar file path. 810 * @return If the host controller jar file contained in the zipped package, 811 * return true; else, return false. 812 */ 813 private boolean validateHostControllerJar(ArrayList<String> filePathList, 814 String hostControllerJarPath) { 815 if ((hostControllerJarPath != null) && (hostControllerJarPath.length() != 0)) { 816 String targetFilePath = 817 mRoot + File.separator + hostControllerJarPath + FILE_SUFFIX_JAR; 818 if (filePathList.contains(targetFilePath)) { 819 return true; 820 } 821 } else { 822 return true; 823 } 824 825 //String jarFileName = getPackageName(hostControllerJarPath); 826 Log.e("Package error: host controler jar file " 827 + hostControllerJarPath + FILE_SUFFIX_JAR 828 + " is not contained in the package zip file.", null); 829 return false; 830 } 831 832 /** 833 * Validate target APK file described in the package description XML file. 834 * 835 * @param filePathList The files contained in the zipped package file. 836 * @param targetName The target APK name. 837 * @return If the target APK file contained in the zipped package file, return true; 838 * else, return false. 839 */ 840 private boolean validateTargetApk(ArrayList<String> filePathList, String targetName) { 841 if ((targetName != null) && (targetName.length() != 0)) { 842 String targetFileName = mRoot + File.separator + targetName + FILE_SUFFIX_APK; 843 if (filePathList.contains(targetFileName)) { 844 return true; 845 } 846 } else { 847 return true; 848 } 849 850 Log.e("Package error: target file " + targetName + FILE_SUFFIX_APK 851 + " is not contained in the package zip file.", null); 852 return false; 853 } 854 } 855 856 /** 857 * Write the input stream to file. 858 * 859 * @param in The input stream. 860 * @param path The file to write to. 861 */ 862 private void writeToFile(InputStream in, String path) throws IOException { 863 OutputStream out = new BufferedOutputStream(new FileOutputStream(path)); 864 byte[] buffer = new byte[1024]; 865 int len; 866 867 while ((len = in.read(buffer)) >= 0) { 868 out.write(buffer, 0, len); 869 } 870 871 in.close(); 872 out.close(); 873 } 874 875 /** 876 * Remove packages from case repository. 877 * 878 * @param packageName Package to be removed. 879 */ 880 public void removePackages(String packageName) { 881 if ((packageName == null) || (packageName.length() == 0)) { 882 return; 883 } 884 885 if (packageName.equals(ALL)) { 886 ArrayList<String> packageNames = getCaseRepository().getPackageNames(); 887 for (String pkgName : packageNames) { 888 removePackage(pkgName); 889 } 890 } else { 891 if (!getPackageNames().contains(packageName)) { 892 Log.e("Package " + packageName + " doesn't exist in repository!", null); 893 return; 894 } 895 removePackage(packageName); 896 } 897 } 898 899 /** 900 * Remove the specified package. 901 * 902 * @param packageName The package name. 903 */ 904 private void removePackage(String packageName) { 905 TestPackage pkg = getTestPackage(packageName); 906 if (pkg != null) { 907 ArrayList<String> targetBinaryNames = getTargetBinaryNames(); 908 String targetBinaryName = pkg.getTargetBinaryName(); 909 if ((targetBinaryName != null) && (targetBinaryName.length() != 0) 910 && (getReferenceCount(targetBinaryNames, targetBinaryName) == 1)) { 911 String targetBinaryFileName = mRoot + File.separator + targetBinaryName 912 + FILE_SUFFIX_APK; 913 deleteFile(targetBinaryFileName); 914 } 915 916 ArrayList<String> hostControllers = getHostControllers(); 917 String hostControllerPath = pkg.getJarPath(); 918 if ((hostControllerPath != null) && (hostControllerPath.length() != 0) 919 && (getReferenceCount(hostControllers, hostControllerPath) == 1)) { 920 String jarFilePath = mRoot + File.separator 921 + hostControllerPath + FILE_SUFFIX_JAR; 922 deleteFile(jarFilePath); 923 } 924 } 925 926 String packageBinaryName = pkg.getAppBinaryName(); 927 mTestPackageMap.remove(pkg.getAppPackageName()); 928 929 String apkPath = mRoot + File.separator + packageBinaryName + FILE_SUFFIX_APK; 930 String xmlPath = mRoot + File.separator + packageBinaryName + FILE_SUFFIX_XML; 931 deleteFile(apkPath); 932 deleteFile(xmlPath); 933 } 934 935 /** 936 * Get the reference count of the specific value against the value list. 937 * 938 * @param list The value list to be checked against. 939 * @param value The value to be checked. 940 * @return The reference count. 941 */ 942 private int getReferenceCount(ArrayList<String> list, String value) { 943 if ((list == null) || (list.size() == 0) || (value == null)) { 944 return 0; 945 } 946 947 int count = 0; 948 for (String str : list) { 949 if (value.equals(str)) { 950 count ++; 951 } 952 } 953 954 return count; 955 } 956 957 /** 958 * Get the target binary names contained with the test package description XML files. 959 * 960 * @return The target binary names. 961 */ 962 private ArrayList<String> getTargetBinaryNames() { 963 ArrayList<String> targetBinaryNames = new ArrayList<String>(); 964 for (TestPackage pkg : mTestPackageMap.values()) { 965 targetBinaryNames.add(pkg.getTargetBinaryName()); 966 } 967 return targetBinaryNames; 968 } 969 970 /** 971 * Get the host controllers contained with the test package description XML files. 972 * 973 * @return The host controllers. 974 */ 975 private ArrayList<String> getHostControllers() { 976 ArrayList<String> hostControllers = new ArrayList<String>(); 977 for (TestPackage pkg : mTestPackageMap.values()) { 978 hostControllers.add(pkg.getJarPath()); 979 } 980 return hostControllers; 981 } 982 983 /** 984 * Delete the specific file. 985 * 986 * @param filepath The file to be deleted. 987 */ 988 private void deleteFile(String filepath) { 989 File file = new File(filepath); 990 if (file.exists() && file.isFile()) { 991 file.delete(); 992 } 993 } 994 995 /** 996 * Get package's APK file path via the package name. 997 * 998 * @param packageName The package name. 999 * @return The package's APK file path. 1000 */ 1001 public String getApkPath(String packageName) { 1002 return mRoot + File.separator + packageName + FILE_SUFFIX_APK; 1003 } 1004 1005 /** 1006 * Get package's XML file path via the package name. 1007 * @param packageName The package name. 1008 * @return The package's XML file path. 1009 */ 1010 public String getXmlPath(String packageName) { 1011 return mRoot + File.separator + packageName + FILE_SUFFIX_XML; 1012 } 1013 1014 /** 1015 * List available package and suite. 1016 * 1017 * @param expectPackage expected package name 1018 * @return list which contains available packages, suites and cases. 1019 */ 1020 @SuppressWarnings("unchecked") 1021 public List<ArrayList<String>> listAvailablePackage(String expectPackage) { 1022 ArrayList<String> packageList = new ArrayList<String>(); 1023 ArrayList<String> suiteList = new ArrayList<String>(); 1024 ArrayList<String> caseList = new ArrayList<String>(); 1025 ArrayList<String> testList = new ArrayList<String>(); 1026 1027 for (TestPackage testPackage : mTestPackageMap.values()) { 1028 String appPackageName = testPackage.getAppPackageName(); 1029 if (expectPackage.equals(appPackageName)) { 1030 testPackage.getTestSuiteNames(appPackageName, suiteList, caseList); 1031 } else if (appPackageName.startsWith(expectPackage)) { 1032 packageList.add(appPackageName); 1033 } else { 1034 if (expectPackage.indexOf(Test.METHOD_SEPARATOR) == -1) { 1035 testPackage.getTestCaseNames(expectPackage, caseList, testList); 1036 } else { 1037 testPackage.getTestNames(expectPackage, testList); 1038 } 1039 } 1040 } 1041 1042 return Arrays.asList(packageList, suiteList, caseList, testList); 1043 } 1044 } 1045 1046 /** 1047 * Storing information of test plans. 1048 * 1049 */ 1050 class PlanRepository extends Repository { 1051 1052 PlanRepository(String root) { 1053 super(root); 1054 } 1055 1056 /** 1057 * Get the path of the specified plan. 1058 * 1059 * @param name The plan name. 1060 * @return The plan path. 1061 */ 1062 public String getPlanPath(String name) { 1063 if (mRoot == null) { 1064 Log.e("Repository uninitialized!", null); 1065 return null; 1066 } 1067 1068 return mRoot + File.separator + name + FILE_SUFFIX_XML; 1069 } 1070 1071 /** 1072 * Get all plan names in the plan repository. 1073 * @return Plan names. 1074 */ 1075 public ArrayList<String> getAllPlanNames() { 1076 ArrayList<String> plans = new ArrayList<String>(); 1077 1078 if (mRoot == null) { 1079 Log.e("Not specify repository, please check your cts config", 1080 null); 1081 return plans; 1082 } 1083 1084 File planRepository = new File(mRoot); 1085 if (!planRepository.exists()) { 1086 Log.e("Plan Repository doesn't exist: " + mRoot, null); 1087 return null; 1088 } 1089 1090 for (File f : planRepository.listFiles()) { 1091 String name = f.getName(); 1092 1093 if (name.endsWith(FILE_SUFFIX_XML)) { 1094 plans.add(name.substring(0, name.length() - FILE_SUFFIX_XML.length())); 1095 } 1096 } 1097 1098 return plans; 1099 } 1100 } 1101 } 1102