diff --git a/.idea/dictionaries/metalava.xml b/.idea/dictionaries/metalava.xml
index ce2b5a0cacf02e4b22dda205842cda134e058b01..e458e6aff1363336df5f2369c04ab0362bb67b3f 100644
--- a/.idea/dictionaries/metalava.xml
+++ b/.idea/dictionaries/metalava.xml
@@ -22,6 +22,7 @@
       <w>doclet</w>
       <w>docletpath</w>
       <w>doconly</w>
+      <w>doesnt</w>
       <w>dokka</w>
       <w>droiddoc</w>
       <w>federationapi</w>
@@ -60,6 +61,7 @@
       <w>nullable</w>
       <w>nullness</w>
       <w>offlinemode</w>
+      <w>parcelable</w>
       <w>parsecomments</w>
       <w>prebuilts</w>
       <w>proguard</w>
diff --git a/API-LINT.md b/API-LINT.md
index f0ab779e205e235b9b8046637c3185490d6aeeeb..76b2d09277dda64a60a6685a5e4aa7d79a887c41 100644
--- a/API-LINT.md
+++ b/API-LINT.md
@@ -66,6 +66,22 @@ diff to make sure you're really only marking the issues you intended to include.
 
           baseline.txt
 
+## Copying File Manually
+
+In the near future the build system will not allow source files to be modified
+by the build. At that point you'll need to manually copy in the file instead.
+During the build, before failing, metalava will emit a message like this:
+
+    ...
+    metalava wrote updated baseline to out/something/baseline.txt
+    ...
+
+At that point you can copy the file to where you need:
+
+```sh
+$ cp out/something/baseline.txt frameworks/base/api/baseline.txt
+```
+
 ## Existing Issues
 
 You can view the exact set of existing issues (current APIs that get flagged by
@@ -146,4 +162,4 @@ here's the existing distribution as of early 2019:
     --------------------------------------------------
      4902
 
-(This is generated when metalava is invoked with both --verbose and --update-baseline.)
\ No newline at end of file
+(This is generated when metalava is invoked with both --verbose and --update-baseline.)
diff --git a/FORMAT.md b/FORMAT.md
index efe73b95cfde90c21e568fbe94b396bc5fc54ec7..d27c867deb8083b9f67cf6f9efd496b7ca3f39d2 100644
--- a/FORMAT.md
+++ b/FORMAT.md
@@ -480,6 +480,20 @@ java.lang.reflect.Method will **not** be shortened to reflect.Method.
 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:
diff --git a/src/main/java/com/android/tools/metalava/ApiLint.kt b/src/main/java/com/android/tools/metalava/ApiLint.kt
index 2290f3193bd7f334e068e3b9721d6ccf1939cba9..aa1e432b34447adba87b41398db333f636a41aa1 100644
--- a/src/main/java/com/android/tools/metalava/ApiLint.kt
+++ b/src/main/java/com/android/tools/metalava/ApiLint.kt
@@ -143,75 +143,82 @@ import java.util.function.Predicate
  * The [ApiLint] analyzer checks the API against a known set of preferred API practices
  * by the Android API council.
  */
-class ApiLint {
-    fun check(codebase: Codebase) {
-        val prevCount = reporter.totalCount
-
-        // The previous Kotlin interop tests are also part of API lint now (though they can be
-        // run independently as well; therefore, only run them here if not running separately)
-        val kotlinInterop = if (!options.checkKotlinInterop) KotlinInteropChecks() else null
+class ApiLint(private var codebase: Codebase) : ApiVisitor(
+    // Sort by source order such that warnings follow source line number order
+    methodComparator = MethodItem.sourceOrderComparator,
+    fieldComparator = FieldItem.comparator,
+    ignoreShown = options.showUnannotated
+) {
+    private fun report(id: Error, item: Item, message: String) {
+        // Don't flag api warnings on deprecated APIs; these are obviously already known to
+        // be problematic.
+        if (item.deprecated) {
+            return
+        }
 
-        codebase.accept(object : ApiVisitor(
-            // Sort by source order such that warnings follow source line number order
-            methodComparator = MethodItem.sourceOrderComparator,
-            fieldComparator = FieldItem.comparator
-        ) {
-            override fun skip(item: Item): Boolean {
-                return super.skip(item) || item is ClassItem && !isInteresting(item)
-            }
-
-            private var isKotlin = false
-
-            override fun visitClass(cls: ClassItem) {
-                val methods = cls.filteredMethods(filterEmit).asSequence()
-                val fields = cls.filteredFields(filterEmit, showUnannotated).asSequence()
-                val constructors = cls.filteredConstructors(filterEmit)
-                val superClass = cls.filteredSuperclass(filterReference)
-                val interfaces = cls.filteredInterfaceTypes(filterReference).asSequence()
-                val allMethods = methods.asSequence() + constructors.asSequence()
-                checkClass(
-                    cls, methods, constructors, allMethods, fields, superClass, interfaces,
-                    filterReference
-                )
+        // With show annotations we might be flagging API that is filtered out: hide these here
+        val testItem = if (item is ParameterItem) item.containingMethod() else item
+        if (!filterEmit.test(testItem)) {
+            return
+        }
 
-                isKotlin = cls.isKotlin()
-            }
+        reporter.report(id, item, message)
+    }
 
-            override fun visitMethod(method: MethodItem) {
-                checkMethod(method, filterReference)
-                val returnType = method.returnType()
-                if (returnType != null) {
-                    checkType(returnType, method)
-                }
-                for (parameter in method.parameters()) {
-                    checkType(parameter.type(), parameter)
-                }
-                kotlinInterop?.checkMethod(method, isKotlin)
-            }
+    private fun check() {
+        val prevCount = reporter.totalCount
 
-            override fun visitField(field: FieldItem) {
-                checkField(field)
-                checkType(field.type(), field)
-                kotlinInterop?.checkField(field, isKotlin)
-            }
-        })
+        codebase.accept(this)
 
         val apiLintIssues = reporter.totalCount - prevCount
         if (apiLintIssues > 0) {
             // We've reported API lint violations; emit some verbiage to explain
             // how to suppress the error rules.
-            options.stdout.println("$apiLintIssues new API lint issues were found. See tools/metalava/API-LINT.md for how to handle these.")
+            options.stdout.println("\n$apiLintIssues new API lint issues were found. See tools/metalava/API-LINT.md for how to handle these.")
         }
     }
 
-    private fun report(id: Error, item: Item, message: String) {
-        // Don't flag api warnings on deprecated APIs; these are obviously already known to
-        // be problematic.
-        if (item.deprecated) {
-            return
+    override fun skip(item: Item): Boolean {
+        return super.skip(item) || item is ClassItem && !isInteresting(item)
+    }
+
+    // The previous Kotlin interop tests are also part of API lint now (though they can be
+    // run independently as well; therefore, only run them here if not running separately)
+    private val kotlinInterop = if (!options.checkKotlinInterop) KotlinInteropChecks() else null
+
+    private var isKotlin = false
+
+    override fun visitClass(cls: ClassItem) {
+        val methods = cls.filteredMethods(filterReference).asSequence()
+        val fields = cls.filteredFields(filterReference, showUnannotated).asSequence()
+        val constructors = cls.filteredConstructors(filterReference)
+        val superClass = cls.filteredSuperclass(filterReference)
+        val interfaces = cls.filteredInterfaceTypes(filterReference).asSequence()
+        val allMethods = methods.asSequence() + constructors.asSequence()
+        checkClass(
+            cls, methods, constructors, allMethods, fields, superClass, interfaces,
+            filterReference
+        )
+
+        isKotlin = cls.isKotlin()
+    }
+
+    override fun visitMethod(method: MethodItem) {
+        checkMethod(method, filterReference)
+        val returnType = method.returnType()
+        if (returnType != null) {
+            checkType(returnType, method)
+        }
+        for (parameter in method.parameters()) {
+            checkType(parameter.type(), parameter)
         }
+        kotlinInterop?.checkMethod(method, isKotlin)
+    }
 
-        reporter.report(id, item, message)
+    override fun visitField(field: FieldItem) {
+        checkField(field)
+        checkType(field.type(), field)
+        kotlinInterop?.checkField(field, isKotlin)
     }
 
     private fun checkType(type: TypeItem, item: Item) {
@@ -3288,5 +3295,9 @@ class ApiLint {
                 }
             }
         }
+
+        fun check(codebase: Codebase) {
+            ApiLint(codebase).check()
+        }
     }
 }
diff --git a/src/main/java/com/android/tools/metalava/Baseline.kt b/src/main/java/com/android/tools/metalava/Baseline.kt
index 885e99a111d74219acc7969901703b6ed42f570a..6867bf75857104937bdf9d2f40bdc98db8d9e277 100644
--- a/src/main/java/com/android/tools/metalava/Baseline.kt
+++ b/src/main/java/com/android/tools/metalava/Baseline.kt
@@ -37,7 +37,13 @@ import java.io.PrintWriter
 
 const val DEFAULT_BASELINE_NAME = "baseline.txt"
 
-class Baseline(val file: File, var create: Boolean = !file.isFile) {
+class Baseline(
+    val file: File,
+    var create: Boolean = !file.isFile,
+    private var format: FileFormat = FileFormat.BASELINE,
+    private var headerComment: String = ""
+) {
+
     /** Map from issue id to element id to message */
     private val map = HashMap<Errors.Error, MutableMap<String, String>>()
 
@@ -193,6 +199,9 @@ class Baseline(val file: File, var create: Boolean = !file.isFile) {
     private fun write() {
         if (!map.isEmpty()) {
             val sb = StringBuilder()
+            sb.append(format.header())
+            sb.append(headerComment)
+
             map.keys.asSequence().sortedBy { it.name ?: it.code.toString() }.forEach { error ->
                 val idMap = map[error]
                 idMap?.keys?.sorted()?.forEach { elementId ->
diff --git a/src/main/java/com/android/tools/metalava/Compatibility.kt b/src/main/java/com/android/tools/metalava/Compatibility.kt
index 5594e4a731f58694edc953f06afc41edcb1ea97a..32f5a1e2389ffce20a45a1fbaddd33bb01934bdf 100644
--- a/src/main/java/com/android/tools/metalava/Compatibility.kt
+++ b/src/main/java/com/android/tools/metalava/Compatibility.kt
@@ -31,9 +31,6 @@ class Compatibility(
     val compat: Boolean = COMPAT_MODE_BY_DEFAULT
 ) {
 
-    /** Whether to inline fields from implemented interfaces into concrete classes */
-    var inlineInterfaceFields: Boolean = compat
-
     /** In signature files, use "implements" instead of "extends" for the super class of
      * an interface */
     var extendsForInterfaceSuperClass: Boolean = compat
@@ -57,12 +54,6 @@ class Compatibility(
      * `@Deprecated` annotation before the modifier list */
     var nonstandardModifierOrder: Boolean = compat
 
-    /** In signature files, skip the native modifier from the modifier lists */
-    var skipNativeModifier: Boolean = true
-
-    /** In signature files, skip the strictfp modifier from the modifier lists */
-    var skipStrictFpModifier: Boolean = true
-
     /** Whether to include instance methods in annotation classes for the annotation properties */
     var skipAnnotationInstanceMethods: Boolean = compat
 
@@ -96,47 +87,14 @@ class Compatibility(
      */
     var filterThrowsClasses: Boolean = !compat
 
-    /**
-     * Include a single space in front of package private classes with no other modifiers
-     * (this doesn't align well, but is supported to make the output 100% identical to the
-     * doclava1 format
-     */
-    var extraSpaceForEmptyModifiers: Boolean = compat
-
     /** Format `Map<K,V>` as `Map<K, V>` */
     var spaceAfterCommaInTypes: Boolean = compat
 
-    /**
-     * Doclava1 sorts classes/interfaces by class name instead of qualified name
-     */
-    var sortClassesBySimpleName: Boolean = compat
-
     /**
      * Doclava1 omits type parameters in interfaces (in signature files, not in stubs)
      */
     var omitTypeParametersInInterfaces: Boolean = compat
 
-    /**
-     * Doclava1 sorted the methods like this:
-     *
-     *      public final class RoundingMode extends java.lang.Enum {
-     *          method public static java.math.RoundingMode valueOf(java.lang.String);
-     *          method public static java.math.RoundingMode valueOf(int);
-     *          ...
-     *
-     * Note how the two valueOf methods are out of order. With this compatibility mode,
-     * we try to perform the same sorting.
-     */
-    var sortEnumValueOfMethodFirst: Boolean = compat
-
-    /**
-     * Whether packages should be treated as recursive for documentation. In other words,
-     * if a directory has a `packages.html` file containing a `@hide` comment, then
-     * all "sub" packages (directories below this one) will also inherit the same comment.
-     * Java packages aren't supposed to work that way, but doclava does.
-     */
-    var inheritPackageDocs: Boolean = compat
-
     /** Force methods named "values" in enums to be marked final. This was done by
      * doclava1 with this comment:
      *
@@ -175,7 +133,7 @@ class Compatibility(
     /**
      * Whether to include parameter names in the signature file
      */
-    var parameterNames: Boolean = true
+    var parameterNames: Boolean = !compat
 
     /**
      * *Some* signatures for doclava1 wrote "<?>" as "<? extends java.lang.Object>",
@@ -184,6 +142,21 @@ class Compatibility(
      */
     var includeExtendsObjectInWildcard = compat
 
+    /**
+     * Whether deprecation should be shown in signature files as an annotation
+     * instead of a pseudo-modifier
+     */
+    var deprecatedAsAnnotation = !compat
+
+    /** Whether synchronized should be part of the output */
+    var includeSynchronized = compat
+
+    /** Whether we should omit common packages such as java.lang.* and kotlin.* from signature output */
+    var omitCommonPackages = !compat
+
+    /** Whether we should explicitly include retention when class even if not explicitly defined */
+    var explicitlyListClassRetention = !compat
+
     /**
      * If true, a @Deprecated class will automatically deprecate all its inner classes
      * as well.
@@ -196,6 +169,12 @@ class Compatibility(
      */
     var propagateDeprecatedMembers = !compat
 
+    /**
+     * If an overriding method differs from its super method only by final or deprecated
+     * and the containing class is final or deprecated, skip it in the signature file
+     */
+    var hideDifferenceImplicit = !compat
+
     /** Whether inner enums should be listed as static in the signature file. */
     var staticEnums = compat
 
@@ -224,5 +203,19 @@ class Compatibility(
      */
     var xmlSkipEnumFields = compat
 
+    /**
+     * Doclava was missing annotation instance methods in JDiff reports
+     */
+    var xmlSkipAnnotationMethods = compat
+
+    /** Doclava lists character field values as integers instead of chars */
+    var xmlCharAsInt = compat
+
+    /**
+     * Doclava listed the superclass of annotations as
+     * java.lang.Object.
+     */
+    var xmlAnnotationAsObject = compat
+
     // Other examples: sometimes we sort by qualified name, sometimes by full name
 }
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/CompatibilityCheck.kt b/src/main/java/com/android/tools/metalava/CompatibilityCheck.kt
index dfef0ff6436eea479d5147e694a6ef55fa845231..22d359fa78afcef8888bc98ffff86c8966061eb2 100644
--- a/src/main/java/com/android/tools/metalava/CompatibilityCheck.kt
+++ b/src/main/java/com/android/tools/metalava/CompatibilityCheck.kt
@@ -77,7 +77,7 @@ class CompatibilityCheck(
      * so in these cases we want to ignore certain changes such as considering
      * StringBuilder.setLength a newly added method.
      */
-    private val comparingWithPartialSignatures = oldCodebase is TextCodebase && oldCodebase.format.major < 2
+    private val comparingWithPartialSignatures = oldCodebase is TextCodebase && oldCodebase.format == FileFormat.V1
 
     var foundProblems = false
 
@@ -710,19 +710,6 @@ class CompatibilityCheck(
         val error = if (new.isInterface()) {
             Errors.ADDED_INTERFACE
         } else {
-            if (options.compatOutput &&
-                new.qualifiedName() == "android.telephony.ims.feature.ImsFeature.Capabilities"
-            ) {
-                // Special case: Doclava and metalava signature files for the system api
-                // differ in only one way: Metalava believes ImsFeature.Capabilities should
-                // be in the signature file for @SystemApi, and doclava does not. However,
-                // this API is referenced from other system APIs that doclava does include
-                // (MmTelFeature.MmTelCapabilities's constructor) so it is clearly part of the
-                // API even if it's not listed in the signature file and we should not list
-                // this as an incompatible, added API.
-                return
-            }
-
             Errors.ADDED_CLASS
         }
         handleAdded(error, new)
@@ -769,7 +756,7 @@ class CompatibilityCheck(
         }
 
         // In old signature files, annotation methods are missing! This will show up as an added method.
-        if (new.containingClass().isAnnotationType() && oldCodebase is TextCodebase && oldCodebase.format.major == 1) {
+        if (new.containingClass().isAnnotationType() && oldCodebase is TextCodebase && oldCodebase.format == FileFormat.V1) {
             return
         }
 
diff --git a/src/main/java/com/android/tools/metalava/Driver.kt b/src/main/java/com/android/tools/metalava/Driver.kt
index e48ae23c33b9e013aded3519da8f7823756a6df2..0bcc1c9d83c3e009e8b2314f31841e54da12338d 100644
--- a/src/main/java/com/android/tools/metalava/Driver.kt
+++ b/src/main/java/com/android/tools/metalava/Driver.kt
@@ -155,8 +155,11 @@ fun run(
         exitValue = false
     }
 
-    if (options.verbose && options.updateBaseline) {
-        options.baseline?.dumpStats(options.stdout)
+    if (options.updateBaseline) {
+        if (options.verbose) {
+            options.baseline?.dumpStats(options.stdout)
+        }
+        stdout.println("$PROGRAM_NAME wrote updated baseline to ${options.baseline?.file}")
     }
     options.baseline?.close()
 
@@ -482,23 +485,50 @@ fun processNonCodebaseFlags() {
                     kotlinStyleNulls = options.inputKotlinStyleNulls
                 )
 
-                TextCodebase.computeDelta(baseFile, baseApi, signatureApi)
+                val includeFields =
+                    if (convert.outputFormat == FileFormat.V2) true else compatibility.includeFieldsInApiDiff
+                TextCodebase.computeDelta(baseFile, baseApi, signatureApi, includeFields)
             } else {
                 signatureApi
             }
 
-        if (outputApi.isEmpty() && baseFile != null) {
+        if (outputApi.isEmpty() && baseFile != null && compatibility.compat) {
             // doclava compatibility: emits error warning instead of emitting empty <api/> element
             options.stdout.println("No API change detected, not generating diff")
         } else {
-            val output = convert.toXmlFile
-            if (output.path.endsWith(DOT_TXT)) {
-                createReportFile(outputApi, output, "Diff API File") { printWriter ->
-                    SignatureWriter(printWriter, apiEmit, apiReference, signatureApi.preFiltered && !strip)
+            val output = convert.outputFile
+            if (convert.outputFormat == FileFormat.JDIFF) {
+                // See JDiff's XMLToAPI#nameAPI
+                val apiName = convert.outputFile.nameWithoutExtension.replace(' ', '_')
+                createReportFile(outputApi, output, "JDiff File") { printWriter ->
+                    JDiffXmlWriter(printWriter, apiEmit, apiReference, signatureApi.preFiltered && !strip, apiName)
                 }
             } else {
-                createReportFile(outputApi, output, "JDiff File") { printWriter ->
-                    JDiffXmlWriter(printWriter, apiEmit, apiReference, signatureApi.preFiltered && !strip)
+                val prevOptions = options
+                val prevCompatibility = compatibility
+                try {
+                    when (convert.outputFormat) {
+                        FileFormat.V1 -> {
+                            compatibility = Compatibility(true)
+                            options = Options(emptyArray(), options.stdout, options.stderr)
+                            FileFormat.V1.configureOptions(options, compatibility)
+                        }
+                        FileFormat.V2 -> {
+                            compatibility = Compatibility(false)
+                            options = Options(emptyArray(), options.stdout, options.stderr)
+                            FileFormat.V2.configureOptions(options, compatibility)
+                        }
+                        else -> error("Unsupported format ${convert.outputFormat}")
+                    }
+
+                    createReportFile(outputApi, output, "Diff API File") { printWriter ->
+                        SignatureWriter(
+                            printWriter, apiEmit, apiReference, signatureApi.preFiltered && !strip
+                        )
+                    }
+                } finally {
+                    options = prevOptions
+                    compatibility = prevCompatibility
                 }
             }
         }
@@ -525,7 +555,7 @@ fun checkCompatibility(
             )
         }
 
-    if (current is TextCodebase && current.format.major > 1 && options.outputFormat < 1) {
+    if (current is TextCodebase && current.format > FileFormat.V1 && options.outputFormat == FileFormat.V1) {
         throw DriverException("Cannot perform compatibility check of signature file $signatureFile in format ${current.format} without analyzing current codebase with $ARG_FORMAT=${current.format}")
     }
 
@@ -734,7 +764,7 @@ private fun loadFromSources(): Codebase {
 
     if (options.checkApi) {
         val localTimer = Stopwatch.createStarted()
-        ApiLint().check(codebase)
+        ApiLint.check(codebase)
         progress("\n$PROGRAM_NAME ran api-lint in ${localTimer.elapsed(SECONDS)} seconds")
     }
 
@@ -881,9 +911,6 @@ private fun createStubFiles(stubDir: File, codebase: Codebase, docStubs: Boolean
     val localTimer = Stopwatch.createStarted()
     val prevCompatibility = compatibility
     if (compatibility.compat) {
-        // if (!options.quiet) {
-        //    options.stderr.println("Warning: Turning off compat mode when generating stubs")
-        // }
         compatibility = Compatibility(false)
         // But preserve the setting for whether we want to erase throws signatures (to ensure the API
         // stays compatible)
diff --git a/src/main/java/com/android/tools/metalava/FileFormat.kt b/src/main/java/com/android/tools/metalava/FileFormat.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7e895e0453ec9b6b40e4ddd0d729cefd585392e2
--- /dev/null
+++ b/src/main/java/com/android/tools/metalava/FileFormat.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.metalava
+
+import com.android.SdkConstants.DOT_TXT
+import com.android.SdkConstants.DOT_XML
+
+/** File formats that metalava can emit APIs to */
+enum class FileFormat(val description: String, val version: String? = null) {
+    UNKNOWN("?"),
+    JDIFF("JDiff"),
+    BASELINE("Metalava baseline file", "1.0"),
+    SINCE_XML("Metalava API-level file", "1.0"),
+
+    // signature formats should be last to make comparisons work (for example in [configureOptions])
+    V1("Doclava signature file", "1.0"),
+    V2("Metalava signature file", "2.0"),
+    V3("Metalava signature file", "3.0");
+
+    /** Configures the option object such that the output format will be the given format */
+    fun configureOptions(options: Options, compatibility: Compatibility) {
+        if (this == JDIFF) {
+            return
+        }
+        options.outputFormat = this
+        options.compatOutput = this == V1
+        options.outputKotlinStyleNulls = this >= V3
+        options.outputDefaultValues = this >= V2
+        compatibility.omitCommonPackages = this >= V2
+        options.includeSignatureFormatVersion = this >= V2
+    }
+
+    fun useKotlinStyleNulls(): Boolean {
+        return this >= V3
+    }
+
+    fun signatureFormatAsInt(): Int {
+        return when (this) {
+            V1 -> 1
+            V2 -> 2
+            V3 -> 3
+
+            BASELINE,
+            JDIFF,
+            SINCE_XML,
+            UNKNOWN -> error("this method is only allowed on signature formats, was $this")
+        }
+    }
+
+    fun outputFlag(): String {
+        return if (isSignatureFormat()) {
+            "$ARG_FORMAT=v${signatureFormatAsInt()}"
+        } else {
+            ""
+        }
+    }
+
+    fun preferredExtension(): String {
+        return when (this) {
+            V1,
+            V2,
+            V3 -> DOT_TXT
+
+            BASELINE -> DOT_TXT
+
+            JDIFF, SINCE_XML -> DOT_XML
+
+            UNKNOWN -> ""
+        }
+    }
+
+    fun header(): String? {
+        val prefix = headerPrefix() ?: return null
+        return prefix + version + "\n"
+    }
+
+    fun headerPrefix(): String? {
+        return when (this) {
+            V1 -> null
+            V2, V3 -> "// Signature format: "
+            BASELINE -> "// Baseline format: "
+            JDIFF, SINCE_XML -> "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+            UNKNOWN -> null
+        }
+    }
+
+    fun isSignatureFormat(): Boolean {
+        return this == V1 || this == V2 || this == V3
+    }
+
+    companion object {
+        private fun firstLine(s: String): String {
+            val index = s.indexOf('\n')
+            if (index == -1) {
+                return s
+            }
+            // Chop off \r if a Windows \r\n file
+            val end = if (index > 0 && s[index - 1] == '\r') index - 1 else index
+            return s.substring(0, end)
+        }
+
+        fun parseHeader(fileContents: String): FileFormat {
+            val firstLine = firstLine(fileContents)
+            for (format in values()) {
+                val header = format.header()
+                if (header == null) {
+                    if (firstLine.startsWith("package ")) {
+                        // Old signature files
+                        return V1
+                    } else if (firstLine.startsWith("<api")) {
+                        return JDIFF
+                    }
+                } else if (header.startsWith(firstLine)) {
+                    if (format == JDIFF) {
+                        if (!fileContents.contains("<api")) {
+                            // The JDIFF header is the general XML header: don't accept XML documents that
+                            // don't contain an empty API definition
+                            return UNKNOWN
+                        }
+                        // Both JDiff and API-level files use <api> as the root tag (unfortunate but too late to
+                        // change) so distinguish on whether the file contains any since elementss
+                        if (fileContents.contains("since=")) {
+                            return SINCE_XML
+                        }
+                    }
+                    return format
+                }
+            }
+
+            return UNKNOWN
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/JDiffXmlWriter.kt b/src/main/java/com/android/tools/metalava/JDiffXmlWriter.kt
index 5f9b32336d90a6da99cf0ae7f17c46d5c8a32cb0..d4dbe0b4eb3a935c543a8ab6b8a3c7134aeb64b1 100644
--- a/src/main/java/com/android/tools/metalava/JDiffXmlWriter.kt
+++ b/src/main/java/com/android/tools/metalava/JDiffXmlWriter.kt
@@ -44,7 +44,8 @@ class JDiffXmlWriter(
     private val writer: PrintWriter,
     filterEmit: Predicate<Item>,
     filterReference: Predicate<Item>,
-    private val preFiltered: Boolean
+    private val preFiltered: Boolean,
+    private val apiName: String? = null
 ) : ApiVisitor(
     visitConstructorsAsMethods = false,
     nestInnerClasses = false,
@@ -56,7 +57,16 @@ class JDiffXmlWriter(
     showUnannotated = options.showUnannotated
 ) {
     override fun visitCodebase(codebase: Codebase) {
-        writer.println("<api>")
+        writer.print("<api")
+
+        if (apiName != null && !options.compatOutput) {
+            // See JDiff's XMLToAPI#nameAPI
+            writer.print(" name=\"")
+            writer.print(apiName)
+            writer.print("\"")
+        }
+
+        writer.println(">")
     }
 
     override fun afterVisitCodebase(codebase: Codebase) {
@@ -102,6 +112,36 @@ class JDiffXmlWriter(
         writer.println("\"\n>")
 
         writeInterfaceList(cls)
+
+        if (cls.isEnum() && compatibility.defaultEnumMethods) {
+            writer.println(
+                """
+                <method name="valueOf"
+                 return="${cls.qualifiedName()}"
+                 abstract="false"
+                 native="false"
+                 synchronized="false"
+                 static="true"
+                 final="false"
+                 deprecated="not deprecated"
+                 visibility="public"
+                >
+                <parameter name="null" type="java.lang.String">
+                </parameter>
+                </method>
+                <method name="values"
+                 return="${cls.qualifiedName()}[]"
+                 abstract="false"
+                 native="false"
+                 synchronized="false"
+                 static="true"
+                 final="true"
+                 deprecated="not deprecated"
+                 visibility="public"
+                >
+                </method>""".trimIndent()
+            )
+        }
     }
 
     fun deprecation(item: Item): String {
@@ -154,7 +194,11 @@ class JDiffXmlWriter(
         val modifiers = field.modifiers
         val initialValue = field.initialValue(true)
         val value = if (initialValue != null) {
-            escapeAttributeValue(CodePrinter.constantToSource(initialValue))
+            if (initialValue is Char && compatibility.xmlCharAsInt) {
+                initialValue.toInt().toString()
+            } else {
+                escapeAttributeValue(CodePrinter.constantToSource(initialValue))
+            }
         } else null
 
         val fullTypeName = escapeAttributeValue(field.type().toTypeString())
@@ -170,9 +214,10 @@ class JDiffXmlWriter(
         if (value != null) {
             writer.print("\"\n value=\"")
             writer.print(value)
-        } else if (compatibility.xmlShowArrayFieldsAsNull && field.type().isArray()) {
+        } else if (compatibility.xmlShowArrayFieldsAsNull && (field.type().isArray())) {
             writer.print("\"\n value=\"null")
         }
+
         writer.print("\"\n static=\"")
         writer.print(modifiers.isStatic())
         writer.print("\"\n final=\"")
@@ -193,6 +238,10 @@ class JDiffXmlWriter(
     override fun visitMethod(method: MethodItem) {
         val modifiers = method.modifiers
 
+        if (method.containingClass().isAnnotationType() && compatibility.xmlSkipAnnotationMethods) {
+            return
+        }
+
         // Note - to match doclava we don't write out the type parameter list
         // (method.typeParameterList()) in JDiff files!
 
@@ -200,7 +249,7 @@ class JDiffXmlWriter(
         writer.print(method.name())
         method.returnType()?.let {
             writer.print("\"\n return=\"")
-            writer.print(escapeAttributeValue(it.toTypeString()))
+            writer.print(escapeAttributeValue(formatType(it)))
         }
         writer.print("\"\n abstract=\"")
         writer.print(modifiers.isAbstract())
@@ -224,12 +273,22 @@ class JDiffXmlWriter(
     }
 
     private fun writeSuperClassAttribute(cls: ClassItem) {
+        if (cls.isInterface() && compatibility.extendsForInterfaceSuperClass) {
+            // Written in the interface section instead
+            return
+        }
+
         val superClass = if (preFiltered)
             cls.superClassType()
         else cls.filteredSuperClassType(filterReference)
 
         val superClassString =
             when {
+                cls.isAnnotationType() -> if (compatibility.xmlAnnotationAsObject) {
+                    JAVA_LANG_OBJECT
+                } else {
+                    JAVA_LANG_ANNOTATION
+                }
                 superClass != null -> {
                     // doclava seems to include java.lang.Object for classes but not interfaces
                     if (!cls.isClass() && superClass.isJavaLangObject()) {
@@ -242,7 +301,6 @@ class JDiffXmlWriter(
                         )
                     )
                 }
-                cls.isAnnotationType() -> JAVA_LANG_ANNOTATION
                 cls.isEnum() -> JAVA_LANG_ENUM
                 else -> return
             }
@@ -252,10 +310,17 @@ class JDiffXmlWriter(
     }
 
     private fun writeInterfaceList(cls: ClassItem) {
-        val interfaces = if (preFiltered)
+        var interfaces = if (preFiltered)
             cls.interfaceTypes().asSequence()
         else cls.filteredInterfaceTypes(filterReference).asSequence()
 
+        if (cls.isInterface() && compatibility.extendsForInterfaceSuperClass) {
+            val superClassType = cls.superClassType()
+            if (superClassType?.isJavaLangObject() == false) {
+                interfaces += superClassType
+            }
+        }
+
         if (interfaces.any()) {
             interfaces.sortedWith(TypeItem.comparator).forEach { item ->
                 writer.print("<implements name=\"")
@@ -272,12 +337,21 @@ class JDiffXmlWriter(
             // NOTE: We report parameter name as "null" rather than the real name to match
             // doclava's behavior
             writer.print("<parameter name=\"null\" type=\"")
-            writer.print(escapeAttributeValue(parameter.type().toTypeString()))
+            writer.print(escapeAttributeValue(formatType(parameter.type())))
             writer.println("\">")
             writer.println("</parameter>")
         }
     }
 
+    private fun formatType(type: TypeItem): String {
+        val typeString = type.toTypeString()
+        return if (compatibility.spaceAfterCommaInTypes) {
+            typeString.replace(",", ", ").replace(",  ", ", ")
+        } else {
+            typeString
+        }
+    }
+
     private fun writeThrowsList(method: MethodItem) {
         val throws = when {
             preFiltered -> method.throwsTypes().asSequence()
@@ -287,7 +361,11 @@ class JDiffXmlWriter(
         if (throws.any()) {
             throws.asSequence().sortedWith(ClassItem.fullNameComparator).forEach { type ->
                 writer.print("<exception name=\"")
-                writer.print(type.fullName())
+                if (options.compatOutput) {
+                    writer.print(type.simpleName())
+                } else {
+                    writer.print(type.fullName())
+                }
                 writer.print("\" type=\"")
                 writer.print(type.qualifiedName())
                 writer.println("\">")
diff --git a/src/main/java/com/android/tools/metalava/Options.kt b/src/main/java/com/android/tools/metalava/Options.kt
index 147be0b86f490969c1d2e97b04cbc3bcd6bda86b..b2ab95cfee8dffa724d983fd16968230300cc6ca 100644
--- a/src/main/java/com/android/tools/metalava/Options.kt
+++ b/src/main/java/com/android/tools/metalava/Options.kt
@@ -30,6 +30,7 @@ import java.io.IOException
 import java.io.OutputStreamWriter
 import java.io.PrintWriter
 import java.io.StringWriter
+import java.util.Locale
 import kotlin.reflect.KMutableProperty1
 import kotlin.reflect.full.memberProperties
 
@@ -51,6 +52,10 @@ const val ARG_API = "--api"
 const val ARG_XML_API = "--api-xml"
 const val ARG_CONVERT_TO_JDIFF = "--convert-to-jdiff"
 const val ARG_CONVERT_NEW_TO_JDIFF = "--convert-new-to-jdiff"
+const val ARG_CONVERT_TO_V1 = "--convert-to-v1"
+const val ARG_CONVERT_TO_V2 = "--convert-to-v2"
+const val ARG_CONVERT_NEW_TO_V1 = "--convert-new-to-v1"
+const val ARG_CONVERT_NEW_TO_V2 = "--convert-new-to-v2"
 const val ARG_PRIVATE_API = "--private-api"
 const val ARG_DEX_API = "--dex-api"
 const val ARG_PRIVATE_DEX_API = "--private-dex-api"
@@ -133,7 +138,6 @@ const val ARG_DEX_API_MAPPING = "--dex-api-mapping"
 const val ARG_GENERATE_DOCUMENTATION = "--generate-documentation"
 const val ARG_BASELINE = "--baseline"
 const val ARG_UPDATE_BASELINE = "--update-baseline"
-const val ARG_CONVERT_XML = "--convert-signature-to-xml"
 
 class Options(
     args: Array<String>,
@@ -241,16 +245,13 @@ class Options(
     var compatOutput = useCompatMode(args)
 
     /** Whether nullness annotations should be displayed as ?/!/empty instead of with @NonNull/@Nullable. */
-    var outputKotlinStyleNulls = !compatOutput
+    var outputKotlinStyleNulls = false // requires v3
 
     /** Whether default values should be included in signature files */
     var outputDefaultValues = !compatOutput
 
-    /** Whether we should omit common packages such as java.lang.* and kotlin.* from signature output */
-    var omitCommonPackages = !compatOutput
-
     /** The output format version being used */
-    var outputFormat: Int = 1
+    var outputFormat: FileFormat = if (compatOutput) FileFormat.V1 else FileFormat.V2
 
     /**
      * Whether reading signature files should assume the input is formatted as Kotlin-style nulls
@@ -471,7 +472,7 @@ class Options(
     /** Level to include for javadoc */
     var docLevel = DocLevel.PROTECTED
 
-    /** Whether to include the signature file format version number ([CURRENT_SIGNATURE_FORMAT]) in signature files */
+    /** Whether to include the signature file format version header in signature files */
     var includeSignatureFormatVersion: Boolean = !compatOutput
 
     /** A baseline to check against */
@@ -501,12 +502,13 @@ class Options(
     /** List of signature files to export as JDiff files */
     val convertToXmlFiles: List<ConvertFile> = mutableConvertToXmlFiles
 
-    /** JDiff file conversion candidates */
+    /** File conversion tasks */
     data class ConvertFile(
         val fromApiFile: File,
-        val toXmlFile: File,
+        val outputFile: File,
         val baseApiFile: File? = null,
-        val strip: Boolean
+        val strip: Boolean = false,
+        val outputFormat: FileFormat = FileFormat.JDIFF
     )
 
     /** Temporary folder to use instead of the JDK default, if any */
@@ -701,9 +703,14 @@ class Options(
                 }
 
                 ARG_BASELINE -> {
-                    val file = stringToNewOrExistingFile(getValue(args, ++index))
+                    val relative = getValue(args, ++index)
+                    val file = stringToNewOrExistingFile(relative)
                     assert(baseline == null) { "Only one baseline is allowed; found both ${baseline!!.file} and $file" }
-                    baseline = Baseline(file, updateBaseline || !file.isFile)
+                    val headerComment = if (relative.contains("frameworks/base/"))
+                        "// See tools/metalava/API-LINT.md for how to update this file.\n\n"
+                    else
+                        ""
+                    baseline = Baseline(file, updateBaseline || !file.isFile, FileFormat.BASELINE, headerComment)
                 }
 
                 ARG_UPDATE_BASELINE -> {
@@ -866,8 +873,8 @@ class Options(
                     // Already processed above but don't flag it here as invalid
                 }
 
-                ARG_OMIT_COMMON_PACKAGES, "$ARG_OMIT_COMMON_PACKAGES=yes" -> omitCommonPackages = true
-                "$ARG_OMIT_COMMON_PACKAGES=no" -> omitCommonPackages = false
+                ARG_OMIT_COMMON_PACKAGES, "$ARG_OMIT_COMMON_PACKAGES=yes" -> compatibility.omitCommonPackages = true
+                "$ARG_OMIT_COMMON_PACKAGES=no" -> compatibility.omitCommonPackages = false
 
                 ARG_SKIP_JAVA_IN_COVERAGE_REPORT -> omitRuntimePackageStats = true
 
@@ -956,23 +963,45 @@ class Options(
                     artifactRegistrations.register(artifactId, descriptor)
                 }
 
-                ARG_CONVERT_TO_JDIFF, "-convert2xml", "-convert2xmlnostrip" -> {
-                    val signatureFile = stringToExistingFile(getValue(args, ++index))
-                    val jDiffFile = stringToNewFile(getValue(args, ++index))
+                ARG_CONVERT_TO_JDIFF,
+                ARG_CONVERT_TO_V1,
+                ARG_CONVERT_TO_V2,
+                // doclava compatibility:
+                "-convert2xml",
+                "-convert2xmlnostrip" -> {
                     val strip = arg == "-convert2xml"
-                    mutableConvertToXmlFiles.add(ConvertFile(signatureFile, jDiffFile, null, strip))
-                }
+                    val format = when (arg) {
+                        ARG_CONVERT_TO_V1 -> FileFormat.V1
+                        ARG_CONVERT_TO_V2 -> FileFormat.V2
+                        else -> FileFormat.JDIFF
+                    }
 
-                ARG_CONVERT_NEW_TO_JDIFF, "-new_api", "-new_api_no_strip" -> {
-                    val baseFile = stringToExistingFile(getValue(args, ++index))
                     val signatureFile = stringToExistingFile(getValue(args, ++index))
-                    val jDiffFile = stringToNewFile(getValue(args, ++index))
+                    val outputFile = stringToNewFile(getValue(args, ++index))
+                    mutableConvertToXmlFiles.add(ConvertFile(signatureFile, outputFile, null, strip, format))
+                }
+
+                ARG_CONVERT_NEW_TO_JDIFF,
+                ARG_CONVERT_NEW_TO_V1,
+                ARG_CONVERT_NEW_TO_V2,
+                // doclava compatibility:
+                "-new_api",
+                "-new_api_no_strip" -> {
+                    val format = when (arg) {
+                        ARG_CONVERT_NEW_TO_V1 -> FileFormat.V1
+                        ARG_CONVERT_NEW_TO_V2 -> FileFormat.V2
+                        else -> FileFormat.JDIFF
+                    }
                     val strip = arg == "-new_api"
                     if (arg != ARG_CONVERT_NEW_TO_JDIFF) {
                         // Using old doclava flags: Compatibility behavior: don't include fields in the output
                         compatibility.includeFieldsInApiDiff = false
                     }
-                    mutableConvertToXmlFiles.add(ConvertFile(signatureFile, jDiffFile, baseFile, strip))
+
+                    val baseFile = stringToExistingFile(getValue(args, ++index))
+                    val signatureFile = stringToExistingFile(getValue(args, ++index))
+                    val jDiffFile = stringToNewFile(getValue(args, ++index))
+                    mutableConvertToXmlFiles.add(ConvertFile(signatureFile, jDiffFile, baseFile, strip, format))
                 }
 
                 "--write-android-jar-signatures" -> {
@@ -1138,7 +1167,7 @@ class Options(
                             yesNo(arg.substring(ARG_OUTPUT_DEFAULT_VALUES.length + 1))
                         }
                     } else if (arg.startsWith(ARG_OMIT_COMMON_PACKAGES)) {
-                        omitCommonPackages = if (arg == ARG_OMIT_COMMON_PACKAGES) {
+                        compatibility.omitCommonPackages = if (arg == ARG_OMIT_COMMON_PACKAGES) {
                             true
                         } else {
                             yesNo(arg.substring(ARG_OMIT_COMMON_PACKAGES.length + 1))
@@ -1153,9 +1182,15 @@ class Options(
                         else yesNo(arg.substring(ARG_INCLUDE_SIG_VERSION.length + 1))
                     } else if (arg.startsWith(ARG_FORMAT)) {
                         when (arg) {
-                            "$ARG_FORMAT=v1" -> setFormat(1)
-                            "$ARG_FORMAT=v2" -> setFormat(2)
-                            "$ARG_FORMAT=v3" -> setFormat(3)
+                            "$ARG_FORMAT=v1" -> {
+                                FileFormat.V1.configureOptions(this, compatibility)
+                            }
+                            "$ARG_FORMAT=v2" -> {
+                                FileFormat.V2.configureOptions(this, compatibility)
+                            }
+                            "$ARG_FORMAT=v3" -> {
+                                FileFormat.V3.configureOptions(this, compatibility)
+                            }
                             else -> throw DriverException(stderr = "Unexpected signature format; expected v1, v2 or v3")
                         }
                     } else if (arg.startsWith("-")) {
@@ -1231,9 +1266,9 @@ class Options(
             artifactRegistrations.clear()
         }
 
-        if (baseline == null && sourcePath.isNotEmpty() && !sourcePath[0].path.isBlank()) {
-            val defaultBaseline = File(sourcePath[0], DEFAULT_BASELINE_NAME)
-            if (defaultBaseline.isFile || updateBaseline) {
+        if (baseline == null) {
+            val defaultBaseline = getDefaultBaselineFile()
+            if (defaultBaseline != null && (defaultBaseline.isFile || updateBaseline)) {
                 baseline = Baseline(defaultBaseline, !defaultBaseline.isFile || updateBaseline)
             }
         }
@@ -1241,15 +1276,6 @@ class Options(
         checkFlagConsistency()
     }
 
-    private fun setFormat(format: Int) {
-        outputFormat = format
-        compatOutput = format == 1
-        outputKotlinStyleNulls = format >= 3
-        outputDefaultValues = format >= 2
-        omitCommonPackages = format >= 2
-        includeSignatureFormatVersion = format >= 2
-    }
-
     private fun findCompatibilityFlag(arg: String): KMutableProperty1<Compatibility, Boolean>? {
         val index = arg.indexOf('=')
         val name = arg
@@ -1264,6 +1290,27 @@ class Options(
             }
     }
 
+    /**
+     * Produce a default file name for the baseline. It's normally "baseline.txt", but can
+     * be prefixed by show annotations; e.g. @TestApi -> test-baseline.txt, @SystemApi -> system-baseline.txt,
+     * etc.
+     */
+    private fun getDefaultBaselineFile(): File? {
+        if (sourcePath.isNotEmpty() && !sourcePath[0].path.isBlank()) {
+            fun annotationToPrefix(qualifiedName: String): String {
+                val name = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1)
+                return name.toLowerCase(Locale.US).removeSuffix("api") + "-"
+            }
+            val sb = StringBuilder()
+            showAnnotations.forEach { sb.append(annotationToPrefix(it)).append('-') }
+            showSingleAnnotations.forEach { sb.append(annotationToPrefix(it)).append('-') }
+            sb.append(DEFAULT_BASELINE_NAME)
+            return File(sourcePath[0], sb.toString())
+        } else {
+            return null
+        }
+    }
+
     private fun findAndroidJars(
         androidJarPatterns: List<String>,
         currentApiLevel: Int,
@@ -1739,6 +1786,12 @@ class Options(
                 "in the JDiff XML format. Can be specified multiple times.",
             "$ARG_CONVERT_NEW_TO_JDIFF <old> <new> <xml>", "Reads in the given old and new api files, " +
                 "computes the difference, and writes out only the new parts of the API in the JDiff XML format.",
+            "$ARG_CONVERT_TO_V1 <sig> <sig>", "Reads in the given signature file and writes it out as a " +
+                "signature file in the original v1/doclava format.",
+            "$ARG_CONVERT_TO_V2 <sig> <sig>", "Reads in the given signature file and writes it out as a " +
+                "signature file in the new signature format, v2.",
+            "$ARG_CONVERT_NEW_TO_V2 <old> <new> <sig>", "Reads in the given old and new api files, " +
+                "computes the difference, and writes out only the new parts of the API in the v2 format.",
 
             "", "\nStatistics:",
             ARG_ANNOTATION_COVERAGE_STATS, "Whether $PROGRAM_NAME should emit coverage statistics for " +
diff --git a/src/main/java/com/android/tools/metalava/Reporter.kt b/src/main/java/com/android/tools/metalava/Reporter.kt
index 939913b0ad314b5e33b103495ae3ef2f8b1f195e..56009b7bf4c12b26e26092ef59c2d1534a503549 100644
--- a/src/main/java/com/android/tools/metalava/Reporter.kt
+++ b/src/main/java/com/android/tools/metalava/Reporter.kt
@@ -133,7 +133,7 @@ open class Reporter(private val rootFolder: File? = null) {
                 report(severity, item.psi(), message, id)
             }
             is TextItem -> report(severity, (item as? TextItem)?.position.toString(), message, id)
-            else -> report(severity, "<unknown location>", message, id)
+            else -> report(severity, null as String?, message, id)
         }
     }
 
@@ -294,7 +294,9 @@ open class Reporter(private val rootFolder: File? = null) {
         if (color) {
             sb.append(terminalAttributes(bold = true))
             if (!options.omitLocations) {
-                location?.let { sb.append(it).append(": ") }
+                location?.let {
+                    sb.append(it).append(": ")
+                }
             }
             when (effectiveSeverity) {
                 LINT -> sb.append(terminalAttributes(foreground = TerminalColor.CYAN)).append("lint: ")
diff --git a/src/main/java/com/android/tools/metalava/SignatureFormat.kt b/src/main/java/com/android/tools/metalava/SignatureFormat.kt
deleted file mode 100644
index 794eedbc5ed730b3f9e519fe9797f0d50111d385..0000000000000000000000000000000000000000
--- a/src/main/java/com/android/tools/metalava/SignatureFormat.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tools.metalava
-
-/** Current signature format. */
-const val CURRENT_SIGNATURE_FORMAT = "2.0"
-
-/** Marker comment at the beginning of the signature file */
-const val SIGNATURE_FORMAT_PREFIX = "// Signature format: "
-
-fun useKotlinStyleNulls(format: Int): Boolean {
-    return format >= 3
-}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/SignatureWriter.kt b/src/main/java/com/android/tools/metalava/SignatureWriter.kt
index 1b9ee50c567eb7c2db280025364f4b3b89e7f3f8..c38a1a125355217db8fc674d1ef6e3ac99511d35 100644
--- a/src/main/java/com/android/tools/metalava/SignatureWriter.kt
+++ b/src/main/java/com/android/tools/metalava/SignatureWriter.kt
@@ -50,8 +50,7 @@ class SignatureWriter(
 ) {
     init {
         if (options.includeSignatureFormatVersion) {
-            writer.print(SIGNATURE_FORMAT_PREFIX)
-            writer.println(CURRENT_SIGNATURE_FORMAT)
+            writer.print(options.outputFormat.header())
         }
     }
 
@@ -139,10 +138,6 @@ class SignatureWriter(
     override fun visitClass(cls: ClassItem) {
         writer.print("  ")
 
-        if (compatibility.extraSpaceForEmptyModifiers && cls.isPackagePrivate && cls.isPackagePrivate) {
-            writer.print(" ")
-        }
-
         writeModifiers(cls)
 
         if (cls.isAnnotationType()) {
@@ -185,7 +180,7 @@ class SignatureWriter(
             includeDeprecated = true,
             includeAnnotations = compatibility.annotationsInSignatures,
             skipNullnessAnnotations = options.outputKotlinStyleNulls,
-            omitCommonPackages = options.omitCommonPackages
+            omitCommonPackages = compatibility.omitCommonPackages
         )
     }
 
@@ -317,7 +312,7 @@ class SignatureWriter(
         )
 
         // Strip java.lang. prefix?
-        if (options.omitCommonPackages) {
+        if (compatibility.omitCommonPackages) {
             typeString = TypeItem.shortenTypes(typeString)
         }
 
diff --git a/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java b/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
index 5189d34babad4fbe8966b3ff813e2372fd30883e..f1d672ec2801a305881b5f6eea117a387f439933 100644
--- a/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
+++ b/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
@@ -16,9 +16,8 @@
 
 package com.android.tools.metalava.doclava1;
 
-import com.android.ide.common.repository.GradleVersion;
 import com.android.tools.lint.checks.infrastructure.ClassNameKt;
-import com.android.tools.metalava.SignatureFormatKt;
+import com.android.tools.metalava.FileFormat;
 import com.android.tools.metalava.model.AnnotationItem;
 import com.android.tools.metalava.model.DefaultModifierList;
 import com.android.tools.metalava.model.TypeParameterList;
@@ -37,6 +36,7 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Charsets;
 import com.google.common.io.Files;
 import kotlin.Pair;
+import kotlin.text.StringsKt;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
@@ -49,7 +49,6 @@ import static com.android.tools.metalava.ConstantsKt.ANDROIDX_NULLABLE;
 import static com.android.tools.metalava.ConstantsKt.JAVA_LANG_ANNOTATION;
 import static com.android.tools.metalava.ConstantsKt.JAVA_LANG_ENUM;
 import static com.android.tools.metalava.ConstantsKt.JAVA_LANG_STRING;
-import static com.android.tools.metalava.SignatureFormatKt.SIGNATURE_FORMAT_PREFIX;
 import static com.android.tools.metalava.model.FieldItemKt.javaUnescapeString;
 
 //
@@ -71,25 +70,20 @@ public class ApiFile {
         }
     }
 
+    @SuppressWarnings("StatementWithEmptyBody")
     @VisibleForTesting
     public static TextCodebase parseApi(String filename, String apiText,
                                         Boolean kotlinStyleNulls) throws ApiParseException {
-        GradleVersion format = null;
-        if (apiText.startsWith(SIGNATURE_FORMAT_PREFIX)) {
-            int begin = SIGNATURE_FORMAT_PREFIX.length();
-            int end = apiText.indexOf('\n', begin);
-            if (end == -1) {
-                end = apiText.length();
-            } else if (end > 0 && apiText.charAt(end - 1) == '\r') {
-                end--;
-            }
-            String formatString = apiText.substring(begin, end).trim();
-            if (!formatString.isEmpty()) {
-                format = GradleVersion.tryParse(formatString);
-                if (kotlinStyleNulls == null) {
-                    kotlinStyleNulls = SignatureFormatKt.useKotlinStyleNulls(format.getMajor());
-                }
+        FileFormat format = FileFormat.Companion.parseHeader(apiText);
+        if (format.isSignatureFormat()) {
+            if (kotlinStyleNulls == null || !kotlinStyleNulls) {
+                kotlinStyleNulls = format.useKotlinStyleNulls();
             }
+        } else if (StringsKt.isBlank(apiText)) {
+            // Signature files are sometimes blank, particularly with show annotations
+            kotlinStyleNulls = false;
+        } else {
+            throw new ApiParseException("Unknown file format of " + filename);
         }
 
         if (apiText.contains("/*")) {
@@ -99,12 +93,8 @@ public class ApiFile {
         final Tokenizer tokenizer = new Tokenizer(filename, apiText.toCharArray());
         final TextCodebase api = new TextCodebase(new File(filename));
         api.setDescription("Codebase loaded from " + filename);
-        if (format != null) {
-            api.setFormat(format);
-        }
-        if (kotlinStyleNulls != null) {
-            api.setKotlinStyleNulls(kotlinStyleNulls);
-        }
+        api.setFormat(format);
+        api.setKotlinStyleNulls(kotlinStyleNulls);
 
         while (true) {
             String token = tokenizer.getToken();
@@ -180,6 +170,7 @@ public class ApiFile {
             token = tokenizer.requireToken();
         } else if ("interface".equals(token)) {
             isInterface = true;
+            modifiers.setAbstract(true);
             token = tokenizer.requireToken();
         } else if ("@interface".equals(token)) {
             // Annotation
@@ -242,6 +233,10 @@ public class ApiFile {
         }
         if (JAVA_LANG_ENUM.equals(ext)) {
             cl.setIsEnum(true);
+            // Above we marked all enums as static but for a top level class it's implicit
+            if (!cl.fullName().contains(".")) {
+                cl.getModifiers().setStatic(false);
+            }
         } else if (isAnnotation) {
             api.mapClassToInterface(cl, JAVA_LANG_ANNOTATION);
         } else if (api.implementsInterface(cl, JAVA_LANG_ANNOTATION)) {
@@ -423,6 +418,9 @@ public class ApiFile {
         name = token;
         method = new TextMethodItem(api, name, cl, modifiers, returnType, tokenizer.pos());
         method.setDeprecated(modifiers.isDeprecated());
+        if (cl.isInterface() && !modifiers.isDefault()) {
+            modifiers.setAbstract(true);
+        }
         method.setTypeParameterList(typeParameterList);
         if (typeParameterList instanceof TextTypeParameterList) {
             ((TextTypeParameterList) typeParameterList).setOwner(method);
@@ -754,7 +752,9 @@ public class ApiFile {
             if (typeString.endsWith("...")) {
                 modifiers.setVarArg(true);
             }
-            TextTypeItem typeInfo = api.obtainTypeFromString(typeString);
+            TextTypeItem typeInfo = api.obtainTypeFromString(typeString,
+                (TextClassItem) method.containingClass(),
+                method.typeParameterList());
 
             String name;
             String publicName;
diff --git a/src/main/java/com/android/tools/metalava/doclava1/TextCodebase.kt b/src/main/java/com/android/tools/metalava/doclava1/TextCodebase.kt
index 94c055ea56c2c333aff9a25a1ffb36c3e06bf73f..f90daf5068debd40671847f520449f38b76c00fd 100644
--- a/src/main/java/com/android/tools/metalava/doclava1/TextCodebase.kt
+++ b/src/main/java/com/android/tools/metalava/doclava1/TextCodebase.kt
@@ -16,10 +16,10 @@
 
 package com.android.tools.metalava.doclava1
 
-import com.android.ide.common.repository.GradleVersion
 import com.android.tools.metalava.ApiType
 import com.android.tools.metalava.CodebaseComparator
 import com.android.tools.metalava.ComparisonVisitor
+import com.android.tools.metalava.FileFormat
 import com.android.tools.metalava.JAVA_LANG_ANNOTATION
 import com.android.tools.metalava.JAVA_LANG_ENUM
 import com.android.tools.metalava.JAVA_LANG_OBJECT
@@ -77,7 +77,7 @@ class TextCodebase(location: File) : DefaultCodebase(location) {
      * Signature file format version, if found. Type "GradleVersion" is misleading; it's just a convenient
      * version class.
      */
-    var format: GradleVersion = GradleVersion.parse("1.0") // not specifying format: assumed to be doclava, 1.0
+    var format: FileFormat = FileFormat.V1 // not specifying format: assumed to be doclava, 1.0
 
     override fun getPackages(): PackageList {
         val list = ArrayList<PackageItem>(mPackages.values)
@@ -340,7 +340,8 @@ class TextCodebase(location: File) : DefaultCodebase(location) {
         fun computeDelta(
             baseFile: File,
             baseApi: Codebase,
-            signatureApi: Codebase
+            signatureApi: Codebase,
+            includeFieldsInApiDiff: Boolean = compatibility.includeFieldsInApiDiff
         ): TextCodebase {
             // Compute just the delta
             val delta = TextCodebase(baseFile)
@@ -367,7 +368,7 @@ class TextCodebase(location: File) : DefaultCodebase(location) {
                 }
 
                 override fun added(new: FieldItem) {
-                    if (!compatibility.includeFieldsInApiDiff) {
+                    if (!includeFieldsInApiDiff) {
                         return
                     }
                     val cls = getOrAddClass(new.containingClass())
diff --git a/src/main/java/com/android/tools/metalava/model/ClassItem.kt b/src/main/java/com/android/tools/metalava/model/ClassItem.kt
index e1746c94157deab578efb620c4c77051154acfa2..e06784caea513cae3abdaaed3a75a0304492c06b 100644
--- a/src/main/java/com/android/tools/metalava/model/ClassItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/ClassItem.kt
@@ -21,7 +21,6 @@ import com.android.tools.metalava.ApiAnalyzer
 import com.android.tools.metalava.JAVA_LANG_ANNOTATION
 import com.android.tools.metalava.JAVA_LANG_ENUM
 import com.android.tools.metalava.JAVA_LANG_OBJECT
-import com.android.tools.metalava.compatibility
 import com.android.tools.metalava.model.visitors.ApiVisitor
 import com.android.tools.metalava.model.visitors.ItemVisitor
 import com.android.tools.metalava.model.visitors.TypeVisitor
@@ -375,12 +374,7 @@ interface ClassItem : Item {
             a.qualifiedName().compareTo(b.qualifiedName())
         }
 
-        fun classNameSorter(): Comparator<in ClassItem> =
-            if (compatibility.sortClassesBySimpleName) {
-                ClassItem.comparator
-            } else {
-                ClassItem.qualifiedComparator
-            }
+        fun classNameSorter(): Comparator<in ClassItem> = ClassItem.qualifiedComparator
     }
 
     fun findMethod(
diff --git a/src/main/java/com/android/tools/metalava/model/DefaultModifierList.kt b/src/main/java/com/android/tools/metalava/model/DefaultModifierList.kt
index 65ebd48bb004306f6aa0dfacd470dae8a63af81b..21a328fabff5bd4d55100853308d5ed9cdfe5324 100644
--- a/src/main/java/com/android/tools/metalava/model/DefaultModifierList.kt
+++ b/src/main/java/com/android/tools/metalava/model/DefaultModifierList.kt
@@ -16,6 +16,7 @@
 
 package com.android.tools.metalava.model
 
+import com.android.tools.metalava.compatibility
 import com.android.tools.metalava.model.psi.PsiModifierItem
 
 open class DefaultModifierList(
@@ -245,13 +246,24 @@ open class DefaultModifierList(
     override fun equivalentTo(other: ModifierList): Boolean {
         if (other is PsiModifierItem) {
             val flags2 = other.flags
-            val mask = EQUIVALENCE_MASK
-
-            // Skipping the "default" flag
-            // TODO: Compatibility: skipNativeModifier and skipStrictFpModifier modifier flags!
-            // if (!compatibility.skipNativeModifier && isNative() != other.isNative()) return false
-            // if (!compatibility.skipStrictFpModifier && isStrictFp() != other.isStrictFp()) return false
-            return flags and mask == flags2 and mask
+            val mask = if (compatibility.includeSynchronized) COMPAT_EQUIVALENCE_MASK else EQUIVALENCE_MASK
+
+            val masked1 = flags and mask
+            val masked2 = flags2 and mask
+            val same = masked1 xor masked2
+            if (same == 0) {
+                return true
+            } else if (compatibility.hideDifferenceImplicit) {
+                if (same == FINAL &&
+                    // Only differ in final: not significant if implied by containing class
+                    isFinal() && (owner as? MethodItem)?.containingClass()?.modifiers?.isFinal() == true) {
+                    return true
+                } else if (same == DEPRECATED &&
+                    // Only differ in deprecated: not significant if implied by containing class
+                    isDeprecated() && (owner as? MethodItem)?.containingClass()?.deprecated == true) {
+                    return true
+                }
+            }
         }
         return false
     }
@@ -309,7 +321,9 @@ open class DefaultModifierList(
          * to consider whether an override of a method is different from its super implementation
          */
         private const val EQUIVALENCE_MASK = PUBLIC or PROTECTED or PRIVATE or STATIC or ABSTRACT or
-            FINAL or TRANSIENT or VOLATILE or SYNCHRONIZED or DEPRECATED or VARARG or
+            FINAL or TRANSIENT or VOLATILE or DEPRECATED or VARARG or
             SEALED or INTERNAL or INFIX or OPERATOR or SUSPEND
+
+        private const val COMPAT_EQUIVALENCE_MASK = EQUIVALENCE_MASK or SYNCHRONIZED
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/model/MethodItem.kt b/src/main/java/com/android/tools/metalava/model/MethodItem.kt
index 173842fdc0e1b854926bf55e2f969c9358251090..bdb3b899ad7cdcde416caa580c921bf0903201ca 100644
--- a/src/main/java/com/android/tools/metalava/model/MethodItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/MethodItem.kt
@@ -16,6 +16,7 @@
 
 package com.android.tools.metalava.model
 
+import com.android.tools.metalava.compatibility
 import com.android.tools.metalava.doclava1.TextCodebase
 import com.android.tools.metalava.model.visitors.ItemVisitor
 import com.android.tools.metalava.model.visitors.TypeVisitor
@@ -111,7 +112,7 @@ interface MethodItem : MemberItem {
     ): LinkedHashSet<ClassItem> {
 
         for (cls in throwsTypes()) {
-            if (predicate.test(cls)) {
+            if (predicate.test(cls) || cls.isTypeParameter && !compatibility.useErasureInThrows) {
                 classes.add(cls)
             } else {
                 // Excluded, but it may have super class throwables that are included; if so, include those
@@ -276,8 +277,8 @@ interface MethodItem : MemberItem {
                 return false
             }
 
-            // IntentService#onStart - is it here because they vary in deprecation status?
-            if (method.deprecated != superMethod.deprecated) {
+            if (method.deprecated != superMethod.deprecated &&
+                (!compatibility.hideDifferenceImplicit || !method.deprecated)) {
                 return false
             }
 
diff --git a/src/main/java/com/android/tools/metalava/model/ModifierList.kt b/src/main/java/com/android/tools/metalava/model/ModifierList.kt
index 3ce5d30c685ad0c0eebd45cb2185d8a9437adcd3..d645edf3d9744a95b5560d0ff113883384b266cd 100644
--- a/src/main/java/com/android/tools/metalava/model/ModifierList.kt
+++ b/src/main/java/com/android/tools/metalava/model/ModifierList.kt
@@ -69,10 +69,8 @@ interface ModifierList {
 
         if (isStatic() != other.isStatic()) return false
         if (isAbstract() != other.isAbstract()) return false
-        if (isFinal() != other.isFinal()) return false
-        if (!compatibility.skipNativeModifier && isNative() != other.isNative()) return false
-        if (isSynchronized() != other.isSynchronized()) return false
-        if (!compatibility.skipStrictFpModifier && isStrictFp() != other.isStrictFp()) return false
+        if (isFinal() != other.isFinal()) { return false }
+        if (compatibility.includeSynchronized && isSynchronized() != other.isSynchronized()) return false
         if (isTransient() != other.isTransient()) return false
         if (isVolatile() != other.isVolatile()) return false
 
@@ -270,10 +268,6 @@ interface ModifierList {
                 return
             }
 
-            if (compatibility.extraSpaceForEmptyModifiers && item.isPackagePrivate && item is MemberItem) {
-                writer.write(" ")
-            }
-
             // Kotlin order:
             //   https://kotlinlang.org/docs/reference/coding-conventions.html#modifiers
 
@@ -342,22 +336,18 @@ interface ModifierList {
                     writer.write("abstract ")
                 }
 
-                if (list.isNative() && (target.isStubsFile() || !compatibility.skipNativeModifier)) {
+                if (list.isNative() && target.isStubsFile()) {
                     writer.write("native ")
                 }
 
-                if (item.deprecated && includeDeprecated && !target.isStubsFile() && options.compatOutput) {
+                if (item.deprecated && includeDeprecated && !target.isStubsFile() && !compatibility.deprecatedAsAnnotation) {
                     writer.write("deprecated ")
                 }
 
-                if (list.isSynchronized() && (options.compatOutput || target.isStubsFile())) {
+                if (list.isSynchronized() && (compatibility.includeSynchronized || target.isStubsFile())) {
                     writer.write("synchronized ")
                 }
 
-                if (!compatibility.skipStrictFpModifier && list.isStrictFp()) {
-                    writer.write("strictfp ")
-                }
-
                 if (list.isTransient()) {
                     writer.write("transient ")
                 }
@@ -366,7 +356,7 @@ interface ModifierList {
                     writer.write("volatile ")
                 }
             } else {
-                if (item.deprecated && includeDeprecated && !target.isStubsFile() && options.compatOutput) {
+                if (item.deprecated && includeDeprecated && !target.isStubsFile() && !compatibility.deprecatedAsAnnotation) {
                     writer.write("deprecated ")
                 }
 
@@ -434,17 +424,13 @@ interface ModifierList {
                     writer.write("volatile ")
                 }
 
-                if (list.isSynchronized() && (options.compatOutput || target.isStubsFile())) {
+                if (list.isSynchronized() && (compatibility.includeSynchronized || target.isStubsFile())) {
                     writer.write("synchronized ")
                 }
 
-                if (list.isNative() && (target.isStubsFile() || !compatibility.skipNativeModifier)) {
+                if (list.isNative() && target.isStubsFile()) {
                     writer.write("native ")
                 }
-
-                if (!compatibility.skipStrictFpModifier && list.isStrictFp()) {
-                    writer.write("strictfp ")
-                }
             }
         }
 
@@ -463,7 +449,7 @@ interface ModifierList {
             //  unless runtimeOnly is false, in which case we'd include it too
             // e.g. emit @Deprecated if includeDeprecated && !runtimeOnly
             if (item.deprecated &&
-                (!options.compatOutput || target.isStubsFile()) &&
+                (compatibility.deprecatedAsAnnotation || target.isStubsFile()) &&
                 (runtimeAnnotationsOnly || includeDeprecated)
             ) {
                 writer.write("@Deprecated")
diff --git a/src/main/java/com/android/tools/metalava/model/TypeItem.kt b/src/main/java/com/android/tools/metalava/model/TypeItem.kt
index 1469e91aaac4f80bf3cadd9bd6b5a4d8fc5b9d63..7ca5ff98dd28d9983281cc3480ec1ab953e42de2 100644
--- a/src/main/java/com/android/tools/metalava/model/TypeItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/TypeItem.kt
@@ -21,7 +21,6 @@ import com.android.tools.metalava.JAVA_LANG_OBJECT
 import com.android.tools.metalava.JAVA_LANG_PREFIX
 import com.android.tools.metalava.JAVA_LANG_STRING
 import com.android.tools.metalava.compatibility
-import com.android.tools.metalava.options
 import java.util.function.Predicate
 
 /**
@@ -168,7 +167,7 @@ interface TypeItem {
     companion object {
         /** Shortens types, if configured */
         fun shortenTypes(type: String): String {
-            if (options.omitCommonPackages) {
+            if (compatibility.omitCommonPackages) {
                 var cleaned = type
                 if (cleaned.contains("@androidx.annotation.")) {
                     cleaned = cleaned.replace("@androidx.annotation.", "@")
diff --git a/src/main/java/com/android/tools/metalava/model/TypeParameterListOwner.kt b/src/main/java/com/android/tools/metalava/model/TypeParameterListOwner.kt
index bcd526beb70aba97a7252dba1d6b284ef9a58641..7fac61b694544b7a94297d08798416090d33d06c 100644
--- a/src/main/java/com/android/tools/metalava/model/TypeParameterListOwner.kt
+++ b/src/main/java/com/android/tools/metalava/model/TypeParameterListOwner.kt
@@ -20,4 +20,7 @@ interface TypeParameterListOwner {
     fun typeParameterList(): TypeParameterList
     /** Given a variable in this owner, resolves to a type parameter item */
     fun resolveParameter(variable: String): TypeParameterItem?
+
+    /** Parent type parameter list owner */
+    fun typeParameterListOwnerParent(): TypeParameterListOwner?
 }
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/model/psi/CodePrinter.kt b/src/main/java/com/android/tools/metalava/model/psi/CodePrinter.kt
index 9eb7b85f40f7458dffd152d9a9a265eaab255808..bbe8f86d2e730ce13e1c21191746de1224f96802 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/CodePrinter.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/CodePrinter.kt
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiAnnotationMemberValue
 import com.intellij.psi.PsiArrayInitializerMemberValue
 import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiClassObjectAccessExpression
+import com.intellij.psi.PsiElement
 import com.intellij.psi.PsiField
 import com.intellij.psi.PsiLiteral
 import com.intellij.psi.PsiReference
@@ -40,6 +41,7 @@ import org.jetbrains.uast.UBinaryExpression
 import org.jetbrains.uast.UBinaryExpressionWithType
 import org.jetbrains.uast.UBlockExpression
 import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UElement
 import org.jetbrains.uast.UExpression
 import org.jetbrains.uast.ULambdaExpression
 import org.jetbrains.uast.ULiteralExpression
@@ -62,8 +64,12 @@ open class CodePrinter(
     /** An optional filter to use to determine if we should emit a reference to an item */
     private val filterReference: Predicate<Item>? = null
 ) {
-    open fun warning(message: String) {
-        reporter.report(Errors.INTERNAL_ERROR, null as Item?, message)
+    open fun warning(message: String, psiElement: PsiElement? = null) {
+        reporter.report(Errors.INTERNAL_ERROR, psiElement, message)
+    }
+
+    open fun warning(message: String, uElement: UElement) {
+        warning(message, uElement.sourcePsi ?: uElement.javaPsi)
     }
 
     /** Given an annotation member value, returns the corresponding Java source expression */
@@ -186,7 +192,7 @@ open class CodePrinter(
 
                     val declaringClass = field.containingClass
                     if (declaringClass == null) {
-                        warning("No containing class found for " + field.name)
+                        warning("No containing class found for " + field.name, field)
                         return false
                     }
                     val qualifiedName = declaringClass.qualifiedName
@@ -225,7 +231,7 @@ open class CodePrinter(
                 }
                 else -> {
                     if (skipUnknown) {
-                        warning("Unexpected reference to $expression")
+                        warning("Unexpected reference to $expression", expression)
                         return false
                     }
                     sb.append(expression.asSourceString())
@@ -361,7 +367,7 @@ open class CodePrinter(
             }
         }
 
-        warning("Unexpected annotation expression of type ${expression.javaClass} and is $expression")
+        warning("Unexpected annotation expression of type ${expression.javaClass} and is $expression", expression)
 
         return false
     }
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiBasedCodebase.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiBasedCodebase.kt
index d1872c33f2511503ca4fc8c47588623771b935c8..1bc4bf6a79800865ae4d6f7d7224c855ff4c7246 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiBasedCodebase.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiBasedCodebase.kt
@@ -17,7 +17,6 @@
 package com.android.tools.metalava.model.psi
 
 import com.android.SdkConstants
-import com.android.tools.metalava.compatibility
 import com.android.tools.metalava.doclava1.Errors
 import com.android.tools.metalava.model.ClassItem
 import com.android.tools.metalava.model.DefaultCodebase
@@ -414,7 +413,7 @@ open class PsiBasedCodebase(location: File, override var description: String = "
                     }
                 }
                 val last = pkg.lastIndexOf('.')
-                if (last == -1 || !compatibility.inheritPackageDocs) {
+                if (last == -1) {
                     hiddenPackages[packageName] = false
                     break
                 } else {
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiClassItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiClassItem.kt
index 09158644b5ceaec6c2911b24e9e4445798ee08d0..f3efc401295bb83d93596cb85c8a526a28873238 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiClassItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiClassItem.kt
@@ -27,7 +27,6 @@ import com.android.tools.metalava.model.PackageItem
 import com.android.tools.metalava.model.PropertyItem
 import com.android.tools.metalava.model.TypeItem
 import com.android.tools.metalava.model.TypeParameterList
-import com.android.tools.metalava.options
 import com.intellij.lang.jvm.types.JvmReferenceType
 import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiClassType
@@ -408,7 +407,7 @@ open class PsiClassItem(
 
             if (classType == ClassType.ENUM) {
                 addEnumMethods(codebase, item, psiClass, methods)
-            } else if (classType == ClassType.ANNOTATION_TYPE && !options.compatOutput &&
+            } else if (classType == ClassType.ANNOTATION_TYPE && compatibility.explicitlyListClassRetention &&
                 modifiers.findAnnotation("java.lang.annotation.Retention") == null
             ) {
                 // By policy, include explicit retention policy annotation if missing
diff --git a/src/main/java/com/android/tools/metalava/model/text/TextClassItem.kt b/src/main/java/com/android/tools/metalava/model/text/TextClassItem.kt
index 86c72e5222af8920fce17a744c27b9e28fe26c7b..f78e825140f09212389c4b69e6c03c9f553dd3ed 100644
--- a/src/main/java/com/android/tools/metalava/model/text/TextClassItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/text/TextClassItem.kt
@@ -142,6 +142,10 @@ open class TextClassItem(
         return typeParameterList!!
     }
 
+    override fun typeParameterListOwnerParent(): TypeParameterListOwner? {
+        return containingClass
+    }
+
     override fun resolveParameter(variable: String): TypeParameterItem? {
         if (hasTypeVariables()) {
             for (t in typeParameterList().typeParameters()) {
diff --git a/src/main/java/com/android/tools/metalava/model/text/TextMethodItem.kt b/src/main/java/com/android/tools/metalava/model/text/TextMethodItem.kt
index e2a6699f2da5249144723b4e48e355cf68ed44ab..a1a38d2aec7f7e47141473fd2965daccb4b65f83 100644
--- a/src/main/java/com/android/tools/metalava/model/text/TextMethodItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/text/TextMethodItem.kt
@@ -120,6 +120,10 @@ open class TextMethodItem(
 
     override fun typeParameterList(): TypeParameterList = typeParameterList
 
+    override fun typeParameterListOwnerParent(): TypeParameterListOwner? {
+        return containingClass() as TextClassItem?
+    }
+
     override fun resolveParameter(variable: String): TypeParameterItem? {
         for (t in typeParameterList.typeParameters()) {
             if (t.simpleName() == variable) {
diff --git a/src/main/java/com/android/tools/metalava/model/text/TextTypeItem.kt b/src/main/java/com/android/tools/metalava/model/text/TextTypeItem.kt
index 08ef2eecc770a0895c9303fb3102dd29f2c0fb35..d74415b4461c5605d010933bbc5b839ed2d80bcb 100644
--- a/src/main/java/com/android/tools/metalava/model/text/TextTypeItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/text/TextTypeItem.kt
@@ -34,6 +34,7 @@ class TextTypeItem(
     val codebase: TextCodebase,
     val type: String
 ) : TypeItem {
+
     override fun toString(): String = type
 
     override fun toErasedTypeString(context: Item?): String {
diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties
index 2836f16557ab8617bed31b0f06495288434dddbd..7495ade0b2840f859dded608ee369f4eae91fc4f 100644
--- a/src/main/resources/version.properties
+++ b/src/main/resources/version.properties
@@ -2,4 +2,4 @@
 # Version definition
 # This file is read by gradle build scripts, but also packaged with metalava
 # as a resource for the Version classes to read.
-metalavaVersion=1.2.1
+metalavaVersion=1.2.2
diff --git a/src/test/java/com/android/tools/metalava/ApiFileTest.kt b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
index 8998f8ce9e4e63b53eed3c01e4419762ab9d5ffa..c96c46de341a1959d47e2f577602724258795b0e 100644
--- a/src/test/java/com/android/tools/metalava/ApiFileTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
@@ -84,6 +84,7 @@ class ApiFileTest : DriverTest() {
     fun `Parameter Names in Java`() {
         // Java code which explicitly specifies parameter names
         check(
+            compatibilityMode = false, // parameter names only in v2
             sourceFiles = *arrayOf(
                 java(
                     """
@@ -115,7 +116,7 @@ class ApiFileTest : DriverTest() {
     fun `Default Values Names in Java`() {
         // Java code which explicitly specifies parameter names
         check(
-            compatibilityMode = false,
+            format = FileFormat.V3,
             sourceFiles = *arrayOf(
                 java(
                     """
@@ -134,6 +135,7 @@ class ApiFileTest : DriverTest() {
                 supportDefaultValue
             ),
             api = """
+                // Signature format: 3.0
                 package test.pkg {
                   public class Foo {
                     ctor public Foo();
@@ -150,6 +152,7 @@ class ApiFileTest : DriverTest() {
     fun `Default Values and Names in Kotlin`() {
         // Kotlin code which explicitly specifies parameter names
         check(
+            format = FileFormat.V3,
             compatibilityMode = false,
             sourceFiles = *arrayOf(
                 kotlin(
@@ -193,7 +196,7 @@ class ApiFileTest : DriverTest() {
                 )
             ),
             api = """
-                // Signature format: $CURRENT_SIGNATURE_FORMAT
+                // Signature format: 3.0
                 package test.pkg {
                   public final class Foo {
                     ctor public Foo();
@@ -221,7 +224,7 @@ class ApiFileTest : DriverTest() {
         // Testing trickier default values; regression test for problem
         // observed in androidx.core.util with LruCache
         check(
-            compatibilityMode = false,
+            format = FileFormat.V3,
             sourceFiles = *arrayOf(
                 kotlin(
                     """
@@ -278,7 +281,7 @@ class ApiFileTest : DriverTest() {
                 androidxNonNullSource
             ),
             api = """
-                // Signature format: $CURRENT_SIGNATURE_FORMAT
+                // Signature format: 3.0
                 package androidx.core.util {
                   public final class TestKt {
                     ctor public TestKt();
@@ -295,6 +298,8 @@ class ApiFileTest : DriverTest() {
     @Test
     fun `Basic Kotlin class`() {
         check(
+            format = FileFormat.V1,
+            extraArguments = arrayOf("--parameter-names=true"),
             sourceFiles = *arrayOf(
                 kotlin(
                     """
@@ -431,6 +436,7 @@ class ApiFileTest : DriverTest() {
     @Test
     fun `Kotlin Reified Methods 2`() {
         check(
+            compatibilityMode = false,
             sourceFiles = *arrayOf(
                 kotlin(
                     """
@@ -465,6 +471,7 @@ class ApiFileTest : DriverTest() {
     @Test
     fun `Suspend functions`() {
         check(
+            compatibilityMode = false,
             sourceFiles = *arrayOf(
                 kotlin(
                     """
@@ -477,7 +484,7 @@ class ApiFileTest : DriverTest() {
                 package test.pkg {
                   public final class TestKt {
                     ctor public TestKt();
-                    method public static suspend inline java.lang.Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit> p);
+                    method public static suspend inline Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit> p);
                   }
                 }
                 """,
@@ -488,6 +495,7 @@ class ApiFileTest : DriverTest() {
     @Test
     fun `Kotlin Generics`() {
         check(
+            format = FileFormat.V3,
             sourceFiles = *arrayOf(
                 kotlin(
                     """
@@ -500,7 +508,9 @@ class ApiFileTest : DriverTest() {
                     """
                 )
             ),
+            compatibilityMode = false,
             api = """
+                // Signature format: 3.0
                 package test.pkg {
                   public final class Bar {
                     ctor public Bar();
@@ -731,6 +741,7 @@ class ApiFileTest : DriverTest() {
     fun `JvmOverloads`() {
         // Regression test for https://github.com/android/android-ktx/issues/366
         check(
+            format = FileFormat.V3,
             compatibilityMode = false,
             sourceFiles = *arrayOf(
                 kotlin(
@@ -762,6 +773,7 @@ class ApiFileTest : DriverTest() {
                 )
             ),
             api = """
+                // Signature format: 3.0
                 package androidx.content {
                   public final class TestKt {
                     ctor public TestKt();
@@ -2715,26 +2727,26 @@ class ApiFileTest : DriverTest() {
             privateApi = """
                 package test.pkg {
                   public class Class1 implements test.pkg.MyInterface {
-                    ctor  Class1(int);
+                    ctor Class1(int);
                     method public void method1();
-                    method  void method2();
+                    method void method2();
                     method private void method3();
-                    method  void myVarargsMethod(int, java.lang.String...);
-                    field  int field3;
-                    field  float[][] field4;
-                    field  long[] field5;
+                    method void myVarargsMethod(int, java.lang.String...);
+                    field int field3;
+                    field float[][] field4;
+                    field long[] field5;
                     field private int field6;
                   }
-                   class Class2 {
-                    ctor  Class2();
+                  class Class2 {
+                    ctor Class2();
                     method public void method4();
                   }
                   private class Class2.Class3 {
                     ctor private Class2.Class3();
                     method public void method5();
                   }
-                   class Class4 {
-                    ctor  Class4();
+                  class Class4 {
+                    ctor Class4();
                     method public void method5();
                   }
                   public abstract interface MyInterface {
@@ -2835,7 +2847,7 @@ class ApiFileTest : DriverTest() {
             privateApi = """
                 package test.pkg {
                   public class Class1 extends test.pkg.PrivateParent implements test.pkg.MyInterface {
-                    ctor  Class1(int);
+                    ctor Class1(int);
                   }
                   private abstract class Class1.AmsTask extends java.util.concurrent.FutureTask {
                   }
@@ -2847,9 +2859,9 @@ class ApiFileTest : DriverTest() {
                     enum_constant public static final test.pkg.MyEnum BAR;
                     enum_constant public static final test.pkg.MyEnum FOO;
                   }
-                   class PrivateParent {
-                    ctor  PrivateParent();
-                    method  final java.lang.String getValue();
+                  class PrivateParent {
+                    ctor PrivateParent();
+                    method final java.lang.String getValue();
                   }
                 }
                 """,
@@ -2979,4 +2991,129 @@ class ApiFileTest : DriverTest() {
             checkDoclava1 = false // doclava is unaware of @suppress
         )
     }
+
+    @Test
+    fun `Check skipping implicit final or deprecated override`() {
+        // Regression test for 122358225
+        check(
+            compatibilityMode = false,
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                    package test.pkg;
+
+                    public class Parent {
+                        public void foo1() { }
+                        public void foo2() { }
+                        public void foo3() { }
+                        public void foo4() { }
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package test.pkg;
+
+                    public final class Child1 extends Parent {
+                        private Child1() { }
+                        public final void foo1() { }
+                        public void foo2() { }
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package test.pkg;
+
+                    /** @deprecated */
+                    @Deprecated
+                    public final class Child2 extends Parent {
+                        private Child2() { }
+                        /** @deprecated */
+                        @Deprecated
+                        public void foo3() { }
+                        public void foo4() { }
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package test.pkg;
+
+                    /** @deprecated */
+                    @Deprecated
+                    public final class Child3 extends Parent {
+                        private Child3() { }
+                        public final void foo1() { }
+                        public void foo2() { }
+                        /** @deprecated */
+                        @Deprecated
+                        public void foo3() { }
+                        /** @deprecated */
+                        @Deprecated
+                        public final void foo4() { }
+                    }
+                    """
+                )
+            ),
+            api = """
+                package test.pkg {
+                  public final class Child1 extends test.pkg.Parent {
+                  }
+                  @Deprecated public final class Child2 extends test.pkg.Parent {
+                  }
+                  @Deprecated public final class Child3 extends test.pkg.Parent {
+                  }
+                  public class Parent {
+                    ctor public Parent();
+                    method public void foo1();
+                    method public void foo2();
+                    method public void foo3();
+                    method public void foo4();
+                  }
+                }
+                """
+        )
+    }
+
+    @Test
+    fun `Ignore synchronized differences`() {
+        check(
+            compatibilityMode = false,
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                    package test.pkg2;
+
+                    public class Parent {
+                        public void foo1() { }
+                        public synchronized void foo2() { }
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package test.pkg2;
+
+                    public class Child1 extends Parent {
+                        private Child1() { }
+                        public synchronized void foo1() { }
+                        public void foo2() { }
+                    }
+                    """
+                )
+            ),
+            api = """
+                package test.pkg2 {
+                  public class Child1 extends test.pkg2.Parent {
+                  }
+                  public class Parent {
+                    ctor public Parent();
+                    method public void foo1();
+                    method public void foo2();
+                  }
+                }
+                """
+        )
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt b/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt
index 1b0fc39ade2bb9e79f722bbfc9075fff426feaab..deb072b657356dd1b844f79cd7600e2167f35ed5 100644
--- a/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt
@@ -45,7 +45,7 @@ class ApiFromTextTest : DriverTest() {
     @Test
     fun `Handle lambas as default values`() {
         val source = """
-            // Signature format: $CURRENT_SIGNATURE_FORMAT
+            // Signature format: 3.0
             package androidx.collection {
               public final class LruCacheKt {
                 ctor public LruCacheKt();
@@ -55,6 +55,7 @@ class ApiFromTextTest : DriverTest() {
         """
 
         check(
+            format = FileFormat.V3,
             compatibilityMode = false,
             inputKotlinStyleNulls = true,
             signatureSource = source,
@@ -66,7 +67,7 @@ class ApiFromTextTest : DriverTest() {
     @Test
     fun `Handle enum constants as default values`() {
         val source = """
-            // Signature format: $CURRENT_SIGNATURE_FORMAT
+            // Signature format: 3.0
             package test.pkg {
               public final class Foo {
                 ctor public Foo();
@@ -89,6 +90,7 @@ class ApiFromTextTest : DriverTest() {
             """
 
         check(
+            format = FileFormat.V3,
             compatibilityMode = false,
             inputKotlinStyleNulls = true,
             signatureSource = source,
@@ -100,7 +102,7 @@ class ApiFromTextTest : DriverTest() {
     @Test
     fun `Handle complex expressions as default values`() {
         val source = """
-            // Signature format: $CURRENT_SIGNATURE_FORMAT
+            // Signature format: 3.0
             package androidx.paging {
               public final class PagedListConfigKt {
                 ctor public PagedListConfigKt();
@@ -122,6 +124,7 @@ class ApiFromTextTest : DriverTest() {
         """
 
         check(
+            format = FileFormat.V3,
             compatibilityMode = false,
             inputKotlinStyleNulls = true,
             signatureSource = source,
@@ -286,7 +289,7 @@ class ApiFromTextTest : DriverTest() {
                 package test.pkg {
                   public deprecated class MyTest {
                     ctor public deprecated MyTest(int, int);
-                    method public static final deprecated void edit(android.content.SharedPreferences, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit> action);
+                    method public static final deprecated void edit(android.content.SharedPreferences, kotlin.jvm.functions.Function1<? super android.content.SharedPreferences.Editor,kotlin.Unit>);
                     field public static deprecated java.util.List<java.lang.String> LIST;
                   }
                 }
@@ -387,7 +390,7 @@ class ApiFromTextTest : DriverTest() {
                   }
                   protected static abstract deprecated interface Foo.Inner3 {
                     method public default void method3();
-                    method public static void method4(int);
+                    method public static abstract void method4(int);
                   }
                 }
                 """
@@ -421,20 +424,19 @@ class ApiFromTextTest : DriverTest() {
     fun `Loading a signature file with annotations on classes, fields, methods and parameters`() {
         @Language("TEXT")
         val source = """
+                // Signature format: 3.0
                 package test.pkg {
-                  @androidx.annotation.UiThread public class MyTest {
+                  @UiThread public class MyTest {
                     ctor public MyTest();
-                    method @androidx.annotation.IntRange(from=10, to=20) public int clamp(int);
-                    method public java.lang.Double? convert(java.lang.Float myPublicName);
-                    field public java.lang.Number? myNumber;
+                    method @IntRange(from=10, to=20) public int clamp(int);
+                    method public Double? convert(Float myPublicName);
+                    field public Number? myNumber;
                   }
                 }
                 """
 
         check(
-            compatibilityMode = false,
-            inputKotlinStyleNulls = true,
-            omitCommonPackages = false,
+            format = FileFormat.V3,
             signatureSource = source,
             api = source
         )
@@ -509,7 +511,7 @@ class ApiFromTextTest : DriverTest() {
                   }
                 }
                 package test.pkg {
-                  public static final class Foo extends java.lang.Enum {
+                  public final class Foo extends java.lang.Enum {
                     enum_constant public static final test.pkg.Foo A;
                     enum_constant public static final test.pkg.Foo B;
                   }
@@ -551,22 +553,21 @@ class ApiFromTextTest : DriverTest() {
     fun `Loading a signature file with default values`() {
         @Language("TEXT")
         val source = """
+                // Signature format: 3.0
                 package test.pkg {
                   public final class Foo {
                     ctor public Foo();
-                    method public final void error(int p = 42, java.lang.Integer? int2 = null);
+                    method public final void error(int p = 42, Integer? int2 = null);
                   }
                   public class Foo2 {
                     ctor public Foo2();
-                    method public void foo(java.lang.String! = null, java.lang.String! = "(Hello) World", int = 42);
+                    method public void foo(String! = null, String! = "(Hello) World", int = 42);
                   }
                 }
                 """
 
         check(
-            compatibilityMode = false,
-            inputKotlinStyleNulls = true,
-            omitCommonPackages = false,
+            format = FileFormat.V3,
             signatureSource = source,
             api = source
         )
@@ -575,6 +576,7 @@ class ApiFromTextTest : DriverTest() {
     @Test
     fun `Signatures with default annotation method values`() {
         val source = """
+                // Signature format: 3.0
                 package libcore.util {
                   public @interface NonNull {
                     method public abstract int from() default java.lang.Integer.MIN_VALUE;
@@ -586,8 +588,7 @@ class ApiFromTextTest : DriverTest() {
                 """
 
         check(
-            inputKotlinStyleNulls = true,
-            compatibilityMode = false,
+            format = FileFormat.V3,
             signatureSource = source,
             api = source
         )
@@ -596,6 +597,7 @@ class ApiFromTextTest : DriverTest() {
     @Test
     fun `Signatures with many annotations`() {
         val source = """
+            // Signature format: 2.0
             package libcore.util {
               @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public @interface NonNull {
                 method public abstract int from() default java.lang.Integer.MIN_VALUE;
@@ -611,9 +613,9 @@ class ApiFromTextTest : DriverTest() {
         """
 
         check(
+            format = FileFormat.V2,
             compatibilityMode = false,
             signatureSource = source,
-            outputKotlinStyleNulls = false,
             api = source
         )
     }
@@ -621,19 +623,20 @@ class ApiFromTextTest : DriverTest() {
     @Test
     fun `Kotlin Properties`() {
         val source = """
+                // Signature format: 2.0
                 package test.pkg {
                   public final class Kotlin {
-                    ctor public Kotlin(java.lang.String property1, int arg2);
-                    method public java.lang.String getProperty1();
-                    method public java.lang.String getProperty2();
-                    method public void setProperty2(java.lang.String p);
-                    property public final java.lang.String property2;
+                    ctor public Kotlin(String property1, int arg2);
+                    method public String getProperty1();
+                    method public String getProperty2();
+                    method public void setProperty2(String p);
+                    property public final String property2;
                   }
                 }
                 """
 
         check(
-            compatibilityMode = true,
+            format = FileFormat.V2,
             signatureSource = source,
             api = source
         )
@@ -716,10 +719,10 @@ class ApiFromTextTest : DriverTest() {
                 package test.pkg {
                   public final class TestKt {
                     ctor public TestKt();
-                    method public static inline <T> void a(T t);
-                    method public static inline <reified T> void b(T t);
-                    method public static inline <reified T> void e(T t);
-                    method public static inline <reified T> void f(T, T t);
+                    method public static inline <T> void a(T);
+                    method public static inline <reified T> void b(T);
+                    method public static inline <reified T> void e(T);
+                    method public static inline <reified T> void f(T, T);
                   }
                 }
                 """
@@ -737,7 +740,7 @@ class ApiFromTextTest : DriverTest() {
                 package test.pkg {
                   public final class TestKt {
                     ctor public TestKt();
-                    method public static suspend inline java.lang.Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit> p);
+                    method public static suspend inline java.lang.Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit>);
                   }
                 }
                 """
diff --git a/src/test/java/com/android/tools/metalava/BaselineTest.kt b/src/test/java/com/android/tools/metalava/BaselineTest.kt
index fed625a971260e218011bc49c4ceef986960de79..c836c690532a07b4800b6fd96522191e48afeb0c 100644
--- a/src/test/java/com/android/tools/metalava/BaselineTest.kt
+++ b/src/test/java/com/android/tools/metalava/BaselineTest.kt
@@ -35,6 +35,7 @@ class BaselineTest : DriverTest() {
                 "ReferencesHidden"
             ),
             baseline = """
+                // Baseline format: 1.0
                 BothPackageInfoAndHtml: test/visible/package-info.java:
                     It is illegal to provide both a package-info.java file and a package.html file for the same package
                 IgnoringSymlink: test/pkg/sub1/sub2/sub3:
@@ -154,4 +155,82 @@ class BaselineTest : DriverTest() {
             checkDoclava1 = false
         )
     }
+
+    @Test
+    fun `Check baseline with show annotations`() {
+        // When using show annotations we should only reference errors that are present in the delta
+        check(
+            includeSystemApiAnnotations = true,
+            extraArguments = arrayOf(
+                ARG_SHOW_ANNOTATION, "android.annotation.TestApi",
+                ARG_HIDE_PACKAGE, "android.annotation",
+                ARG_HIDE_PACKAGE, "android.support.annotation",
+                ARG_API_LINT
+            ),
+            baseline = """
+                // Baseline format: 1.0
+                PairedRegistration: android.pkg.RegistrationMethods#registerUnpaired2Callback(Runnable):
+                    Found registerUnpaired2Callback but not unregisterUnpaired2Callback in android.pkg.RegistrationMethods
+            """,
+            updateBaseline = true,
+            warnings = "",
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                    package android.pkg;
+                    import android.annotation.TestApi;
+
+                    public class RegistrationMethods {
+                        // Here we have 3 sets of correct registrations: both register and unregister are
+                        // present from the full API that is part of the Test API.
+                        // There is also 2 incorrect registrations: one in the base API and one in the test
+                        // API.
+                        //
+                        // In the test API, we expect to only see the single missing test API one. The missing
+                        // base one should only be in the base baseline.
+                        //
+                        // Furthermore, this test makes sure that when the set of methods to be consulted spans
+                        // the two APIs (one hidden, one not) the code correctly finds both; e.g. iteration of
+                        // the test API does see the full set of methods even if they're left out of the
+                        // signature files and baselines.
+
+                        public void registerOk1Callback(Runnable r) { }
+                        public void unregisterOk1Callback(Runnable r) { }
+
+                        /** @hide */
+                        @TestApi
+                        public void registerOk2Callback(Runnable r) { }
+                        /** @hide */
+                        @TestApi
+                        public void unregisterOk2Callback(Runnable r) { }
+
+                        // In the Test API, both methods are present
+                        public void registerOk3Callback(Runnable r) { }
+                        /** @hide */
+                        @TestApi
+                        public void unregisterOk3Callback(Runnable r) { }
+
+                        public void registerUnpaired1Callback(Runnable r) { }
+
+                        /** @hide */
+                        @TestApi
+                        public void registerUnpaired2Callback(Runnable r) { }
+                    }
+                    """
+                ),
+                testApiSource
+            ),
+            api = """
+                package android.pkg {
+                  public class RegistrationMethods {
+                    method public void registerOk2Callback(java.lang.Runnable);
+                    method public void registerUnpaired2Callback(java.lang.Runnable);
+                    method public void unregisterOk2Callback(java.lang.Runnable);
+                    method public void unregisterOk3Callback(java.lang.Runnable);
+                  }
+                }
+                """,
+            checkDoclava1 = false
+        )
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt b/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
index 1853c57d3954bb0117f302ce96df1a73bcaa9770..d1aa93154befa3396cff152156d439a0836ad5a9 100644
--- a/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
+++ b/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
@@ -2115,14 +2115,13 @@ CompatibilityCheckTest : DriverTest() {
             compatibilityMode = true,
             inputKotlinStyleNulls = true,
             checkCompatibilityApi = """
-                // Signature format: 2.0
                 package androidx.annotation {
-                  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.PACKAGE}) public @interface RestrictTo {
+                  public abstract class RestrictTo implements java.lang.annotation.Annotation {
                     method public abstract androidx.annotation.RestrictTo.Scope[] value();
                   }
 
-                  public static enum RestrictTo.Scope {
-                    enum_constant @Deprecated public static final androidx.annotation.RestrictTo.Scope GROUP_ID;
+                  public static final class RestrictTo.Scope extends java.lang.Enum {
+                    enum_constant deprecated public static final androidx.annotation.RestrictTo.Scope GROUP_ID;
                     enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY;
                     enum_constant public static final androidx.annotation.RestrictTo.Scope LIBRARY_GROUP;
                     enum_constant public static final androidx.annotation.RestrictTo.Scope SUBCLASSES;
diff --git a/src/test/java/com/android/tools/metalava/ConvertTest.kt b/src/test/java/com/android/tools/metalava/ConvertTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e4a71f73ecc2996b247dbaca9ee4172135f6b9f9
--- /dev/null
+++ b/src/test/java/com/android/tools/metalava/ConvertTest.kt
@@ -0,0 +1,778 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.metalava
+
+import org.junit.Test
+
+class ConvertTest : DriverTest() {
+
+    @Test
+    fun `Test conversion flag`() {
+        check(
+            compatibilityMode = true,
+            convertToJDiff = listOf(
+                ConvertData(
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    <api>
+                    <package name="test.pkg"
+                    >
+                    <class name="MyTest1"
+                     extends="java.lang.Object"
+                     abstract="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <constructor name="MyTest1"
+                     type="test.pkg.MyTest1"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </constructor>
+                    </class>
+                    </package>
+                    </api>
+                    """
+                ),
+                ConvertData(
+                    fromApi =
+                    """
+                    package test.pkg {
+                      public class MyTest2 {
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    <api>
+                    <package name="test.pkg"
+                    >
+                    <class name="MyTest2"
+                     extends="java.lang.Object"
+                     abstract="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </class>
+                    </package>
+                    </api>
+                    """
+                )
+            )
+        )
+    }
+
+    @Test
+    fun `Test convert new with compat mode and api strip`() {
+        check(
+            compatibilityMode = true,
+            convertToJDiff = listOf(
+                ConvertData(
+                    strip = true,
+                    fromApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        method public java.lang.Double convert(java.lang.Float);
+                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """,
+                    baseApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    <api>
+                    <package name="test.pkg"
+                    >
+                    <class name="MyTest1"
+                     extends="java.lang.Object"
+                     abstract="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <method name="convert"
+                     return="java.lang.Double"
+                     abstract="false"
+                     native="false"
+                     synchronized="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <parameter name="null" type="java.lang.Float">
+                    </parameter>
+                    </method>
+                    </class>
+                    <class name="MyTest2"
+                     extends="java.lang.Object"
+                     abstract="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <constructor name="MyTest2"
+                     type="test.pkg.MyTest2"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </constructor>
+                    <method name="convert"
+                     return="java.lang.Double"
+                     abstract="false"
+                     native="false"
+                     synchronized="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <parameter name="null" type="java.lang.Float">
+                    </parameter>
+                    </method>
+                    </class>
+                    </package>
+                    <package name="test.pkg.new"
+                    >
+                    <interface name="MyInterface"
+                     abstract="true"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </interface>
+                    <class name="MyTest3"
+                     extends="java.lang.Object"
+                     abstract="true"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </class>
+                    <class name="MyTest4"
+                     extends="java.lang.Object"
+                     abstract="true"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <implements name="test.pkg.new.MyInterface">
+                    </implements>
+                    </class>
+                    </package>
+                    </api>
+                    """
+                )
+            )
+        )
+    }
+
+    @Test
+    fun `Test convert new without compat mode and no strip`() {
+        check(
+            compatibilityMode = false,
+            convertToJDiff = listOf(
+                ConvertData(
+                    strip = false,
+                    fromApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        method public java.lang.Double convert(java.lang.Float);
+                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """,
+                    baseApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    <api name="convert-output1">
+                    <package name="test.pkg"
+                    >
+                    <class name="MyTest1"
+                     extends="java.lang.Object"
+                     abstract="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <method name="convert"
+                     return="java.lang.Double"
+                     abstract="false"
+                     native="false"
+                     synchronized="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <parameter name="null" type="java.lang.Float">
+                    </parameter>
+                    </method>
+                    <field name="ANY_CURSOR_ITEM_TYPE"
+                     type="java.lang.String"
+                     transient="false"
+                     volatile="false"
+                     value="&quot;vnd.android.cursor.item/*&quot;"
+                     static="true"
+                     final="true"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </field>
+                    </class>
+                    <class name="MyTest2"
+                     extends="java.lang.Object"
+                     abstract="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <constructor name="MyTest2"
+                     type="test.pkg.MyTest2"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </constructor>
+                    <method name="convert"
+                     return="java.lang.Double"
+                     abstract="false"
+                     native="false"
+                     synchronized="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <parameter name="null" type="java.lang.Float">
+                    </parameter>
+                    </method>
+                    </class>
+                    </package>
+                    <package name="test.pkg.new"
+                    >
+                    <interface name="MyInterface"
+                     abstract="true"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </interface>
+                    <class name="MyTest3"
+                     extends="java.lang.Object"
+                     abstract="true"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <implements name="java.util.List">
+                    </implements>
+                    </class>
+                    <class name="MyTest4"
+                     extends="java.lang.Object"
+                     abstract="true"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <implements name="test.pkg.new.MyInterface">
+                    </implements>
+                    </class>
+                    </package>
+                    </api>
+                    """
+                )
+            )
+        )
+    }
+
+    @Test
+    fun `Test convert nothing new`() {
+        check(
+            expectedOutput = "No API change detected, not generating diff",
+            compatibilityMode = true,
+            convertToJDiff = listOf(
+                ConvertData(
+                    fromApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        method public java.lang.Double convert(java.lang.Float);
+                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public class MyTest3 {
+                      }
+                    }
+                    """,
+                    baseApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        method public java.lang.Double convert(java.lang.Float);
+                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public class MyTest3 {
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    """
+                )
+            )
+        )
+    }
+
+    @Test
+    fun `Test doclava compat`() {
+        // A few more differences
+        check(
+            compatibilityMode = true,
+            convertToJDiff = listOf(
+                ConvertData(
+                    fromApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public void method(java.util.List<String>);
+                        field protected static final java.lang.String CRLF = "\r\n";
+                        field protected static final byte[] CRLF_BYTES;
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    <api>
+                    <package name="test.pkg"
+                    >
+                    <class name="MyTest1"
+                     extends="java.lang.Object"
+                     abstract="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <constructor name="MyTest1"
+                     type="test.pkg.MyTest1"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    </constructor>
+                    <method name="method"
+                     return="void"
+                     abstract="false"
+                     native="false"
+                     synchronized="false"
+                     static="false"
+                     final="false"
+                     deprecated="not deprecated"
+                     visibility="public"
+                    >
+                    <parameter name="null" type="java.util.List&lt;String&gt;">
+                    </parameter>
+                    </method>
+                    <field name="CRLF"
+                     type="java.lang.String"
+                     transient="false"
+                     volatile="false"
+                     value="&quot;\r\n&quot;"
+                     static="true"
+                     final="true"
+                     deprecated="not deprecated"
+                     visibility="protected"
+                    >
+                    </field>
+                    <field name="CRLF_BYTES"
+                     type="byte[]"
+                     transient="false"
+                     volatile="false"
+                     value="null"
+                     static="true"
+                     final="true"
+                     deprecated="not deprecated"
+                     visibility="protected"
+                    >
+                    </field>
+                    </class>
+                    </package>
+                    </api>
+                    """
+                )
+            )
+        )
+    }
+
+    @Test
+    fun `Test convert new to v2 without compat mode and no strip`() {
+        check(
+            compatibilityMode = false,
+            convertToJDiff = listOf(
+                ConvertData(
+                    strip = false,
+                    format = FileFormat.V2,
+                    fromApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        method public java.lang.Double convert(java.lang.Float);
+                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """,
+                    baseApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    // Signature format: 2.0
+                    package test.pkg {
+                      public class MyTest1 {
+                        method public Double convert(Float);
+                        field public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public Double convert(Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """
+                )
+            )
+        )
+    }
+
+    @Test
+    fun `Test convert new to v1 signatures with compat mode and no strip`() {
+        // Output is similar to the v2 format, but with fully qualified java.lang types
+        // and fields not included
+        check(
+            compatibilityMode = false,
+            convertToJDiff = listOf(
+                ConvertData(
+                    strip = false,
+                    format = FileFormat.V1,
+                    fromApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        method public java.lang.Double convert(java.lang.Float);
+                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public abstract interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """,
+                    baseApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public abstract interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """
+                )
+            )
+        )
+    }
+
+    @Test
+    fun `Test convert v2 to v1`() {
+        check(
+            compatibilityMode = false,
+            convertToJDiff = listOf(
+                ConvertData(
+                    strip = false,
+                    format = FileFormat.V1,
+                    fromApi =
+                    """
+                    // Signature format: 2.0
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method @Deprecated public int clamp(int);
+                        method public Double convert(Float);
+                        field public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field @Deprecated public Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public Double convert(Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        method public java.lang.Double convert(java.lang.Float);
+                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public abstract interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """
+                )
+            )
+        )
+    }
+
+    @Test
+    fun `Test convert v1 to v2`() {
+        check(
+            compatibilityMode = false,
+            convertToJDiff = listOf(
+                ConvertData(
+                    strip = false,
+                    format = FileFormat.V2,
+                    fromApi =
+                    """
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method public deprecated int clamp(int);
+                        method public java.lang.Double convert(java.lang.Float);
+                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field public deprecated java.lang.Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public java.lang.Double convert(java.lang.Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public abstract interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """,
+                    outputFile =
+                    """
+                    // Signature format: 2.0
+                    package test.pkg {
+                      public class MyTest1 {
+                        ctor public MyTest1();
+                        method @Deprecated public int clamp(int);
+                        method public Double convert(Float);
+                        field public static final String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
+                        field @Deprecated public Number myNumber;
+                      }
+                      public class MyTest2 {
+                        ctor public MyTest2();
+                        method public Double convert(Float);
+                      }
+                    }
+                    package test.pkg.new {
+                      public interface MyInterface {
+                      }
+                      public abstract class MyTest3 implements java.util.List {
+                      }
+                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
+                      }
+                    }
+                    """
+                )
+            )
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/DriverTest.kt b/src/test/java/com/android/tools/metalava/DriverTest.kt
index 431e4f8178d95dc49730098ef91ee2f1b9d78fad..39a11b12899a5fa47d2407e78e58aa99e502cd45 100644
--- a/src/test/java/com/android/tools/metalava/DriverTest.kt
+++ b/src/test/java/com/android/tools/metalava/DriverTest.kt
@@ -57,6 +57,7 @@ import java.net.URL
 import kotlin.text.Charsets.UTF_8
 
 const val CHECK_OLD_DOCLAVA_TOO = false
+const val CHECK_JDIFF = true
 const val CHECK_STUB_COMPILATION = false
 
 abstract class DriverTest {
@@ -180,12 +181,13 @@ abstract class DriverTest {
         return System.getenv("JAVA_HOME")
     }
 
-    /** JDiff file conversion candidates */
+    /** File conversion tasks */
     data class ConvertData(
         val fromApi: String,
-        val toXml: String,
+        val outputFile: String,
         val baseApi: String? = null,
-        val strip: Boolean = true
+        val strip: Boolean = true,
+        val format: FileFormat = FileFormat.JDIFF
     )
 
     protected fun check(
@@ -220,18 +222,18 @@ abstract class DriverTest {
         /** Whether the stubs should be written as documentation stubs instead of plain stubs. Decides
          * whether the stubs include @doconly elements, uses rewritten/migration annotations, etc */
         docStubs: Boolean = false,
+        /** Signature file format */
+        format: FileFormat? = null,
         /** Whether to run in doclava1 compat mode */
-        compatibilityMode: Boolean = true,
+        compatibilityMode: Boolean = format == null || format == FileFormat.V1,
         /** Whether to trim the output (leading/trailing whitespace removal) */
         trim: Boolean = true,
         /** Whether to remove blank lines in the output (the signature file usually contains a lot of these) */
         stripBlankLines: Boolean = true,
         /** Warnings expected to be generated when analyzing these sources */
         warnings: String? = "",
-        /** Signature file format */
-        format: Int? = null,
         /** Whether to run doclava1 on the test output and assert that the output is identical */
-        checkDoclava1: Boolean = compatibilityMode && (format == null || format < 2),
+        checkDoclava1: Boolean = compatibilityMode && (format == null || format == FileFormat.V1),
         checkCompilation: Boolean = false,
         /** Annotations to merge in (in .xml format) */
         @Language("XML") mergeXmlAnnotations: String? = null,
@@ -266,7 +268,7 @@ abstract class DriverTest {
         /** Additional arguments to supply */
         extraArguments: Array<String> = emptyArray(),
         /** Whether we should emit Kotlin-style null signatures */
-        outputKotlinStyleNulls: Boolean = !compatibilityMode,
+        outputKotlinStyleNulls: Boolean = format != null && format.useKotlinStyleNulls(),
         /** Whether we should interpret API files being read as having Kotlin-style nullness types */
         inputKotlinStyleNulls: Boolean = false,
         /** Whether we should omit java.lang. etc from signature files */
@@ -716,31 +718,45 @@ abstract class DriverTest {
             emptyArray()
         }
 
-        val convertToJDiffFiles = mutableListOf<Options.ConvertFile>()
-        val convertToJDiffArgs = if (convertToJDiff.isNotEmpty()) {
+        val convertFiles = mutableListOf<Options.ConvertFile>()
+        val convertArgs = if (convertToJDiff.isNotEmpty()) {
             val args = mutableListOf<String>()
             var index = 1
             for (convert in convertToJDiff) {
                 val signature = convert.fromApi
                 val base = convert.baseApi
-                val convertSig = temporaryFolder.newFile("jdiff-signatures$index.txt")
+                val convertSig = temporaryFolder.newFile("convert-signatures$index.txt")
                 convertSig.writeText(signature.trimIndent(), Charsets.UTF_8)
-                val output = temporaryFolder.newFile("jdiff-output$index.xml")
+                val extension = convert.format.preferredExtension()
+                val output = temporaryFolder.newFile("convert-output$index$extension")
                 val baseFile = if (base != null) {
-                    val baseFile = temporaryFolder.newFile("jdiff-signatures$index-base.txt")
+                    val baseFile = temporaryFolder.newFile("convert-signatures$index-base.txt")
                     baseFile.writeText(base.trimIndent(), Charsets.UTF_8)
                     baseFile
                 } else {
                     null
                 }
-                convertToJDiffFiles += Options.ConvertFile(convertSig, output, baseFile, strip = true)
+                convertFiles += Options.ConvertFile(convertSig, output, baseFile,
+                    strip = true, outputFormat = convert.format)
                 index++
 
                 if (baseFile != null) {
-                    args += if (convert.strip) "-new_api" else ARG_CONVERT_NEW_TO_JDIFF
+                    args +=
+                        when {
+                            convert.format == FileFormat.V1 -> ARG_CONVERT_NEW_TO_V1
+                            convert.format == FileFormat.V2 -> ARG_CONVERT_NEW_TO_V2
+                            convert.strip -> "-new_api"
+                            else -> ARG_CONVERT_NEW_TO_JDIFF
+                        }
                     args += baseFile.path
                 } else {
-                    args += if (convert.strip) "-convert2xml" else ARG_CONVERT_TO_JDIFF
+                    args +=
+                        when {
+                            convert.format == FileFormat.V1 -> ARG_CONVERT_TO_V1
+                            convert.format == FileFormat.V2 -> ARG_CONVERT_TO_V2
+                            convert.strip -> "-convert2xml"
+                            else -> ARG_CONVERT_TO_JDIFF
+                        }
                 }
                 args += convertSig.path
                 args += output.path
@@ -882,7 +898,7 @@ abstract class DriverTest {
         }
 
         val signatureFormatArgs = if (format != null) {
-            arrayOf("$ARG_FORMAT=v$format")
+            arrayOf(format.outputFlag())
         } else {
             emptyArray()
         }
@@ -945,7 +961,7 @@ abstract class DriverTest {
             *checkCompatibilityRemovedReleasedArguments,
             *proguardKeepArguments,
             *manifestFileArgs,
-            *convertToJDiffArgs,
+            *convertArgs,
             *applyApiLevelsXmlArgs,
             *baselineArgs,
             *showAnnotationArguments,
@@ -997,11 +1013,12 @@ abstract class DriverTest {
             assertEquals(stripComments(baseline, stripLineComments = false).trimIndent(), actualText)
         }
 
-        if (convertToJDiffFiles.isNotEmpty()) {
+        if (convertFiles.isNotEmpty()) {
             for (i in 0 until convertToJDiff.size) {
-                val expected = convertToJDiff[i].toXml
-                val converted = convertToJDiffFiles[i].toXmlFile
+                val expected = convertToJDiff[i].outputFile
+                val converted = convertFiles[i].outputFile
                 if (convertToJDiff[i].baseApi != null &&
+                    compatibilityMode &&
                     actualOutput.contains("No API change detected, not generating diff")) {
                     continue
                 }
@@ -1278,11 +1295,18 @@ abstract class DriverTest {
             assertEquals(stripComments(apiXml, stripLineComments = false).trimIndent(), actualText)
         }
 
+        if (CHECK_JDIFF && apiXmlFile != null && convertToJDiff.isNotEmpty()) {
+            // Parse the XML file with jdiff too
+        }
+
         if (CHECK_OLD_DOCLAVA_TOO && checkDoclava1 && convertToJDiff.isNotEmpty()) {
             var index = 1
             for (convert in convertToJDiff) {
+                if (convert.format != FileFormat.JDIFF) {
+                    continue
+                }
                 val signature = convert.fromApi
-                val expectedXml = convert.toXml
+                val expectedXml = convert.outputFile
                 val base = convert.baseApi
                 val strip = convert.strip
                 val convertSig = temporaryFolder.newFile("doclava-jdiff-signatures$index.txt")
diff --git a/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt b/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
index 9dcdd219b37c949f5987e61c1e7e2d0e148b6800..bad1733519bae0fd7cbbe3a1dcf3961ee35ae9a7 100644
--- a/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
+++ b/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.tools.metalava
 
-import org.junit.Ignore
 import org.junit.Test
 
 @SuppressWarnings("ALL") // Sample code
@@ -343,7 +342,6 @@ class ExtractAnnotationsTest : DriverTest() {
         )
     }
 
-    @Ignore("Not working reliably -- fails when run and passes when debugged...")
     @Test
     fun `Include merged annotations in exported source annotations`() {
         check(
@@ -352,7 +350,7 @@ class ExtractAnnotationsTest : DriverTest() {
             outputKotlinStyleNulls = false,
             includeSystemApiAnnotations = false,
             omitCommonPackages = false,
-            warnings = "error: Unexpected reference to Nonexistent.Field [AnnotationExtraction]",
+            warnings = "error: Unexpected reference to Nonexistent.Field [InternalError]",
             sourceFiles = *arrayOf(
                 java(
                     """
@@ -361,6 +359,17 @@ class ExtractAnnotationsTest : DriverTest() {
                     public class MyTest {
                         public void test(int arg) { }
                     }"""
+                ),
+                java(
+                    """
+                        package java.util;
+                        public class Calendar {
+                            public static final int ERA = 1;
+                            public static final int YEAR = 2;
+                            public static final int MONTH = 3;
+                            public static final int WEEK_OF_YEAR = 4;
+                        }
+                    """
                 )
             ),
             mergeXmlAnnotations = """<?xml version="1.0" encoding="UTF-8"?>
diff --git a/src/test/java/com/android/tools/metalava/FileFormatTest.kt b/src/test/java/com/android/tools/metalava/FileFormatTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..98de4d27dd889e9b008ec2563d4969b104ff75ac
--- /dev/null
+++ b/src/test/java/com/android/tools/metalava/FileFormatTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.metalava
+
+import org.junit.Assert.assertSame
+import org.junit.Test
+
+class FileFormatTest {
+    @Test
+    fun `Check format parsing`() {
+        assertSame(FileFormat.V1, FileFormat.parseHeader("""
+                package test.pkg {
+                  public class MyTest {
+                    ctor public MyTest();
+                  }
+                }
+        """.trimIndent()))
+
+        assertSame(FileFormat.V2, FileFormat.parseHeader("""
+            // Signature format: 2.0
+            package libcore.util {
+              @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public @interface NonNull {
+                method public abstract int from() default java.lang.Integer.MIN_VALUE;
+                method public abstract int to() default java.lang.Integer.MAX_VALUE;
+              }
+            }
+        """.trimIndent()))
+
+        assertSame(FileFormat.V3, FileFormat.parseHeader("""
+            // Signature format: 3.0
+            package androidx.collection {
+              public final class LruCacheKt {
+                ctor public LruCacheKt();
+              }
+            }
+        """.trimIndent()))
+
+        assertSame(FileFormat.V2, FileFormat.parseHeader(
+            "// Signature format: 2.0\r\n" +
+                "package libcore.util {\\r\n" +
+                "  @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public @interface NonNull {\r\n" +
+                "    method public abstract int from() default java.lang.Integer.MIN_VALUE;\r\n" +
+                "    method public abstract int to() default java.lang.Integer.MAX_VALUE;\r\n" +
+                "  }\r\n" +
+                "}\r\n"))
+
+        assertSame(FileFormat.BASELINE, FileFormat.parseHeader("""
+                // Baseline format: 1.0
+                BothPackageInfoAndHtml: test/visible/package-info.java:
+                    It is illegal to provide both a package-info.java file and a package.html file for the same package
+                IgnoringSymlink: test/pkg/sub1/sub2/sub3:
+                    Ignoring symlink during package.html discovery directory traversal
+        """.trimIndent()))
+
+        assertSame(FileFormat.JDIFF, FileFormat.parseHeader("""
+            <api>
+            <package name="test.pkg"
+            >
+            </api>
+        """.trimIndent()))
+
+        assertSame(FileFormat.JDIFF, FileFormat.parseHeader("""
+            <?xml version="1.0" encoding="utf-8"?>
+            <api>
+            <package name="test.pkg"
+            >
+            </api>
+        """.trimIndent()))
+
+        assertSame(FileFormat.SINCE_XML, FileFormat.parseHeader("""
+            <?xml version="1.0" encoding="utf-8"?>
+            <api version="2">
+                <class name="android/hardware/Camera" since="1" deprecated="21">
+                    <method name="&lt;init>()V"/>
+                    <method name="addCallbackBuffer([B)V" since="8"/>
+                    <method name="getLogo()Landroid/graphics/drawable/Drawable;"/>
+                    <field name="ACTION_NEW_VIDEO" since="14" deprecated="25"/>
+                </class>
+            </api>
+        """.trimIndent()))
+
+        assertSame(FileFormat.UNKNOWN, FileFormat.parseHeader("""
+            blah blah
+        """.trimIndent()))
+
+        assertSame(FileFormat.UNKNOWN, FileFormat.parseHeader("""
+            <?xml version="1.0" encoding="utf-8"?>
+            <manifest />
+        """.trimIndent()))
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/JDiffXmlTest.kt b/src/test/java/com/android/tools/metalava/JDiffXmlTest.kt
index 2118a9251c464064837a6e05f4ad54aade93d57f..efc9315915b6cf93d7a63a6b67bcad36287e18ff 100644
--- a/src/test/java/com/android/tools/metalava/JDiffXmlTest.kt
+++ b/src/test/java/com/android/tools/metalava/JDiffXmlTest.kt
@@ -111,6 +111,54 @@ class JDiffXmlTest : DriverTest() {
         )
     }
 
+    @Test
+    fun `Abstract interfaces`() {
+        check(
+            compatibilityMode = true,
+            format = FileFormat.V2,
+            signatureSource =
+            """
+            // Signature format: 2.0
+            package test.pkg {
+              public interface MyBaseInterface {
+                method public abstract void fun(int, String);
+              }
+            }
+            """,
+            apiXml =
+            """
+            <api>
+            <package name="test.pkg"
+            >
+            <interface name="MyBaseInterface"
+             abstract="true"
+             static="false"
+             final="false"
+             deprecated="not deprecated"
+             visibility="public"
+            >
+            <method name="fun"
+             return="void"
+             abstract="true"
+             native="false"
+             synchronized="false"
+             static="false"
+             final="false"
+             deprecated="not deprecated"
+             visibility="public"
+            >
+            <parameter name="null" type="int">
+            </parameter>
+            <parameter name="null" type="java.lang.String">
+            </parameter>
+            </method>
+            </interface>
+            </package>
+            </api>
+            """
+        )
+    }
+
     @Test
     fun `Test generics, superclasses and interfaces`() {
         val source = """
@@ -170,6 +218,30 @@ class JDiffXmlTest : DriverTest() {
              deprecated="not deprecated"
              visibility="public"
             >
+            <method name="valueOf"
+             return="test.pkg.Foo"
+             abstract="false"
+             native="false"
+             synchronized="false"
+             static="true"
+             final="false"
+             deprecated="not deprecated"
+             visibility="public"
+            >
+            <parameter name="null" type="java.lang.String">
+            </parameter>
+            </method>
+            <method name="values"
+             return="test.pkg.Foo[]"
+             abstract="false"
+             native="false"
+             synchronized="false"
+             static="true"
+             final="true"
+             deprecated="not deprecated"
+             visibility="public"
+            >
+            </method>
             <constructor name="Foo"
              type="test.pkg.Foo"
              static="false"
@@ -437,6 +509,30 @@ class JDiffXmlTest : DriverTest() {
              deprecated="not deprecated"
              visibility="public"
             >
+            <method name="valueOf"
+             return="test.pkg.Foo"
+             abstract="false"
+             native="false"
+             synchronized="false"
+             static="true"
+             final="false"
+             deprecated="not deprecated"
+             visibility="public"
+            >
+            <parameter name="null" type="java.lang.String">
+            </parameter>
+            </method>
+            <method name="values"
+             return="test.pkg.Foo[]"
+             abstract="false"
+             native="false"
+             synchronized="false"
+             static="true"
+             final="true"
+             deprecated="not deprecated"
+             visibility="public"
+            >
+            </method>
             <constructor name="Foo"
              type="test.pkg.Foo"
              static="false"
@@ -559,75 +655,6 @@ class JDiffXmlTest : DriverTest() {
         )
     }
 
-    @Test
-    fun `Test conversion flag`() {
-        check(
-            compatibilityMode = true,
-            convertToJDiff = listOf(
-                ConvertData(
-                    """
-                    package test.pkg {
-                      public class MyTest1 {
-                        ctor public MyTest1();
-                      }
-                    }
-                    """,
-                    toXml =
-                    """
-                    <api>
-                    <package name="test.pkg"
-                    >
-                    <class name="MyTest1"
-                     extends="java.lang.Object"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <constructor name="MyTest1"
-                     type="test.pkg.MyTest1"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </constructor>
-                    </class>
-                    </package>
-                    </api>
-                    """
-                ),
-                ConvertData(
-                    fromApi =
-                    """
-                    package test.pkg {
-                      public class MyTest2 {
-                      }
-                    }
-                    """,
-                    toXml =
-                    """
-                    <api>
-                    <package name="test.pkg"
-                    >
-                    <class name="MyTest2"
-                     extends="java.lang.Object"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </class>
-                    </package>
-                    </api>
-                    """
-                )
-            )
-        )
-    }
-
     @Test
     fun `Generics in interfaces`() {
         check(
@@ -685,7 +712,7 @@ class JDiffXmlTest : DriverTest() {
             >
             <interface name="AbstractList"
              extends="test.pkg.List&lt;A,B,C>"
-             abstract="false"
+             abstract="true"
              static="false"
              final="false"
              deprecated="not deprecated"
@@ -694,7 +721,7 @@ class JDiffXmlTest : DriverTest() {
             </interface>
             <interface name="ConcreteList"
              extends="test.pkg.AbstractList&lt;D,E,F>"
-             abstract="false"
+             abstract="true"
              static="false"
              final="false"
              deprecated="not deprecated"
@@ -702,7 +729,7 @@ class JDiffXmlTest : DriverTest() {
             >
             </interface>
             <interface name="List"
-             abstract="false"
+             abstract="true"
              static="false"
              final="false"
              deprecated="not deprecated"
@@ -820,427 +847,67 @@ class JDiffXmlTest : DriverTest() {
     }
 
     @Test
-    fun `Test convert new with compat mode and api strip`() {
+    fun `Interface extends, compat mode`() {
         check(
             compatibilityMode = true,
-            convertToJDiff = listOf(
-                ConvertData(
-                    strip = true,
-                    fromApi =
-                    """
-                    package test.pkg {
-                      public class MyTest1 {
-                        ctor public MyTest1();
-                        method public deprecated int clamp(int);
-                        method public java.lang.Double convert(java.lang.Float);
-                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
-                        field public deprecated java.lang.Number myNumber;
-                      }
-                      public class MyTest2 {
-                        ctor public MyTest2();
-                        method public java.lang.Double convert(java.lang.Float);
-                      }
-                    }
-                    package test.pkg.new {
-                      public interface MyInterface {
-                      }
-                      public abstract class MyTest3 implements java.util.List {
-                      }
-                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
-                      }
-                    }
-                    """,
-                    baseApi =
-                    """
-                    package test.pkg {
-                      public class MyTest1 {
-                        ctor public MyTest1();
-                        method public deprecated int clamp(int);
-                        field public deprecated java.lang.Number myNumber;
-                      }
-                    }
-                    """,
-                    toXml =
-                    """
-                    <api>
-                    <package name="test.pkg"
-                    >
-                    <class name="MyTest1"
-                     extends="java.lang.Object"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <method name="convert"
-                     return="java.lang.Double"
-                     abstract="false"
-                     native="false"
-                     synchronized="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <parameter name="null" type="java.lang.Float">
-                    </parameter>
-                    </method>
-                    </class>
-                    <class name="MyTest2"
-                     extends="java.lang.Object"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <constructor name="MyTest2"
-                     type="test.pkg.MyTest2"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </constructor>
-                    <method name="convert"
-                     return="java.lang.Double"
-                     abstract="false"
-                     native="false"
-                     synchronized="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <parameter name="null" type="java.lang.Float">
-                    </parameter>
-                    </method>
-                    </class>
-                    </package>
-                    <package name="test.pkg.new"
-                    >
-                    <interface name="MyInterface"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </interface>
-                    <class name="MyTest3"
-                     extends="java.lang.Object"
-                     abstract="true"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </class>
-                    <class name="MyTest4"
-                     extends="java.lang.Object"
-                     abstract="true"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <implements name="test.pkg.new.MyInterface">
-                    </implements>
-                    </class>
-                    </package>
-                    </api>
-                    """
-                )
-            )
+            format = FileFormat.V1,
+            signatureSource = """
+            // Signature format: 2.0
+            package android.companion {
+              public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable {
+              }
+            }
+            """,
+            apiXml =
+            """
+            <api>
+            <package name="android.companion"
+            >
+            <interface name="DeviceFilter"
+             abstract="true"
+             static="false"
+             final="false"
+             deprecated="not deprecated"
+             visibility="public"
+            >
+            <implements name="android.os.Parcelable">
+            </implements>
+            </interface>
+            </package>
+            </api>
+            """
         )
     }
 
     @Test
-    fun `Test convert new without compat mode and no strip`() {
+    fun `Interface extends, non-compat mode`() {
         check(
             compatibilityMode = false,
-            convertToJDiff = listOf(
-                ConvertData(
-                    strip = false,
-                    fromApi =
-                    """
-                    package test.pkg {
-                      public class MyTest1 {
-                        ctor public MyTest1();
-                        method public deprecated int clamp(int);
-                        method public java.lang.Double convert(java.lang.Float);
-                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
-                        field public deprecated java.lang.Number myNumber;
-                      }
-                      public class MyTest2 {
-                        ctor public MyTest2();
-                        method public java.lang.Double convert(java.lang.Float);
-                      }
-                    }
-                    package test.pkg.new {
-                      public interface MyInterface {
-                      }
-                      public abstract class MyTest3 implements java.util.List {
-                      }
-                      public abstract class MyTest4 implements test.pkg.new.MyInterface {
-                      }
-                    }
-                    """,
-                    baseApi =
-                    """
-                    package test.pkg {
-                      public class MyTest1 {
-                        ctor public MyTest1();
-                        method public deprecated int clamp(int);
-                        field public deprecated java.lang.Number myNumber;
-                      }
-                    }
-                    """,
-                    toXml =
-                    """
-                    <api>
-                    <package name="test.pkg"
-                    >
-                    <class name="MyTest1"
-                     extends="java.lang.Object"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <method name="convert"
-                     return="java.lang.Double"
-                     abstract="false"
-                     native="false"
-                     synchronized="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <parameter name="null" type="java.lang.Float">
-                    </parameter>
-                    </method>
-                    <field name="ANY_CURSOR_ITEM_TYPE"
-                     type="java.lang.String"
-                     transient="false"
-                     volatile="false"
-                     value="&quot;vnd.android.cursor.item/*&quot;"
-                     static="true"
-                     final="true"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </field>
-                    </class>
-                    <class name="MyTest2"
-                     extends="java.lang.Object"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <constructor name="MyTest2"
-                     type="test.pkg.MyTest2"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </constructor>
-                    <method name="convert"
-                     return="java.lang.Double"
-                     abstract="false"
-                     native="false"
-                     synchronized="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <parameter name="null" type="java.lang.Float">
-                    </parameter>
-                    </method>
-                    </class>
-                    </package>
-                    <package name="test.pkg.new"
-                    >
-                    <interface name="MyInterface"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </interface>
-                    <class name="MyTest3"
-                     extends="java.lang.Object"
-                     abstract="true"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <implements name="java.util.List">
-                    </implements>
-                    </class>
-                    <class name="MyTest4"
-                     extends="java.lang.Object"
-                     abstract="true"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <implements name="test.pkg.new.MyInterface">
-                    </implements>
-                    </class>
-                    </package>
-                    </api>
-                    """
-                )
-            )
-        )
-    }
-
-    @Test
-    fun `Test convert nothing new`() {
-        check(
-            expectedOutput = "No API change detected, not generating diff",
-            compatibilityMode = true,
-            convertToJDiff = listOf(
-                ConvertData(
-                    fromApi =
-                    """
-                    package test.pkg {
-                      public class MyTest1 {
-                        ctor public MyTest1();
-                        method public deprecated int clamp(int);
-                        method public java.lang.Double convert(java.lang.Float);
-                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
-                        field public deprecated java.lang.Number myNumber;
-                      }
-                      public class MyTest2 {
-                        ctor public MyTest2();
-                        method public java.lang.Double convert(java.lang.Float);
-                      }
-                    }
-                    package test.pkg.new {
-                      public class MyTest3 {
-                      }
-                    }
-                    """,
-                    baseApi =
-                    """
-                    package test.pkg {
-                      public class MyTest1 {
-                        ctor public MyTest1();
-                        method public deprecated int clamp(int);
-                        method public java.lang.Double convert(java.lang.Float);
-                        field public static final java.lang.String ANY_CURSOR_ITEM_TYPE = "vnd.android.cursor.item/*";
-                        field public deprecated java.lang.Number myNumber;
-                      }
-                      public class MyTest2 {
-                        ctor public MyTest2();
-                        method public java.lang.Double convert(java.lang.Float);
-                      }
-                    }
-                    package test.pkg.new {
-                      public class MyTest3 {
-                      }
-                    }
-                    """,
-                    toXml =
-                    """
-                    """
-                )
-            )
-        )
-    }
-
-    @Test
-    fun `Test doclava compat`() {
-        // A few more differences
-        check(
-            compatibilityMode = true,
-            convertToJDiff = listOf(
-                ConvertData(
-                    fromApi =
-                    """
-                    package test.pkg {
-                      public class MyTest1 {
-                        ctor public MyTest1();
-                        method public void method(java.util.List<String>);
-                        field protected static final java.lang.String CRLF = "\r\n";
-                        field protected static final byte[] CRLF_BYTES;
-                      }
-                    }
-                    """,
-                    toXml =
-                    """
-                    <api>
-                    <package name="test.pkg"
-                    >
-                    <class name="MyTest1"
-                     extends="java.lang.Object"
-                     abstract="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <constructor name="MyTest1"
-                     type="test.pkg.MyTest1"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    </constructor>
-                    <method name="method"
-                     return="void"
-                     abstract="false"
-                     native="false"
-                     synchronized="false"
-                     static="false"
-                     final="false"
-                     deprecated="not deprecated"
-                     visibility="public"
-                    >
-                    <parameter name="null" type="java.util.List&lt;String&gt;">
-                    </parameter>
-                    </method>
-                    <field name="CRLF"
-                     type="java.lang.String"
-                     transient="false"
-                     volatile="false"
-                     value="&quot;\r\n&quot;"
-                     static="true"
-                     final="true"
-                     deprecated="not deprecated"
-                     visibility="protected"
-                    >
-                    </field>
-                    <field name="CRLF_BYTES"
-                     type="byte[]"
-                     transient="false"
-                     volatile="false"
-                     value="null"
-                     static="true"
-                     final="true"
-                     deprecated="not deprecated"
-                     visibility="protected"
-                    >
-                    </field>
-                    </class>
-                    </package>
-                    </api>
-                    """
-                )
-            )
+            format = FileFormat.V2,
+            signatureSource = """
+            // Signature format: 2.0
+            package android.companion {
+              public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable {
+              }
+            }
+            """,
+            apiXml =
+            """
+            <api>
+            <package name="android.companion"
+            >
+            <interface name="DeviceFilter"
+             extends="android.os.Parcelable"
+             abstract="true"
+             static="false"
+             final="false"
+             deprecated="not deprecated"
+             visibility="public"
+            >
+            </interface>
+            </package>
+            </api>
+            """
         )
     }
 }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt b/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
index c43bfc6bb09f7c6d1176cfa85f4aa6c838d6faae..90332ec64124717eb0f022c5f4e79b38c92950d9 100644
--- a/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
+++ b/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
@@ -23,7 +23,7 @@ class NullnessMigrationTest : DriverTest() {
     @Test
     fun `Test Kotlin-style null signatures`() {
         check(
-            compatibilityMode = false,
+            format = FileFormat.V3,
             sourceFiles = *arrayOf(
                 java(
                     """
@@ -42,6 +42,7 @@ class NullnessMigrationTest : DriverTest() {
                 androidxNullableSource
             ),
             api = """
+                // Signature format: 3.0
                 package test.pkg {
                   public class MyTest {
                     ctor public MyTest();
@@ -327,7 +328,7 @@ class NullnessMigrationTest : DriverTest() {
     @Test
     fun `Check type use annotations`() {
         check(
-            format = 2, // compat=false, kotlin-style-nulls=false
+            format = FileFormat.V2, // compat=false, kotlin-style-nulls=false
             sourceFiles = *arrayOf(
                 java(
                     """
diff --git a/src/test/java/com/android/tools/metalava/OptionsTest.kt b/src/test/java/com/android/tools/metalava/OptionsTest.kt
index 8dfcedc3f7ec0b9c396184af600e2ec68a5758e1..dcaf7ee90ae403e3b4ad514c20ad3b89133914a9 100644
--- a/src/test/java/com/android/tools/metalava/OptionsTest.kt
+++ b/src/test/java/com/android/tools/metalava/OptionsTest.kt
@@ -231,6 +231,15 @@ JDiff:
 --convert-new-to-jdiff <old> <new> <xml>  Reads in the given old and new api files,
                                           computes the difference, and writes out only the
                                           new parts of the API in the JDiff XML format.
+--convert-to-v1 <sig> <sig>               Reads in the given signature file and writes it
+                                          out as a signature file in the original
+                                          v1/doclava format.
+--convert-to-v2 <sig> <sig>               Reads in the given signature file and writes it
+                                          out as a signature file in the new signature
+                                          format, v2.
+--convert-new-to-v2 <old> <new> <sig>     Reads in the given old and new api files,
+                                          computes the difference, and writes out only the
+                                          new parts of the API in the v2 format.
 
 Statistics:
 --annotation-coverage-stats               Whether metalava should emit coverage statistics
diff --git a/src/test/java/com/android/tools/metalava/ShowAnnotationTest.kt b/src/test/java/com/android/tools/metalava/ShowAnnotationTest.kt
index 4c17cd51d23f5557087190a6b625410be7c33a72..c2e6832ab1a77423b6e102ce0096756d0bac6d31 100644
--- a/src/test/java/com/android/tools/metalava/ShowAnnotationTest.kt
+++ b/src/test/java/com/android/tools/metalava/ShowAnnotationTest.kt
@@ -246,7 +246,7 @@ class ShowAnnotationTest : DriverTest() {
             ),
             // Empty API: showUnannotated=false
             api = """
-                """,
+                """.trimIndent(),
             includeSystemApiAnnotations = true,
             extraArguments = arrayOf(
                 ARG_SHOW_ANNOTATION, "android.annotation.TestApi",
diff --git a/src/test/java/com/android/tools/metalava/StubsTest.kt b/src/test/java/com/android/tools/metalava/StubsTest.kt
index dbfb6f0654810f4c77ddb1a68b40ace0a32f52d1..e48c39935b30a4a23632d0976af086d24401d473 100644
--- a/src/test/java/com/android/tools/metalava/StubsTest.kt
+++ b/src/test/java/com/android/tools/metalava/StubsTest.kt
@@ -1441,7 +1441,7 @@ class StubsTest : DriverTest() {
                 package test.pkg {
                   public class Foo {
                     ctor public Foo();
-                    method public void foo(int, java.util.Map<java.lang.String, java.lang.String>!);
+                    method public void foo(int, java.util.Map<java.lang.String, java.lang.String>);
                   }
                 }
                 """
@@ -1450,7 +1450,7 @@ class StubsTest : DriverTest() {
                 package test.pkg {
                   public class Foo {
                     ctor public Foo();
-                    method public void foo(int, java.util.Map<java.lang.String,java.lang.String>!);
+                    method public void foo(int, java.util.Map<java.lang.String,java.lang.String>);
                   }
                 }
                 """
@@ -2242,18 +2242,18 @@ class StubsTest : DriverTest() {
                   }
                   public class Generics.MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent<X,Y> implements test.pkg.Generics.PublicInterface<X,Y> {
                     ctor public Generics.MyClass();
-                    method public java.util.Map<X,java.util.Map<Y,java.lang.String>>! createMap(java.util.List<X>!) throws java.io.IOException;
-                    method public java.util.List<X>! foo();
+                    method public java.util.Map<X,java.util.Map<Y,java.lang.String>> createMap(java.util.List<X>) throws java.io.IOException;
+                    method public java.util.List<X> foo();
                   }
                   public static interface Generics.PublicInterface<A, B> {
-                    method public java.util.Map<A,java.util.Map<B,java.lang.String>>! createMap(java.util.List<A>!) throws java.io.IOException;
+                    method public java.util.Map<A,java.util.Map<B,java.lang.String>> createMap(java.util.List<A>) throws java.io.IOException;
                   }
                   public abstract class Generics.PublicParent<A, B extends java.lang.Number> {
                     ctor public Generics.PublicParent();
-                    method protected abstract java.util.List<A>! foo();
+                    method protected abstract java.util.List<A> foo();
                   }
                 }
-                    """,
+                """,
             source = """
                     package test.pkg;
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
@@ -3635,7 +3635,7 @@ class StubsTest : DriverTest() {
             package test.pkg {
               public class Foo {
                 ctor public Foo();
-                method @Deprecated protected boolean inClass(String!);
+                method @Deprecated protected boolean inClass(String);
               }
             }
             """,
diff --git a/src/test/java/com/android/tools/metalava/model/TypeItemTest.kt b/src/test/java/com/android/tools/metalava/model/TypeItemTest.kt
index 6d5cbc867f4eece22558c6aea8c6b78929c4b445..112d51304ecd3259c62bf39fb117917882a2475e 100644
--- a/src/test/java/com/android/tools/metalava/model/TypeItemTest.kt
+++ b/src/test/java/com/android/tools/metalava/model/TypeItemTest.kt
@@ -17,14 +17,14 @@
 package com.android.tools.metalava.model
 
 import com.android.tools.metalava.JAVA_LANG_STRING
-import com.android.tools.metalava.options
+import com.android.tools.metalava.compatibility
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 
 class TypeItemTest {
     @Test
     fun test() {
-        options.omitCommonPackages = true
+        compatibility.omitCommonPackages = true
         assertThat(TypeItem.shortenTypes("@android.support.annotation.Nullable")).isEqualTo("@Nullable")
         assertThat(TypeItem.shortenTypes(JAVA_LANG_STRING)).isEqualTo("String")
         assertThat(TypeItem.shortenTypes("java.lang.reflect.Method")).isEqualTo("java.lang.reflect.Method")
diff --git a/src/test/java/com/android/tools/metalava/model/text/TextTypeItemTest.kt b/src/test/java/com/android/tools/metalava/model/text/TextTypeItemTest.kt
index d11074f11add696045270b03b3fc1d1dafe3e734..476b6548033e422c2edfd7b0e05fd5ad8dd53765 100644
--- a/src/test/java/com/android/tools/metalava/model/text/TextTypeItemTest.kt
+++ b/src/test/java/com/android/tools/metalava/model/text/TextTypeItemTest.kt
@@ -77,7 +77,7 @@ class TextTypeItemTest {
                 method public D build();
               }
             }
-        """, false
+        """.trimIndent(), false
         )
         val cls = codebase.findClass("androidx.navigation.NavDestinationBuilder")
         val method = cls?.findMethod("build", "") as TextMethodItem
@@ -113,7 +113,7 @@ class TextTypeItemTest {
                 method public D build();
               }
             }
-        """, false
+        """.trimIndent(), false
         )
         val cls = codebase.findClass("test.pkg.TestClass")
         val method = cls?.findMethod("build", "") as TextMethodItem
@@ -145,7 +145,7 @@ class TextTypeItemTest {
                 method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
               }
             }
-        """, false
+        """.trimIndent(), false
         )
         val cls = codebase.findClass("test.pkg.EnumMap")
         val method = cls?.findMethod("clone", "") as TextMethodItem
diff --git a/src/test/java/com/android/tools/metalava/model/text/TextTypeParameterItemTest.kt b/src/test/java/com/android/tools/metalava/model/text/TextTypeParameterItemTest.kt
index 9a00d2927801e8c6d4cc42616f7a3647d7826e1f..8dc559cd463fffd8feb41b81a9390ded0278930d 100644
--- a/src/test/java/com/android/tools/metalava/model/text/TextTypeParameterItemTest.kt
+++ b/src/test/java/com/android/tools/metalava/model/text/TextTypeParameterItemTest.kt
@@ -47,7 +47,7 @@ class TextTypeParameterItemTest {
                 method public D build();
               }
             }
-        """, false
+        """.trimIndent(), false
         )
         val cls = codebase.findClass("androidx.navigation.NavDestinationBuilder")
         val method = cls?.findMethod("build", "") as TextMethodItem