19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.test; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.google.android.collect.Maps; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.google.android.collect.Sets; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport dalvik.system.DexFile; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File; 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Enumeration; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Map; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Set; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.TreeSet; 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.zip.ZipEntry; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.zip.ZipFile; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Generate {@link ClassPathPackageInfo}s by scanning apk paths. 36b51617f4668ef8cf0e6d8d0fc3284eae51dbd8caStephan Linzner * 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide} Not needed for 1.0 SDK. 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 39b51617f4668ef8cf0e6d8d0fc3284eae51dbd8caStephan Linzner@Deprecated 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class ClassPathPackageInfoSource { 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String CLASS_EXTENSION = ".class"; 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final ClassLoader CLASS_LOADER 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project = ClassPathPackageInfoSource.class.getClassLoader(); 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final SimpleCache<String, ClassPathPackageInfo> cache = 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new SimpleCache<String, ClassPathPackageInfo>() { 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project protected ClassPathPackageInfo load(String pkgName) { 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return createPackageInfo(pkgName); 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // The class path of the running application 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final String[] classPath; 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static String[] apkPaths; 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // A cache of jar file contents 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private final Map<File, Set<String>> jarFiles = Maps.newHashMap(); 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private ClassLoader classLoader; 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ClassPathPackageInfoSource() { 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classPath = getClassPath(); 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static void setApkPaths(String[] apkPaths) { 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ClassPathPackageInfoSource.apkPaths = apkPaths; 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public ClassPathPackageInfo getPackageInfo(String pkgName) { 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return cache.get(pkgName); 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private ClassPathPackageInfo createPackageInfo(String packageName) { 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> subpackageNames = new TreeSet<String>(); 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> classNames = new TreeSet<String>(); 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<Class<?>> topLevelClasses = Sets.newHashSet(); 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project findClasses(packageName, classNames, subpackageNames); 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (String className : classNames) { 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (className.endsWith(".R") || className.endsWith(".Manifest")) { 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Don't try to load classes that are generated. They usually aren't in test apks. 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project continue; 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 86b51617f4668ef8cf0e6d8d0fc3284eae51dbd8caStephan Linzner 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // We get errors in the emulator if we don't use the caller's class loader. 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project topLevelClasses.add(Class.forName(className, false, 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (classLoader != null) ? classLoader : CLASS_LOADER)); 910180dfeff341639011ce005a6707c9dc3b55f5dfRaluca Sauciuc } catch (ClassNotFoundException | NoClassDefFoundError e) { 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Should not happen unless there is a generated class that is not included in 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the .apk. 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w("ClassPathPackageInfoSource", "Cannot load class. " 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + "Make sure it is in your apk. Class name: '" + className 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project + "'. Message: " + e.getMessage(), e); 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new ClassPathPackageInfo(this, packageName, subpackageNames, 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project topLevelClasses); 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Finds all classes and sub packages that are below the packageName and 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * add them to the respective sets. Searches the package on the whole class 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * path. 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void findClasses(String packageName, Set<String> classNames, 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> subpackageNames) { 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String packagePrefix = packageName + '.'; 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String pathPrefix = packagePrefix.replace('.', '/'); 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (String entryName : classPath) { 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File classPathEntry = new File(entryName); 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Forge may not have brought over every item in the classpath. Be 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // polite and ignore missing entries. 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (classPathEntry.exists()) { 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryName.endsWith(".apk")) { 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project findClassesInApk(entryName, packageName, classNames, subpackageNames); 12208065b9f09ead8895d97b2971622af8c179e1768Brian Carlstrom } else { 12308065b9f09ead8895d97b2971622af8c179e1768Brian Carlstrom // scan the directories that contain apk files. 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (String apkPath : apkPaths) { 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File file = new File(apkPath); 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project scanForApkFiles(file, packageName, classNames, subpackageNames); 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new AssertionError("Can't read classpath entry " + 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project entryName + ": " + e.getMessage()); 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void scanForApkFiles(File source, String packageName, 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> classNames, Set<String> subpackageNames) throws IOException { 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (source.getPath().endsWith(".apk")) { 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project findClassesInApk(source.getPath(), packageName, classNames, subpackageNames); 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File[] files = source.listFiles(); 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (files != null) { 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (File file : files) { 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project scanForApkFiles(file, packageName, classNames, subpackageNames); 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Finds all classes and sub packages that are below the packageName and 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * add them to the respective sets. Searches the package in a class directory. 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void findClassesInDirectory(File classDir, 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String packagePrefix, String pathPrefix, Set<String> classNames, 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> subpackageNames) 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws IOException { 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File directory = new File(classDir, pathPrefix); 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (directory.exists()) { 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (File f : directory.listFiles()) { 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String name = f.getName(); 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (name.endsWith(CLASS_EXTENSION) && isToplevelClass(name)) { 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classNames.add(packagePrefix + getClassName(name)); 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (f.isDirectory()) { 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project subpackageNames.add(packagePrefix + name); 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Finds all classes and sub packages that are below the packageName and 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * add them to the respective sets. Searches the package in a single jar file. 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void findClassesInJar(File jarFile, String pathPrefix, 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> classNames, Set<String> subpackageNames) 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws IOException { 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> entryNames = getJarEntries(jarFile); 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // check if the Jar contains the package. 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!entryNames.contains(pathPrefix)) { 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int prefixLength = pathPrefix.length(); 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (String entryName : entryNames) { 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryName.startsWith(pathPrefix)) { 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryName.endsWith(CLASS_EXTENSION)) { 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // check if the class is in the package itself or in one of its 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // subpackages. 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int index = entryName.indexOf('/', prefixLength); 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (index >= 0) { 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String p = entryName.substring(0, index).replace('/', '.'); 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project subpackageNames.add(p); 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (isToplevelClass(entryName)) { 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classNames.add(getClassName(entryName).replace('/', '.')); 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Finds all classes and sub packages that are below the packageName and 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * add them to the respective sets. Searches the package in a single apk file. 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void findClassesInApk(String apkPath, String packageName, 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> classNames, Set<String> subpackageNames) 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws IOException { 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project DexFile dexFile = null; 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dexFile = new DexFile(apkPath); 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Enumeration<String> apkClassNames = dexFile.entries(); 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (apkClassNames.hasMoreElements()) { 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String className = apkClassNames.nextElement(); 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (className.startsWith(packageName)) { 2192c62f84add1fdddd51b38d0cc373be6b8b75a28bBrett Chabot String subPackageName = packageName; 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lastPackageSeparator = className.lastIndexOf('.'); 2212c62f84add1fdddd51b38d0cc373be6b8b75a28bBrett Chabot if (lastPackageSeparator > 0) { 2222c62f84add1fdddd51b38d0cc373be6b8b75a28bBrett Chabot subPackageName = className.substring(0, lastPackageSeparator); 2232c62f84add1fdddd51b38d0cc373be6b8b75a28bBrett Chabot } 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (subPackageName.length() > packageName.length()) { 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project subpackageNames.add(subPackageName); 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (isToplevelClass(className)) { 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classNames.add(className); 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 23243a17654cf4bfe7f1ec22bd8b7b32daccdf27c09Joe Onorato if (false) { 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w("ClassPathPackageInfoSource", 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project "Error finding classes at apk path: " + apkPath, e); 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dexFile != null) { 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Todo: figure out why closing causes a dalvik error resulting in vm shutdown. 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// dexFile.close(); 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Gets the class and package entries from a Jar. 2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Set<String> getJarEntries(File jarFile) 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws IOException { 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> entryNames = jarFiles.get(jarFile); 2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryNames == null) { 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project entryNames = Sets.newHashSet(); 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ZipFile zipFile = new ZipFile(jarFile); 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Enumeration<? extends ZipEntry> entries = zipFile.entries(); 2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (entries.hasMoreElements()) { 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String entryName = entries.nextElement().getName(); 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryName.endsWith(CLASS_EXTENSION)) { 2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // add the entry name of the class 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project entryNames.add(entryName); 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // add the entry name of the classes package, i.e. the entry name of 2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the directory that the class is in. Used to quickly skip jar files 2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // if they do not contain a certain package. 2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // 2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Also add parent packages so that a JAR that contains 2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // pkg1/pkg2/Foo.class will be marked as containing pkg1/ in addition 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // to pkg1/pkg2/ and pkg1/pkg2/Foo.class. We're still interested in 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // JAR files that contains subpackages of a given package, even if 2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // an intermediate package contains no direct classes. 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Classes in the default package will cause a single package named 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // "" to be added instead. 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lastIndex = entryName.lastIndexOf('/'); 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project do { 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String packageName = entryName.substring(0, lastIndex + 1); 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project entryNames.add(packageName); 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lastIndex = entryName.lastIndexOf('/', lastIndex - 1); 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } while (lastIndex > 0); 2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project jarFiles.put(jarFile, entryNames); 2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return entryNames; 2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Checks if a given file name represents a toplevel class. 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static boolean isToplevelClass(String fileName) { 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return fileName.indexOf('$') < 0; 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Given the absolute path of a class file, return the class name. 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static String getClassName(String className) { 2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int classNameEnd = className.length() - CLASS_EXTENSION.length(); 2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return className.substring(0, classNameEnd); 2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Gets the class path from the System Property "java.class.path" and splits 3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * it up into the individual elements. 3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static String[] getClassPath() { 3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String classPath = System.getProperty("java.class.path"); 3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String separator = System.getProperty("path.separator", ":"); 3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return classPath.split(Pattern.quote(separator)); 3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setClassLoader(ClassLoader classLoader) { 3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this.classLoader = classLoader; 3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 314