diff --git a/API-LINT.md b/API-LINT.md
index 76b2d09277dda64a60a6685a5e4aa7d79a887c41..f32cbb062ce5672704c5242d5a1b50c97df8ebfd 100644
--- a/API-LINT.md
+++ b/API-LINT.md
@@ -44,18 +44,40 @@ it is silently ignored.
 
 You can pass a flag to metalava ("--update-baseline") to tell it to update the
 baseline files with any new errors it comes across instead of reporting
-them. With soong, you'd do something like this:
+them. With soong, if you specify the baseline explicitly, like this:
 
     --- a/Android.bp
     +++ b/Android.bp
     @@ -1678,6 +1678,7 @@ droidstubs {
          },
          api_lint: true,
-         baseline_filename: "baseline.txt",
-    +    update_baseline: true,
+    ==>  baseline_filename: "api/baseline.txt", <==
          jdiff_enabled: true,
      }
 
+then the build system will automatically supply `--update-baseline` on your
+behalf to a generated file in the out/ folder, and if there's a failure metalava
+will tell you where to find the updated baseline which you can then copy into
+place:
+
+    ...
+    93 new API lint issues were found. See tools/metalava/API-LINT.md for how to handle these.
+    ************************************************************
+    Your API changes are triggering API Lint warnings or errors.
+    To make these errors go away, you have two choices:
+
+    1. You can suppress the errors with @SuppressLint("<id>")
+    2. You can update the baseline by executing the following
+       command:
+           cp \
+           out/soong/.intermediates/frameworks/base/system-api-stubs-docs/android_common/api/system-baseline.txt \
+           frameworks/base/api/system-baseline.txt
+       To submit the revised baseline.txt to the main Android
+       repository, you will need approval.
+    ************************************************************
+
+
+
 Then re-run the build and you should now see diffs to the baseline file; git
 diff to make sure you're really only marking the issues you intended to include.
 
diff --git a/Android.bp b/Android.bp
index 669e14a3cb8e78e8e157518c0a464581e4e573dc..c6ad0b3545efd0bffba24e17e9a944a422c30889 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,7 +18,7 @@ java_binary_host {
         "src/main/java/**/*.java",
         "src/main/java/**/*.kt",
     ],
-    java_resources: ["src/main/resources/**/*"],
+    java_resource_dirs: ["src/main/resources/"],
     static_libs: [
         "kotlin-reflect",
         "metalava-tools-common-m2-deps",
diff --git a/build.gradle b/build.gradle
index eedd62afb7b259591a580a5bcf9b253cd010713c..1fb56b1b6507ea6b2384e4893c5240c44b04ade8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
 buildscript {
-    ext.gradle_version = '3.2.1'
-    ext.studio_version = '26.2.1'
-    ext.kotlin_version = '1.3.11'
+    ext.gradle_version = '3.4.0-beta01'
+    ext.studio_version = '26.4.0-beta01'
+    ext.kotlin_version = '1.3.20'
     repositories {
         google()
         jcenter()
@@ -66,7 +66,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.2'
+        classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.4'
     }
 }
 apply plugin: 'com.github.johnrengelman.shadow'
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7e98c337e7d3b987786c1e9498d294ccdc673c90..eb78b8ef41b470cbf6f61351497b5981d1d3008a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -4,4 +4,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-milestone-1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1-all.zip
diff --git a/src/main/java/com/android/tools/metalava/AnnotationsDiffer.kt b/src/main/java/com/android/tools/metalava/AnnotationsDiffer.kt
index d9a276de3a3ee9920491f3d8c6b8b4517b53d553..760ebfbb821e05609c9b977dcf81cfcac7d66e74 100644
--- a/src/main/java/com/android/tools/metalava/AnnotationsDiffer.kt
+++ b/src/main/java/com/android/tools/metalava/AnnotationsDiffer.kt
@@ -26,6 +26,7 @@ import java.io.IOException
 import java.io.PrintWriter
 import java.io.StringWriter
 import java.util.function.Predicate
+import kotlin.text.Charsets.UTF_8
 
 /**
  * The [AnnotationsDiffer] can take a codebase with annotations, and subtract
@@ -135,7 +136,7 @@ class AnnotationsDiffer(
                 }
             }
 
-            apiFile.writeText(cleanedUp, Charsets.UTF_8)
+            apiFile.writeText(cleanedUp, UTF_8)
         } catch (e: IOException) {
             reporter.report(Errors.IO_ERROR, apiFile, "Cannot open file for write.")
         }
diff --git a/src/main/java/com/android/tools/metalava/AnnotationsMerger.kt b/src/main/java/com/android/tools/metalava/AnnotationsMerger.kt
index e7814c1e761679fe3aa0745b1ca42f8bf9740c61..76d430d61207d99750541ffda78e42a2b6bfdd72 100644
--- a/src/main/java/com/android/tools/metalava/AnnotationsMerger.kt
+++ b/src/main/java/com/android/tools/metalava/AnnotationsMerger.kt
@@ -56,13 +56,17 @@ import com.android.tools.metalava.model.ClassItem
 import com.android.tools.metalava.model.Codebase
 import com.android.tools.metalava.model.DefaultAnnotationItem
 import com.android.tools.metalava.model.DefaultAnnotationValue
+import com.android.tools.metalava.model.FieldItem
 import com.android.tools.metalava.model.Item
 import com.android.tools.metalava.model.MethodItem
+import com.android.tools.metalava.model.ModifierList
+import com.android.tools.metalava.model.ParameterItem
+import com.android.tools.metalava.model.TypeItem
 import com.android.tools.metalava.model.parseDocument
 import com.android.tools.metalava.model.psi.PsiAnnotationItem
 import com.android.tools.metalava.model.psi.PsiBasedCodebase
+import com.android.tools.metalava.model.psi.PsiTypeItem
 import com.android.tools.metalava.model.visitors.ApiVisitor
-import com.google.common.base.Charsets
 import com.google.common.io.ByteStreams
 import com.google.common.io.Closeables
 import com.google.common.io.Files
@@ -76,6 +80,7 @@ import java.lang.reflect.Field
 import java.util.jar.JarInputStream
 import java.util.regex.Pattern
 import java.util.zip.ZipEntry
+import kotlin.text.Charsets.UTF_8
 
 /** Merges annotations into classes already registered in the given [Codebase] */
 class AnnotationsMerger(
@@ -152,7 +157,7 @@ class AnnotationsMerger(
             mergeFromJar(file)
         } else if (file.path.endsWith(DOT_XML)) {
             try {
-                val xml = Files.asCharSource(file, Charsets.UTF_8).read()
+                val xml = Files.asCharSource(file, UTF_8).read()
                 mergeAnnotationsXml(file.path, xml)
             } catch (e: IOException) {
                 error("Aborting: I/O problem during transform: " + e.toString())
@@ -182,7 +187,7 @@ class AnnotationsMerger(
             while (entry != null) {
                 if (entry.name.endsWith(".xml")) {
                     val bytes = ByteStreams.toByteArray(zis)
-                    val xml = String(bytes, Charsets.UTF_8)
+                    val xml = String(bytes, UTF_8)
                     mergeAnnotationsXml(jar.path + ": " + entry, xml)
                 }
                 entry = zis.nextEntry
@@ -240,32 +245,70 @@ class AnnotationsMerger(
             override fun compare(old: Item, new: Item) {
                 val newModifiers = new.modifiers
                 for (annotation in old.modifiers.annotations()) {
-                    var addAnnotation = false
-                    if (annotation.isNullnessAnnotation()) {
-                        if (!newModifiers.hasNullnessInfo()) {
-                            addAnnotation = true
-                        }
-                    } else {
-                        // TODO: Check for other incompatibilities than nullness?
-                        val qualifiedName = annotation.qualifiedName() ?: continue
-                        if (newModifiers.findAnnotation(qualifiedName) == null) {
-                            addAnnotation = true
-                        }
+                    mergeAnnotation(annotation, newModifiers, new)
+                }
+            }
+
+            private fun mergeAnnotation(
+                annotation: AnnotationItem,
+                newModifiers: ModifierList,
+                new: Item
+            ) {
+                var addAnnotation = false
+                if (annotation.isNullnessAnnotation()) {
+                    if (!newModifiers.hasNullnessInfo()) {
+                        addAnnotation = true
                     }
+                } else {
+                    // TODO: Check for other incompatibilities than nullness?
+                    val qualifiedName = annotation.qualifiedName() ?: return
+                    if (newModifiers.findAnnotation(qualifiedName) == null) {
+                        addAnnotation = true
+                    }
+                }
 
-                    if (addAnnotation) {
-                        // Don't map annotation names - this would turn newly non null back into non null
-                        new.mutableModifiers().addAnnotation(
-                            new.codebase.createAnnotation(
-                                annotation.toSource(),
-                                new,
-                                mapName = false
-                            )
+                if (addAnnotation) {
+                    // Don't map annotation names - this would turn newly non null back into non null
+                    new.mutableModifiers().addAnnotation(
+                        new.codebase.createAnnotation(
+                            annotation.toSource(),
+                            new,
+                            mapName = false
                         )
+                    )
+                }
+            }
+
+            override fun compare(old: ParameterItem, new: ParameterItem) {
+                mergeTypeAnnotations(old.type(), new)
+            }
+
+            override fun compare(old: FieldItem, new: FieldItem) {
+                mergeTypeAnnotations(old.type(), new)
+            }
+
+            override fun compare(old: MethodItem, new: MethodItem) {
+                mergeTypeAnnotations(old.returnType(), new)
+            }
+
+            // Merge in type annotations
+            private fun mergeTypeAnnotations(
+                typeItem: TypeItem?,
+                new: Item
+            ) {
+                typeItem ?: return
+                val type = (typeItem as? PsiTypeItem)?.psiType ?: return
+                val typeAnnotations = type.annotations
+                if (typeAnnotations.isNotEmpty()) {
+                    for (annotation in typeAnnotations) {
+                        val codebase = new.codebase as PsiBasedCodebase
+                        val annotationItem = PsiAnnotationItem.create(codebase, annotation)
+                        mergeAnnotation(annotationItem, new.modifiers, new)
                     }
                 }
             }
         }
+
         CodebaseComparator().compare(
             visitor, externalCodebase, codebase, ApiPredicate()
         )
diff --git a/src/main/java/com/android/tools/metalava/ApiLint.kt b/src/main/java/com/android/tools/metalava/ApiLint.kt
index abe0b2db9f773c7ce0288b2e60303d92ad446add..26cda4335e8cc84c9b306a6a01a88f2ac3719228 100644
--- a/src/main/java/com/android/tools/metalava/ApiLint.kt
+++ b/src/main/java/com/android/tools/metalava/ApiLint.kt
@@ -197,22 +197,26 @@ class ApiLint(private val codebase: Codebase, private val oldCodebase: Codebase?
         if (apiLintIssues > 0) {
             // We've reported API lint violations; emit some verbiage to explain
             // how to suppress the error rules.
-            options.stdout.println("\n$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.")
             val baseline = options.baseline
             if (baseline?.updateFile != null && baseline.file != null && !baseline.silentUpdate) {
                 options.stdout.println("""
-                ******************************
+                ************************************************************
                 Your API changes are triggering API Lint warnings or errors.
                 To make these errors go away, you have two choices:
-                   1. You can suppress the errors with @SuppressLint("<id>")
-                   2. You can update the baseline by executing the following command:
-                         cp \
-                         ${baseline.updateFile} \
-                         ${baseline.file}
-                      To submit the revised baseline.txt to the main Android repository,
-                      you will need approval.
-                ******************************
+
+                1. You can suppress the errors with @SuppressLint("<id>")
+                2. You can update the baseline by executing the following
+                   command:
+                       cp \
+                       ${baseline.updateFile} \
+                       ${baseline.file}
+                   To submit the revised baseline.txt to the main Android
+                   repository, you will need approval.
+                ************************************************************
                 """.trimIndent())
+            } else {
+                options.stdout.println("See tools/metalava/API-LINT.md for how to handle these.")
             }
         }
     }
diff --git a/src/main/java/com/android/tools/metalava/Baseline.kt b/src/main/java/com/android/tools/metalava/Baseline.kt
index 335cee4abfa1c381ef11604c0ab5b20e9ed537f4..02c4f4c04d74137940b24cd928300373533bb9dc 100644
--- a/src/main/java/com/android/tools/metalava/Baseline.kt
+++ b/src/main/java/com/android/tools/metalava/Baseline.kt
@@ -35,6 +35,7 @@ import com.intellij.psi.PsiParameter
 import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
 import java.io.File
 import java.io.PrintWriter
+import kotlin.text.Charsets.UTF_8
 
 const val DEFAULT_BASELINE_NAME = "baseline.txt"
 
@@ -181,7 +182,7 @@ class Baseline(
 
     private fun read() {
         val file = this.file ?: return
-        val lines = file.readLines(Charsets.UTF_8)
+        val lines = file.readLines(UTF_8)
         for (i in 0 until lines.size - 1) {
             val line = lines[i]
             if (line.startsWith("//") ||
@@ -218,7 +219,7 @@ class Baseline(
 
     private fun write() {
         val updateFile = this.updateFile ?: return
-        if (!map.isEmpty()) {
+        if (!map.isEmpty() || !options.deleteEmptyBaselines) {
             val sb = StringBuilder()
             sb.append(format.header())
             sb.append(headerComment)
@@ -234,8 +235,13 @@ class Baseline(
                 }
                 sb.append("\n\n")
             }
+
+            if (sb.endsWith("\n\n")) {
+                sb.setLength(sb.length - 2)
+            }
+
             updateFile.parentFile?.mkdirs()
-            updateFile.writeText(sb.toString(), Charsets.UTF_8)
+            updateFile.writeText(sb.toString(), UTF_8)
         } else {
             updateFile.delete()
         }
diff --git a/src/main/java/com/android/tools/metalava/ComparisonVisitor.kt b/src/main/java/com/android/tools/metalava/ComparisonVisitor.kt
index 77d3dc070ae397093bd82e6eb0c4775abc2afde7..6b703c8007b9ab84c790e5bc7c8a462ba2e80bd1 100644
--- a/src/main/java/com/android/tools/metalava/ComparisonVisitor.kt
+++ b/src/main/java/com/android/tools/metalava/ComparisonVisitor.kt
@@ -333,7 +333,17 @@ class CodebaseComparator {
                                         val simpleType2 = parameter2.type().toCanonicalType()
                                         delta = simpleType1.compareTo(simpleType2)
                                         if (delta != 0) {
-                                            break
+                                            // Special case: Kotlin coroutines
+                                            if (simpleType1.startsWith("kotlin.coroutines.") && simpleType2.startsWith("kotlin.coroutines.")) {
+                                                val t1 = simpleType1.removePrefix("kotlin.coroutines.").removePrefix("experimental.")
+                                                val t2 = simpleType2.removePrefix("kotlin.coroutines.").removePrefix("experimental.")
+                                                delta = t1.compareTo(t2)
+                                                if (delta != 0) {
+                                                    break
+                                                }
+                                            } else {
+                                                break
+                                            }
                                         }
                                     }
                                 }
diff --git a/src/main/java/com/android/tools/metalava/CompatibilityCheck.kt b/src/main/java/com/android/tools/metalava/CompatibilityCheck.kt
index 78aebe16ff64d43041b399eaee7c5cb14bc0392d..481aae827fde73dc209788b13f03296259e3b00a 100644
--- a/src/main/java/com/android/tools/metalava/CompatibilityCheck.kt
+++ b/src/main/java/com/android/tools/metalava/CompatibilityCheck.kt
@@ -114,6 +114,9 @@ class CompatibilityCheck(
                     return
                 }
                 val name = AnnotationItem.simpleName(oldNullnessAnnotation)
+                if (old.type()?.primitive == true) {
+                    return
+                }
                 report(
                     Errors.INVALID_NULL_CONVERSION, new,
                     "Attempted to remove $name annotation from ${describe(new)}"
@@ -510,7 +513,7 @@ class CompatibilityCheck(
             }
         }
 
-        if (new.modifiers.isInline() && new.isKotlin()) {
+        if (new.modifiers.isInline()) {
             val oldTypes = old.typeParameterList().typeParameters()
             val newTypes = new.typeParameterList().typeParameters()
             for (i in 0 until oldTypes.size) {
diff --git a/src/main/java/com/android/tools/metalava/Constants.kt b/src/main/java/com/android/tools/metalava/Constants.kt
index 4e0a3c7318296344175a75d0a49690a90e8b5f50..59ffda5afd20f5534e256fea05f8b5f5d72e4903 100644
--- a/src/main/java/com/android/tools/metalava/Constants.kt
+++ b/src/main/java/com/android/tools/metalava/Constants.kt
@@ -32,6 +32,11 @@ const val ANDROID_SYSTEM_API = "android.annotation.SystemApi"
 const val ANDROID_REQUIRES_PERMISSION = "android.annotation.RequiresPermission"
 const val RECENTLY_NULLABLE = "androidx.annotation.RecentlyNullable"
 const val RECENTLY_NONNULL = "androidx.annotation.RecentlyNonNull"
+const val ANDROID_NULLABLE = "android.annotation.Nullable"
+const val ANDROID_NONNULL = "android.annotation.NonNull"
+const val ANDROIDX_VISIBLE_FOR_TESTING = "androidx.annotation.VisibleForTesting"
+const val ANDROID_SUPPORT_VISIBLE_FOR_TESTING = "android.support.annotation.VisibleForTesting"
+const val ATTR_OTHERWISE = "otherwise"
 
 const val ENV_VAR_METALAVA_TESTS_RUNNING = "METALAVA_TESTS_RUNNING"
 const val ENV_VAR_METALAVA_DUMP_ARGV = "METALAVA_DUMP_ARGV"
diff --git a/src/main/java/com/android/tools/metalava/DocAnalyzer.kt b/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
index 44eed3b410b3c0c72b61d468d2f5bfad8846b55b..b831a5b39dccb1e9c8117f4f7ac00ed08ae60f0c 100644
--- a/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
+++ b/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
@@ -224,7 +224,8 @@ class DocAnalyzer(
             }
 
             private fun handleKotlinDeprecation(annotation: AnnotationItem, item: Item) {
-                val text = annotation.findAttribute(ATTR_VALUE)?.value?.value()?.toString() ?: return
+                val text = (annotation.findAttribute("message") ?: annotation.findAttribute(ATTR_VALUE))
+                    ?.value?.value()?.toString() ?: return
                 if (text.isBlank() || item.documentation.contains(text)) {
                     return
                 }
diff --git a/src/main/java/com/android/tools/metalava/Driver.kt b/src/main/java/com/android/tools/metalava/Driver.kt
index 400c139b48cbaaccce9d7590f0b54c1546039bac..c455923ac760d109a489fd7a2f007e5c6caaa45f 100644
--- a/src/main/java/com/android/tools/metalava/Driver.kt
+++ b/src/main/java/com/android/tools/metalava/Driver.kt
@@ -48,9 +48,13 @@ import com.android.utils.StdLogger.Level.ERROR
 import com.google.common.base.Stopwatch
 import com.google.common.collect.Lists
 import com.google.common.io.Files
+import com.intellij.core.CoreApplicationEnvironment
 import com.intellij.openapi.diagnostic.DefaultLogger
+import com.intellij.openapi.extensions.Extensions
 import com.intellij.openapi.roots.LanguageLevelProjectExtension
 import com.intellij.openapi.util.Disposer
+import com.intellij.psi.javadoc.CustomJavadocTagProvider
+import com.intellij.psi.javadoc.JavadocTagInfo
 import com.intellij.util.execution.ParametersListUtil
 import java.io.File
 import java.io.IOException
@@ -139,7 +143,7 @@ fun run(
         options = Options(modifiedArgs, stdout, stderr)
         processFlags()
 
-        if (reporter.hasErrors() && !options.updateBaseline) {
+        if (reporter.hasErrors() && !options.passBaselineUpdates) {
             exitCode = -1
         }
         exitValue = true
@@ -154,13 +158,17 @@ fun run(
         }
         exitCode = e.exitCode
         exitValue = false
+    } finally {
+        Disposer.dispose(LintCoreApplicationEnvironment.get().parentDisposable)
     }
 
     if (options.updateBaseline) {
         if (options.verbose) {
             options.baseline?.dumpStats(options.stdout)
         }
-        stdout.println("$PROGRAM_NAME wrote updated baseline to ${options.baseline?.file}")
+        if (!options.quiet) {
+            stdout.println("$PROGRAM_NAME wrote updated baseline to ${options.baseline?.updateFile}")
+        }
     }
     options.baseline?.close()
 
@@ -432,8 +440,6 @@ private fun processFlags() {
         AnnotationStatistics(codebase).measureCoverageOf(options.annotationCoverageOf)
     }
 
-    Disposer.dispose(LintCoreApplicationEnvironment.get().parentDisposable)
-
     if (options.verbose) {
         val packageCount = codebase.size()
         options.stdout.println("\n$PROGRAM_NAME finished handling $packageCount packages in $stopwatch")
@@ -731,7 +737,7 @@ fun invokeDocumentationTool() {
 class PrintWriterOutputStream(private val writer: PrintWriter) : OutputStream() {
 
     override fun write(b: ByteArray) {
-        writer.write(String(b, Charsets.UTF_8))
+        writer.write(String(b, UTF_8))
     }
 
     override fun write(b: Int) {
@@ -739,7 +745,7 @@ class PrintWriterOutputStream(private val writer: PrintWriter) : OutputStream()
     }
 
     override fun write(b: ByteArray, off: Int, len: Int) {
-        writer.write(String(b, off, len, Charsets.UTF_8))
+        writer.write(String(b, off, len, UTF_8))
     }
 
     override fun flush() {
@@ -898,7 +904,7 @@ fun loadFromJarFile(apiJar: File, manifest: File? = null, preFiltered: Boolean =
 private fun createProjectEnvironment(): LintCoreProjectEnvironment {
     ensurePsiFileCapacity()
     val appEnv = LintCoreApplicationEnvironment.get()
-    val parentDisposable = Disposer.newDisposable()
+    val parentDisposable = appEnv.parentDisposable
 
     if (!assertionsEnabled() &&
         System.getenv(ENV_VAR_METALAVA_DUMP_ARGV) == null &&
@@ -907,7 +913,20 @@ private fun createProjectEnvironment(): LintCoreProjectEnvironment {
         DefaultLogger.disableStderrDumping(parentDisposable)
     }
 
-    return LintCoreProjectEnvironment.create(parentDisposable, appEnv)
+    val environment = LintCoreProjectEnvironment.create(parentDisposable, appEnv)
+
+    // Missing service needed in metalava but not in lint: javadoc handling
+    environment.project.registerService(
+        com.intellij.psi.javadoc.JavadocManager::class.java,
+        com.intellij.psi.impl.source.javadoc.JavadocManagerImpl::class.java
+    )
+    environment.registerProjectExtensionPoint(JavadocTagInfo.EP_NAME,
+        com.intellij.psi.javadoc.JavadocTagInfo::class.java)
+    CoreApplicationEnvironment.registerExtensionPoint(
+        Extensions.getRootArea(), CustomJavadocTagProvider.EP_NAME, CustomJavadocTagProvider::class.java
+    )
+
+    return environment
 }
 
 private fun ensurePsiFileCapacity() {
@@ -1018,7 +1037,7 @@ fun createReportFile(
     }
     val localTimer = Stopwatch.createStarted()
     try {
-        val writer = PrintWriter(Files.asCharSink(apiFile, Charsets.UTF_8).openBufferedStream())
+        val writer = PrintWriter(Files.asCharSink(apiFile, UTF_8).openBufferedStream())
         writer.use { printWriter ->
             val apiWriter = createVisitor(printWriter)
             codebase.accept(apiWriter)
@@ -1139,7 +1158,7 @@ private fun addHiddenPackages(
             }
             else -> return
         }
-        var contents = Files.asCharSource(file, Charsets.UTF_8).read()
+        var contents = Files.asCharSource(file, UTF_8).read()
         if (javadoc) {
             contents = packageHtmlToJavadoc(contents)
         }
@@ -1226,7 +1245,7 @@ private fun findRoot(file: File): File? {
 
 /** Finds the package of the given Java/Kotlin source file, if possible */
 fun findPackage(file: File): String? {
-    val source = Files.asCharSource(file, Charsets.UTF_8).read()
+    val source = Files.asCharSource(file, UTF_8).read()
     return findPackage(source)
 }
 
diff --git a/src/main/java/com/android/tools/metalava/ExtractAnnotations.kt b/src/main/java/com/android/tools/metalava/ExtractAnnotations.kt
index dc33b27f2c46bb9bba98c961e3dc0d21339196c5..9076f31d2d5fc73dd4b0bd037650b7fd9c6bc68b 100644
--- a/src/main/java/com/android/tools/metalava/ExtractAnnotations.kt
+++ b/src/main/java/com/android/tools/metalava/ExtractAnnotations.kt
@@ -34,8 +34,8 @@ import com.android.tools.metalava.model.psi.CodePrinter
 import com.android.tools.metalava.model.psi.PsiAnnotationItem
 import com.android.tools.metalava.model.psi.PsiClassItem
 import com.android.tools.metalava.model.psi.PsiMethodItem
+import com.android.tools.metalava.model.psi.UAnnotationItem
 import com.android.tools.metalava.model.visitors.ApiVisitor
-import com.google.common.base.Charsets
 import com.google.common.xml.XmlEscapers
 import com.intellij.psi.JavaRecursiveElementVisitor
 import com.intellij.psi.PsiAnnotation
@@ -63,6 +63,7 @@ import java.io.StringWriter
 import java.util.ArrayList
 import java.util.jar.JarEntry
 import java.util.jar.JarOutputStream
+import kotlin.text.Charsets.UTF_8
 
 // Like the tools/base Extractor class, but limited to our own (mapped) AnnotationItems,
 // and only those with source retention (and in particular right now that just means the
@@ -148,7 +149,7 @@ class ExtractAnnotations(
                         }
                         writer.println("</root>\n")
                         writer.close()
-                        val bytes = writer.contents.toByteArray(Charsets.UTF_8)
+                        val bytes = writer.contents.toByteArray(UTF_8)
                         zos.write(bytes)
                         zos.closeEntry()
                     }
@@ -234,26 +235,33 @@ class ExtractAnnotations(
                         )
                     }
 
-                    if (typeDefAnnotation is PsiAnnotationItem && typeDefClass is PsiClassItem) {
-                        val result = AnnotationHolder(
-                            typeDefClass, typeDefAnnotation,
-                            annotationLookup.findRealAnnotation(
-                                typeDefAnnotation.psiAnnotation,
-                                typeDefClass.psiClass,
-                                null
+                    val result =
+                        if (typeDefAnnotation is PsiAnnotationItem && typeDefClass is PsiClassItem) {
+                            AnnotationHolder(
+                                typeDefClass, typeDefAnnotation,
+                                annotationLookup.findRealAnnotation(
+                                    typeDefAnnotation.psiAnnotation,
+                                    typeDefClass.psiClass,
+                                    null
+                                )
                             )
-                        )
-                        classToAnnotationHolder[className] = result
-                        addItem(item, result)
-
-                        if (item is PsiMethodItem && result.uAnnotation != null &&
-                            !reporter.isSuppressed(Errors.RETURNING_UNEXPECTED_CONSTANT)
-                        ) {
-                            verifyReturnedConstants(item, result.uAnnotation, result, className)
+                        } else if (typeDefAnnotation is UAnnotationItem && typeDefClass is PsiClassItem) {
+                            AnnotationHolder(
+                                typeDefClass, typeDefAnnotation, typeDefAnnotation.uAnnotation
+                            )
+                        } else {
+                            continue
                         }
 
-                        continue
+                    classToAnnotationHolder[className] = result
+                    addItem(item, result)
+
+                    if (item is PsiMethodItem && result.uAnnotation != null &&
+                        !reporter.isSuppressed(Errors.RETURNING_UNEXPECTED_CONSTANT)
+                    ) {
+                        verifyReturnedConstants(item, result.uAnnotation, result, className)
                     }
+                    continue
                 }
             }
         }
@@ -469,11 +477,12 @@ class ExtractAnnotations(
     ) {
         val annotationItem = annotationHolder.annotationItem
         val uAnnotation = annotationHolder.uAnnotation
-            ?: if (annotationItem is PsiAnnotationItem) {
-                // Imported annotation
-                JavaUAnnotation.wrap(annotationItem.psiAnnotation)
-            } else {
-                return
+            ?: when (annotationItem) {
+                is UAnnotationItem -> annotationItem.uAnnotation
+                is PsiAnnotationItem ->
+                    // Imported annotation
+                    JavaUAnnotation.wrap(annotationItem.psiAnnotation)
+                else -> return
             }
         val qualifiedName = annotationItem.qualifiedName()
 
diff --git a/src/main/java/com/android/tools/metalava/KotlinInteropChecks.kt b/src/main/java/com/android/tools/metalava/KotlinInteropChecks.kt
index a904af59aa4c320d1c3fcbc719203bbc40cf4a78..278d1c7fc4b92671307817852735d12a61b596cc 100644
--- a/src/main/java/com/android/tools/metalava/KotlinInteropChecks.kt
+++ b/src/main/java/com/android/tools/metalava/KotlinInteropChecks.kt
@@ -95,10 +95,8 @@ class KotlinInteropChecks {
             if (checked) {
                 val annotation = method.modifiers.findAnnotation("kotlin.jvm.Throws")
                 if (annotation != null) {
-                    val attribute =
-                        annotation.findAttribute("exceptionClasses") ?: annotation.findAttribute("value")
-                        ?: annotation.attributes().firstOrNull()
-                    if (attribute != null) {
+                    // There can be multiple values
+                    for (attribute in annotation.attributes()) {
                         for (v in attribute.leafValues()) {
                             val source = v.toSource()
                             if (source.endsWith(exception.simpleName() + "::class")) {
diff --git a/src/main/java/com/android/tools/metalava/NullabilityAnnotationsValidator.kt b/src/main/java/com/android/tools/metalava/NullabilityAnnotationsValidator.kt
index a5833b288658b246cc562d24018bf5c560d56027..0bdba6725546a49c68e0a6d50be2c1d6291d76c9 100644
--- a/src/main/java/com/android/tools/metalava/NullabilityAnnotationsValidator.kt
+++ b/src/main/java/com/android/tools/metalava/NullabilityAnnotationsValidator.kt
@@ -28,7 +28,7 @@ import com.android.tools.metalava.model.visitors.ApiVisitor
 import com.google.common.io.Files
 import java.io.File
 import java.io.PrintWriter
-import java.nio.charset.StandardCharsets
+import kotlin.text.Charsets.UTF_8
 
 private const val RETURN_LABEL = "return value"
 
@@ -106,7 +106,7 @@ class NullabilityAnnotationsValidator {
     fun validateAllFrom(codebase: Codebase, topLevelClassesList: File?) {
         if (topLevelClassesList != null) {
             val classes =
-                Files.readLines(topLevelClassesList, StandardCharsets.UTF_8)
+                Files.readLines(topLevelClassesList, UTF_8)
                     .filterNot { it.isBlank() }
                     .map { it.trim() }
                     .filterNot { it.startsWith("#") }
@@ -212,7 +212,7 @@ class NullabilityAnnotationsValidator {
 
         // Non-fatal issues are written to the warnings .txt file if present, else logged.
         if (warningsTxtFile != null) {
-            PrintWriter(Files.asCharSink(warningsTxtFile, Charsets.UTF_8).openBufferedStream()).use { w ->
+            PrintWriter(Files.asCharSink(warningsTxtFile, UTF_8).openBufferedStream()).use { w ->
                 nonFatalIssues.forEach { w.println(it) }
             }
         } else {
diff --git a/src/main/java/com/android/tools/metalava/Options.kt b/src/main/java/com/android/tools/metalava/Options.kt
index 33a26da263eca01836e683ffc9d7a7fb77963d92..81f980fb5b63ea747a47aea6ef3ab1983f4d291e 100644
--- a/src/main/java/com/android/tools/metalava/Options.kt
+++ b/src/main/java/com/android/tools/metalava/Options.kt
@@ -33,6 +33,7 @@ import java.io.StringWriter
 import java.util.Locale
 import kotlin.reflect.KMutableProperty1
 import kotlin.reflect.full.memberProperties
+import kotlin.text.Charsets.UTF_8
 
 /** Global options for the metadata extraction tool */
 var options = Options(emptyArray())
@@ -136,6 +137,7 @@ const val ARG_REWRITE_ANNOTATIONS = "--rewrite-annotations"
 const val ARG_INCLUDE_SOURCE_RETENTION = "--include-source-retention"
 const val ARG_INCLUDE_SIG_VERSION = "--include-signature-version"
 const val ARG_UPDATE_API = "--update-api"
+const val ARG_PASS_BASELINE_UPDATES = "--pass-baseline-updates"
 const val ARG_DEX_API_MAPPING = "--dex-api-mapping"
 const val ARG_GENERATE_DOCUMENTATION = "--generate-documentation"
 const val ARG_BASELINE = "--baseline"
@@ -143,9 +145,10 @@ const val ARG_UPDATE_BASELINE = "--update-baseline"
 const val ARG_MERGE_BASELINE = "--merge-baseline"
 const val ARG_STUB_PACKAGES = "--stub-packages"
 const val ARG_STUB_IMPORT_PACKAGES = "--stub-import-packages"
+const val ARG_DELETE_EMPTY_BASELINES = "--delete-empty-baselines"
 
 class Options(
-    args: Array<String>,
+    private val args: Array<String>,
     /** Writer to direct output to */
     var stdout: PrintWriter = PrintWriter(OutputStreamWriter(System.out)),
     /** Writer to direct error messages to */
@@ -502,6 +505,12 @@ class Options(
     /** Whether all baseline files need to be updated */
     var updateBaseline = false
 
+    /** If updating baselines, don't fail the build */
+    var passBaselineUpdates = false
+
+    /** If updating baselines and the baseline is empty, delete the file */
+    var deleteEmptyBaselines = false
+
     /** Whether the baseline should only contain errors */
     var baselineErrorsOnly = false
 
@@ -568,6 +577,7 @@ class Options(
         var baselineFile: File? = null
         var mergeBaseline = false
         var delayedCheckApiFiles = false
+        var skipGenerateAnnotations = false
 
         var index = 0
         while (index < args.size) {
@@ -750,6 +760,8 @@ class Options(
                         }
                     }
                 }
+                ARG_PASS_BASELINE_UPDATES -> passBaselineUpdates = true
+                ARG_DELETE_EMPTY_BASELINES -> deleteEmptyBaselines = true
 
                 ARG_PUBLIC, "-public" -> docLevel = DocLevel.PUBLIC
                 ARG_PROTECTED, "-protected" -> docLevel = DocLevel.PROTECTED
@@ -1237,18 +1249,19 @@ class Options(
                             true
                         else yesNo(arg.substring(ARG_INCLUDE_SIG_VERSION.length + 1))
                     } else if (arg.startsWith(ARG_FORMAT)) {
-                        when (arg) {
+                        outputFormat = when (arg) {
                             "$ARG_FORMAT=v1" -> {
-                                FileFormat.V1.configureOptions(this, compatibility)
+                                FileFormat.V1
                             }
                             "$ARG_FORMAT=v2" -> {
-                                FileFormat.V2.configureOptions(this, compatibility)
+                                FileFormat.V2
                             }
                             "$ARG_FORMAT=v3" -> {
-                                FileFormat.V3.configureOptions(this, compatibility)
+                                FileFormat.V3
                             }
                             else -> throw DriverException(stderr = "Unexpected signature format; expected v1, v2 or v3")
                         }
+                        outputFormat.configureOptions(this, compatibility)
                     } else if (arg.startsWith("-")) {
                         // Compatibility flag; map to mutable properties in the Compatibility
                         // class and assign it
@@ -1293,6 +1306,12 @@ class Options(
                         } else {
                             // All args that don't start with "-" are taken to be filenames
                             mutableSources.addAll(stringToExistingFiles(arg))
+
+                            // Temporary workaround for
+                            // aosp/I73ff403bfc3d9dfec71789a3e90f9f4ea95eabe3
+                            if (arg.endsWith("hwbinder-stubs-docs-stubs.srcjar.rsp")) {
+                                skipGenerateAnnotations = true
+                            }
                         }
                     }
                 }
@@ -1302,13 +1321,19 @@ class Options(
         }
 
         if (generateApiLevelXml != null) {
-            if (androidJarPatterns == null) {
-                androidJarPatterns = mutableListOf(
-                    "prebuilts/tools/common/api-versions/android-%/android.jar",
-                    "prebuilts/sdk/%/public/android.jar"
-                )
+            val patterns = androidJarPatterns ?: run {
+                mutableListOf<String>()
             }
-            apiLevelJars = findAndroidJars(androidJarPatterns!!, currentApiLevel, currentCodeName, currentJar)
+            // Fallbacks
+            patterns.add("prebuilts/tools/common/api-versions/android-%/android.jar")
+            patterns.add("prebuilts/sdk/%/public/android.jar")
+            apiLevelJars = findAndroidJars(patterns, currentApiLevel, currentCodeName, currentJar)
+        }
+
+        // outputKotlinStyleNulls implies format=v3
+        if (outputKotlinStyleNulls) {
+            outputFormat = FileFormat.V3
+            outputFormat.configureOptions(this, compatibility)
         }
 
         // If the caller has not explicitly requested that unannotated classes and
@@ -1321,6 +1346,10 @@ class Options(
             allowReferencingUnknownClasses = false
         }
 
+        if (skipGenerateAnnotations) {
+            generateAnnotations = false
+        }
+
         if (updateApi) {
             // We're running in update API mode: cancel other "action" flags; only signature file generation
             // flags count
@@ -1349,6 +1378,9 @@ class Options(
         if (baselineFile == null) {
             val defaultBaselineFile = getDefaultBaselineFile()
             if (defaultBaselineFile != null && defaultBaselineFile.isFile) {
+                if (updateBaseline && updateBaselineFile == null) {
+                    updateBaselineFile = defaultBaselineFile
+                }
                 baseline = Baseline(defaultBaselineFile, updateBaselineFile, mergeBaseline)
             } else if (updateBaselineFile != null) {
                 baseline = Baseline(null, updateBaselineFile, mergeBaseline)
@@ -1359,6 +1391,9 @@ class Options(
                 "// See tools/metalava/API-LINT.md for how to update this file.\n\n"
             else
                 ""
+            if (updateBaseline && updateBaselineFile == null) {
+                updateBaselineFile = baselineFile
+            }
             baseline = Baseline(baselineFile, updateBaselineFile, mergeBaseline, headerComment)
         }
 
@@ -1440,6 +1475,19 @@ class Options(
                     if (verbose) {
                         stdout.println("Last API level found: ${apiLevel - 1}")
                     }
+
+                    if (apiLevel < 28) {
+                        // Clearly something is wrong with the patterns; this should result in a build error
+                        val argList = mutableListOf<String>()
+                        args.forEachIndexed { index, arg ->
+                            if (arg == ARG_ANDROID_JAR_PATTERN) {
+                                argList.add(args[index + 1])
+                            }
+                        }
+                        throw DriverException(stderr = "Could not find android.jar for API level $apiLevel; the " +
+                            "$ARG_ANDROID_JAR_PATTERN set might be invalid: ${argList.joinToString()}")
+                    }
+
                     break
                 }
                 if (verbose) {
@@ -1609,7 +1657,7 @@ class Options(
                     if (!listFile.isFile) {
                         throw DriverException("$listFile is not a file")
                     }
-                    val contents = Files.asCharSource(listFile, Charsets.UTF_8).read()
+                    val contents = Files.asCharSource(listFile, UTF_8).read()
                     val pathList = Splitter.on(CharMatcher.whitespace()).trimResults().omitEmptyStrings().split(
                         contents
                     )
@@ -1886,6 +1934,11 @@ class Options(
                 "in the baseline, it will merge the existing baseline with the new baseline. This is useful " +
                 "if $PROGRAM_NAME runs multiple times on the same source tree with different flags at different " +
                 "times, such as occasionally with $ARG_API_LINT.",
+            ARG_PASS_BASELINE_UPDATES, "Normally, encountering error will fail the build, even when updating " +
+                "baselines. This flag allows you to tell $PROGRAM_NAME to continue without errors, such that " +
+                "all the baselines in the source tree can be updated in one go.",
+            ARG_DELETE_EMPTY_BASELINES, "Whether to delete baseline files if they are updated and there is nothing " +
+                "to include.",
 
             "", "\nJDiff:",
             "$ARG_XML_API <file>", "Like $ARG_API, but emits the API in the JDiff XML format instead",
diff --git a/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt b/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt
index 38f6a263d54fd12a0c7e354ad45026e4961407fe..d256e10631ea397fd3d37c32ffd92aedb4d233e8 100644
--- a/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt
+++ b/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt
@@ -37,6 +37,7 @@ import java.nio.file.attribute.FileTime
 import java.util.jar.JarEntry
 import java.util.zip.ZipInputStream
 import java.util.zip.ZipOutputStream
+import kotlin.text.Charsets.UTF_8
 
 /**
  * Converts public stub annotation sources into package private annotation sources.
@@ -61,7 +62,7 @@ class RewriteAnnotations {
             // Copy and convert
             target.parentFile.mkdirs()
             target.writeText(
-                source.readText(Charsets.UTF_8).replace(
+                source.readText(UTF_8).replace(
                     "\npublic @interface",
                     "\n@interface"
                 )
@@ -102,9 +103,13 @@ class RewriteAnnotations {
         for (file in files) {
             // Jump directly into androidx/annotation if it appears we were invoked at the top level
             if (file.isDirectory) {
-                val annotations = File(file, "androidx${File.separator}annotation/")
-                if (annotations.isDirectory) {
-                    rewriteAnnotations(annotations)
+                val android = File(file, "android${File.separator}annotation/")
+                if (android.isDirectory) {
+                    rewriteAnnotations(android)
+                    val androidx = File(file, "androidx${File.separator}annotation/")
+                    if (androidx.isDirectory) {
+                        rewriteAnnotations(androidx)
+                    }
                     continue
                 }
             }
@@ -118,8 +123,10 @@ class RewriteAnnotations {
      */
     private fun hasSourceRetention(codebase: Codebase?, qualifiedName: String): Boolean {
         when {
-            qualifiedName == "androidx.annotation.RecentlyNullable" ||
-                qualifiedName == "androidx.annotation.RecentlyNonNull" -> return false
+            qualifiedName == RECENTLY_NULLABLE ||
+                qualifiedName == RECENTLY_NONNULL ||
+                qualifiedName == ANDROID_NULLABLE ||
+                qualifiedName == ANDROID_NONNULL -> return false
             qualifiedName.startsWith("androidx.annotation.") -> return true
         }
 
@@ -171,10 +178,10 @@ class RewriteAnnotations {
                 superName: String?,
                 interfaces: Array<out String>?
             ) {
-                // Only process public annotations in androidx.annotation
+                // Only process public annotations in android.annotation and androidx.annotation
                 if (access and Opcodes.ACC_PUBLIC != 0 &&
                     access and Opcodes.ACC_ANNOTATION != 0 &&
-                    name.startsWith("androidx/annotation/")
+                    (name.startsWith("android/annotation/") || name.startsWith("androidx/annotation/"))
                 ) {
                     skip = false
                     val flagsWithoutPublic = access and Opcodes.ACC_PUBLIC.inv()
@@ -236,7 +243,7 @@ class RewriteAnnotations {
 
                 // read the content of the entry from the input stream, and write it into the archive.
                 if (name.endsWith(DOT_CLASS) &&
-                    name.startsWith("androidx/annotation/") &&
+                    (name.startsWith("android/annotation/") || name.startsWith("androidx/annotation/")) &&
                     name.indexOf("$") == -1 &&
                     !entry.isDirectory
                 ) {
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 00f3eb3126cb9c21107824d7d5c82ca1a67614a9..7a28c4ea47332b31438e1e2a890778a4d9bd7735 100644
--- a/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
+++ b/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
@@ -33,7 +33,6 @@ import com.android.tools.metalava.model.text.TextPropertyItem;
 import com.android.tools.metalava.model.text.TextTypeItem;
 import com.android.tools.metalava.model.text.TextTypeParameterList;
 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;
@@ -52,6 +51,7 @@ 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.model.FieldItemKt.javaUnescapeString;
+import static kotlin.text.Charsets.UTF_8;
 
 //
 // Copied from doclava1, but adapted to metalava's code model (plus tweaks to handle
@@ -65,7 +65,7 @@ public class ApiFile {
     public static TextCodebase parseApi(File file,
                                         Boolean kotlinStyleNulls) throws ApiParseException {
         try {
-            String apiText = Files.asCharSource(file, Charsets.UTF_8).read();
+            String apiText = Files.asCharSource(file, UTF_8).read();
             return parseApi(file.getPath(), apiText, kotlinStyleNulls);
         } catch (IOException ex) {
             throw new ApiParseException("Error reading API file", ex);
diff --git a/src/main/java/com/android/tools/metalava/doclava1/Errors.java b/src/main/java/com/android/tools/metalava/doclava1/Errors.java
index 9587ae95bbad18c5cbb7d6c38201e6635812efff..b467735481c3b4dd567fee7e049f81926abeaf28 100644
--- a/src/main/java/com/android/tools/metalava/doclava1/Errors.java
+++ b/src/main/java/com/android/tools/metalava/doclava1/Errors.java
@@ -206,7 +206,7 @@ public class Errors {
     public static final Error UNAVAILABLE_SYMBOL = new Error(110, WARNING, Category.DOCUMENTATION);
     public static final Error HIDDEN_SUPERCLASS = new Error(111, WARNING, Category.DOCUMENTATION);
     public static final Error DEPRECATED = new Error(112, HIDDEN, Category.DOCUMENTATION);
-    public static final Error DEPRECATION_MISMATCH = new Error(113, WARNING, Category.DOCUMENTATION);
+    public static final Error DEPRECATION_MISMATCH = new Error(113, ERROR, Category.DOCUMENTATION);
     public static final Error MISSING_COMMENT = new Error(114, LINT, Category.DOCUMENTATION);
     public static final Error IO_ERROR = new Error(115, ERROR);
     public static final Error NO_SINCE_DATA = new Error(116, HIDDEN, Category.DOCUMENTATION);
diff --git a/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt b/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
index 0e6921d22c624dec1744526a7a18ec2100aefbce..ae61fdd9cf9c3fb9935adac9c7aa30dd2923d1f9 100644
--- a/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
@@ -27,6 +27,8 @@ import com.android.tools.lint.annotations.Extractor.ANDROID_STRING_DEF
 import com.android.tools.metalava.ANDROIDX_ANNOTATION_PREFIX
 import com.android.tools.metalava.ANDROIDX_NONNULL
 import com.android.tools.metalava.ANDROIDX_NULLABLE
+import com.android.tools.metalava.ANDROID_NONNULL
+import com.android.tools.metalava.ANDROID_NULLABLE
 import com.android.tools.metalava.ANDROID_SUPPORT_ANNOTATION_PREFIX
 import com.android.tools.metalava.Compatibility
 import com.android.tools.metalava.JAVA_LANG_PREFIX
@@ -235,14 +237,21 @@ interface AnnotationItem {
                 "android.annotation.Dimension" -> return "androidx.annotation.Dimension"
 
                 // Null
-                "android.support.annotation.NonNull",
-                "android.annotation.NonNull" -> return "androidx.annotation.NonNull"
+                // We only change recently/newly nullable annotation in stubs
+                RECENTLY_NULLABLE -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NULLABLE
+                RECENTLY_NONNULL -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NONNULL
+
+                ANDROIDX_NULLABLE,
+                ANDROID_NULLABLE,
                 "android.support.annotation.Nullable",
-                "android.annotation.Nullable" -> return "androidx.annotation.Nullable"
-                "libcore.util.NonNull" -> return "androidx.annotation.NonNull"
-                "libcore.util.Nullable" -> return "androidx.annotation.Nullable"
-                "org.jetbrains.annotations.NotNull" -> return "androidx.annotation.NonNull"
-                "org.jetbrains.annotations.Nullable" -> return "androidx.annotation.Nullable"
+                "libcore.util.Nullable",
+                "org.jetbrains.annotations.Nullable" -> return if (target == AnnotationTarget.SDK_STUBS_FILE) ANDROID_NULLABLE else ANDROIDX_NULLABLE
+
+                ANDROIDX_NONNULL,
+                ANDROID_NONNULL,
+                "android.support.annotation.NonNull",
+                "libcore.util.NonNull",
+                "org.jetbrains.annotations.NotNull" -> return if (target == AnnotationTarget.SDK_STUBS_FILE) ANDROID_NONNULL else ANDROIDX_NONNULL
 
                 // Typedefs
                 "android.support.annotation.IntDef",
@@ -303,8 +312,6 @@ interface AnnotationItem {
                 "android.annotation.TargetApi",
                 "android.annotation.SuppressLint" -> return qualifiedName
 
-                RECENTLY_NULLABLE, RECENTLY_NONNULL -> return qualifiedName
-
                 else -> {
                     // Some new annotations added to the platform: assume they are support annotations?
                     return when {
@@ -313,8 +320,8 @@ interface AnnotationItem {
                             "kotlin.annotations.jvm.internal${qualifiedName.substring(qualifiedName.lastIndexOf('.'))}"
 
                         // Other third party nullness annotations?
-                        isNullableAnnotation(qualifiedName) -> "androidx.annotation.Nullable"
-                        isNonNullAnnotation(qualifiedName) -> "androidx.annotation.NonNull"
+                        isNullableAnnotation(qualifiedName) -> ANDROIDX_NULLABLE
+                        isNonNullAnnotation(qualifiedName) -> ANDROIDX_NONNULL
 
                         // Support library annotations are all included, as is the built-in stuff like @Retention
                         qualifiedName.startsWith(ANDROIDX_ANNOTATION_PREFIX) -> return qualifiedName
@@ -402,6 +409,16 @@ interface AnnotationItem {
                 "java.lang.annotation.Target" -> return ANNOTATION_IN_ALL_STUBS
             }
 
+            // @android.annotation.Nullable and NonNullable specially recognized annotations by the Kotlin
+            // compiler 1.3 and above: they always go in the stubs.
+            if (qualifiedName == ANDROID_NULLABLE ||
+                qualifiedName == ANDROID_NONNULL ||
+                qualifiedName == ANDROIDX_NULLABLE ||
+                qualifiedName == ANDROIDX_NONNULL
+            ) {
+                return ANNOTATION_IN_ALL_STUBS
+            }
+
             if (qualifiedName.startsWith("android.annotation.")) {
                 // internal annotations not mapped to androidx: things like @SystemApi. Skip from
                 // stubs, external annotations, signature files, etc.
@@ -410,10 +427,10 @@ interface AnnotationItem {
 
             // @RecentlyNullable and @RecentlyNonNull are specially recognized annotations by the Kotlin
             // compiler: they always go in the stubs.
-            if (qualifiedName == "androidx.annotation.RecentlyNullable" ||
-                qualifiedName == "androidx.annotation.RecentlyNonNull"
+            if (qualifiedName == RECENTLY_NULLABLE ||
+                qualifiedName == RECENTLY_NONNULL
             ) {
-                return ANNOTATION_IN_SDK_STUBS
+                return ANNOTATION_IN_ALL_STUBS
             }
 
             // Determine the retention of the annotation: source retention annotations go
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 7dc221b37a2f718ce5bf7df0c8ea7d37b95b5066..0d072fa6103a9faaf16cf3bc35319c1f7f368eff 100644
--- a/src/main/java/com/android/tools/metalava/model/ClassItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/ClassItem.kt
@@ -198,6 +198,8 @@ interface ClassItem : Item {
     /** Gets the type for this class */
     fun toType(): TypeItem
 
+    override fun type(): TypeItem? = null
+
     /** Returns true if this class has type parameters */
     fun hasTypeVariables(): Boolean
 
diff --git a/src/main/java/com/android/tools/metalava/model/FieldItem.kt b/src/main/java/com/android/tools/metalava/model/FieldItem.kt
index 92e49ddf7908b50c01f5829b77dda4901652f823..e65bda9ed2b914668fc1e60f8c1409c5b939982e 100644
--- a/src/main/java/com/android/tools/metalava/model/FieldItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/FieldItem.kt
@@ -23,7 +23,7 @@ import java.io.PrintWriter
 
 interface FieldItem : MemberItem {
     /** The type of this field */
-    fun type(): TypeItem
+    override fun type(): TypeItem
 
     /**
      * The initial/constant value, if any. If [requireConstant] the initial value will
diff --git a/src/main/java/com/android/tools/metalava/model/Item.kt b/src/main/java/com/android/tools/metalava/model/Item.kt
index 9fbc546a6505b93336eaa1fe4d6739f8c01dbbd0..af2042dcec78ecfdd46419fb9adbe9b2d396a61f 100644
--- a/src/main/java/com/android/tools/metalava/model/Item.kt
+++ b/src/main/java/com/android/tools/metalava/model/Item.kt
@@ -210,6 +210,13 @@ interface Item {
      */
     fun containingClass(strict: Boolean = true): ClassItem?
 
+    /**
+     * Returns the associated type if any. For example, for a field, property or parameter,
+     * this is the type of the variable; for a method, it's the return type.
+     * For packages, classes and compilation units, it's null.
+     */
+    fun type(): TypeItem?
+
     companion object {
         fun describe(item: Item, capitalize: Boolean = false): String {
             return when (item) {
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 bdb3b899ad7cdcde416caa580c921bf0903201ca..e27b682217cfb5688987db9f477949db2f8cf4b2 100644
--- a/src/main/java/com/android/tools/metalava/model/MethodItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/MethodItem.kt
@@ -39,6 +39,8 @@ interface MethodItem : MemberItem {
     /** Returns the super methods that this method is overriding */
     fun superMethods(): List<MethodItem>
 
+    override fun type(): TypeItem? = returnType()
+
     /**
      * Like [internalName] but is the desc-portion of the internal signature,
      * e.g. for the method "void create(int x, int y)" the internal name of
diff --git a/src/main/java/com/android/tools/metalava/model/PackageItem.kt b/src/main/java/com/android/tools/metalava/model/PackageItem.kt
index 970d2eb7416f4f71cedccc022e146dd047d38214..711c0d9fd9c92e1290bd36e0cc10d15295007507 100644
--- a/src/main/java/com/android/tools/metalava/model/PackageItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/PackageItem.kt
@@ -33,6 +33,8 @@ interface PackageItem : Item {
         return topLevelClasses().asSequence().flatMap { it.allClasses() }
     }
 
+    override fun type(): TypeItem? = null
+
     val isDefault get() = qualifiedName().isEmpty()
 
     override fun parent(): PackageItem? = if (qualifiedName().isEmpty()) null else containingPackage()
diff --git a/src/main/java/com/android/tools/metalava/model/ParameterItem.kt b/src/main/java/com/android/tools/metalava/model/ParameterItem.kt
index 3610d51685185d5383f2b21522b8e60e8da907d6..a7194e6317c35689dcc102f2551faa8b1b58b0b6 100644
--- a/src/main/java/com/android/tools/metalava/model/ParameterItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/ParameterItem.kt
@@ -24,7 +24,7 @@ interface ParameterItem : Item {
     fun name(): String
 
     /** The type of this field */
-    fun type(): TypeItem
+    override fun type(): TypeItem
 
     /** The containing method */
     fun containingMethod(): MethodItem
diff --git a/src/main/java/com/android/tools/metalava/model/PropertyItem.kt b/src/main/java/com/android/tools/metalava/model/PropertyItem.kt
index f82708c54c7172a1b0ad100970ac30ac917cf5d8..5f8478a2a6feb7048960ea0e0f13974d85d528db 100644
--- a/src/main/java/com/android/tools/metalava/model/PropertyItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/PropertyItem.kt
@@ -21,7 +21,7 @@ import com.android.tools.metalava.model.visitors.TypeVisitor
 
 interface PropertyItem : MemberItem {
     /** The type of this property */
-    fun type(): TypeItem
+    override fun type(): TypeItem
 
     override fun accept(visitor: ItemVisitor) {
         if (visitor.skip(this)) {
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 7ca5ff98dd28d9983281cc3480ec1ab953e42de2..c39e072f7f9d935cd46ad4cf95e14e1e1cf99876 100644
--- a/src/main/java/com/android/tools/metalava/model/TypeItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/TypeItem.kt
@@ -297,5 +297,46 @@ interface TypeItem {
 
             return dimension + base
         }
+
+        /** Compares two strings, ignoring space diffs (spaces, not whitespace in general) */
+        fun equalsWithoutSpace(s1: String, s2: String): Boolean {
+            if (s1 == s2) {
+                return true
+            }
+            val sp1 = s1.indexOf(' ') // first space
+            val sp2 = s2.indexOf(' ')
+            if (sp1 == -1 && sp2 == -1) {
+                // no spaces in strings and aren't equal
+                return false
+            }
+
+            val l1 = s1.length
+            val l2 = s2.length
+            var i1 = 0
+            var i2 = 0
+
+            while (i1 < l1 && i2 < l2) {
+                var c1 = s1[i1++]
+                var c2 = s2[i2++]
+
+                while (c1 == ' ' && i1 < l1) {
+                    c1 = s1[i1++]
+                }
+                while (c2 == ' ' && i2 < l2) {
+                    c2 = s2[i2++]
+                }
+                if (c1 != c2) {
+                    return false
+                }
+            }
+            // Skip trailing spaces
+            while (i1 < l1 && s1[i1] == ' ') {
+                i1++
+            }
+            while (i2 < l2 && s2[i2] == ' ') {
+                i2++
+            }
+            return i1 == l1 && i2 == l2
+        }
     }
 }
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiAnnotationItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiAnnotationItem.kt
index e8c6acb218495dce993a589c699c5ce1f56e57ab..decece4fe4962c4faa5e7d662f457fcc74dfc47f 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiAnnotationItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiAnnotationItem.kt
@@ -284,7 +284,7 @@ class PsiAnnotationSingleAttributeValue(
     override val value: Any?
         get() {
             if (psiValue is PsiLiteral) {
-                return psiValue.value
+                return psiValue.value ?: psiValue.text.removeSurrounding("\"")
             }
 
             val value = ConstantEvaluator.evaluate(null, psiValue)
@@ -292,7 +292,7 @@ class PsiAnnotationSingleAttributeValue(
                 return value
             }
 
-            return psiValue.text
+            return psiValue.text ?: psiValue.text.removeSurrounding("\"")
         }
 
     override fun value(): Any? = value
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 564e15cbe5afe41f8da6339ce06fae61df1624a4..4a5ba9d34aae13be80225f69e26930fca0e3a72e 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
@@ -420,6 +420,8 @@ open class PsiClassItem(
                 )
             }
 
+            val isKotlin = isKotlin(psiClass)
+
             val constructors: MutableList<PsiConstructorItem> = ArrayList(5)
             for (psiMethod in psiMethods) {
                 if (psiMethod.isPrivate() || psiMethod.isPackagePrivate()) {
@@ -468,7 +470,7 @@ open class PsiClassItem(
             item.fields = fields
 
             item.properties = emptyList()
-            if (isKotlin(psiClass)) {
+            if (isKotlin) {
                 // Try to initialize the Kotlin properties
                 val properties = mutableListOf<PsiPropertyItem>()
                 for (method in psiMethods) {
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiMethodItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiMethodItem.kt
index f856c9193d17f9ef6d826628676a3ed7fb3862b9..c337332dc2deaad874e5b85312cb79afa7b338e8 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiMethodItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiMethodItem.kt
@@ -357,9 +357,16 @@ open class PsiMethodItem(
                 // methods with super methods also consider this method non-final.)
                 modifiers.setFinal(false)
             }
-            val parameters = psiMethod.parameterList.parameters.mapIndexed { index, parameter ->
-                PsiParameterItem.create(codebase, parameter, index)
-            }
+            val parameters =
+                if (psiMethod is UMethod) {
+                    psiMethod.uastParameters.mapIndexed { index, parameter ->
+                        PsiParameterItem.create(codebase, parameter, index)
+                    }
+                } else {
+                    psiMethod.parameterList.parameters.mapIndexed { index, parameter ->
+                        PsiParameterItem.create(codebase, parameter, index)
+                    }
+                }
             val returnType = codebase.getType(psiMethod.returnType!!)
             val method = PsiMethodItem(
                 codebase = codebase,
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiModifierItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiModifierItem.kt
index 3a112957cc7003ee2398ed9cc220c71c5335c18b..5ac50e57ac5301a42c3dd906a12c47b24cab7422 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiModifierItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiModifierItem.kt
@@ -16,6 +16,9 @@
 
 package com.android.tools.metalava.model.psi
 
+import com.android.tools.metalava.ANDROIDX_VISIBLE_FOR_TESTING
+import com.android.tools.metalava.ANDROID_SUPPORT_VISIBLE_FOR_TESTING
+import com.android.tools.metalava.ATTR_OTHERWISE
 import com.android.tools.metalava.model.AnnotationItem
 import com.android.tools.metalava.model.Codebase
 import com.android.tools.metalava.model.DefaultModifierList
@@ -24,12 +27,18 @@ import com.android.tools.metalava.model.MutableModifierList
 import com.intellij.psi.PsiDocCommentOwner
 import com.intellij.psi.PsiMethod
 import com.intellij.psi.PsiModifier
+import com.intellij.psi.PsiModifierList
 import com.intellij.psi.PsiModifierListOwner
 import com.intellij.psi.PsiReferenceExpression
+import com.intellij.psi.PsiPrimitiveType
 import org.jetbrains.kotlin.asJava.elements.KtLightModifierList
+import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
 import org.jetbrains.kotlin.lexer.KtTokens
 import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.uast.UAnnotated
 import org.jetbrains.uast.UMethod
+import org.jetbrains.uast.UVariable
+import org.jetbrains.uast.kotlin.KotlinNullabilityUAnnotation
 
 class PsiModifierItem(
     codebase: Codebase,
@@ -38,8 +47,12 @@ class PsiModifierItem(
 ) : DefaultModifierList(codebase, flags, annotations), ModifierList, MutableModifierList {
     companion object {
         fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner, documentation: String?): PsiModifierItem {
-            val modifiers = create(codebase, element)
-
+            val modifiers =
+                if (element is UAnnotated) {
+                    create(codebase, element, element)
+                } else {
+                    create(codebase, element)
+                }
             if (documentation?.contains("@deprecated") == true ||
                 // Check for @Deprecated annotation
                 ((element as? PsiDocCommentOwner)?.isDeprecated == true)
@@ -50,9 +63,7 @@ class PsiModifierItem(
             return modifiers
         }
 
-        private fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner): PsiModifierItem {
-            val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
-
+        private fun computeFlag(element: PsiModifierListOwner, modifierList: PsiModifierList): Int {
             var flags = 0
             if (modifierList.hasModifierProperty(PsiModifier.PUBLIC)) {
                 flags = flags or PUBLIC
@@ -139,6 +150,14 @@ class PsiModifierItem(
                 }
             }
 
+            return flags
+        }
+
+        private fun create(codebase: PsiBasedCodebase, element: PsiModifierListOwner): PsiModifierItem {
+            val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
+
+            var flags = computeFlag(element, modifierList)
+
             val psiAnnotations = modifierList.annotations
             return if (psiAnnotations.isEmpty()) {
                 PsiModifierItem(codebase, flags)
@@ -146,22 +165,16 @@ class PsiModifierItem(
                 val annotations: MutableList<AnnotationItem> =
                     psiAnnotations.map {
                         val qualifiedName = it.qualifiedName
-                        // TODO: com.android.internal.annotations.VisibleForTesting?
-                        if (qualifiedName == "androidx.annotation.VisibleForTesting" ||
-                            qualifiedName == "android.support.annotation.VisibleForTesting") {
-                            val otherwise = it.findAttributeValue("otherwise")
+                        // Consider also supporting com.android.internal.annotations.VisibleForTesting?
+                        if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING ||
+                            qualifiedName == ANDROID_SUPPORT_VISIBLE_FOR_TESTING) {
+                            val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
                             val ref = when {
                                 otherwise is PsiReferenceExpression -> otherwise.referenceName ?: ""
                                 otherwise != null -> otherwise.text
                                 else -> ""
                             }
-                            if (ref.endsWith("PROTECTED")) {
-                                flags = (flags and PUBLIC.inv() and PRIVATE.inv() and INTERNAL.inv()) or PROTECTED
-                            } else if (ref.endsWith("PACKAGE_PRIVATE")) {
-                                flags = (flags and PUBLIC.inv() and PRIVATE.inv() and INTERNAL.inv() and PROTECTED.inv())
-                            } else if (ref.endsWith("PRIVATE") || ref.endsWith("NONE")) {
-                                flags = (flags and PUBLIC.inv() and PROTECTED.inv() and INTERNAL.inv()) or PRIVATE
-                            }
+                            flags = getVisibilityFlag(ref, flags)
                         }
 
                         PsiAnnotationItem.create(codebase, it, qualifiedName)
@@ -170,6 +183,74 @@ class PsiModifierItem(
             }
         }
 
+        private fun create(
+            codebase: PsiBasedCodebase,
+            element: PsiModifierListOwner,
+            annotated: UAnnotated
+        ): PsiModifierItem {
+            val modifierList = element.modifierList ?: return PsiModifierItem(codebase)
+            var flags = computeFlag(element, modifierList)
+            val uAnnotations = annotated.annotations
+
+            return if (uAnnotations.isEmpty()) {
+                val psiAnnotations = modifierList.annotations
+                if (!psiAnnotations.isEmpty()) {
+                    val annotations: MutableList<AnnotationItem> =
+                        psiAnnotations.map { PsiAnnotationItem.create(codebase, it) }.toMutableList()
+                    PsiModifierItem(codebase, flags, annotations)
+                } else {
+                    PsiModifierItem(codebase, flags)
+                }
+            } else {
+                val isPrimitiveVariable = element is UVariable && element.type is PsiPrimitiveType
+
+                val annotations: MutableList<AnnotationItem> = uAnnotations
+                    // Uast sometimes puts nullability annotations on primitives!?
+                    .filter { !isPrimitiveVariable || it !is KotlinNullabilityUAnnotation }
+                    .map {
+
+                        val qualifiedName = it.qualifiedName
+                        if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING ||
+                            qualifiedName == ANDROID_SUPPORT_VISIBLE_FOR_TESTING) {
+                            val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
+                            val ref = when {
+                                otherwise is PsiReferenceExpression -> otherwise.referenceName ?: ""
+                                otherwise != null -> otherwise.asSourceString()
+                                else -> ""
+                            }
+                            flags = getVisibilityFlag(ref, flags)
+                        }
+
+                        UAnnotationItem.create(codebase, it, qualifiedName)
+                    }.toMutableList()
+
+                if (!isPrimitiveVariable) {
+                    val psiAnnotations = modifierList.annotations
+                    if (psiAnnotations.isNotEmpty() && annotations.none { it.isNullnessAnnotation() }) {
+                        val ktNullAnnotation = psiAnnotations.firstOrNull { it is KtLightNullabilityAnnotation }
+                        ktNullAnnotation?.let {
+                            annotations.add(PsiAnnotationItem.create(codebase, it))
+                        }
+                    }
+                }
+
+                PsiModifierItem(codebase, flags, annotations)
+            }
+        }
+
+        /** Modifies the modifier flags based on the VisibleForTesting otherwise constants */
+        private fun getVisibilityFlag(ref: String, flags: Int): Int {
+            return if (ref.endsWith("PROTECTED")) {
+                (flags and PUBLIC.inv() and PRIVATE.inv() and INTERNAL.inv()) or PROTECTED
+            } else if (ref.endsWith("PACKAGE_PRIVATE")) {
+                (flags and PUBLIC.inv() and PRIVATE.inv() and INTERNAL.inv() and PROTECTED.inv())
+            } else if (ref.endsWith("PRIVATE") || ref.endsWith("NONE")) {
+                (flags and PUBLIC.inv() and PROTECTED.inv() and INTERNAL.inv()) or PRIVATE
+            } else {
+                flags
+            }
+        }
+
         fun create(codebase: PsiBasedCodebase, original: PsiModifierItem): PsiModifierItem {
             val originalAnnotations = original.annotations ?: return PsiModifierItem(codebase, original.flags)
             val copy: MutableList<AnnotationItem> = ArrayList(originalAnnotations.size)
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiTypeItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiTypeItem.kt
index ef758ff660a88b602d1b8e738f61ad15f30591c5..507731483f1b5c997a3185b01b54593792aaef5d 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiTypeItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiTypeItem.kt
@@ -56,7 +56,7 @@ import com.intellij.psi.util.TypeConversionUtil
 import org.jetbrains.kotlin.asJava.elements.KtLightTypeParameter
 
 /** Represents a type backed by PSI */
-class PsiTypeItem private constructor(private val codebase: PsiBasedCodebase, private val psiType: PsiType) : TypeItem {
+class PsiTypeItem private constructor(private val codebase: PsiBasedCodebase, val psiType: PsiType) : TypeItem {
     private var toString: String? = null
     private var toAnnotatedString: String? = null
     private var toInnerAnnotatedString: String? = null
@@ -149,7 +149,7 @@ class PsiTypeItem private constructor(private val codebase: PsiBasedCodebase, pr
         if (this === other) return true
 
         return when (other) {
-            is TypeItem -> toTypeString().replace(" ", "") == other.toTypeString().replace(" ", "")
+            is TypeItem -> TypeItem.equalsWithoutSpace(toTypeString(), other.toTypeString())
             else -> false
         }
     }
diff --git a/src/main/java/com/android/tools/metalava/model/psi/UAnnotationItem.kt b/src/main/java/com/android/tools/metalava/model/psi/UAnnotationItem.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0d0266e6db7d36b9c8f4278cb6e35322502bd11a
--- /dev/null
+++ b/src/main/java/com/android/tools/metalava/model/psi/UAnnotationItem.kt
@@ -0,0 +1,326 @@
+/*
+ * 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.model.psi
+
+import com.android.SdkConstants
+import com.android.tools.lint.detector.api.ConstantEvaluator
+import com.android.tools.metalava.model.AnnotationArrayAttributeValue
+import com.android.tools.metalava.model.AnnotationAttribute
+import com.android.tools.metalava.model.AnnotationAttributeValue
+import com.android.tools.metalava.model.AnnotationItem
+import com.android.tools.metalava.model.AnnotationSingleAttributeValue
+import com.android.tools.metalava.model.AnnotationTarget
+import com.android.tools.metalava.model.ClassItem
+import com.android.tools.metalava.model.DefaultAnnotationItem
+import com.android.tools.metalava.model.Item
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiExpression
+import com.intellij.psi.PsiField
+import com.intellij.psi.PsiLiteral
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.impl.JavaConstantExpressionEvaluator
+import org.jetbrains.kotlin.asJava.elements.KtLightNullabilityAnnotation
+import org.jetbrains.uast.UAnnotation
+import org.jetbrains.uast.UBinaryExpression
+import org.jetbrains.uast.UCallExpression
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UExpression
+import org.jetbrains.uast.ULiteralExpression
+import org.jetbrains.uast.UReferenceExpression
+import org.jetbrains.uast.util.isArrayInitializer
+
+class UAnnotationItem private constructor(
+    override val codebase: PsiBasedCodebase,
+    val uAnnotation: UAnnotation,
+    private val originalName: String?
+) : DefaultAnnotationItem(codebase) {
+    private val qualifiedName = AnnotationItem.mapName(codebase, originalName)
+
+    private var attributes: List<AnnotationAttribute>? = null
+
+    override fun originalName(): String? = originalName
+
+    override fun toString(): String = toSource()
+
+    override fun toSource(target: AnnotationTarget): String {
+        val sb = StringBuilder(60)
+        appendAnnotation(codebase, sb, uAnnotation, originalName, target)
+        return sb.toString()
+    }
+
+    override fun resolve(): ClassItem? {
+        return codebase.findClass(originalName ?: return null)
+    }
+
+    override fun isNonNull(): Boolean {
+        if (uAnnotation.javaPsi is KtLightNullabilityAnnotation &&
+            originalName == ""
+        ) {
+            // Hack/workaround: some UAST annotation nodes do not provide qualified name :=(
+            return true
+        }
+        return super.isNonNull()
+    }
+
+    override fun qualifiedName() = qualifiedName
+
+    override fun attributes(): List<AnnotationAttribute> {
+        if (attributes == null) {
+            val uAttributes = uAnnotation.attributeValues
+            attributes = if (uAttributes.isEmpty()) {
+                emptyList()
+            } else {
+                val list = mutableListOf<AnnotationAttribute>()
+                for (parameter in uAttributes) {
+                    list.add(
+                        UAnnotationAttribute(
+                            codebase,
+                            parameter.name ?: SdkConstants.ATTR_VALUE, parameter.expression
+                        )
+                    )
+                }
+                list
+            }
+        }
+
+        return attributes!!
+    }
+
+    companion object {
+        fun create(codebase: PsiBasedCodebase, uAnnotation: UAnnotation, qualifiedName: String? = uAnnotation.qualifiedName): UAnnotationItem {
+            return UAnnotationItem(codebase, uAnnotation, qualifiedName)
+        }
+
+        fun create(codebase: PsiBasedCodebase, original: UAnnotationItem): UAnnotationItem {
+            return UAnnotationItem(codebase, original.uAnnotation, original.originalName)
+        }
+
+        private fun appendAnnotation(
+            codebase: PsiBasedCodebase,
+            sb: StringBuilder,
+            uAnnotation: UAnnotation,
+            originalName: String?,
+            target: AnnotationTarget
+        ) {
+            val qualifiedName = AnnotationItem.mapName(codebase, originalName, null, target) ?: return
+
+            val attributes = uAnnotation.attributeValues
+            if (attributes.isEmpty()) {
+                sb.append("@$qualifiedName")
+                return
+            }
+
+            sb.append("@")
+            sb.append(qualifiedName)
+            sb.append("(")
+            if (attributes.size == 1 && (attributes[0].name == null || attributes[0].name == SdkConstants.ATTR_VALUE)) {
+                // Special case: omit "value" if it's the only attribute
+                appendValue(codebase, sb, attributes[0].expression, target)
+            } else {
+                var first = true
+                for (attribute in attributes) {
+                    if (first) {
+                        first = false
+                    } else {
+                        sb.append(", ")
+                    }
+                    sb.append(attribute.name ?: SdkConstants.ATTR_VALUE)
+                    sb.append('=')
+                    appendValue(codebase, sb, attribute.expression, target)
+                }
+            }
+            sb.append(")")
+        }
+
+        private fun appendValue(
+            codebase: PsiBasedCodebase,
+            sb: StringBuilder,
+            value: UExpression?,
+            target: AnnotationTarget
+        ) {
+            // Compute annotation string -- we don't just use value.text here
+            // because that may not use fully qualified names, e.g. the source may say
+            //  @RequiresPermission(Manifest.permission.ACCESS_COARSE_LOCATION)
+            // and we want to compute
+            //  @android.support.annotation.RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+            when (value) {
+                null -> sb.append("null")
+                is ULiteralExpression -> sb.append(CodePrinter.constantToSource(value.value))
+                is UReferenceExpression -> {
+                    val resolved = value.resolve()
+                    when (resolved) {
+                        is PsiField -> {
+                            val containing = resolved.containingClass
+                            if (containing != null) {
+                                // If it's a field reference, see if it looks like the field is hidden; if
+                                // so, inline the value
+                                val cls = codebase.findOrCreateClass(containing)
+                                val initializer = resolved.initializer
+                                if (initializer != null) {
+                                    val fieldItem = cls.findField(resolved.name)
+                                    if (fieldItem == null || fieldItem.isHiddenOrRemoved()) {
+                                        // Use the literal value instead
+                                        val source = getConstantSource(initializer)
+                                        if (source != null) {
+                                            sb.append(source)
+                                            return
+                                        }
+                                    }
+                                }
+                                containing.qualifiedName?.let {
+                                    sb.append(it).append('.')
+                                }
+                            }
+
+                            sb.append(resolved.name)
+                        }
+                        is PsiClass -> resolved.qualifiedName?.let { sb.append(it) }
+                        else -> {
+                            sb.append(value.sourcePsi?.text ?: value.asSourceString())
+                        }
+                    }
+                }
+                is UBinaryExpression -> {
+                    appendValue(codebase, sb, value.leftOperand, target)
+                    sb.append(' ')
+                    sb.append(value.operator.text)
+                    sb.append(' ')
+                    appendValue(codebase, sb, value.rightOperand, target)
+                }
+                is UCallExpression -> {
+                    if (value.isArrayInitializer()) {
+                        sb.append('{')
+                        var first = true
+                        for (initializer in value.valueArguments) {
+                            if (first) {
+                                first = false
+                            } else {
+                                sb.append(", ")
+                            }
+                            appendValue(codebase, sb, initializer, target)
+                        }
+                        sb.append('}')
+                    } else {
+                        println("todo")
+                    }
+                }
+                is UAnnotation -> {
+                    appendAnnotation(codebase, sb, value, value.qualifiedName, target)
+                }
+                else -> {
+                    val source = getConstantSource(value)
+                    if (source != null) {
+                        sb.append(source)
+                        return
+                    }
+                    sb.append(value.sourcePsi?.text ?: value.asSourceString())
+                }
+            }
+        }
+
+        private fun getConstantSource(value: UExpression): String? {
+            val constant = value.evaluate()
+            return CodePrinter.constantToExpression(constant)
+        }
+
+        private fun getConstantSource(value: PsiExpression): String? {
+            val constant = JavaConstantExpressionEvaluator.computeConstantExpression(value, false)
+            return CodePrinter.constantToExpression(constant)
+        }
+    }
+}
+
+class UAnnotationAttribute(
+    codebase: PsiBasedCodebase,
+    override val name: String,
+    psiValue: UExpression
+) : AnnotationAttribute {
+    override val value: AnnotationAttributeValue = UAnnotationValue.create(
+        codebase, psiValue
+    )
+}
+
+abstract class UAnnotationValue : AnnotationAttributeValue {
+    companion object {
+        fun create(codebase: PsiBasedCodebase, value: UExpression): UAnnotationValue {
+            return if (value.isArrayInitializer()) {
+                UAnnotationArrayAttributeValue(codebase, value as UCallExpression)
+            } else {
+                UAnnotationSingleAttributeValue(codebase, value)
+            }
+        }
+    }
+
+    override fun toString(): String = toSource()
+}
+
+class UAnnotationSingleAttributeValue(
+    private val codebase: PsiBasedCodebase,
+    private val psiValue: UExpression
+) : UAnnotationValue(), AnnotationSingleAttributeValue {
+    override val valueSource: String = getText(psiValue)
+    override val value: Any?
+        get() {
+            if (psiValue is ULiteralExpression) {
+                val value = psiValue.value
+                if (value != null) {
+                    return value
+                } else if (psiValue.isNull) {
+                    return null
+                }
+            }
+            if (psiValue is PsiLiteral) {
+                return psiValue.value ?: getText(psiValue).removeSurrounding("\"")
+            }
+
+            val value = ConstantEvaluator.evaluate(null, psiValue)
+            if (value != null) {
+                return value
+            }
+
+            return getText(psiValue).removeSurrounding("\"")
+        }
+
+    override fun value(): Any? = value
+
+    override fun toSource(): String = getText(psiValue)
+
+    override fun resolve(): Item? {
+        if (psiValue is UReferenceExpression) {
+            val resolved = psiValue.resolve()
+            when (resolved) {
+                is PsiField -> return codebase.findField(resolved)
+                is PsiClass -> return codebase.findOrCreateClass(resolved)
+                is PsiMethod -> return codebase.findMethod(resolved)
+            }
+        }
+        return null
+    }
+}
+
+class UAnnotationArrayAttributeValue(codebase: PsiBasedCodebase, private val value: UCallExpression) :
+    UAnnotationValue(), AnnotationArrayAttributeValue {
+    override val values = value.valueArguments.map {
+        create(codebase, it)
+    }.toList()
+
+    override fun toSource(): String = getText(value)
+}
+
+private fun getText(element: UElement): String {
+    return element.sourcePsi?.text ?: element.asSourceString()
+}
\ No newline at end of file
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 d74415b4461c5605d010933bbc5b839ed2d80bcb..9dae73fcc89188c829ca3c532ae09441b9c7ab52 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
@@ -73,20 +73,15 @@ class TextTypeItem(
         if (this === other) return true
 
         return when (other) {
-            is TextTypeItem -> toString() == other.toString()
+            // Note: when we support type-use annotations, this is not safe: there could be a string
+            // literal inside which is significant
+            is TextTypeItem -> TypeItem.equalsWithoutSpace(toString(), other.toString())
             is TypeItem -> {
                 val thisString = toTypeString()
                 val otherString = other.toTypeString()
-                if (thisString == otherString) {
+                if (TypeItem.equalsWithoutSpace(thisString, otherString)) {
                     return true
                 }
-                if (thisString[0] == otherString[0]) {
-                    val thisCondensed = thisString.replace(" ", "")
-                    val otherCondensed = otherString.replace(" ", "")
-                    if (thisCondensed == otherCondensed) {
-                        return true
-                    }
-                }
                 if (thisString.startsWith(JAVA_LANG_PREFIX) && thisString.endsWith(otherString) &&
                     thisString.length == otherString.length + JAVA_LANG_PREFIX.length
                 ) {
diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties
index 233e17ea97e1c8f6cf1cc9d0fa73c97a81797f0f..82254daf6b0abda134a44b1124916b63ede1603e 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.5
+metalavaVersion=1.2.7
diff --git a/src/test/java/com/android/tools/metalava/AnnotationsDifferTest.kt b/src/test/java/com/android/tools/metalava/AnnotationsDifferTest.kt
index 9e3999841c5184e169bad33eab5c5bdfcaa6af4a..382174480735cd91367e8d0c3a02788039a11acc 100644
--- a/src/test/java/com/android/tools/metalava/AnnotationsDifferTest.kt
+++ b/src/test/java/com/android/tools/metalava/AnnotationsDifferTest.kt
@@ -22,6 +22,7 @@ import org.junit.Assert.assertTrue
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.TemporaryFolder
+import kotlin.text.Charsets.UTF_8
 
 class AnnotationsDifferTest {
     @get:Rule
@@ -60,7 +61,7 @@ class AnnotationsDifferTest {
         options = Options(emptyArray())
         AnnotationsDiffer(codebase, codebase2).writeDiffSignature(apiFile)
         assertTrue(apiFile.exists())
-        val actual = apiFile.readText(Charsets.UTF_8)
+        val actual = apiFile.readText(UTF_8)
         assertEquals(
             """
             package test.pkg {
diff --git a/src/test/java/com/android/tools/metalava/AnnotationsMergerTest.kt b/src/test/java/com/android/tools/metalava/AnnotationsMergerTest.kt
index cc7eb68c3c8845c98f32e019078176867b691b9d..f1d6a12b9ae8bbc1251567ad8d921c18c9986c15 100644
--- a/src/test/java/com/android/tools/metalava/AnnotationsMergerTest.kt
+++ b/src/test/java/com/android/tools/metalava/AnnotationsMergerTest.kt
@@ -211,24 +211,70 @@ class AnnotationsMergerTest : DriverTest() {
     }
 
     @Test
-    fun `Merge inclusion annotations from Java stub files`() {
+    fun `Merge type use qualifier annotations from Java stub files`() {
+        // See b/123223339
         check(
-            warnings = "src/test/pkg/Example.java:6: error: @test.annotation.Show APIs must also be marked @hide: method test.pkg.Example.cShown() [UnhiddenSystemApi]",
             sourceFiles = *arrayOf(
                 java(
                     """
                 package test.pkg;
 
-                public interface Example {
-                    void aNotAnnotated();
-                    void bHidden();
-                    void cShown();
+                public class Test {
+                    private Test() { }
+                    public void foo(Object... args) { }
                 }
+                """
+                ),
+                libcoreNonNullSource,
+                libcoreNullableSource
+            ),
+            compatibilityMode = false,
+            outputKotlinStyleNulls = false,
+            omitCommonPackages = false,
+            mergeJavaStubAnnotations = """
+                package test.pkg;
 
-                public interface HiddenExample {
-                    void method();
+                public class Test {
+                    public void foo(java.lang.@libcore.util.Nullable Object @libcore.util.NonNull ... args) { throw new RuntimeException("Stub!"); }
                 }
-                """
+                """,
+            api = """
+                package test.pkg {
+                  public class Test {
+                    method public void foo(@androidx.annotation.NonNull java.lang.Object...);
+                  }
+                }
+                """,
+            extraArguments = arrayOf(ARG_HIDE_PACKAGE, "libcore.util")
+        )
+    }
+
+    @Test
+    fun `Merge inclusion annotations from Java stub files`() {
+        check(
+            warnings = "src/test/pkg/Example.annotated.java:6: error: @test.annotation.Show APIs must also be marked @hide: method test.pkg.Example.cShown() [UnhiddenSystemApi]",
+            sourceFiles = *arrayOf(
+                java(
+                    "src/test/pkg/Example.annotated.java",
+                    """
+                    package test.pkg;
+
+                    public interface Example {
+                        void aNotAnnotated();
+                        void bHidden();
+                        void cShown();
+                    }
+                    """
+                ),
+                java(
+                    "src/test/pkg/HiddenExample.annotated.java",
+                    """
+                    package test.pkg;
+
+                    public interface HiddenExample {
+                        void method();
+                    }
+                    """
                 )
             ),
             compatibilityMode = false,
@@ -267,14 +313,15 @@ class AnnotationsMergerTest : DriverTest() {
         check(
             sourceFiles = *arrayOf(
                 java(
+                    "src/test/pkg/Example.annotated.java",
                     """
-                package test.pkg;
+                    package test.pkg;
 
-                public interface Example {
-                    void aNotAnnotated();
-                    void bShown();
-                }
-                """
+                    public interface Example {
+                        void aNotAnnotated();
+                        void bShown();
+                    }
+                    """
                 )
             ),
             compatibilityMode = false,
diff --git a/src/test/java/com/android/tools/metalava/ApiFileTest.kt b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
index 962f9c6147d277003c36d1497da9ec5ebd586d57..9c19b5b170b2d4899e6b7c6f9cb558d573f8d05c 100644
--- a/src/test/java/com/android/tools/metalava/ApiFileTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
@@ -285,7 +285,7 @@ class ApiFileTest : DriverTest() {
                 package androidx.core.util {
                   public final class TestKt {
                     ctor public TestKt();
-                    method public static inline <K, V> android.util.LruCache<K,V> lruCache(int maxSize, kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf = { _, _ -> 1 }, kotlin.jvm.functions.Function1<? super K,? extends V> create = { (V)null }, kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved = { _, _, _, _ ->  });
+                    method public static inline <K, V> android.util.LruCache<K,V> lruCache(int maxSize, kotlin.jvm.functions.Function2<? super K,? super V,java.lang.Integer> sizeOf = { _, _ -> 1 }, kotlin.jvm.functions.Function1<? super K,? extends V> create = { (java.lang.Object)null }, kotlin.jvm.functions.Function4<? super java.lang.Boolean,? super K,? super V,? super V,kotlin.Unit> onEntryRemoved = { _, _, _, _ ->  });
                   }
                 }
                 """,
@@ -457,10 +457,10 @@ class ApiFileTest : 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(@Nullable T t);
+                    method public static inline <reified T> void b(@Nullable T t);
+                    method public static inline <reified T> void e(@Nullable T t);
+                    method public static inline <reified T> void f(@Nullable T, @Nullable T t);
                   }
                 }
                 """,
@@ -484,7 +484,7 @@ class ApiFileTest : DriverTest() {
                 package test.pkg {
                   public final class TestKt {
                     ctor public TestKt();
-                    method public static suspend inline Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit> p);
+                    method public static suspend inline Object hello(@NonNull kotlin.coroutines.Continuation<? super kotlin.Unit> p);
                   }
                 }
                 """,
@@ -613,6 +613,7 @@ class ApiFileTest : DriverTest() {
                 androidxNullableSource
             ),
             api = """
+                // Signature format: 3.0
                 package androidx.util {
                   public class NonNullableJavaPair<F, S> {
                     ctor public NonNullableJavaPair(F, S);
@@ -712,6 +713,7 @@ class ApiFileTest : DriverTest() {
                 androidxNullableSource
             ),
             api = """
+                // Signature format: 3.0
                 package test {
                   public class MyClass {
                     ctor public MyClass();
@@ -1419,12 +1421,12 @@ class ApiFileTest : DriverTest() {
             ),
 
             warnings = """
-                    src/test/pkg/Foo.java:7: warning: Method test.pkg.Foo.method1(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
-                    src/test/pkg/Foo.java:8: warning: Method test.pkg.Foo.method2(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
-                    src/test/pkg/Foo.java:9: warning: Class test.pkg.Foo.Inner1: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
-                    src/test/pkg/Foo.java:10: warning: Class test.pkg.Foo.Inner2: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
-                    src/test/pkg/Foo.java:11: warning: Class test.pkg.Foo.Inner3: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
-                        """,
+                src/test/pkg/Foo.java:7: error: Method test.pkg.Foo.method1(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
+                src/test/pkg/Foo.java:8: error: Method test.pkg.Foo.method2(): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
+                src/test/pkg/Foo.java:9: error: Class test.pkg.Foo.Inner1: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
+                src/test/pkg/Foo.java:10: error: Class test.pkg.Foo.Inner2: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
+                src/test/pkg/Foo.java:11: error: Class test.pkg.Foo.Inner3: @Deprecated annotation (present) and @deprecated doc tag (not present) do not match [DeprecationMismatch]
+                """,
 
             api = """
                     package test.pkg {
@@ -3212,8 +3214,8 @@ class ApiFileTest : DriverTest() {
                     import androidx.annotation.VisibleForTesting;
 
                     @SuppressWarnings({"ClassNameDiffersFromFileName", "WeakerAccess"})
-                    public class ProductionCode {
-                        private ProductionCode() { }
+                    public class ProductionCodeJava {
+                        private ProductionCodeJava() { }
 
                         @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
                         public void shouldBeProtected() {
@@ -3242,7 +3244,7 @@ class ApiFileTest : DriverTest() {
                     package test.pkg
                     import androidx.annotation.VisibleForTesting
 
-                    open class ProductionCode2 private constructor() {
+                    open class ProductionCodeKotlin private constructor() {
 
                         @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
                         fun shouldBeProtected() {
@@ -3270,10 +3272,10 @@ class ApiFileTest : DriverTest() {
             ),
             api = """
                 package test.pkg {
-                  public class ProductionCode {
+                  public class ProductionCodeJava {
                     method protected void shouldBeProtected();
                   }
-                  public class ProductionCode2 {
+                  public class ProductionCodeKotlin {
                     method protected final void shouldBeProtected();
                   }
                 }
@@ -3331,4 +3333,4 @@ class ApiFileTest : DriverTest() {
             )
         )
     }
-}
\ 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 bbc1439e528c29ed8cc7d5cf4d216b48173fdfe9..abbf5f1e3c1649260f99a5177528827b9823747e 100644
--- a/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt
@@ -653,6 +653,7 @@ class ApiFromTextTest : DriverTest() {
     @Test
     fun `Deprecated enum constant`() {
         val source = """
+                // Signature format: 3.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 {
                     method public abstract androidx.annotation.RestrictTo.Scope[] value();
@@ -677,8 +678,9 @@ class ApiFromTextTest : DriverTest() {
     }
 
     @Test
-    fun `Type parameters in v2 format`() {
+    fun `Type parameters in v3 format`() {
         val source = """
+                // Signature format: 3.0
                 package androidx.collection {
                   public class Constants {
                     field public static final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
diff --git a/src/test/java/com/android/tools/metalava/ApiLintTest.kt b/src/test/java/com/android/tools/metalava/ApiLintTest.kt
index dbbc41a8447596abc888d5f0a9212fabe0a362f9..bcb59a86a46f4a0494325f089c1f6e4f5b878295 100644
--- a/src/test/java/com/android/tools/metalava/ApiLintTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiLintTest.kt
@@ -110,7 +110,10 @@ class ApiLintTest : DriverTest() {
                     """
                 )
             ),
-            expectedOutput = "9 new API lint issues were found. See tools/metalava/API-LINT.md for how to handle these."
+            expectedOutput = """
+                9 new API lint issues were found.
+                See tools/metalava/API-LINT.md for how to handle these.
+            """
         )
     }
 
@@ -1453,7 +1456,7 @@ class ApiLintTest : DriverTest() {
                     import java.io.InputStream;
 
                     public class CheckFiles {
-                        public MyClass(Context context, File file) {
+                        public CheckFiles(Context context, File file) {
                         }
                         public void ok(int i, File file) { }
                         public void ok(int i, InputStream stream) { }
@@ -1484,7 +1487,7 @@ class ApiLintTest : DriverTest() {
                     import java.io.InputStream;
 
                     public class CheckFiles {
-                        public MyClass(Context context, File file) {
+                        public CheckFiles(Context context, File file) {
                         }
                         public void ok(int i, File file) { }
                         public void ok(int i, InputStream stream) { }
diff --git a/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt b/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
index 6c01e32849a29e733c05e0fc4d821d9d86937be1..e7b30ebb8bbdd90e3801bd744a7e6005c74a6138 100644
--- a/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
+++ b/src/test/java/com/android/tools/metalava/CompatibilityCheckTest.kt
@@ -19,6 +19,7 @@ package com.android.tools.metalava
 import org.junit.Ignore
 import org.junit.Test
 import java.io.File
+import kotlin.text.Charsets.UTF_8
 
 class
 CompatibilityCheckTest : DriverTest() {
@@ -148,6 +149,11 @@ CompatibilityCheckTest : DriverTest() {
                     method @NonNull public Double convert4(@NonNull Float);
                     method @Nullable public Double convert5(@Nullable Float);
                     method @NonNull public Double convert6(@NonNull Float);
+                    // booleans cannot reasonably be annotated with @Nullable/@NonNull but
+                    // the compiler accepts it and we had a few of these accidentally annotated
+                    // that way in API 28, such as Boolean.getBoolean. Make sure we don't flag
+                    // these as incompatible changes when they're dropped.
+                    method public void convert7(@NonNull boolean);
                   }
                 }
                 """,
@@ -161,6 +167,7 @@ CompatibilityCheckTest : DriverTest() {
                     method public Double convert4(Float);
                     method @NonNull public Double convert5(@NonNull Float);
                     method @Nullable public Double convert6(@Nullable Float);
+                    method public void convert7(boolean);
                   }
                 }
                 """
@@ -283,6 +290,32 @@ CompatibilityCheckTest : DriverTest() {
         )
     }
 
+    @Test
+    fun `Kotlin Coroutines`() {
+        check(
+            warnings = "",
+            compatibilityMode = false,
+            inputKotlinStyleNulls = true,
+            outputKotlinStyleNulls = true,
+            checkCompatibilityApi = """
+                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>);
+                  }
+                }
+                """,
+            signatureSource = """
+                package test.pkg {
+                  public final class TestKt {
+                    ctor public TestKt();
+                    method public static suspend inline Object hello(@NonNull kotlin.coroutines.Continuation<? super kotlin.Unit> p);
+                  }
+                }
+                """
+        )
+    }
+
     @Test
     fun `Add flag new methods but not overrides from platform`() {
         check(
@@ -2248,6 +2281,30 @@ CompatibilityCheckTest : DriverTest() {
         )
     }
 
+    @Test
+    fun `Insignificant type formatting differences`() {
+        check(
+            checkCompatibilityApi = """
+                package test.pkg {
+                  public final class UsageStatsManager {
+                    method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
+                    method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
+                    field public java.util.Map<java.lang.String, java.lang.Integer> map;
+                  }
+                }
+                """,
+            signatureSource = """
+                package test.pkg {
+                  public final class UsageStatsManager {
+                    method public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
+                    method public void setAppStandbyBuckets(java.util.Map<java.lang.String,java.lang.Integer>);
+                    field public java.util.Map<java.lang.String,java.lang.Integer> map;
+                  }
+                }
+                """
+        )
+    }
+
     @Test
     fun `Adding and removing reified`() {
         check(
@@ -2421,7 +2478,7 @@ CompatibilityCheckTest : DriverTest() {
                     println("Couldn't find $signatureFile: Check that pwd for test is correct. Skipping this test.")
                     return
                 }
-                val previousSignatureApi = signatureFile.readText(Charsets.UTF_8)
+                val previousSignatureApi = signatureFile.readText(UTF_8)
 
                 check(
                     checkDoclava1 = false,
diff --git a/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt b/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
index bc85a784ddef4862dbe33b8210f43aeb5de34a58..d309a11500f08fb46e1add323f9d776cc9bc74c5 100644
--- a/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
+++ b/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
@@ -7,6 +7,7 @@ import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Test
 import java.io.File
+import kotlin.text.Charsets.UTF_8
 
 /** Tests for the [DocAnalyzer] which enhances the docs */
 class DocAnalyzerTest : DriverTest() {
@@ -1332,7 +1333,7 @@ class DocAnalyzerTest : DriverTest() {
                  * @deprecated Blah blah blah 1
                  */
                 @Deprecated
-                @androidx.annotation.NonNull
+                @android.annotation.NonNull
                 public java.lang.String toString() { throw new RuntimeException("Stub!"); }
                 /**
                  * My description
@@ -1552,7 +1553,7 @@ class DocAnalyzerTest : DriverTest() {
             )
         )
 
-        val doc = File(html, "test/pkg/LocationManager.html").readText(Charsets.UTF_8)
+        val doc = File(html, "test/pkg/LocationManager.html").readText(UTF_8)
         assertTrue(
             "Did not find matching javadoc fragment in LocationManager.html: actual content is\n$doc",
             doc.contains(
diff --git a/src/test/java/com/android/tools/metalava/DriverTest.kt b/src/test/java/com/android/tools/metalava/DriverTest.kt
index f862f3494177a1bf9ac964a1116617ac84b32c6a..e54df8105f49f68edb3208ffcce5903ae69be7c4 100644
--- a/src/test/java/com/android/tools/metalava/DriverTest.kt
+++ b/src/test/java/com/android/tools/metalava/DriverTest.kt
@@ -35,10 +35,10 @@ import com.android.tools.metalava.model.parseDocument
 import com.android.utils.FileUtils
 import com.android.utils.SdkUtils
 import com.android.utils.StdLogger
-import com.google.common.base.Charsets
 import com.google.common.io.ByteStreams
 import com.google.common.io.Closeables
 import com.google.common.io.Files
+import com.intellij.openapi.util.Disposer
 import org.intellij.lang.annotations.Language
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertNotNull
@@ -95,6 +95,9 @@ abstract class DriverTest {
 
             val sw = StringWriter()
             val writer = PrintWriter(sw)
+
+            Disposer.setDebugMode(true)
+
             if (!com.android.tools.metalava.run(arrayOf(*args), writer, writer)) {
                 val actualFail = cleanupString(sw.toString(), null)
                 if (cleanupString(expectedFail, null).replace(".", "").trim() !=
@@ -125,6 +128,8 @@ abstract class DriverTest {
                 fail("Printed newlines with nothing else")
             }
 
+            Disposer.assertIsEmpty(true)
+
             return printedOutput
         } finally {
             System.setOut(previousOut)
@@ -132,6 +137,9 @@ abstract class DriverTest {
         }
     }
 
+    // This is here to make sure we don't have any unexpected random println's
+    // in the source that are left behind after debugging and ends up polluting
+    // the production output
     class OutputForbiddenWriter(private val stream: String) : ByteArrayOutputStream() {
         override fun write(b: ByteArray?, off: Int, len: Int) {
             fail("Unexpected write directly to $stream")
@@ -761,12 +769,12 @@ abstract class DriverTest {
                 val signature = convert.fromApi
                 val base = convert.baseApi
                 val convertSig = temporaryFolder.newFile("convert-signatures$index.txt")
-                convertSig.writeText(signature.trimIndent(), Charsets.UTF_8)
+                convertSig.writeText(signature.trimIndent(), UTF_8)
                 val extension = convert.format.preferredExtension()
                 val output = temporaryFolder.newFile("convert-output$index$extension")
                 val baseFile = if (base != null) {
                     val baseFile = temporaryFolder.newFile("convert-signatures$index-base.txt")
-                    baseFile.writeText(base.trimIndent(), Charsets.UTF_8)
+                    baseFile.writeText(base.trimIndent(), UTF_8)
                     baseFile
                 } else {
                     null
@@ -1039,7 +1047,7 @@ abstract class DriverTest {
             val actualText = readFile(apiXmlFile, stripBlankLines, trim)
             assertEquals(stripComments(apiXml, stripLineComments = false).trimIndent(), actualText)
             // Make sure we can read back the files we write
-            parseDocument(apiXmlFile.readText(Charsets.UTF_8), false)
+            parseDocument(apiXmlFile.readText(UTF_8), false)
         }
 
         if (baseline != null && baselineFile != null) {
@@ -1205,7 +1213,7 @@ abstract class DriverTest {
                 validateNullabilityTxt.isFile
             )
             val actualReport =
-                Files.asCharSource(validateNullabilityTxt, Charsets.UTF_8).readLines().map(String::trim).toSet()
+                Files.asCharSource(validateNullabilityTxt, UTF_8).readLines().map(String::trim).toSet()
             assertEquals(validateNullability, actualReport)
         }
 
@@ -1320,7 +1328,7 @@ abstract class DriverTest {
             val signatureFile: File =
                 apiFile ?: if (signatureSource != null) {
                     val temp = temporaryFolder.newFile("jdiff-doclava-api.txt")
-                    temp.writeText(signatureSource.trimIndent(), Charsets.UTF_8)
+                    temp.writeText(signatureSource.trimIndent(), UTF_8)
                     temp
                 } else {
                     fail("When verifying XML files with doclava you must either specify signatureSource or api")
@@ -1349,11 +1357,11 @@ abstract class DriverTest {
                 val base = convert.baseApi
                 val strip = convert.strip
                 val convertSig = temporaryFolder.newFile("doclava-jdiff-signatures$index.txt")
-                convertSig.writeText(signature.trimIndent(), Charsets.UTF_8)
+                convertSig.writeText(signature.trimIndent(), UTF_8)
                 val output = temporaryFolder.newFile("doclava-jdiff-output$index.xml")
                 val baseFile = if (base != null) {
                     val baseFile = temporaryFolder.newFile("doclava-jdiff-signatures$index-base.txt")
-                    baseFile.writeText(base.trimIndent(), Charsets.UTF_8)
+                    baseFile.writeText(base.trimIndent(), UTF_8)
                     baseFile
                 } else {
                     null
@@ -1570,7 +1578,7 @@ abstract class DriverTest {
         try {
             val bytes = ByteStreams.toByteArray(stream)
             assertNotNull(bytes)
-            val xml = String(bytes, Charsets.UTF_8).replace("\r\n", "\n")
+            val xml = String(bytes, UTF_8).replace("\r\n", "\n")
             assertEquals(expected.trimIndent().trim(), xml.trimIndent().trim())
         } finally {
             Closeables.closeQuietly(stream)
@@ -1894,6 +1902,10 @@ abstract class DriverTest {
             }
         }
 
+        fun java(to: String, @Language("JAVA") source: String): LintDetectorTest.TestFile {
+            return TestFiles.java(to, source.trimIndent())
+        }
+
         fun java(@Language("JAVA") source: String): LintDetectorTest.TestFile {
             return TestFiles.java(source.trimIndent())
         }
@@ -1907,7 +1919,7 @@ abstract class DriverTest {
         }
 
         private fun readFile(file: File, stripBlankLines: Boolean = false, trim: Boolean = false): String {
-            var apiLines: List<String> = Files.asCharSource(file, Charsets.UTF_8).readLines()
+            var apiLines: List<String> = Files.asCharSource(file, UTF_8).readLines()
             if (stripBlankLines) {
                 apiLines = apiLines.asSequence().filter { it.isNotBlank() }.toList()
             }
diff --git a/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt b/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
index bad1733519bae0fd7cbbe3a1dcf3961ee35ae9a7..2d9269567e19aedc5a3a8c28d1bb2be07db43fbc 100644
--- a/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
+++ b/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
@@ -157,9 +157,6 @@ class ExtractAnnotationsTest : DriverTest() {
                 "test.pkg" to """
                     <?xml version="1.0" encoding="UTF-8"?>
                     <root>
-                      <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 0">
-                        <annotation name="androidx.annotation.NonNull"/>
-                      </item>
                       <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 1">
                         <annotation name="androidx.annotation.LongDef">
                           <val name="flag" val="true" />
@@ -171,24 +168,12 @@ class ExtractAnnotationsTest : DriverTest() {
                           <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT}" />
                         </annotation>
                       </item>
-                      <item name="test.pkg.LongDefTest.Inner boolean isNull(java.lang.String) 0">
-                        <annotation name="androidx.annotation.Nullable"/>
-                      </item>
                       <item name="test.pkg.LongDefTest.Inner void setInner(int) 0">
                         <annotation name="androidx.annotation.LongDef">
                           <val name="flag" val="true" />
                           <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4L}" />
                         </annotation>
                       </item>
-                      <item name="test.pkg.LongDefTestKt TYPE_1">
-                        <annotation name="androidx.annotation.NonNull"/>
-                      </item>
-                      <item name="test.pkg.LongDefTestKt TYPE_2">
-                        <annotation name="androidx.annotation.NonNull"/>
-                      </item>
-                      <item name="test.pkg.LongDefTestKt UNRELATED_TYPE">
-                        <annotation name="androidx.annotation.NonNull"/>
-                      </item>
                     </root>
                 """
             )
@@ -420,6 +405,20 @@ class ExtractAnnotationsTest : DriverTest() {
                 intRangeAnnotationSource,
                 recentlyNullableSource
             ),
+            stubs = arrayOf(
+                """
+                package test.pkg;
+                @SuppressWarnings({"unchecked", "deprecation", "all"})
+                public class Test {
+                public Test() { throw new RuntimeException("Stub!"); }
+                /**
+                 * @param value Value is 10 or greater
+                 */
+                @androidx.annotation.RecentlyNullable
+                public static java.lang.String sayHello(int value) { throw new RuntimeException("Stub!"); }
+                }
+                """
+            ),
             extractAnnotations = mapOf(
                 "test.pkg" to """
                     <?xml version="1.0" encoding="UTF-8"?>
diff --git a/src/test/java/com/android/tools/metalava/KotlinInteropChecksTest.kt b/src/test/java/com/android/tools/metalava/KotlinInteropChecksTest.kt
index f3efebca9faa75b147450c21d21cc6ffb2fde66e..037100f1d1383ce8d2791a571c9a7fa8208ab99d 100644
--- a/src/test/java/com/android/tools/metalava/KotlinInteropChecksTest.kt
+++ b/src/test/java/com/android/tools/metalava/KotlinInteropChecksTest.kt
@@ -91,6 +91,7 @@ class KotlinInteropChecksTest : DriverTest() {
             extraArguments = arrayOf(ARG_CHECK_KOTLIN_INTEROP),
             warnings = """
                 src/test/pkg/Foo.kt:7: warning: Companion object constants like INTEGER_ONE should be marked @JvmField for Java interoperability; see https://android.github.io/kotlin-guides/interop.html#companion-constants [MissingJvmstatic]
+                src/test/pkg/Foo.kt:10: warning: Companion object constants like WRONG2 should be using @JvmField, not @JvmStatic; see https://android.github.io/kotlin-guides/interop.html#companion-constants [MissingJvmstatic]
                 src/test/pkg/Foo.kt:13: warning: Companion object methods like missing should be marked @JvmStatic for Java interoperability; see https://android.github.io/kotlin-guides/interop.html#companion-functions [MissingJvmstatic]
                 """,
             sourceFiles = *arrayOf(
@@ -105,7 +106,7 @@ class KotlinInteropChecksTest : DriverTest() {
                             const val INTEGER_ONE = 1
                             var BIG_INTEGER_ONE = BigInteger.ONE
                             @JvmStatic val WRONG = 2 // not yet flagged
-                            @JvmStatic @JvmField val WRONG2 = 2 // not yet flagged
+                            @JvmStatic @JvmField val WRONG2 = 2
                             @JvmField val ok3 = 3
 
                             fun missing() { }
diff --git a/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt b/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
index 90332ec64124717eb0f022c5f4e79b38c92950d9..a3ddfaf87021e7709380f4888138f80d4d7e3cd3 100644
--- a/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
+++ b/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
@@ -210,12 +210,12 @@ class NullnessMigrationTest : DriverTest() {
                 public java.lang.Double convert0(java.lang.Float f) { throw new RuntimeException("Stub!"); }
                 @androidx.annotation.RecentlyNullable
                 public java.lang.Double convert1(@androidx.annotation.RecentlyNonNull java.lang.Float f) { throw new RuntimeException("Stub!"); }
-                @androidx.annotation.Nullable
-                public java.lang.Double convert2(@androidx.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); }
-                @androidx.annotation.Nullable
-                public java.lang.Double convert3(@androidx.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); }
-                @androidx.annotation.Nullable
-                public java.lang.Double convert4(@androidx.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); }
+                @android.annotation.Nullable
+                public java.lang.Double convert2(@android.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); }
+                @android.annotation.Nullable
+                public java.lang.Double convert3(@android.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); }
+                @android.annotation.Nullable
+                public java.lang.Double convert4(@android.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); }
                 }
                 """
             ),
@@ -259,6 +259,7 @@ class NullnessMigrationTest : DriverTest() {
                 }
                 """,
             api = """
+                // Signature format: 3.0
                 package test.pkg {
                   public class MyTest {
                     ctor public MyTest();
@@ -471,9 +472,9 @@ class NullnessMigrationTest : DriverTest() {
                     public Foo() { throw new RuntimeException("Stub!"); }
                     public static char @androidx.annotation.RecentlyNonNull [] toChars(int codePoint) { throw new RuntimeException("Stub!"); }
                     public static int codePointAt(char @androidx.annotation.RecentlyNonNull [] a, int index) { throw new RuntimeException("Stub!"); }
-                    public <T> T @androidx.annotation.RecentlyNonNull [] toArray(T @androidx.annotation.RecentlyNonNull [] a) { throw new RuntimeException("Stub!"); }
+                    public <T> T @android.annotation.RecentlyNonNull [] toArray(T @androidx.annotation.RecentlyNonNull [] a) { throw new RuntimeException("Stub!"); }
                     @androidx.annotation.NonNull
-                    public static java.lang.String newMethod(@androidx.annotation.Nullable java.lang.String argument) { throw new RuntimeException("Stub!"); }
+                    public static java.lang.String newMethod(@android.annotation.Nullable java.lang.String argument) { throw new RuntimeException("Stub!"); }
                     }
                 """
                 )
@@ -487,8 +488,8 @@ class NullnessMigrationTest : DriverTest() {
                     public static char[] toChars(int codePoint) { throw new RuntimeException("Stub!"); }
                     public static int codePointAt(char[] a, int index) { throw new RuntimeException("Stub!"); }
                     public <T> T[] toArray(T[] a) { throw new RuntimeException("Stub!"); }
-                    @androidx.annotation.NonNull
-                    public static java.lang.String newMethod(@androidx.annotation.Nullable java.lang.String argument) { throw new RuntimeException("Stub!"); }
+                    @android.annotation.NonNull
+                    public static java.lang.String newMethod(@android.annotation.Nullable java.lang.String argument) { throw new RuntimeException("Stub!"); }
                     }
                     """
                 )
diff --git a/src/test/java/com/android/tools/metalava/OptionsTest.kt b/src/test/java/com/android/tools/metalava/OptionsTest.kt
index 7c61c9eac59ae290eb06e303ba54052f58d7753f..36d02bf03f2735ac1277cb4e3293ab13f91f6a2f 100644
--- a/src/test/java/com/android/tools/metalava/OptionsTest.kt
+++ b/src/test/java/com/android/tools/metalava/OptionsTest.kt
@@ -241,6 +241,13 @@ Diffs and Checks:
                                           on the same source tree with different flags at
                                           different times, such as occasionally with
                                           --api-lint.
+--pass-baseline-updates                   Normally, encountering error will fail the
+                                          build, even when updating baselines. This flag
+                                          allows you to tell metalava to continue without
+                                          errors, such that all the baselines in the
+                                          source tree can be updated in one go.
+--delete-empty-baselines                  Whether to delete baseline files if they are
+                                          updated and there is nothing to include.
 
 JDiff:
 --api-xml <file>                          Like --api, but emits the API in the JDiff XML
diff --git a/src/test/java/com/android/tools/metalava/RewriteAnnotationsTest.kt b/src/test/java/com/android/tools/metalava/RewriteAnnotationsTest.kt
index b8c2a416bee3f24671e251c7224095f4340b52df..a95fa2e3a5b5dd19323085510d1eee00be27e982 100644
--- a/src/test/java/com/android/tools/metalava/RewriteAnnotationsTest.kt
+++ b/src/test/java/com/android/tools/metalava/RewriteAnnotationsTest.kt
@@ -26,6 +26,7 @@ import org.junit.Test
 import java.io.File
 import java.lang.reflect.Modifier
 import java.net.URLClassLoader
+import kotlin.text.Charsets.UTF_8
 
 class RewriteAnnotationsTest : DriverTest() {
     @Test
@@ -82,7 +83,7 @@ class RewriteAnnotationsTest : DriverTest() {
             @Retention(CLASS)
             @Target({METHOD, PARAMETER, FIELD})
             @interface RecentlyNullable {}
-        """.trimIndent().trim(), recentlyNull.readText(Charsets.UTF_8).trim().replace("\r\n", "\n")
+        """.trimIndent().trim(), recentlyNull.readText(UTF_8).trim().replace("\r\n", "\n")
         )
     }
 
diff --git a/src/test/java/com/android/tools/metalava/StubsTest.kt b/src/test/java/com/android/tools/metalava/StubsTest.kt
index 807bd9193c3fa65cbbfa4a2cf1c0294990fd9cac..da65e5c80cee2a87718896ce951c4873e2cecad9 100644
--- a/src/test/java/com/android/tools/metalava/StubsTest.kt
+++ b/src/test/java/com/android/tools/metalava/StubsTest.kt
@@ -1352,17 +1352,17 @@ class StubsTest : DriverTest() {
                     /** My class doc */
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
                     public final class Kotlin extends test.pkg.Parent {
-                    public Kotlin(@androidx.annotation.NonNull java.lang.String property1, int arg2) { throw new RuntimeException("Stub!"); }
-                    @androidx.annotation.NonNull
+                    public Kotlin(@android.annotation.NonNull java.lang.String property1, int arg2) { throw new RuntimeException("Stub!"); }
+                    @android.annotation.NonNull
                     public java.lang.String method() { throw new RuntimeException("Stub!"); }
                     /** My method doc */
                     public void otherMethod(boolean ok, int times) { throw new RuntimeException("Stub!"); }
                     /** property doc */
-                    @androidx.annotation.Nullable
+                    @android.annotation.Nullable
                     public java.lang.String getProperty2() { throw new RuntimeException("Stub!"); }
                     /** property doc */
-                    public void setProperty2(@androidx.annotation.Nullable java.lang.String p) { throw new RuntimeException("Stub!"); }
-                    @androidx.annotation.NonNull
+                    public void setProperty2(@android.annotation.Nullable java.lang.String p) { throw new RuntimeException("Stub!"); }
+                    @android.annotation.NonNull
                     public java.lang.String getProperty1() { throw new RuntimeException("Stub!"); }
                     public int someField2;
                     }
@@ -3246,9 +3246,9 @@ class StubsTest : DriverTest() {
                     ctor public Test();
                   }
                 }
-            """, // WRONG: I should include package annotations!
+            """, // WRONG: I should include package annotations in the signature file!
             source = """
-                @androidx.annotation.Nullable
+                @android.annotation.Nullable
                 package test.pkg;
                 """,
             extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation")
@@ -3607,6 +3607,70 @@ class StubsTest : DriverTest() {
         )
     }
 
+    @Test
+    fun `Annotation nested rewriting`() {
+        checkStubs(
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                    package test.pkg;
+
+                    import android.view.Gravity;
+
+                    public class ActionBar {
+                        @ViewDebug.ExportedProperty(category = "layout", mapping = {
+                                @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
+                                @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
+                                @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
+                                @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
+                        })
+                        public int gravity = Gravity.NO_GRAVITY;
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package test.pkg;
+
+                    import java.lang.annotation.ElementType;
+                    import java.lang.annotation.Retention;
+                    import java.lang.annotation.RetentionPolicy;
+                    import java.lang.annotation.Target;
+
+                    public class ViewDebug {
+                        @Target({ElementType.FIELD, ElementType.METHOD})
+                        @Retention(RetentionPolicy.RUNTIME)
+                        public @interface ExportedProperty {
+                            boolean resolveId() default false;
+                            IntToString[] mapping() default {};
+                            IntToString[] indexMapping() default {};
+                            boolean deepExport() default false;
+                            String prefix() default "";
+                            String category() default "";
+                            boolean formatToHexString() default false;
+                            boolean hasAdjacentMapping() default false;
+                        }
+                        @Target({ElementType.TYPE})
+                        @Retention(RetentionPolicy.RUNTIME)
+                        public @interface IntToString {
+                            int from();
+                            String to();
+                        }
+                    }
+                    """
+                )
+            ),
+            source = """
+                package test.pkg;
+                @SuppressWarnings({"unchecked", "deprecation", "all"})
+                public class ActionBar {
+                public ActionBar() { throw new RuntimeException("Stub!"); }
+                @test.pkg.ViewDebug.ExportedProperty(category="layout", mapping={@test.pkg.ViewDebug.IntToString(from=0xffffffff, to="NONE"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.NO_GRAVITY, to="NONE"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.TOP, to="TOP"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.BOTTOM, to="BOTTOM")}) public int gravity = 0; // 0x0
+                }
+                """
+        )
+    }
+
     @Test(expected = FileNotFoundException::class)
     fun `Test update-api should not generate stubs`() {
         check(
diff --git a/src/test/java/com/android/tools/metalava/SymlinkTest.kt b/src/test/java/com/android/tools/metalava/SymlinkTest.kt
index a82194615e446244fb05eca64b5996e0a09acdbe..83916ab16b45ecf1d4e87f0d9cd2f34c6ce53d56 100644
--- a/src/test/java/com/android/tools/metalava/SymlinkTest.kt
+++ b/src/test/java/com/android/tools/metalava/SymlinkTest.kt
@@ -102,22 +102,22 @@ class SymlinkTest : DriverTest() {
                      * @param factor2 This value must never be {@code null}.
                      * @return This value may be {@code null}.
                      */
-                    @androidx.annotation.Nullable
-                    public java.lang.Double method1(@androidx.annotation.NonNull java.lang.Double factor1, @androidx.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); }
+                    @android.annotation.Nullable
+                    public java.lang.Double method1(@android.annotation.NonNull java.lang.Double factor1, @android.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); }
                     /**
                      * These are the docs for method2. It can sometimes return null.
                      * @param factor1 This value must never be {@code null}.
                      * @param factor2 This value must never be {@code null}.
                      */
-                    @androidx.annotation.Nullable
-                    public java.lang.Double method2(@androidx.annotation.NonNull java.lang.Double factor1, @androidx.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); }
+                    @android.annotation.Nullable
+                    public java.lang.Double method2(@android.annotation.NonNull java.lang.Double factor1, @android.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); }
                     /**
                      * @param factor1 This value must never be {@code null}.
                      * @param factor2 This value must never be {@code null}.
                      * @return This value may be {@code null}.
                      */
-                    @androidx.annotation.Nullable
-                    public java.lang.Double method3(@androidx.annotation.NonNull java.lang.Double factor1, @androidx.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); }
+                    @android.annotation.Nullable
+                    public java.lang.Double method3(@android.annotation.NonNull java.lang.Double factor1, @android.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); }
                     }
                     """
                 )
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 112d51304ecd3259c62bf39fb117917882a2475e..908248d16452205d0965bc47e8b1899733487d72 100644
--- a/src/test/java/com/android/tools/metalava/model/TypeItemTest.kt
+++ b/src/test/java/com/android/tools/metalava/model/TypeItemTest.kt
@@ -33,4 +33,22 @@ class TypeItemTest {
             "java.util.List<@NonNull java.lang.String>"
         )
     }
+
+    @Test
+    fun testEqualsWithoutSpace() {
+        assertThat(TypeItem.equalsWithoutSpace("", "")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace(" ", "")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace("", " ")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace(" ", " ")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace("true", "tr ue")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace("tr ue", "true")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace("true", "true ")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace("true ", "true")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace("true ", "true")).isTrue()
+        assertThat(TypeItem.equalsWithoutSpace("true", " true")).isTrue()
+
+        assertThat(TypeItem.equalsWithoutSpace("true", "false")).isFalse()
+        assertThat(TypeItem.equalsWithoutSpace("true", " true  false")).isFalse()
+        assertThat(TypeItem.equalsWithoutSpace("false ", "falser")).isFalse()
+    }
 }
\ No newline at end of file
diff --git a/stub-annotations/src/main/java/android/annotation/NonNull.java b/stub-annotations/src/main/java/android/annotation/NonNull.java
new file mode 100644
index 0000000000000000000000000000000000000000..c826461be7ee6d1209a7470bada9e91d1f532765
--- /dev/null
+++ b/stub-annotations/src/main/java/android/annotation/NonNull.java
@@ -0,0 +1,30 @@
+/*
+ * 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 android.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** Stub only annotation. Do not use directly. */
+@Retention(CLASS)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface NonNull {}
diff --git a/stub-annotations/src/main/java/android/annotation/Nullable.java b/stub-annotations/src/main/java/android/annotation/Nullable.java
new file mode 100644
index 0000000000000000000000000000000000000000..93b73e3de5ad45606241749e69c1f08cfb08cf3d
--- /dev/null
+++ b/stub-annotations/src/main/java/android/annotation/Nullable.java
@@ -0,0 +1,30 @@
+/*
+ * 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 android.annotation;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE_USE;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/** Stub only annotation. Do not use directly. */
+@Retention(CLASS)
+@Target({METHOD, PARAMETER, FIELD})
+public @interface Nullable {}