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