Android APK Checker This compares the set of classes, fields, and methods used by an Android application against the published API. It identifies and reports the use of any unpublished members or methods. The public API description files live in the source tree, in frameworks/base/api/. The tip-of-tree version is in "current.xml", and each officially released API has a numbered file (e.g. "6.xml"). They're generated from the sources, and can take into acount javadoc annotations like "@hide" in comments. The dependency set for an APK can be generated with "dexdeps". It finds all classes, fields, and methods that are referenced by classes.dex but not defined locally. The tool can't easily tell anything about a dependency beyond the name (e.g. whether a class is a static or non-static inner class), so while the output from dexdeps is similar in structure to the API XML file, it has much less detail. ==== Usage ==== % apkcheck [options] public-api.xml apk1.xml ... Provide the public API data file of choice, and one or more XML files generated by dexdeps. The time required to parse and manipulate the public API XML file is generally much larger than the time required to analyze the APK, so if you have a large set of APKs it's best to run them through in large batches. Options: --help Show options summary. --uses-library= Load additional public API list. This is intended for APKs that use "uses-library" directives to pull in external libraries. Since the external libraries are not part of the public API, their use would otherwise be flagged as illegal by apkcheck. --ignore-package= Ignore errors generated by references to the named package (e.g. "com.google.android.maps"). Warnings will be generated instead. Useful for ignoring references to shared library content when XML API data is not available. --[no-]warn Enable or disable warning messages. These are disabled by default. --[no-]error Enable or disable error messages. These are enabled by default. If you disable both warnings and errors you will only see a summary. In some cases involving generic signatures it may not be possible to accurately reconstruct the public API. Some popular cases have been hard-coded into the program. They can be included by specifying "--uses-library=BUILTIN". Example use: % dexdeps out/target/product/sapphire/system/app/Gmail.apk > Gmail.apk.xml % apkcheck --uses-library=BUILTIN frameworks/base/api/current.xml Gmail.apk.xml Gmail.apk.xml: summary: 0 errors, 15 warnings ==== Limitations ==== The API XML files have some ambiguous entries and are missing important pieces. A summary of the issues follows. (1) Class names are not in binary form Example: type="android.os.Parcelable.Creator" This could be a Creator class in the package android.os.Parcelable, or Parcelable.Creator in the package android.os. We can guess based on capitalization, but that's unreliable. The API XML does specify each package in a tag, so we should have the full set of packages available. From this we can remove one element at a time from the right until we match a known package. This will work unless "android.os" and "android.os.Parcelable" are both valid packages. (2) Public enums are not enumerated Enumeration classes are included, and always have two methods ("valueOf" and "values"). What isn't included are entries for the fields representing the enumeration values. This makes it look like an APK is referring to non-public fields in the class. If apkcheck sees a reference to an unknown field, and the field's defining class appears to be an Enum (the superclass is java.lang.Enum), we emit a warning instead of an error. (3) Public annotation methods are not listed Annotation classes have trivial entries that show only the class name and "implements java.lang.annotation.Annotation". It is not possible to verify that a method call on an annotation is valid. If apkcheck sees a method call to an unknown method, and the class appears to be an annotation (extends Object, implements Annotation, defines no fields or methods), we emit a warning instead of an error. (4) Covariant return types Suppose a class defines a method "public Foo gimmeFoo()". Any subclass that overrides that method must also return Foo, so it would seem that there's no need to emit a method entry for gimmeFoo() in the subclasses. However, it's possible to override gimmeFoo with "public MegaFoo gimmeFoo()" so long as MegaFoo is an instance of Foo. In that case it is necessary to emit a new method entry, but the public API XML generator does not. If apkcheck can't find an exact match for a method reference, but can find a method that matches on everything but the return type, it will emit a warning instead of an error. (We could be more thorough and try to verify that the return types are related, but that's more trouble than it's worth.) (5) Generic signatures When generic signatures are used, the public API file will contain entries like these: The generic types are generally indistinguishable from classes in the default package (i.e. that have no package name). In most cases they're a single letter, so apkcheck includes a kluge that converts single-letter class names to java.lang.Object. This often works, but falls apart in a few cases. For example: public T getParcelableExtra(String name) { return mExtras == null ? null : mExtras.getParcelable(name); } This is emitted as: which gets converted to java.lang.Object. Unfortunately the APK wants a method with a more specific return type (android.os.Parcelable), so the lookup fails. There is no way to recover the actual type, because the generic signature details are not present in the XML. This particular case will be handled as a covariant return type. When the generic type is in the parameter list, though, this isn't handled so easily. These cases are relatively few, so they were handled by baking the signatures into the code (--uses-library=BUILTIN). (At some point it may be worthwhile to try a little harder here.) (6) Use of opaque non-public types Some classes are not meant for public consumption, but are still referred to by application code. For example, an opaque type might be passed to the app as a cookie. Another example is the Dalvik annotation classes, like dalvik.annotation.InnerClass. These are emitted by "dx", and referenced from the DEX file, but not intended to be used by application code. If an APK refers to a non-public class, but doesn't access any fields or methods, a warning is emitted instead of an error.