| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.test.runner; |
| |
| import dalvik.system.DexFile; |
| |
| import java.io.IOException; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.Set; |
| |
| /** |
| * Finds class entries in apks. |
| * <p/> |
| * Adapted from tools/tradefederation/..ClassPathScanner |
| */ |
| class ClassPathScanner { |
| |
| /** |
| * A filter for classpath entry paths |
| * <p/> |
| * Patterned after {@link java.io.FileFilter} |
| */ |
| public static interface ClassNameFilter { |
| /** |
| * Tests whether or not the specified abstract pathname should be included in a class path |
| * entry list. |
| * |
| * @param pathName the relative path of the class path entry |
| */ |
| boolean accept(String className); |
| } |
| |
| /** |
| * A {@link ClassNameFilter} that accepts all class names. |
| */ |
| public static class AcceptAllFilter implements ClassNameFilter { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean accept(String className) { |
| return true; |
| } |
| |
| } |
| |
| /** |
| * A {@link ClassNameFilter} that chains one or more filters together |
| */ |
| public static class ChainedClassNameFilter implements ClassNameFilter { |
| private final ClassNameFilter[] mFilters; |
| |
| public ChainedClassNameFilter(ClassNameFilter... filters) { |
| mFilters = filters; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean accept(String className) { |
| for (ClassNameFilter filter : mFilters) { |
| if (!filter.accept(className)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| /** |
| * A {@link ClassNameFilter} that rejects inner classes. |
| */ |
| public static class ExternalClassNameFilter implements ClassNameFilter { |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean accept(String pathName) { |
| return !pathName.contains("$"); |
| } |
| } |
| |
| /** |
| * A {@link ClassNameFilter} that only accepts package names within the given namespace. |
| */ |
| public static class InclusivePackageNameFilter implements ClassNameFilter { |
| |
| private final String mPkgName; |
| |
| InclusivePackageNameFilter(String pkgName) { |
| if (!pkgName.endsWith(".")) { |
| mPkgName = String.format("%s.", pkgName); |
| } else { |
| mPkgName = pkgName; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean accept(String pathName) { |
| return pathName.startsWith(mPkgName); |
| } |
| } |
| |
| /** |
| * A {@link ClassNameFilter} that only rejects a given package names within the given namespace. |
| */ |
| public static class ExcludePackageNameFilter implements ClassNameFilter { |
| |
| private final String mPkgName; |
| |
| ExcludePackageNameFilter(String pkgName) { |
| if (!pkgName.endsWith(".")) { |
| mPkgName = String.format("%s.", pkgName); |
| } else { |
| mPkgName = pkgName; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean accept(String pathName) { |
| return !pathName.startsWith(mPkgName); |
| } |
| } |
| |
| private Set<String> mApkPaths = new HashSet<String>(); |
| |
| public ClassPathScanner(String... apkPaths) { |
| for (String apkPath : apkPaths) { |
| mApkPaths.add(apkPath); |
| } |
| } |
| |
| /** |
| * Gets the names of all entries contained in given apk file, that match given filter. |
| * @throws IOException |
| */ |
| private void addEntriesFromApk(Set<String> entryNames, String apkPath, ClassNameFilter filter) |
| throws IOException { |
| DexFile dexFile = null; |
| try { |
| dexFile = new DexFile(apkPath); |
| Enumeration<String> apkClassNames = getDexEntries(dexFile); |
| while (apkClassNames.hasMoreElements()) { |
| String apkClassName = apkClassNames.nextElement(); |
| if (filter.accept(apkClassName)) { |
| entryNames.add(apkClassName); |
| } |
| } |
| } finally { |
| if (dexFile != null) { |
| dexFile.close(); |
| } |
| } |
| } |
| |
| /** |
| * Retrieves the entry names from given {@link DexFile}. |
| * <p/> |
| * Exposed for unit testing. |
| * |
| * @param dexFile |
| * @return {@link Enumeration} of {@link String}s |
| */ |
| Enumeration<String> getDexEntries(DexFile dexFile) { |
| return dexFile.entries(); |
| } |
| |
| /** |
| * Retrieves set of classpath entries that match given {@link ClassNameFilter}. |
| * @throws IOException |
| */ |
| public Set<String> getClassPathEntries(ClassNameFilter filter) throws IOException { |
| // use LinkedHashSet for predictable order |
| Set<String> entryNames = new LinkedHashSet<String>(); |
| for (String apkPath : mApkPaths) { |
| addEntriesFromApk(entryNames, apkPath, filter); |
| } |
| return entryNames; |
| } |
| } |