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.Config; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.google.android.collect.Maps; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport com.google.android.collect.Sets; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport dalvik.system.DexFile; 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException; 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Enumeration; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Map; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Set; 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.TreeSet; 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.zip.ZipEntry; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.zip.ZipFile; 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Generate {@link ClassPathPackageInfo}s by scanning apk paths. 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide} Not needed for 1.0 SDK. 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 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 } 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 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)); 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (ClassNotFoundException 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); 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if ("true".equals(System.getProperty("android.vm.dexfile", "false"))) { 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If the vm supports dex files then scan the directories that contain 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // apk files. 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (String apkPath : apkPaths) { 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File file = new File(apkPath); 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project scanForApkFiles(file, packageName, classNames, subpackageNames); 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (entryName.endsWith(".jar")) { 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project findClassesInJar(classPathEntry, pathPrefix, 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classNames, subpackageNames); 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (classPathEntry.isDirectory()) { 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project findClassesInDirectory(classPathEntry, packagePrefix, pathPrefix, 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classNames, subpackageNames); 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new AssertionError("Don't understand classpath entry " + 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classPathEntry); 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throw new AssertionError("Can't read classpath entry " + 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project entryName + ": " + e.getMessage()); 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void scanForApkFiles(File source, String packageName, 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> classNames, Set<String> subpackageNames) throws IOException { 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (source.getPath().endsWith(".apk")) { 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project findClassesInApk(source.getPath(), packageName, classNames, subpackageNames); 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File[] files = source.listFiles(); 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (files != null) { 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (File file : files) { 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project scanForApkFiles(file, packageName, classNames, subpackageNames); 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Finds all classes and sub packages that are below the packageName and 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * add them to the respective sets. Searches the package in a class directory. 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void findClassesInDirectory(File classDir, 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String packagePrefix, String pathPrefix, Set<String> classNames, 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> subpackageNames) 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws IOException { 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project File directory = new File(classDir, pathPrefix); 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (directory.exists()) { 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (File f : directory.listFiles()) { 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String name = f.getName(); 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (name.endsWith(CLASS_EXTENSION) && isToplevelClass(name)) { 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classNames.add(packagePrefix + getClassName(name)); 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (f.isDirectory()) { 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project subpackageNames.add(packagePrefix + name); 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Finds all classes and sub packages that are below the packageName and 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * add them to the respective sets. Searches the package in a single jar file. 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void findClassesInJar(File jarFile, String pathPrefix, 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> classNames, Set<String> subpackageNames) 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws IOException { 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> entryNames = getJarEntries(jarFile); 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // check if the Jar contains the package. 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (!entryNames.contains(pathPrefix)) { 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int prefixLength = pathPrefix.length(); 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (String entryName : entryNames) { 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryName.startsWith(pathPrefix)) { 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryName.endsWith(CLASS_EXTENSION)) { 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // check if the class is in the package itself or in one of its 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // subpackages. 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int index = entryName.indexOf('/', prefixLength); 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (index >= 0) { 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String p = entryName.substring(0, index).replace('/', '.'); 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project subpackageNames.add(p); 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (isToplevelClass(entryName)) { 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classNames.add(getClassName(entryName).replace('/', '.')); 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Finds all classes and sub packages that are below the packageName and 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * add them to the respective sets. Searches the package in a single apk file. 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void findClassesInApk(String apkPath, String packageName, 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> classNames, Set<String> subpackageNames) 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws IOException { 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project DexFile dexFile = null; 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dexFile = new DexFile(apkPath); 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Enumeration<String> apkClassNames = dexFile.entries(); 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (apkClassNames.hasMoreElements()) { 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String className = apkClassNames.nextElement(); 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (className.startsWith(packageName)) { 2292c62f84add1fdddd51b38d0cc373be6b8b75a28bBrett Chabot String subPackageName = packageName; 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lastPackageSeparator = className.lastIndexOf('.'); 2312c62f84add1fdddd51b38d0cc373be6b8b75a28bBrett Chabot if (lastPackageSeparator > 0) { 2322c62f84add1fdddd51b38d0cc373be6b8b75a28bBrett Chabot subPackageName = className.substring(0, lastPackageSeparator); 2332c62f84add1fdddd51b38d0cc373be6b8b75a28bBrett Chabot } 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (subPackageName.length() > packageName.length()) { 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project subpackageNames.add(subPackageName); 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (isToplevelClass(className)) { 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project classNames.add(className); 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Config.LOGV) { 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w("ClassPathPackageInfoSource", 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project "Error finding classes at apk path: " + apkPath, e); 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (dexFile != null) { 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Todo: figure out why closing causes a dalvik error resulting in vm shutdown. 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// dexFile.close(); 2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Gets the class and package entries from a Jar. 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Set<String> getJarEntries(File jarFile) 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project throws IOException { 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Set<String> entryNames = jarFiles.get(jarFile); 2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryNames == null) { 2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project entryNames = Sets.newHashSet(); 2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ZipFile zipFile = new ZipFile(jarFile); 2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Enumeration<? extends ZipEntry> entries = zipFile.entries(); 2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (entries.hasMoreElements()) { 2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String entryName = entries.nextElement().getName(); 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (entryName.endsWith(CLASS_EXTENSION)) { 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // add the entry name of the class 2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project entryNames.add(entryName); 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // add the entry name of the classes package, i.e. the entry name of 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the directory that the class is in. Used to quickly skip jar files 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // if they do not contain a certain package. 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Also add parent packages so that a JAR that contains 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // pkg1/pkg2/Foo.class will be marked as containing pkg1/ in addition 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // to pkg1/pkg2/ and pkg1/pkg2/Foo.class. We're still interested in 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // JAR files that contains subpackages of a given package, even if 2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // an intermediate package contains no direct classes. 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // 2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Classes in the default package will cause a single package named 2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // "" to be added instead. 2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int lastIndex = entryName.lastIndexOf('/'); 2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project do { 2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String packageName = entryName.substring(0, lastIndex + 1); 2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project entryNames.add(packageName); 2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project lastIndex = entryName.lastIndexOf('/', lastIndex - 1); 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } while (lastIndex > 0); 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project jarFiles.put(jarFile, entryNames); 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return entryNames; 2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Checks if a given file name represents a toplevel class. 2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static boolean isToplevelClass(String fileName) { 2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return fileName.indexOf('$') < 0; 3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Given the absolute path of a class file, return the class name. 3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static String getClassName(String className) { 3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int classNameEnd = className.length() - CLASS_EXTENSION.length(); 3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return className.substring(0, classNameEnd); 3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Gets the class path from the System Property "java.class.path" and splits 3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * it up into the individual elements. 3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static String[] getClassPath() { 3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String classPath = System.getProperty("java.class.path"); 3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String separator = System.getProperty("path.separator", ":"); 3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return classPath.split(Pattern.quote(separator)); 3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setClassLoader(ClassLoader classLoader) { 3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this.classLoader = classLoader; 3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 324