You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
518 lines
17 KiB
518 lines
17 KiB
# Signature Formats
|
|
|
|
This document describes the signature file format created and used by metalava,
|
|
doclava, apicheck, etc.
|
|
|
|
There are currently 3 versions of this format:
|
|
|
|
1. The format emitted by doclava, and used for Android's signature files up
|
|
through Android P. Note that this isn't actually a single format; it evolved
|
|
over time, so older signature files vary a bit (many of these changes were
|
|
due to bugs getting fixed, such as type parameters missing from classes
|
|
and methods until they start appearing), and some were deliberate changes,
|
|
such as dropping the "final" modifier in front of every member if the
|
|
containing class is final.
|
|
|
|
2. The "new" format, which is described below, and is used in Android Q. This
|
|
format adds new information, such as annotations, parameter names and default
|
|
values, as well as cleans up a number of things (such as dropping
|
|
java.lang. prefixes on types, etc)
|
|
|
|
3. This is format v2, but with all nullness annotations replaced by a
|
|
Kotlin-syntax, e.g. "?" for nullable types, "!" for unknown/platform types,
|
|
and no suffix for non-nullable types. The initial plan was to include this
|
|
in format v2, but it was deferred since type-use annotations introduces
|
|
some complexities in the implementation.
|
|
|
|
|
|
## Motivation
|
|
|
|
Why did we change from the historical doclava signature format (v1)
|
|
to a new format?
|
|
|
|
In order to support Kotlin better (though this will also benefit Java
|
|
developers), we'd like to have nullness annotations (as well as some other
|
|
annotations) be a formal part of the SDK.
|
|
|
|
That means the annotations should be part of the signature files too -- such
|
|
that we can not just record explicitly what the API contract is, but also
|
|
enforce that changes are not only deliberate changes but also compatible
|
|
changes. (For example, you can change the return value of a final method from
|
|
nullable to non null, but not the other way around.)
|
|
|
|
And if we were going to change the signature format, we might as well make some
|
|
other changes too.
|
|
|
|
|
|
### Comments
|
|
|
|
In v2, line comments (starting with //) are allowed. This allows us to leave
|
|
reminders and other issues with the signature source (though the update-api task
|
|
will generally blow these away, so use sparingly.)
|
|
|
|
### Header
|
|
|
|
New signature files (v2+) generally include a file header comment which states
|
|
the version number. This makes it possible for tools to more safely interpret
|
|
signature files. For example, in v3 the type "String" means "@NonNull String",
|
|
but in v2 "String" means "String with unknown nullness".
|
|
|
|
The header looks like this:
|
|
|
|
```
|
|
// Signature format: 2.0
|
|
```
|
|
|
|
Here "2" is the major format version; the .0 allows for compatible minor
|
|
variations of the format.
|
|
|
|
### Include Annotations
|
|
|
|
The new signature format now includes annotations; not all annotations (such as
|
|
@Override etc); only those which are significant for the API, such as nullness
|
|
annotations, etc.
|
|
|
|
Annotations are included on the same line as the class/field/method, right
|
|
before the modifiers.
|
|
|
|
Here's how this looks:
|
|
|
|
|
|
```
|
|
method @Nullable public static Integer compute1(@Nullable java.util.List<java.lang.String>);
|
|
```
|
|
|
|
|
|
(Notice how the annotations are not using fully qualified name; that's discussed
|
|
below.)
|
|
|
|
The annotations to be included are annotations for annotation types that are not
|
|
hidden, and have class file or runtime retention.
|
|
|
|
The annotations should be sorted alphabetically by fully qualified name.
|
|
|
|
|
|
### Use Special Syntax or Nullness Annotations
|
|
|
|
(Note: Only in version format 3+)
|
|
|
|
As a special optimization, since we eventually want **all** APIs to have
|
|
explicit nullness, use Kotlin's syntax for nullness. That means that for
|
|
nullable elements, we add "?" after the type, for unknown nullness we add "!",
|
|
and otherwise there's no suffix. In other words:
|
|
|
|
|
|
<table>
|
|
<tr>
|
|
<td>
|
|
</td>
|
|
<td>Java Type
|
|
</td>
|
|
<td>Signature File Type
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Nullable
|
|
</td>
|
|
<td>@Nullable String
|
|
</td>
|
|
<td>String?
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Not nullable
|
|
</td>
|
|
<td>@NonNull String
|
|
</td>
|
|
<td>String
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Unknown nullability
|
|
</td>
|
|
<td>String
|
|
</td>
|
|
<td>String!
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
|
|
The above signature line is turned into
|
|
|
|
|
|
```
|
|
method public Integer? compute1(java.util.List<java.lang.String!>?);
|
|
```
|
|
|
|
|
|
### Clean Up Terminology
|
|
|
|
Format v2 also cleans up some of the terminology used to describe the class
|
|
structure in the signature file. For example, in v1, an interface is called an
|
|
"abstract interface"; an interface extending another interface is said to
|
|
"implement" it instead of "extend"-ing it, etc; enums and annotations are just
|
|
referred to as classes that extend java.lang.Enum, or java.lang.Annotation etc.
|
|
|
|
With these changes, these lines from v1 signature files:
|
|
|
|
|
|
```
|
|
public abstract interface List<E> implements java.util.Collection { ... }
|
|
public class TimeUnit extends java.lang.Enum { ... }
|
|
public abstract class SuppressLint implements java.lang.annotation.Annotation { ... }
|
|
```
|
|
|
|
|
|
are replaced by
|
|
|
|
|
|
```
|
|
public interface List<E> extends java.util.Collection<E> { ... }
|
|
public enum TimeUnit { ... }
|
|
public @interface SuppressLint { ... }
|
|
```
|
|
|
|
|
|
|
|
### Use Generics Everywhere
|
|
|
|
The v1 signature files uses raw types in some places but not others. Note that
|
|
in the above it was missing from super interface Collection:
|
|
|
|
|
|
```
|
|
public abstract interface List<E> implements java.util.Collection { ... }
|
|
```
|
|
|
|
|
|
whereas in the v2 format it's included:
|
|
|
|
|
|
```
|
|
public interface List<E> extends java.util.Collection<E> { ... }
|
|
```
|
|
|
|
|
|
Similarly, v1 used erasure in throws clauses. For example, for this method:
|
|
|
|
|
|
```
|
|
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
|
|
```
|
|
|
|
v1 used this signature:
|
|
|
|
|
|
```
|
|
method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws java.lang.Throwable;
|
|
```
|
|
|
|
Note how that's "throws Throwable" instead of "throws X". This results in b/110302703.
|
|
|
|
In the v2 format we instead use the correct throws type:
|
|
|
|
```
|
|
method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws X;
|
|
```
|
|
|
|
|
|
### Support Annotations
|
|
|
|
The old format was completely missing annotation type methods:
|
|
|
|
```
|
|
public static abstract class ViewDebug.ExportedProperty implements java.lang.annotation.Annotation {
|
|
}
|
|
```
|
|
|
|
We need to include annotation member methods, as well as their default values
|
|
since those are API-significant. Here's how this looks in the v2 file format
|
|
(also applying the @interface terminology change described above) :
|
|
|
|
|
|
```
|
|
public static @interface ViewDebug.ExportedProperty {
|
|
method public abstract String category() default "";
|
|
method public abstract boolean deepExport() default false;
|
|
method public abstract android.view.ViewDebug.FlagToString[] flagMapping() default {};
|
|
method public abstract boolean formatToHexString() default false;
|
|
method public abstract boolean hasAdjacentMapping() default false;
|
|
method public abstract android.view.ViewDebug.IntToString[] indexMapping() default {};
|
|
method public abstract android.view.ViewDebug.IntToString[] mapping() default {};
|
|
method public abstract String prefix() default "";
|
|
method public abstract boolean resolveId() default false;
|
|
}
|
|
```
|
|
|
|
|
|
### Support Kotlin Modifiers
|
|
|
|
This doesn't currently apply to the SDK, but the signature files are also used
|
|
in the support library, and some of these are written in Kotlin and exposes
|
|
Kotlin-specific APIs.
|
|
|
|
That means the v2 format can express API-significant aspects of Kotlin. This
|
|
includes special modifiers, such as sealed, inline, operator, infix, etc:
|
|
|
|
```
|
|
method public static operator int get(android.graphics.Bitmap, int x, int y);
|
|
method public static infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r);
|
|
```
|
|
|
|
### Support Kotlin Properties
|
|
|
|
Kotlin's Java support means that it wil take a Kotlin property and compile it
|
|
into getters and setters which you can call from Java. But you cannot calls
|
|
these getters and setters from Kotlin; you **must** use the property
|
|
syntax. Therefore, we need to also capture properties in the signature files. If
|
|
you have this Kotlin code:
|
|
|
|
|
|
```
|
|
var property2: String? = "initial"
|
|
```
|
|
|
|
it will get recorded in the signature files like this:
|
|
|
|
```
|
|
property public java.lang.String? property2 = "initial";
|
|
method public java.lang.String? getProperty2();
|
|
method public void setProperty2(java.lang.String? p);
|
|
```
|
|
|
|
The last two elements are "redundant"; they could be computed from the property
|
|
name (and included if the property declaration uses special annotations to name
|
|
the getters and setters away from the defaults), but it's helpful to be explicit
|
|
(and this allows us to specify the default value).
|
|
|
|
### Support Named Parameters
|
|
|
|
Kotlin supports default values for parameters, and these are a part of the API
|
|
contract, so we need to include them in the signature format.
|
|
|
|
Here's an example:
|
|
|
|
```
|
|
method public static void edit(android.content.SharedPreferences, boolean commit);
|
|
```
|
|
|
|
In v1 files we only list type names, but in v2 we allow an optional parameter
|
|
name to be specified; "commit" in the above.
|
|
|
|
Note that this isn't just for Kotlin. Just like there are special nullness
|
|
annotations to mark up the null contract for an element, we will also have a
|
|
special annotation to explicitly name a Java parameter:
|
|
@android.annotation.ParameterName (which is hidden). This obviously isn't usable
|
|
from Java, but Kotlin client code can now reference the parameter.
|
|
|
|
Therefore, the following Java code (not signature code) will also produce
|
|
exactly the same signature as the above:
|
|
|
|
```
|
|
public static void edit(SharedPreferences prefs, @ParameterName("commit") boolean ct) {…}
|
|
```
|
|
|
|
(Note how the implementation parameter doesn't have to match the public, API
|
|
name of the parameter.)
|
|
|
|
### Support Default Values
|
|
|
|
In addition to named parameters, Kotlin also supports default values. These are
|
|
also be part of the v2 signature since (as an example) removing a default value
|
|
is a compile-incompatible change.
|
|
|
|
Therefore, the v2 format allows default values to be specified after the type
|
|
and/or parameter name:
|
|
|
|
```
|
|
method public static void edit(SharedPreferences, boolean commit = false);
|
|
```
|
|
|
|
For Kotlin code, the default parameter values are extracted automatically, and
|
|
for Java, just as with parameter names, you can specify a special annotation to
|
|
record the default value for usage from languages that support default parameter
|
|
values:
|
|
|
|
```
|
|
public static void edit(SharedPreferences prefs, @DefaultValue("false") boolean ct) {…}
|
|
```
|
|
|
|
|
|
### Include Inherited Methods
|
|
|
|
Consider a scenario where a public class extends a hidden class, and that hidden
|
|
class defines a public method.
|
|
|
|
Doclava did not include these methods in the signature files, but they **were**
|
|
present in the stub files (and therefore part of the API). In the v2 signature
|
|
file format, we include these.
|
|
|
|
An example of this is StringBuilder#setLength. According to the old signature
|
|
files, that method does not exist, but clearly it's there in the SDK. The reason
|
|
this happens is that StringBuilder is a public class which extends hidden class
|
|
AbstractStringBuilder, which defines the public method setLength.
|
|
|
|
|
|
### No Hardcoded Enum Methods
|
|
|
|
Doclava always inserted two special methods in the signature files for every
|
|
enum: values() and valueOf():
|
|
|
|
```
|
|
public static final class CursorJoiner.Result extends java.lang.Enum {
|
|
method public static android.database.CursorJoiner.Result valueOf(java.lang.String);
|
|
method public static final android.database.CursorJoiner.Result[] values();
|
|
enum_constant public static final android.database.CursorJoiner.Result BOTH;
|
|
enum_constant public static final android.database.CursorJoiner.Result LEFT;
|
|
enum_constant public static final android.database.CursorJoiner.Result RIGHT;
|
|
}
|
|
```
|
|
|
|
It didn't do that in stubs, because you can't: those are special methods
|
|
generated by the compiler. There's no reason to list these in the signature
|
|
files since they're entirely implied by the enum, you can't change them, and
|
|
it's just extra noise.
|
|
|
|
In the new v2 format these are no longer present:
|
|
|
|
```
|
|
public static enum CursorJoiner.Result {
|
|
enum_constant public static final android.database.CursorJoiner.Result BOTH;
|
|
enum_constant public static final android.database.CursorJoiner.Result LEFT;
|
|
enum_constant public static final android.database.CursorJoiner.Result RIGHT;
|
|
}
|
|
```
|
|
|
|
### Remove "deprecated" Modifier
|
|
|
|
The old signature file format used "deprecated" as if it was a modifier. In the
|
|
new format, we instead list these using annotations, @Deprecated.
|
|
|
|
### Standard Modifier Order
|
|
|
|
Doclava had a "random" (but stable) order of modifiers.
|
|
|
|
In the new signature format, we're using the standard modifier order for Java
|
|
and Kotlin, wihch more closely mirrors what is done in the source code.
|
|
|
|
Version format 1 order:
|
|
|
|
```
|
|
public/protected/private default static final abstract synchronized transient volatile
|
|
```
|
|
|
|
Version format 2 order:
|
|
|
|
```
|
|
public/protected/internal/private abstract default static final transient volatile synchronized
|
|
```
|
|
|
|
The above list doesn't include the Kotlin modifiers, which are inserted
|
|
according to the Kotlin language style guide:
|
|
https://kotlinlang.org/docs/reference/coding-conventions.html#modifiers
|
|
|
|
### Sort Classes By Fully Qualified Names
|
|
|
|
In "extends" lists, the signature file can list a comma separated list of
|
|
classes. The classes are listed by fully qualified name, but in v1 it was sorted
|
|
by simple name. In the v2 format, we sort by fully qualified name instead.
|
|
|
|
### Use Wildcards Consistently
|
|
|
|
Doclava (v1) would sometimes use the type bound <?> and other times use <?
|
|
extends Object>. These are equivalent. In the v2 format, <? extends Object> is
|
|
always written as <?>.
|
|
|
|
### Annotation Simple Names
|
|
|
|
We have a number of annotations which are significant for the API -- not just
|
|
the nullness as deprecation ones (which are specially supported in v3 via the
|
|
?/! Kotlin syntax and the deprecated "modifier"), but annotations for permission
|
|
requirements, range constraints, valid constant values for an integer, and so
|
|
on.
|
|
|
|
In the codebase, these are typically in the android.annotation. package,
|
|
referencing annotation classes that are generally **not** part of the API. When
|
|
we generate the SDK, we translate these into publicly known annotations,
|
|
androidx.annotation, such that Studio, lint, the Kotlin compiler and others can
|
|
recognize the metadata.
|
|
|
|
That begs the question: which fully qualified name should we put in the
|
|
signature file? The one that appeared in the source (which is hidden, or in the
|
|
case of Kotlin code, a special JetBrains nullness annotation), or the one that
|
|
it gets translated into?
|
|
|
|
In v2 we do neither: We use only the simple name of the annotations in the
|
|
signature file, for annotations that are in the well known packages. In other
|
|
words, instead of any of these alternative declarations:
|
|
|
|
```
|
|
method public void setTitleTextColor(@android.annotation.ColorInt int);
|
|
method public void setTitleTextColor(@android.support.annotation.ColorInt int);
|
|
method public void setTitleTextColor(@androidx.annotation.ColorInt int);
|
|
```
|
|
|
|
in v2 we have simply
|
|
|
|
```
|
|
method public void setTitleTextColor(@ColorInt int);
|
|
```
|
|
|
|
### Simple Names in Java.lang
|
|
|
|
In Java files, you can implicitly reference classes in java.lang without
|
|
importing them. In v2 offer the same thing in signature files. There are several
|
|
classes from java.lang that are used in lots of places in the signature file
|
|
(java.lang.String alone is present in over 11,000 lines of the API file), and
|
|
other common occurrences are java.lang.Class, java.lang.Integer,
|
|
java.lang.Runtime, etc.
|
|
|
|
This basically builds on the same idea from having an implicit package for
|
|
annotations, and doing the same thing for java.lang: Omitting it when writing
|
|
signature files, and implicitly adding it back when reading in signature files.
|
|
|
|
This only applies to the java.lang package, not any subpackages, so for example
|
|
java.lang.reflect.Method will **not** be shortened to reflect.Method.
|
|
|
|
### Type Use Annotations
|
|
|
|
In v3, "type use annotations" are supported which means annotations can appear
|
|
within types.
|
|
|
|
### Skipping some signatures
|
|
|
|
If a method overrides another method, and the signatures are the same, the
|
|
overriding method is left out of the signature file. This basically compares the
|
|
modifiers, ignoring some that are not API significant (such as "native"). Note
|
|
also that some modifiers are implicit; for example, if a method is implementing
|
|
a method from an interface, the interface method is implicitly abstract, so the
|
|
implementation will be included in the signature file.
|
|
|
|
In v2, we take this one step further: If a method differs **only** from its
|
|
overridden method by "final", **and** if the containing class is final, then the
|
|
method is not included in the signature file. The same is the case for
|
|
deprecated.
|
|
|
|
### Miscellaneous
|
|
|
|
Some other minor tweaks in v2:
|
|
|
|
* Fix formatting for package private elements. These had two spaces of
|
|
indentation; this is probably just a bug. The new format aligns their
|
|
indentation with all other elements.
|
|
* Don't add spaces in type bounds lists (e.g. Map<X,Y>, not Map<X, Y>.)
|
|
|
|
## Historical API Files
|
|
|
|
Metalava can read and write these formats. To switch output formats, invoke it
|
|
with for example --format=v2.
|
|
|
|
The Android source tree also has checked in versions of the signatures for all
|
|
the previous API levels. Metalava can regenerate these for a new format.
|
|
For example, to update all the signature files to v3, run this command:
|
|
|
|
```
|
|
$ metalava --write-android-jar-signatures *<android source dir>* --format=v3
|
|
```
|