12eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden/* 22eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Copyright (C) 2010 The Android Open Source Project 32eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 42eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Licensed under the Apache License, Version 2.0 (the "License"); 52eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * you may not use this file except in compliance with the License. 62eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * You may obtain a copy of the License at 72eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 82eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * http://www.apache.org/licenses/LICENSE-2.0 92eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 102eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Unless required by applicable law or agreed to in writing, software 112eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * distributed under the License is distributed on an "AS IS" BASIS, 122eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * See the License for the specific language governing permissions and 142eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * limitations under the License. 152eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 162eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 172eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFaddenpackage com.android.apkcheck; 182eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 192eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFaddenimport org.xml.sax.*; 202eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFaddenimport org.xml.sax.helpers.*; 21a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFaddenimport java.io.FileReader; 22a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFaddenimport java.io.IOException; 23a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFaddenimport java.io.Reader; 242eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFaddenimport java.util.ArrayList; 25a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFaddenimport java.util.HashSet; 262eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFaddenimport java.util.Iterator; 272eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 282eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 292eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden/** 302eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Checks an APK's dependencies against the published API specification. 312eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 322eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * We need to read two XML files (spec and APK) and perform some operations 332eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * on the elements. The file formats are similar but not identical, so 342eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * we distill it down to common elements. 352eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 362eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * We may also want to read some additional API lists representing 372eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * libraries that would be included with a "uses-library" directive. 382eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 392eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * For performance we want to allow processing of multiple APKs so 402eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * we don't have to re-parse the spec file each time. 412eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 422eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFaddenpublic class ApkCheck { 432eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /* keep track of current APK file name, for error messages */ 442eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden private static ApiList sCurrentApk; 452eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 462eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /* show warnings? */ 472eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden private static boolean sShowWarnings = false; 482eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /* show errors? */ 492eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden private static boolean sShowErrors = true; 502eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 51a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden /* names of packages we're allowed to ignore */ 52a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden private static HashSet<String> sIgnorablePackages = new HashSet<String>(); 53a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden 54a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden 552eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 562eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Program entry point. 572eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 582eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden public static void main(String[] args) { 592eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ApiList apiDescr = new ApiList("public-api"); 602eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 612eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (args.length < 2) { 622eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden usage(); 632eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return; 642eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 652eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 662eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /* process args */ 672eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden int idx; 682eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden for (idx = 0; idx < args.length; idx++) { 692eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (args[idx].equals("--help")) { 702eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden usage(); 712eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return; 722eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else if (args[idx].startsWith("--uses-library=")) { 732eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden String libName = args[idx].substring(args[idx].indexOf('=')+1); 742eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if ("BUILTIN".equals(libName)) { 752eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Reader reader = Builtin.getReader(); 762eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (!parseXml(apiDescr, reader, "BUILTIN")) 772eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return; 782eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else { 792eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (!parseApiDescr(apiDescr, libName)) 802eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return; 812eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 82a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden } else if (args[idx].startsWith("--ignore-package=")) { 83a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden String pkgName = args[idx].substring(args[idx].indexOf('=')+1); 84a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden sIgnorablePackages.add(pkgName); 852eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else if (args[idx].equals("--warn")) { 862eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden sShowWarnings = true; 872eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else if (args[idx].equals("--no-warn")) { 882eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden sShowWarnings = false; 892eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else if (args[idx].equals("--error")) { 902eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden sShowErrors = true; 912eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else if (args[idx].equals("--no-error")) { 922eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden sShowErrors = false; 932eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 942eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else if (args[idx].startsWith("--")) { 952eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (args[idx].equals("--")) { 962eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden // remainder are filenames, even if they start with "--" 972eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden idx++; 982eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden break; 992eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else { 1002eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden // unknown option specified 1012eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("ERROR: unknown option " + 1022eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden args[idx] + " (use \"--help\" for usage info)"); 1032eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return; 1042eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1052eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else { 1062eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden break; 1072eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1082eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1092eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (idx > args.length - 2) { 1102eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden usage(); 1112eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return; 1122eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1132eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1142eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /* parse base API description */ 1152eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (!parseApiDescr(apiDescr, args[idx++])) 1162eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return; 1172eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1182eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /* "flatten" superclasses and interfaces */ 1192eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden sCurrentApk = apiDescr; 1202eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden flattenInherited(apiDescr); 1212eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1222eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /* walk through list of libs we want to scan */ 1232eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden for ( ; idx < args.length; idx++) { 1242eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ApiList apkDescr = new ApiList(args[idx]); 1252eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden sCurrentApk = apkDescr; 1262eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden boolean success = parseApiDescr(apkDescr, args[idx]); 1272eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (!success) { 1282eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (idx < args.length-1) 1292eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("Skipping..."); 1302eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden continue; 1312eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1322eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1332eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden check(apiDescr, apkDescr); 1342eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.out.println(args[idx] + ": summary: " + 1352eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden apkDescr.getErrorCount() + " errors, " + 1362eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden apkDescr.getWarningCount() + " warnings\n"); 1372eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1382eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1392eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1402eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 1412eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Prints usage statement. 1422eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 1432eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden static void usage() { 1442eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("Android APK checker v1.0"); 1452eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("Copyright (C) 2010 The Android Open Source Project\n"); 1462eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("Usage: apkcheck [options] public-api.xml apk1.xml ...\n"); 1472eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("Options:"); 1482eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println(" --help show this message"); 1492eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println(" --uses-library=lib.xml load additional public API list"); 150a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden System.err.println(" --ignore-package=pkg don't show errors for references to this package"); 1512eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println(" --[no-]warn enable or disable display of warnings"); 1522eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println(" --[no-]error enable or disable display of errors"); 1532eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1542eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1552eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 1562eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Opens the file and passes it to parseXml. 1572eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 1582eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * TODO: allow '-' as an alias for stdin? 1592eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 1602eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden static boolean parseApiDescr(ApiList apiList, String fileName) { 1612eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden boolean result = false; 1622eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1632eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden try { 1642eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden FileReader fileReader = new FileReader(fileName); 1652eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden result = parseXml(apiList, fileReader, fileName); 1662eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden fileReader.close(); 1672eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } catch (IOException ioe) { 1682eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("Error opening " + fileName); 1692eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1702eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return result; 1712eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 1722eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1732eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 1742eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Parses an XML file holding an API description. 1752eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 1762eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * @param fileReader Data source. 1772eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * @param apiList Container to add stuff to. 1782eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * @param fileName Input file name, only used for debug messages. 1792eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 1802eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden static boolean parseXml(ApiList apiList, Reader reader, 1812eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden String fileName) { 1822eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden //System.out.println("--- parsing " + fileName); 1832eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden try { 1842eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden XMLReader xmlReader = XMLReaderFactory.createXMLReader(); 1852eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ApiDescrHandler handler = new ApiDescrHandler(apiList); 1862eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden xmlReader.setContentHandler(handler); 1872eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden xmlReader.setErrorHandler(handler); 1882eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden xmlReader.parse(new InputSource(reader)); 1892eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 1902eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden //System.out.println("--- parsing complete"); 1912eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden //dumpApi(apiList); 1922eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return true; 1932eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } catch (SAXParseException ex) { 1942eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("Error parsing " + fileName + " line " + 1952eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ex.getLineNumber() + ": " + ex.getMessage()); 1962eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } catch (Exception ex) { 1972eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.err.println("Error while reading " + fileName + ": " + 1982eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ex.getMessage()); 1992eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ex.printStackTrace(); 2002eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2012eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2022eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden // failed 2032eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return false; 2042eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2052eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2062eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 2072eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Expands lists of fields and methods to recursively include superclass 2082eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * and interface entries. 2092eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 2102eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * The API description files have entries for every method a class 2112eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * declares, even if it's present in the superclass (e.g. toString()). 2122eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Removal of one of these methods doesn't constitute an API change, 2132eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * though, so if we don't find a method in a class we need to hunt 2142eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * through its superclasses. 2152eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 2162eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * We can walk up the hierarchy while analyzing the target APK, 2172eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * or we can "flatten" the methods declared by the superclasses and 2182eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * interfaces before we begin the analysis. Expanding up front can be 2192eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * beneficial if we're analyzing lots of APKs in one go, but detrimental 2202eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * to startup time if we just want to look at one small APK. 2212eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 2222eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * It also means filling the field/method hash tables with lots of 2232eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * entries that never get used, possibly worsening the hash table 2242eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * hit rate. 2252eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 2262eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * We only need to do this for the public API list. The dexdeps output 2272eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * doesn't have this sort of information anyway. 2282eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 2292eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden static void flattenInherited(ApiList pubList) { 2302eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<PackageInfo> pkgIter = pubList.getPackageIterator(); 2312eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (pkgIter.hasNext()) { 2322eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden PackageInfo pubPkgInfo = pkgIter.next(); 2332eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2342eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<ClassInfo> classIter = pubPkgInfo.getClassIterator(); 2352eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (classIter.hasNext()) { 2362eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ClassInfo pubClassInfo = classIter.next(); 2372eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2382eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden pubClassInfo.flattenClass(pubList); 2392eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2402eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2412eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2422eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2432eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 2442eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Checks the APK against the public API. 2452eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 2462eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Run through and find the mismatches. 2472eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * 2482eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * @return true if all is well 2492eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 2502eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden static boolean check(ApiList pubList, ApiList apkDescr) { 2512eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2522eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<PackageInfo> pkgIter = apkDescr.getPackageIterator(); 2532eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (pkgIter.hasNext()) { 2542eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden PackageInfo apkPkgInfo = pkgIter.next(); 2552eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden PackageInfo pubPkgInfo = pubList.getPackage(apkPkgInfo.getName()); 2562eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden boolean badPackage = false; 2572eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2582eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (pubPkgInfo == null) { 2592eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden // "illegal package" not a tremendously useful message 2602eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden //apkError("Illegal package ref: " + apkPkgInfo.getName()); 2612eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden badPackage = true; 2622eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2632eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2642eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<ClassInfo> classIter = apkPkgInfo.getClassIterator(); 2652eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (classIter.hasNext()) { 2662eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ClassInfo apkClassInfo = classIter.next(); 2672eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2682eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (badPackage) { 269a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden /* 270a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden * The package is not present in the public API file, 271a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden * but simply saying "bad package" isn't all that 272a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden * useful, so we emit the names of each of the classes. 273a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden */ 274a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden if (isIgnorable(apkPkgInfo)) { 275a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden apkWarning("Ignoring class ref: " + 276a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden apkPkgInfo.getName() + "." + apkClassInfo.getName()); 277a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden } else { 278a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden apkError("Illegal class ref: " + 279a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden apkPkgInfo.getName() + "." + apkClassInfo.getName()); 280a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden } 2812eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else { 2822eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden checkClass(pubPkgInfo, apkClassInfo); 2832eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2842eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2852eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2862eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2872eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return true; 2882eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 2892eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2902eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 2912eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Checks the class against the public API. We check the class 2922eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * itself and then any fields and methods. 2932eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 2942eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden static boolean checkClass(PackageInfo pubPkgInfo, ClassInfo classInfo) { 2952eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2962eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ClassInfo pubClassInfo = pubPkgInfo.getClass(classInfo.getName()); 2972eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 2982eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (pubClassInfo == null) { 299a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden if (isIgnorable(pubPkgInfo)) { 300a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden apkWarning("Ignoring class ref: " + 301a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden pubPkgInfo.getName() + "." + classInfo.getName()); 302a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden } else if (classInfo.hasNoFieldMethod()) { 3032eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden apkWarning("Hidden class referenced: " + 3042eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden pubPkgInfo.getName() + "." + classInfo.getName()); 3052eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else { 3062eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden apkError("Illegal class ref: " + 3072eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden pubPkgInfo.getName() + "." + classInfo.getName()); 3082eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden // could list specific fields/methods used 3092eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3102eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return false; 3112eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3122eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 3132eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /* 3142eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Check the contents of classInfo against pubClassInfo. 3152eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 3162eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<FieldInfo> fieldIter = classInfo.getFieldIterator(); 3172eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (fieldIter.hasNext()) { 3182eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden FieldInfo apkFieldInfo = fieldIter.next(); 3192eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden String nameAndType = apkFieldInfo.getNameAndType(); 3202eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden FieldInfo pubFieldInfo = pubClassInfo.getField(nameAndType); 3212eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (pubFieldInfo == null) { 3224fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden if (pubClassInfo.isEnum()) { 3232eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden apkWarning("Enum field ref: " + pubPkgInfo.getName() + 3242eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden "." + classInfo.getName() + "." + nameAndType); 3252eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else { 3262eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden apkError("Illegal field ref: " + pubPkgInfo.getName() + 3272eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden "." + classInfo.getName() + "." + nameAndType); 3282eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3292eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3302eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3312eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 3322eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<MethodInfo> methodIter = classInfo.getMethodIterator(); 3332eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (methodIter.hasNext()) { 3342eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden MethodInfo apkMethodInfo = methodIter.next(); 3352eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden String nameAndDescr = apkMethodInfo.getNameAndDescriptor(); 3362eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden MethodInfo pubMethodInfo = pubClassInfo.getMethod(nameAndDescr); 3372eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (pubMethodInfo == null) { 3382eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden pubMethodInfo = pubClassInfo.getMethodIgnoringReturn(nameAndDescr); 3392eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (pubMethodInfo == null) { 3404fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden if (pubClassInfo.isAnnotation()) { 3414fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden apkWarning("Annotation method ref: " + 3424fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden pubPkgInfo.getName() + "." + classInfo.getName() + 3434fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden "." + nameAndDescr); 3444fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden } else { 3454fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden apkError("Illegal method ref: " + pubPkgInfo.getName() + 3464fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden "." + classInfo.getName() + "." + nameAndDescr); 3474fbfbb3914cfcc6d2da41be818e76208f1e59866Andy McFadden } 3482eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } else { 3492eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden apkWarning("Possibly covariant method ref: " + 3502eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden pubPkgInfo.getName() + "." + classInfo.getName() + 3512eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden "." + nameAndDescr); 3522eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3532eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3542eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3552eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 3562eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 3572eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden return true; 3582eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3592eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 360a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden /** 361a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden * Returns true if the package is in the "ignored" list. 362a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden */ 363a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden static boolean isIgnorable(PackageInfo pkgInfo) { 364a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden return sIgnorablePackages.contains(pkgInfo.getName()); 365a4707b1709b7ca7391d6bdc2bb7cf7ff4a92c5a3Andy McFadden } 3662eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 3672eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 3682eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Prints a warning message about an APK problem. 3692eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 3702eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden public static void apkWarning(String msg) { 3712eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (sShowWarnings) { 3722eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.out.println("(warn) " + sCurrentApk.getDebugString() + 3732eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ": " + msg); 3742eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3752eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden sCurrentApk.incrWarnings(); 3762eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3772eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 3782eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 3792eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Prints an error message about an APK problem. 3802eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 3812eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden public static void apkError(String msg) { 3822eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden if (sShowErrors) { 3832eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.out.println(sCurrentApk.getDebugString() + ": " + msg); 3842eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3852eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden sCurrentApk.incrErrors(); 3862eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3872eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 3882eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden /** 3892eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * Recursively dumps the contents of the API. Sort order is not 3902eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden * specified. 3912eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden */ 3922eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden private static void dumpApi(ApiList apiList) { 3932eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<PackageInfo> iter = apiList.getPackageIterator(); 3942eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (iter.hasNext()) { 3952eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden PackageInfo pkgInfo = iter.next(); 3962eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden dumpPackage(pkgInfo); 3972eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3982eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 3992eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 4002eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden private static void dumpPackage(PackageInfo pkgInfo) { 4012eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<ClassInfo> iter = pkgInfo.getClassIterator(); 4022eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.out.println("PACKAGE " + pkgInfo.getName()); 4032eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (iter.hasNext()) { 4042eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden ClassInfo classInfo = iter.next(); 4052eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden dumpClass(classInfo); 4062eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 4072eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 4082eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 4092eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden private static void dumpClass(ClassInfo classInfo) { 4102eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.out.println(" CLASS " + classInfo.getName()); 4112eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<FieldInfo> fieldIter = classInfo.getFieldIterator(); 4122eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (fieldIter.hasNext()) { 4132eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden FieldInfo fieldInfo = fieldIter.next(); 4142eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden dumpField(fieldInfo); 4152eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 4162eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden Iterator<MethodInfo> methIter = classInfo.getMethodIterator(); 4172eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden while (methIter.hasNext()) { 4182eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden MethodInfo methInfo = methIter.next(); 4192eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden dumpMethod(methInfo); 4202eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 4212eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 4222eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 4232eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden private static void dumpMethod(MethodInfo methInfo) { 4242eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.out.println(" METHOD " + methInfo.getNameAndDescriptor()); 4252eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 4262eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 4272eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden private static void dumpField(FieldInfo fieldInfo) { 4282eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden System.out.println(" FIELD " + fieldInfo.getNameAndType()); 4292eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden } 4302eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden} 4312eceaea745773bb654bb4e52c00cafdedc68c0acAndy McFadden 432