1bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot/*
2bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Copyright (C) 2012 The Android Open Source Project
3bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot *
4bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Licensed under the Apache License, Version 2.0 (the "License");
5bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * you may not use this file except in compliance with the License.
6bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * You may obtain a copy of the License at
7bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot *
8bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot *      http://www.apache.org/licenses/LICENSE-2.0
9bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot *
10bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Unless required by applicable law or agreed to in writing, software
11bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * distributed under the License is distributed on an "AS IS" BASIS,
12bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * See the License for the specific language governing permissions and
14bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * limitations under the License.
15bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */
16bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
17bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotpackage com.android.test.runner;
18bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
19bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport dalvik.system.DexFile;
20bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
21bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.io.IOException;
22bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Enumeration;
23bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.HashSet;
24bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.LinkedHashSet;
25bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotimport java.util.Set;
26bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
27bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot/**
28bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Finds class entries in apks.
29bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * <p/>
30bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot * Adapted from tools/tradefederation/..ClassPathScanner
31bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot */
32bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabotclass ClassPathScanner {
33bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
34bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
35bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * A filter for classpath entry paths
36bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * <p/>
37bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * Patterned after {@link java.io.FileFilter}
38bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
39bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public static interface ClassNameFilter {
40bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        /**
41bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         * Tests whether or not the specified abstract pathname should be included in a class path
42bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         * entry list.
43bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         *
44bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         * @param pathName the relative path of the class path entry
45bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         */
46bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        boolean accept(String className);
47bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
48bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
49bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
50bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * A {@link ClassNameFilter} that accepts all class names.
51bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
52bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public static class AcceptAllFilter implements ClassNameFilter {
53bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
54bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        /**
55bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         * {@inheritDoc}
56bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         */
57bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        @Override
58bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        public boolean accept(String className) {
59bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return true;
60bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
61bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
62bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
63bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
64bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
65bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * A {@link ClassNameFilter} that chains one or more filters together
66bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
67bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public static class ChainedClassNameFilter implements ClassNameFilter {
68bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        private final ClassNameFilter[] mFilters;
69bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
70bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        public ChainedClassNameFilter(ClassNameFilter... filters) {
71bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            mFilters = filters;
72bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
73bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
74bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        /**
75bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         * {@inheritDoc}
76bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         */
77bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        @Override
78bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        public boolean accept(String className) {
79bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            for (ClassNameFilter filter : mFilters) {
80bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                if (!filter.accept(className)) {
81bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    return false;
82bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                }
83bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            }
84bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return true;
85bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
86bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
87bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
88bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
89bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * A {@link ClassNameFilter} that rejects inner classes.
90bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
91bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public static class ExternalClassNameFilter implements ClassNameFilter {
92bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        /**
93bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         * {@inheritDoc}
94bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         */
95bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        @Override
96bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        public boolean accept(String pathName) {
97bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return !pathName.contains("$");
98bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
99bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
100bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
101bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
102bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * A {@link ClassNameFilter} that only accepts package names within the given namespace.
103bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
104bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public static class InclusivePackageNameFilter implements ClassNameFilter {
105bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
106bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        private final String mPkgName;
107bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
108bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        InclusivePackageNameFilter(String pkgName) {
109bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            if (!pkgName.endsWith(".")) {
110bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                mPkgName = String.format("%s.", pkgName);
111bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            } else {
112bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                mPkgName = pkgName;
113bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            }
114bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
115bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
116bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        /**
117bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         * {@inheritDoc}
118bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         */
119bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        @Override
120bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        public boolean accept(String pathName) {
121bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return pathName.startsWith(mPkgName);
122bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
123bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
124bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
125bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
126bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * A {@link ClassNameFilter} that only rejects a given package names within the given namespace.
127bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
128bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public static class ExcludePackageNameFilter implements ClassNameFilter {
129bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
130bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        private final String mPkgName;
131bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
132bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        ExcludePackageNameFilter(String pkgName) {
133bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            if (!pkgName.endsWith(".")) {
134bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                mPkgName = String.format("%s.", pkgName);
135bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            } else {
136bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                mPkgName = pkgName;
137bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            }
138bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
139bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
140bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        /**
141bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         * {@inheritDoc}
142bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot         */
143bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        @Override
144bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        public boolean accept(String pathName) {
145bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            return !pathName.startsWith(mPkgName);
146bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
147bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
148bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
149bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private Set<String> mApkPaths = new HashSet<String>();
150bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
151bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public ClassPathScanner(String... apkPaths) {
152bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        for (String apkPath : apkPaths) {
153bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            mApkPaths.add(apkPath);
154bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
155bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
156bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
157bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
158bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * Gets the names of all entries contained in given apk file, that match given filter.
159bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * @throws IOException
160bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
161bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    private void addEntriesFromApk(Set<String> entryNames, String apkPath, ClassNameFilter filter)
162bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            throws IOException {
163bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        DexFile dexFile = null;
164bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        try {
165bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            dexFile = new DexFile(apkPath);
166bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            Enumeration<String> apkClassNames = getDexEntries(dexFile);
167bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            while (apkClassNames.hasMoreElements()) {
168bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                String apkClassName = apkClassNames.nextElement();
169bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                if (filter.accept(apkClassName)) {
170bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                    entryNames.add(apkClassName);
171bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                }
172bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            }
173bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        } finally {
174bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            if (dexFile != null) {
175bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot                dexFile.close();
176bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            }
177bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
178bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
179bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
180bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
181bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * Retrieves the entry names from given {@link DexFile}.
182bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * <p/>
183bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * Exposed for unit testing.
184bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     *
185bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * @param dexFile
186bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * @return {@link Enumeration} of {@link String}s
187bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
188bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    Enumeration<String> getDexEntries(DexFile dexFile) {
189bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        return dexFile.entries();
190bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
191bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot
192bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    /**
193bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * Retrieves set of classpath entries that match given {@link ClassNameFilter}.
194bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     * @throws IOException
195bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot     */
196bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    public Set<String> getClassPathEntries(ClassNameFilter filter) throws IOException {
197bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        // use LinkedHashSet for predictable order
198bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        Set<String> entryNames = new LinkedHashSet<String>();
199bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        for (String apkPath : mApkPaths) {
200bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot            addEntriesFromApk(entryNames, apkPath, filter);
201bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        }
202bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot        return entryNames;
203bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot    }
204bb23e68d4dc19a37df318b8d169e3dfd0dd1c20eBrett Chabot}
205