Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 1 | # Signature Formats |
| 2 | |
| 3 | This document describes the signature file format created and used by metalava, |
| 4 | doclava, apicheck, etc. |
| 5 | |
| 6 | There are currently 3 versions of this format: |
| 7 | |
| 8 | 1. The format emitted by doclava, and used for Android's signature files up |
| 9 | through Android P. Note that this isn't actually a single format; it evolved |
| 10 | over time, so older signature files vary a bit (many of these changes were |
| 11 | due to bugs getting fixed, such as type parameters missing from classes |
| 12 | and methods until they start appearing), and some were deliberate changes, |
| 13 | such as dropping the "final" modifier in front of every member if the |
| 14 | containing class is final. |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 15 | |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 16 | 2. The "new" format, which is described below, and is used in Android Q. This |
| 17 | format adds new information, such as annotations, parameter names and default |
| 18 | values, as well as cleans up a number of things (such as dropping |
| 19 | java.lang. prefixes on types, etc) |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 20 | |
| 21 | 3. This is format v2, but with all nullness annotations replaced by a |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 22 | Kotlin-syntax, e.g. "?" for nullable types, "!" for unknown/platform types, |
| 23 | and no suffix for non-nullable types. The initial plan was to include this |
| 24 | in format v2, but it was deferred since type-use annotations introduces |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 25 | some complexities in the implementation. |
| 26 | |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 27 | |
| 28 | ## Motivation |
| 29 | |
| 30 | Why did we change from the historical doclava signature format (v1) |
| 31 | to a new format? |
| 32 | |
| 33 | In order to support Kotlin better (though this will also benefit Java |
| 34 | developers), we'd like to have nullness annotations (as well as some other |
| 35 | annotations) be a formal part of the SDK. |
| 36 | |
| 37 | That means the annotations should be part of the signature files too -- such |
| 38 | that we can not just record explicitly what the API contract is, but also |
| 39 | enforce that changes are not only deliberate changes but also compatible |
| 40 | changes. (For example, you can change the return value of a final method from |
| 41 | nullable to non null, but not the other way around.) |
| 42 | |
| 43 | And if we were going to change the signature format, we might as well make some |
| 44 | other changes too. |
| 45 | |
| 46 | |
| 47 | ### Comments |
| 48 | |
| 49 | In v2, line comments (starting with //) are allowed. This allows us to leave |
| 50 | reminders and other issues with the signature source (though the update-api task |
| 51 | will generally blow these away, so use sparingly.) |
| 52 | |
| 53 | ### Header |
| 54 | |
| 55 | New signature files (v2+) generally include a file header comment which states |
| 56 | the version number. This makes it possible for tools to more safely interpret |
| 57 | signature files. For example, in v3 the type "String" means "@NonNull String", |
| 58 | but in v2 "String" means "String with unknown nullness". |
| 59 | |
| 60 | The header looks like this: |
| 61 | |
| 62 | ``` |
| 63 | // Signature format: 2.0 |
| 64 | ``` |
| 65 | |
| 66 | Here "2" is the major format version; the .0 allows for compatible minor |
| 67 | variations of the format. |
| 68 | |
| 69 | ### Include Annotations |
| 70 | |
| 71 | The new signature format now includes annotations; not all annotations (such as |
| 72 | @Override etc); only those which are significant for the API, such as nullness |
| 73 | annotations, etc. |
| 74 | |
| 75 | Annotations are included on the same line as the class/field/method, right |
| 76 | before the modifiers. |
| 77 | |
| 78 | Here's how this looks: |
| 79 | |
| 80 | |
| 81 | ``` |
| 82 | method @Nullable public static Integer compute1(@Nullable java.util.List<java.lang.String>); |
| 83 | ``` |
| 84 | |
| 85 | |
| 86 | (Notice how the annotations are not using fully qualified name; that's discussed |
| 87 | below.) |
| 88 | |
| 89 | The annotations to be included are annotations for annotation types that are not |
| 90 | hidden, and have class file or runtime retention. |
| 91 | |
| 92 | The annotations should be sorted alphabetically by fully qualified name. |
| 93 | |
| 94 | |
| 95 | ### Use Special Syntax or Nullness Annotations |
| 96 | |
| 97 | (Note: Only in version format 3+) |
| 98 | |
| 99 | As a special optimization, since we eventually want **all** APIs to have |
| 100 | explicit nullness, use Kotlin's syntax for nullness. That means that for |
| 101 | nullable elements, we add "?" after the type, for unknown nullness we add "!", |
| 102 | and otherwise there's no suffix. In other words: |
| 103 | |
| 104 | |
| 105 | <table> |
| 106 | <tr> |
| 107 | <td> |
| 108 | </td> |
| 109 | <td>Java Type |
| 110 | </td> |
| 111 | <td>Signature File Type |
| 112 | </td> |
| 113 | </tr> |
| 114 | <tr> |
| 115 | <td>Nullable |
| 116 | </td> |
| 117 | <td>@Nullable String |
| 118 | </td> |
| 119 | <td>String? |
| 120 | </td> |
| 121 | </tr> |
| 122 | <tr> |
| 123 | <td>Not nullable |
| 124 | </td> |
| 125 | <td>@NonNull String |
| 126 | </td> |
| 127 | <td>String |
| 128 | </td> |
| 129 | </tr> |
| 130 | <tr> |
| 131 | <td>Unknown nullability |
| 132 | </td> |
| 133 | <td>String |
| 134 | </td> |
| 135 | <td>String! |
| 136 | </td> |
| 137 | </tr> |
| 138 | </table> |
| 139 | |
| 140 | |
| 141 | The above signature line is turned into |
| 142 | |
| 143 | |
| 144 | ``` |
| 145 | method public Integer? compute1(java.util.List<java.lang.String!>?); |
| 146 | ``` |
| 147 | |
| 148 | |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 149 | ### Clean Up Terminology |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 150 | |
| 151 | Format v2 also cleans up some of the terminology used to describe the class |
| 152 | structure in the signature file. For example, in v1, an interface is called an |
| 153 | "abstract interface"; an interface extending another interface is said to |
| 154 | "implement" it instead of "extend"-ing it, etc; enums and annotations are just |
| 155 | referred to as classes that extend java.lang.Enum, or java.lang.Annotation etc. |
| 156 | |
| 157 | With these changes, these lines from v1 signature files: |
| 158 | |
| 159 | |
| 160 | ``` |
| 161 | public abstract interface List<E> implements java.util.Collection { ... } |
| 162 | public class TimeUnit extends java.lang.Enum { ... } |
| 163 | public abstract class SuppressLint implements java.lang.annotation.Annotation { ... } |
| 164 | ``` |
| 165 | |
| 166 | |
| 167 | are replaced by |
| 168 | |
| 169 | |
| 170 | ``` |
| 171 | public interface List<E> extends java.util.Collection<E> { ... } |
| 172 | public enum TimeUnit { ... } |
| 173 | public @interface SuppressLint { ... } |
| 174 | ``` |
| 175 | |
| 176 | |
| 177 | |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 178 | ### Use Generics Everywhere |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 179 | |
| 180 | The v1 signature files uses raw types in some places but not others. Note that |
| 181 | in the above it was missing from super interface Collection: |
| 182 | |
| 183 | |
| 184 | ``` |
| 185 | public abstract interface List<E> implements java.util.Collection { ... } |
| 186 | ``` |
| 187 | |
| 188 | |
| 189 | whereas in the v2 format it's included: |
| 190 | |
| 191 | |
| 192 | ``` |
| 193 | public interface List<E> extends java.util.Collection<E> { ... } |
| 194 | ``` |
| 195 | |
| 196 | |
| 197 | Similarly, v1 used erasure in throws clauses. For example, for this method: |
| 198 | |
| 199 | |
| 200 | ``` |
| 201 | public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X |
| 202 | ``` |
| 203 | |
| 204 | v1 used this signature: |
| 205 | |
| 206 | |
| 207 | ``` |
| 208 | method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws java.lang.Throwable; |
| 209 | ``` |
| 210 | |
| 211 | Note how that's "throws Throwable" instead of "throws X". This results in b/110302703. |
| 212 | |
| 213 | In the v2 format we instead use the correct throws type: |
| 214 | |
| 215 | ``` |
| 216 | method public <X extends java.lang.Throwable> T orElseThrow(java.util.function.Supplier<? extends X>) throws X; |
| 217 | ``` |
| 218 | |
| 219 | |
| 220 | ### Support Annotations |
| 221 | |
| 222 | The old format was completely missing annotation type methods: |
| 223 | |
| 224 | ``` |
| 225 | public static abstract class ViewDebug.ExportedProperty implements java.lang.annotation.Annotation { |
| 226 | } |
| 227 | ``` |
| 228 | |
| 229 | We need to include annotation member methods, as well as their default values |
| 230 | since those are API-significant. Here's how this looks in the v2 file format |
| 231 | (also applying the @interface terminology change described above) : |
| 232 | |
| 233 | |
| 234 | ``` |
| 235 | public static @interface ViewDebug.ExportedProperty { |
| 236 | method public abstract String category() default ""; |
| 237 | method public abstract boolean deepExport() default false; |
| 238 | method public abstract android.view.ViewDebug.FlagToString[] flagMapping() default {}; |
| 239 | method public abstract boolean formatToHexString() default false; |
| 240 | method public abstract boolean hasAdjacentMapping() default false; |
| 241 | method public abstract android.view.ViewDebug.IntToString[] indexMapping() default {}; |
| 242 | method public abstract android.view.ViewDebug.IntToString[] mapping() default {}; |
| 243 | method public abstract String prefix() default ""; |
| 244 | method public abstract boolean resolveId() default false; |
| 245 | } |
| 246 | ``` |
| 247 | |
| 248 | |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 249 | ### Support Kotlin Modifiers |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 250 | |
| 251 | This doesn't currently apply to the SDK, but the signature files are also used |
| 252 | in the support library, and some of these are written in Kotlin and exposes |
| 253 | Kotlin-specific APIs. |
| 254 | |
| 255 | That means the v2 format can express API-significant aspects of Kotlin. This |
| 256 | includes special modifiers, such as sealed, inline, operator, infix, etc: |
| 257 | |
| 258 | ``` |
| 259 | method public static operator int get(android.graphics.Bitmap, int x, int y); |
| 260 | method public static infix android.graphics.Rect and(android.graphics.Rect, android.graphics.Rect r); |
| 261 | ``` |
| 262 | |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 263 | ### Support Kotlin Properties |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 264 | |
| 265 | Kotlin's Java support means that it wil take a Kotlin property and compile it |
| 266 | into getters and setters which you can call from Java. But you cannot calls |
| 267 | these getters and setters from Kotlin; you **must** use the property |
| 268 | syntax. Therefore, we need to also capture properties in the signature files. If |
| 269 | you have this Kotlin code: |
| 270 | |
| 271 | |
| 272 | ``` |
| 273 | var property2: String? = "initial" |
| 274 | ``` |
| 275 | |
| 276 | it will get recorded in the signature files like this: |
| 277 | |
| 278 | ``` |
| 279 | property public java.lang.String? property2 = "initial"; |
| 280 | method public java.lang.String? getProperty2(); |
| 281 | method public void setProperty2(java.lang.String? p); |
| 282 | ``` |
| 283 | |
| 284 | The last two elements are "redundant"; they could be computed from the property |
| 285 | name (and included if the property declaration uses special annotations to name |
| 286 | the getters and setters away from the defaults), but it's helpful to be explicit |
| 287 | (and this allows us to specify the default value). |
| 288 | |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 289 | ### Support Named Parameters |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 290 | |
| 291 | Kotlin supports default values for parameters, and these are a part of the API |
| 292 | contract, so we need to include them in the signature format. |
| 293 | |
| 294 | Here's an example: |
| 295 | |
| 296 | ``` |
| 297 | method public static void edit(android.content.SharedPreferences, boolean commit); |
| 298 | ``` |
| 299 | |
| 300 | In v1 files we only list type names, but in v2 we allow an optional parameter |
| 301 | name to be specified; "commit" in the above. |
| 302 | |
| 303 | Note that this isn't just for Kotlin. Just like there are special nullness |
| 304 | annotations to mark up the null contract for an element, we will also have a |
| 305 | special annotation to explicitly name a Java parameter: |
| 306 | @android.annotation.ParameterName (which is hidden). This obviously isn't usable |
| 307 | from Java, but Kotlin client code can now reference the parameter. |
| 308 | |
| 309 | Therefore, the following Java code (not signature code) will also produce |
| 310 | exactly the same signature as the above: |
| 311 | |
| 312 | ``` |
| 313 | public static void edit(SharedPreferences prefs, @ParameterName("commit") boolean ct) {…} |
| 314 | ``` |
| 315 | |
| 316 | (Note how the implementation parameter doesn't have to match the public, API |
| 317 | name of the parameter.) |
| 318 | |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 319 | ### Support Default Values |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 320 | |
| 321 | In addition to named parameters, Kotlin also supports default values. These are |
| 322 | also be part of the v2 signature since (as an example) removing a default value |
| 323 | is a compile-incompatible change. |
| 324 | |
| 325 | Therefore, the v2 format allows default values to be specified after the type |
| 326 | and/or parameter name: |
| 327 | |
| 328 | ``` |
| 329 | method public static void edit(SharedPreferences, boolean commit = false); |
| 330 | ``` |
| 331 | |
| 332 | For Kotlin code, the default parameter values are extracted automatically, and |
| 333 | for Java, just as with parameter names, you can specify a special annotation to |
| 334 | record the default value for usage from languages that support default parameter |
| 335 | values: |
| 336 | |
| 337 | ``` |
| 338 | public static void edit(SharedPreferences prefs, @DefaultValue("false") boolean ct) {…} |
| 339 | ``` |
| 340 | |
| 341 | |
| 342 | ### Include Inherited Methods |
| 343 | |
| 344 | Consider a scenario where a public class extends a hidden class, and that hidden |
| 345 | class defines a public method. |
| 346 | |
| 347 | Doclava did not include these methods in the signature files, but they **were** |
| 348 | present in the stub files (and therefore part of the API). In the v2 signature |
| 349 | file format, we include these. |
| 350 | |
| 351 | An example of this is StringBuilder#setLength. According to the old signature |
| 352 | files, that method does not exist, but clearly it's there in the SDK. The reason |
| 353 | this happens is that StringBuilder is a public class which extends hidden class |
| 354 | AbstractStringBuilder, which defines the public method setLength. |
| 355 | |
| 356 | |
| 357 | ### No Hardcoded Enum Methods |
| 358 | |
| 359 | Doclava always inserted two special methods in the signature files for every |
| 360 | enum: values() and valueOf(): |
| 361 | |
| 362 | ``` |
| 363 | public static final class CursorJoiner.Result extends java.lang.Enum { |
| 364 | method public static android.database.CursorJoiner.Result valueOf(java.lang.String); |
| 365 | method public static final android.database.CursorJoiner.Result[] values(); |
| 366 | enum_constant public static final android.database.CursorJoiner.Result BOTH; |
| 367 | enum_constant public static final android.database.CursorJoiner.Result LEFT; |
| 368 | enum_constant public static final android.database.CursorJoiner.Result RIGHT; |
| 369 | } |
| 370 | ``` |
| 371 | |
| 372 | It didn't do that in stubs, because you can't: those are special methods |
| 373 | generated by the compiler. There's no reason to list these in the signature |
| 374 | files since they're entirely implied by the enum, you can't change them, and |
| 375 | it's just extra noise. |
| 376 | |
| 377 | In the new v2 format these are no longer present: |
| 378 | |
| 379 | ``` |
| 380 | public static enum CursorJoiner.Result { |
| 381 | enum_constant public static final android.database.CursorJoiner.Result BOTH; |
| 382 | enum_constant public static final android.database.CursorJoiner.Result LEFT; |
| 383 | enum_constant public static final android.database.CursorJoiner.Result RIGHT; |
| 384 | } |
| 385 | ``` |
| 386 | |
| 387 | ### Remove "deprecated" Modifier |
| 388 | |
| 389 | The old signature file format used "deprecated" as if it was a modifier. In the |
| 390 | new format, we instead list these using annotations, @Deprecated. |
| 391 | |
| 392 | ### Standard Modifier Order |
| 393 | |
| 394 | Doclava had a "random" (but stable) order of modifiers. |
| 395 | |
| 396 | In the new signature format, we're using the standard modifier order for Java |
| 397 | and Kotlin, wihch more closely mirrors what is done in the source code. |
| 398 | |
| 399 | Version format 1 order: |
| 400 | |
| 401 | ``` |
| 402 | public/protected/private default static final abstract synchronized transient volatile |
| 403 | ``` |
| 404 | |
| 405 | Version format 2 order: |
| 406 | |
| 407 | ``` |
| 408 | public/protected/internal/private abstract default static final transient volatile synchronized |
| 409 | ``` |
| 410 | |
| 411 | The above list doesn't include the Kotlin modifiers, which are inserted |
| 412 | according to the Kotlin language style guide: |
| 413 | https://kotlinlang.org/docs/reference/coding-conventions.html#modifiers |
| 414 | |
| 415 | ### Sort Classes By Fully Qualified Names |
| 416 | |
| 417 | In "extends" lists, the signature file can list a comma separated list of |
| 418 | classes. The classes are listed by fully qualified name, but in v1 it was sorted |
| 419 | by simple name. In the v2 format, we sort by fully qualified name instead. |
| 420 | |
| 421 | ### Use Wildcards Consistently |
| 422 | |
| 423 | Doclava (v1) would sometimes use the type bound <?> and other times use <? |
| 424 | extends Object>. These are equivalent. In the v2 format, <? extends Object> is |
| 425 | always written as <?>. |
| 426 | |
| 427 | ### Annotation Simple Names |
| 428 | |
| 429 | We have a number of annotations which are significant for the API -- not just |
| 430 | the nullness as deprecation ones (which are specially supported in v3 via the |
| 431 | ?/! Kotlin syntax and the deprecated "modifier"), but annotations for permission |
| 432 | requirements, range constraints, valid constant values for an integer, and so |
| 433 | on. |
| 434 | |
| 435 | In the codebase, these are typically in the android.annotation. package, |
| 436 | referencing annotation classes that are generally **not** part of the API. When |
| 437 | we generate the SDK, we translate these into publicly known annotations, |
| 438 | androidx.annotation, such that Studio, lint, the Kotlin compiler and others can |
| 439 | recognize the metadata. |
| 440 | |
| 441 | That begs the question: which fully qualified name should we put in the |
| 442 | signature file? The one that appeared in the source (which is hidden, or in the |
| 443 | case of Kotlin code, a special JetBrains nullness annotation), or the one that |
| 444 | it gets translated into? |
| 445 | |
| 446 | In v2 we do neither: We use only the simple name of the annotations in the |
| 447 | signature file, for annotations that are in the well known packages. In other |
| 448 | words, instead of any of these alternative declarations: |
| 449 | |
| 450 | ``` |
| 451 | method public void setTitleTextColor(@android.annotation.ColorInt int); |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 452 | method public void setTitleTextColor(@androidx.annotation.ColorInt int); |
| 453 | ``` |
| 454 | |
Tor Norbye | 9584e4f | 2020-06-16 10:55:33 -0700 | [diff] [blame] | 455 | in v2 we have simply |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 456 | |
| 457 | ``` |
| 458 | method public void setTitleTextColor(@ColorInt int); |
| 459 | ``` |
| 460 | |
| 461 | ### Simple Names in Java.lang |
| 462 | |
| 463 | In Java files, you can implicitly reference classes in java.lang without |
| 464 | importing them. In v2 offer the same thing in signature files. There are several |
| 465 | classes from java.lang that are used in lots of places in the signature file |
| 466 | (java.lang.String alone is present in over 11,000 lines of the API file), and |
| 467 | other common occurrences are java.lang.Class, java.lang.Integer, |
| 468 | java.lang.Runtime, etc. |
| 469 | |
| 470 | This basically builds on the same idea from having an implicit package for |
| 471 | annotations, and doing the same thing for java.lang: Omitting it when writing |
| 472 | signature files, and implicitly adding it back when reading in signature files. |
| 473 | |
| 474 | This only applies to the java.lang package, not any subpackages, so for example |
| 475 | java.lang.reflect.Method will **not** be shortened to reflect.Method. |
| 476 | |
| 477 | ### Type Use Annotations |
| 478 | |
| 479 | In v3, "type use annotations" are supported which means annotations can appear |
| 480 | within types. |
| 481 | |
Tor Norbye | 942042f | 2019-01-02 10:02:56 -0800 | [diff] [blame] | 482 | ### Skipping some signatures |
| 483 | |
| 484 | If a method overrides another method, and the signatures are the same, the |
| 485 | overriding method is left out of the signature file. This basically compares the |
| 486 | modifiers, ignoring some that are not API significant (such as "native"). Note |
| 487 | also that some modifiers are implicit; for example, if a method is implementing |
| 488 | a method from an interface, the interface method is implicitly abstract, so the |
| 489 | implementation will be included in the signature file. |
| 490 | |
| 491 | In v2, we take this one step further: If a method differs **only** from its |
| 492 | overridden method by "final", **and** if the containing class is final, then the |
| 493 | method is not included in the signature file. The same is the case for |
| 494 | deprecated. |
| 495 | |
Tor Norbye | 3e42ef0 | 2018-11-14 13:47:28 -0800 | [diff] [blame] | 496 | ### Miscellaneous |
| 497 | |
| 498 | Some other minor tweaks in v2: |
| 499 | |
| 500 | * Fix formatting for package private elements. These had two spaces of |
| 501 | indentation; this is probably just a bug. The new format aligns their |
| 502 | indentation with all other elements. |
| 503 | * Don't add spaces in type bounds lists (e.g. Map<X,Y>, not Map<X, Y>.) |
| 504 | |
| 505 | ## Historical API Files |
| 506 | |
| 507 | Metalava can read and write these formats. To switch output formats, invoke it |
| 508 | with for example --format=v2. |
| 509 | |
| 510 | The Android source tree also has checked in versions of the signatures for all |
| 511 | the previous API levels. Metalava can regenerate these for a new format. |
| 512 | For example, to update all the signature files to v3, run this command: |
| 513 | |
| 514 | ``` |
| 515 | $ metalava --write-android-jar-signatures *<android source dir>* --format=v3 |
| 516 | ``` |