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