Home | History | Annotate | Download | only in cts
      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