• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 
17 package com.android.test.runner;
18 
19 import dalvik.system.DexFile;
20 
21 import java.io.IOException;
22 import java.util.Enumeration;
23 import java.util.HashSet;
24 import java.util.LinkedHashSet;
25 import java.util.Set;
26 
27 /**
28  * Finds class entries in apks.
29  * <p/>
30  * Adapted from tools/tradefederation/..ClassPathScanner
31  */
32 class ClassPathScanner {
33 
34     /**
35      * A filter for classpath entry paths
36      * <p/>
37      * Patterned after {@link java.io.FileFilter}
38      */
39     public static interface ClassNameFilter {
40         /**
41          * Tests whether or not the specified abstract pathname should be included in a class path
42          * entry list.
43          *
44          * @param pathName the relative path of the class path entry
45          */
accept(String className)46         boolean accept(String className);
47     }
48 
49     /**
50      * A {@link ClassNameFilter} that accepts all class names.
51      */
52     public static class AcceptAllFilter implements ClassNameFilter {
53 
54         /**
55          * {@inheritDoc}
56          */
57         @Override
accept(String className)58         public boolean accept(String className) {
59             return true;
60         }
61 
62     }
63 
64     /**
65      * A {@link ClassNameFilter} that chains one or more filters together
66      */
67     public static class ChainedClassNameFilter implements ClassNameFilter {
68         private final ClassNameFilter[] mFilters;
69 
ChainedClassNameFilter(ClassNameFilter... filters)70         public ChainedClassNameFilter(ClassNameFilter... filters) {
71             mFilters = filters;
72         }
73 
74         /**
75          * {@inheritDoc}
76          */
77         @Override
accept(String className)78         public boolean accept(String className) {
79             for (ClassNameFilter filter : mFilters) {
80                 if (!filter.accept(className)) {
81                     return false;
82                 }
83             }
84             return true;
85         }
86     }
87 
88     /**
89      * A {@link ClassNameFilter} that rejects inner classes.
90      */
91     public static class ExternalClassNameFilter implements ClassNameFilter {
92         /**
93          * {@inheritDoc}
94          */
95         @Override
accept(String pathName)96         public boolean accept(String pathName) {
97             return !pathName.contains("$");
98         }
99     }
100 
101     /**
102      * A {@link ClassNameFilter} that only accepts package names within the given namespace.
103      */
104     public static class InclusivePackageNameFilter implements ClassNameFilter {
105 
106         private final String mPkgName;
107 
InclusivePackageNameFilter(String pkgName)108         InclusivePackageNameFilter(String pkgName) {
109             if (!pkgName.endsWith(".")) {
110                 mPkgName = String.format("%s.", pkgName);
111             } else {
112                 mPkgName = pkgName;
113             }
114         }
115 
116         /**
117          * {@inheritDoc}
118          */
119         @Override
accept(String pathName)120         public boolean accept(String pathName) {
121             return pathName.startsWith(mPkgName);
122         }
123     }
124 
125     /**
126      * A {@link ClassNameFilter} that only rejects a given package names within the given namespace.
127      */
128     public static class ExcludePackageNameFilter implements ClassNameFilter {
129 
130         private final String mPkgName;
131 
ExcludePackageNameFilter(String pkgName)132         ExcludePackageNameFilter(String pkgName) {
133             if (!pkgName.endsWith(".")) {
134                 mPkgName = String.format("%s.", pkgName);
135             } else {
136                 mPkgName = pkgName;
137             }
138         }
139 
140         /**
141          * {@inheritDoc}
142          */
143         @Override
accept(String pathName)144         public boolean accept(String pathName) {
145             return !pathName.startsWith(mPkgName);
146         }
147     }
148 
149     private Set<String> mApkPaths = new HashSet<String>();
150 
ClassPathScanner(String... apkPaths)151     public ClassPathScanner(String... apkPaths) {
152         for (String apkPath : apkPaths) {
153             mApkPaths.add(apkPath);
154         }
155     }
156 
157     /**
158      * Gets the names of all entries contained in given apk file, that match given filter.
159      * @throws IOException
160      */
addEntriesFromApk(Set<String> entryNames, String apkPath, ClassNameFilter filter)161     private void addEntriesFromApk(Set<String> entryNames, String apkPath, ClassNameFilter filter)
162             throws IOException {
163         DexFile dexFile = null;
164         try {
165             dexFile = new DexFile(apkPath);
166             Enumeration<String> apkClassNames = getDexEntries(dexFile);
167             while (apkClassNames.hasMoreElements()) {
168                 String apkClassName = apkClassNames.nextElement();
169                 if (filter.accept(apkClassName)) {
170                     entryNames.add(apkClassName);
171                 }
172             }
173         } finally {
174             if (dexFile != null) {
175                 dexFile.close();
176             }
177         }
178     }
179 
180     /**
181      * Retrieves the entry names from given {@link DexFile}.
182      * <p/>
183      * Exposed for unit testing.
184      *
185      * @param dexFile
186      * @return {@link Enumeration} of {@link String}s
187      */
getDexEntries(DexFile dexFile)188     Enumeration<String> getDexEntries(DexFile dexFile) {
189         return dexFile.entries();
190     }
191 
192     /**
193      * Retrieves set of classpath entries that match given {@link ClassNameFilter}.
194      * @throws IOException
195      */
getClassPathEntries(ClassNameFilter filter)196     public Set<String> getClassPathEntries(ClassNameFilter filter) throws IOException {
197         // use LinkedHashSet for predictable order
198         Set<String> entryNames = new LinkedHashSet<String>();
199         for (String apkPath : mApkPaths) {
200             addEntriesFromApk(entryNames, apkPath, filter);
201         }
202         return entryNames;
203     }
204 }
205