diff --git a/README.md b/README.md
index 5a46b2c202c2530d73332c3568dd134e75dcfcb5..58ac765f4408ad6da07cdb8ef3fcc37a303db0de 100644
--- a/README.md
+++ b/README.md
@@ -170,6 +170,15 @@ example, you'll see something like this (unless running with --quiet) :
   inconsistency. In metalava the stub files contain **exactly** the same signatures
   as in the signature files.
 
+  (This turned out to be incredibly important; this revealed for example that
+  StringBuilder.setLength(int) was missing from the API signatures since it is
+  a public method inherited from a package protected super class, which the
+  API extraction code in doclava1 missed, but accidentally included in the SDK
+  anyway since it packages package private classes. Metalava strictly applies
+  the exact same API as is listed in the signature files, and once this was
+  hooked up to the build it immediately became apparent that it was missing
+  important methods that should really be part of the API.)
+
 * Metalava can generate reports about nullness annotation coverage (which helps
   target efforts since we plan to annotate the entire API). First, it can
   generate a raw count:
@@ -194,7 +203,6 @@ example, you'll see something like this (unless running with --quiet) :
     324 methods and fields were missing nullness annotations out of 650 total API references.
     API nullness coverage is 50%
 
-    |--------------------------------------------------------------|------------------|
     | Qualified Class Name                                         |      Usage Count |
     |--------------------------------------------------------------|-----------------:|
     | android.os.Parcel                                            |              146 |
@@ -214,11 +222,9 @@ example, you'll see something like this (unless running with --quiet) :
     | android.text.SpannableStringBuilder                          |               23 |
     | android.view.ViewGroup.MarginLayoutParams                    |               21 |
     | ... (99 more items                                           |                  |
-    |--------------------------------------------------------------|------------------|
 
     Top referenced un-annotated members:
 
-    |--------------------------------------------------------------|------------------|
     | Member                                                       |      Usage Count |
     |--------------------------------------------------------------|-----------------:|
     | Parcel.readString()                                          |               62 |
@@ -238,7 +244,6 @@ example, you'll see something like this (unless running with --quiet) :
     | Context.getResources()                                       |               18 |
     | EditText.getText()                                           |               18 |
     | ... (309 more items                                          |                  |
-    |--------------------------------------------------------------|------------------|
 
 
   From this it's clear that it would be useful to start annotating android.os.Parcel
diff --git a/build.gradle b/build.gradle
index 01e847a4ae09ed1013d8db015e91c8a596f4a191..5a8b5cb3182070c78cf6d51a5448020425ff87ff 100644
--- a/build.gradle
+++ b/build.gradle
@@ -18,7 +18,7 @@ apply plugin: 'kotlin'
 apply plugin: 'maven'
 
 group = 'com.android'
-version = '0.9.2'
+version = '0.9.3'
 
 mainClassName = "com.android.tools.metalava.Driver"
 applicationDefaultJvmArgs = ["-ea", "-Xms2g", "-Xmx4g"]
@@ -69,7 +69,7 @@ shadowJar {
    zip64 = true
 }
 
-defaultTasks 'installDist'
+defaultTasks 'clean', 'installDist'
 
 /*
  * With the build server you are given two env variables:
diff --git a/src/main/java/com/android/tools/metalava/AnnotationStatistics.kt b/src/main/java/com/android/tools/metalava/AnnotationStatistics.kt
index f82e67c326ed5e6291b6a848ed66cadd278c9050..1f2e4aa661e95deccb4f2dc8a498df22b43beb7e 100644
--- a/src/main/java/com/android/tools/metalava/AnnotationStatistics.kt
+++ b/src/main/java/com/android/tools/metalava/AnnotationStatistics.kt
@@ -43,9 +43,11 @@ import java.util.zip.ZipFile
 const val CLASS_COLUMN_WIDTH = 60
 const val COUNT_COLUMN_WIDTH = 16
 const val USAGE_REPORT_MAX_ROWS = 15
+/** Sadly gitiles' markdown support doesn't handle tables with top/bottom horizontal edges */
+const val INCLUDE_HORIZONTAL_EDGES = false
 
 class AnnotationStatistics(val api: Codebase) {
-    val apiFilter = ApiPredicate(api)
+    private val apiFilter = ApiPredicate(api)
 
     /** Measure the coverage statistics for the API */
     fun count() {
@@ -191,7 +193,10 @@ class AnnotationStatistics(val api: Codebase) {
         printer: PrintWriter = options.stdout
     ) {
         // Print table in clean Markdown table syntax
-        edge(printer, CLASS_COLUMN_WIDTH + 2, COUNT_COLUMN_WIDTH + 2)
+        @Suppress("ConstantConditionIf")
+        if (INCLUDE_HORIZONTAL_EDGES) {
+            edge(printer, CLASS_COLUMN_WIDTH + COUNT_COLUMN_WIDTH + 7)
+        }
         printer.printf(
             "| %-${CLASS_COLUMN_WIDTH}s | %${COUNT_COLUMN_WIDTH}s |\n",
             labelHeader, countHeader
@@ -215,7 +220,10 @@ class AnnotationStatistics(val api: Codebase) {
                 break
             }
         }
-        edge(printer, CLASS_COLUMN_WIDTH + 2, COUNT_COLUMN_WIDTH + 2)
+        @Suppress("ConstantConditionIf")
+        if (INCLUDE_HORIZONTAL_EDGES) {
+            edge(printer, CLASS_COLUMN_WIDTH + 2, COUNT_COLUMN_WIDTH + 2)
+        }
     }
 
     private fun printClassTable(classes: List<Item>, classCount: MutableMap<Item, Int>) {
@@ -254,6 +262,11 @@ class AnnotationStatistics(val api: Codebase) {
         }
     }
 
+    private fun edge(printer: PrintWriter, max: Int) {
+        dashes(printer, max)
+        printer.println()
+    }
+
     private fun edge(printer: PrintWriter, column1: Int, column2: Int) {
         printer.print("|")
         dashes(printer, column1)
diff --git a/src/main/java/com/android/tools/metalava/ApiAnalyzer.kt b/src/main/java/com/android/tools/metalava/ApiAnalyzer.kt
index bf36ddaeba4c3c2b69f7e70fb1b6017f64a07cc6..3819c1ee043f771e8786afff7f8f5263a5b4fdf6 100644
--- a/src/main/java/com/android/tools/metalava/ApiAnalyzer.kt
+++ b/src/main/java/com/android/tools/metalava/ApiAnalyzer.kt
@@ -412,6 +412,24 @@ class ApiAnalyzer(
             }
         }
 
+        if (compatibility.includePublicMethodsFromHiddenSuperClasses) {
+            // Also add in any concrete public methods from hidden super classes
+            for (superClass in hiddenSuperClasses) {
+                for (method in superClass.methods()) {
+                    if (method.modifiers.isAbstract()) {
+                        continue
+                    }
+                    val name = method.name()
+                    val list = interfaceNames[name] ?: run {
+                        val list = ArrayList<MethodItem>()
+                        interfaceNames[name] = list
+                        list
+                    }
+                    list.add(method)
+                }
+            }
+        }
+
         // Find all methods that are inherited from these classes into our class
         // (making sure that we don't have duplicates, e.g. a method defined by one
         // inherited class and then overridden by another closer one).
@@ -470,9 +488,14 @@ class ApiAnalyzer(
         // interfaces that are listed in this class. Create stubs for them:
         map.values.flatten().forEach {
             val method = cls.createMethod(it)
+            /* Insert comment marker: This is useful for debugging purposes but doesn't
+               belong in the stub
             method.documentation = "// Inlined stub from hidden parent class ${it.containingClass().qualifiedName()}\n" +
                     method.documentation
-            method.inheritedInterfaceMethod = true
+             */
+            if (it.containingClass().isInterface()) {
+                method.inheritedInterfaceMethod = true
+            }
             cls.addMethod(method)
         }
     }
diff --git a/src/main/java/com/android/tools/metalava/Compatibility.kt b/src/main/java/com/android/tools/metalava/Compatibility.kt
index 7abc4ded965e141137856725758a1704330a20b9..a4cced958e053447629a955b42880ee972680d5b 100644
--- a/src/main/java/com/android/tools/metalava/Compatibility.kt
+++ b/src/main/java/com/android/tools/metalava/Compatibility.kt
@@ -156,7 +156,15 @@ class Compatibility(
     /**
      * Whether to include parameter names in the signature file
      */
-    val parameterNames: Boolean = true
+    var parameterNames: Boolean = true
+
+    /**
+     * Whether we should include public methods from super classes.
+     * Docalava1 did not do this in its signature files, but they
+     * were included in stub files. An example of this scenario
+     * is StringBuilder#setLength.
+     */
+    var includePublicMethodsFromHiddenSuperClasses = !compat
 
     // Other examples: sometimes we sort by qualified name, sometimes by full name
 }
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/Constants.kt b/src/main/java/com/android/tools/metalava/Constants.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7badba36ef57f8da227aaf339a55903a797b621a
--- /dev/null
+++ b/src/main/java/com/android/tools/metalava/Constants.kt
@@ -0,0 +1,25 @@
+/*
+ * 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
+
+const val JAVA_LANG_PREFIX = "java.lang."
+const val JAVA_LANG_OBJECT = "java.lang.Object"
+const val JAVA_LANG_STRING = "java.lang.String"
+const val JAVA_LANG_ENUM = "java.lang.Enum"
+const val JAVA_LANG_ANNOTATION = "java.lang.annotation.Annotation"
+const val ANDROID_SUPPORT_ANNOTATION_PREFIX = "android.support.annotation."
+
diff --git a/src/main/java/com/android/tools/metalava/DocAnalyzer.kt b/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
index 4fe3b2fac2a7517ddb0129fff40d6cbdf2890afc..872531b0ca59476aa10c84bdb3b39e6259e6804c 100644
--- a/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
+++ b/src/main/java/com/android/tools/metalava/DocAnalyzer.kt
@@ -113,7 +113,7 @@ class DocAnalyzer(
                 var result: MutableList<String>? = null
                 for (annotation in annotations) {
                     val name = annotation.qualifiedName()
-                    if (name != null && name.endsWith("Thread") && name.startsWith("android.support.annotation.")) {
+                    if (name != null && name.endsWith("Thread") && name.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX)) {
                         if (result == null) {
                             result = mutableListOf()
                         }
@@ -143,7 +143,7 @@ class DocAnalyzer(
                 item: Item, depth: Int
             ) {
                 val name = annotation.qualifiedName()
-                if (name == null || name.startsWith("java.lang.")) {
+                if (name == null || name.startsWith(JAVA_LANG_PREFIX)) {
                     // Ignore java.lang.Retention etc.
                     return
                 }
@@ -311,6 +311,42 @@ class DocAnalyzer(
                     appendDocumentation(sb.toString(), item, true)
                 }
 
+                // Required Features
+                if (name == "android.annotation.RequiresFeature") {
+                    val value = annotation.findAttribute("value")?.leafValues()?.firstOrNull() ?: return
+                    val sb = StringBuilder(100)
+                    val resolved = value.resolve()
+                    val field = if (resolved is FieldItem)
+                        resolved
+                    else {
+                        val v: Any = value.value() ?: value.toSource()
+                        findPermissionField(codebase, v)
+                    }
+                    sb.append("Requires the ")
+                    if (field == null) {
+                        reporter.report(
+                            Errors.MISSING_PERMISSION, item,
+                            "Cannot find feature field for $value required by $item (may be hidden or removed)"
+                        )
+                        sb.append("{@link ${value.toSource()}}")
+
+                    } else {
+                        if (field.isHiddenOrRemoved()) {
+                            reporter.report(
+                                Errors.MISSING_PERMISSION, item,
+                                "Feature field $value required by $item is hidden or removed"
+                            )
+                        }
+
+                        sb.append("{@link ${field.containingClass().qualifiedName()}#${field.name()} ${field.containingClass().simpleName()}#${field.name()}} ")
+                    }
+
+                    sb.append("feature which can be detected using ")
+                    sb.append("{@link android.content.pm.PackageManager#hasSystemFeature(String) ")
+                    sb.append("PackageManager.hasSystemFeature(String)}.")
+                    appendDocumentation(sb.toString(), item, false)
+                }
+
                 // Thread annotations are ignored here because they're handled as a group afterwards
 
                 // TODO: Resource type annotations
diff --git a/src/main/java/com/android/tools/metalava/DocLevel.kt b/src/main/java/com/android/tools/metalava/DocLevel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..633aa6a7660db80dab3d49846289aba3b70892ca
--- /dev/null
+++ b/src/main/java/com/android/tools/metalava/DocLevel.kt
@@ -0,0 +1,26 @@
+/*
+ * 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
+
+/** Javadoc filtering levels */
+enum class DocLevel {
+    PUBLIC,
+    PROTECTED,
+    PACKAGE,
+    PRIVATE,
+    HIDDEN
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/Driver.kt b/src/main/java/com/android/tools/metalava/Driver.kt
index 4b79517f3c2873bb77a4937d6aa4f8149c767e31..1342e22078229906afe940b8f875541b8c566b1b 100644
--- a/src/main/java/com/android/tools/metalava/Driver.kt
+++ b/src/main/java/com/android/tools/metalava/Driver.kt
@@ -99,14 +99,14 @@ fun run(
                 args
             }
 
+        compatibility = Compatibility(compat = Options.useCompatMode(args))
         options = Options(modifiedArgs, stdout, stderr)
-        compatibility = Compatibility(options.compatOutput)
         processFlags()
         stdout.flush()
         stderr.flush()
 
         if (setExitCode && reporter.hasErrors()) {
-            System.exit(-1)
+            exit(-1)
         }
         return true
     } catch (e: DriverException) {
@@ -117,9 +117,7 @@ fun run(
             stdout.println("\n${e.stdout}")
         }
         if (setExitCode) { // always true in production; not set from tests
-            stdout.flush()
-            stderr.flush()
-            System.exit(e.exitCode)
+            exit(e.exitCode)
         }
     }
     stdout.flush()
@@ -127,6 +125,15 @@ fun run(
     return false
 }
 
+private fun exit(exitCode: Int = 0) {
+    if (options.verbose) {
+        options.stdout.println("$PROGRAM_NAME exiting with exit code $exitCode")
+    }
+    options.stdout.flush()
+    options.stderr.flush()
+    System.exit(exitCode)
+}
+
 private fun processFlags() {
     val stopwatch = Stopwatch.createStarted()
 
@@ -290,22 +297,22 @@ private fun loadFromSources(): Codebase {
         KotlinInteropChecks().check(codebase)
     }
 
-    progress("\nInsert missing constructors: ")
     val ignoreShown = options.showUnannotated
 
+    val filterEmit = ApiPredicate(codebase, ignoreShown = ignoreShown, ignoreRemoved = false)
+    val apiEmit = ApiPredicate(codebase, ignoreShown = ignoreShown)
+    val apiReference = ApiPredicate(codebase, ignoreShown = true)
+
+    // Copy methods from soon-to-be-hidden parents into descendant classes, when necessary
+    progress("\nInsert missing stubs methods: ")
+    analyzer.generateInheritedStubs(apiEmit, apiReference)
+
     // Compute default constructors (and add missing package private constructors
     // to make stubs compilable if necessary)
     if (options.stubsDir != null) {
-        val filterEmit = ApiPredicate(codebase, ignoreShown = ignoreShown, ignoreRemoved = false)
+        progress("\nInsert missing constructors: ")
         analyzer.addConstructors(filterEmit)
 
-        val apiEmit = ApiPredicate(codebase, ignoreShown = ignoreShown)
-        val apiReference = ApiPredicate(codebase, ignoreShown = true)
-
-        // Copy methods from soon-to-be-hidden parents into descendant classes, when necessary
-        progress("\nInsert missing stubs methods: ")
-        analyzer.generateInheritedStubs(apiEmit, apiReference)
-
         progress("\nEnhancing docs: ")
         val docAnalyzer = DocAnalyzer(codebase)
         docAnalyzer.enhance()
@@ -320,9 +327,6 @@ private fun loadFromSources(): Codebase {
     progress("\nPerforming misc API checks: ")
     analyzer.performChecks()
 
-//    // TODO: Move the filtering earlier
-//    progress("\nFiltering API: ")
-//    return filterCodebase(codebase)
     return codebase
 }
 
@@ -422,6 +426,11 @@ private fun generateOutputs(codebase: Codebase) {
             { printWriter -> ProguardWriter(printWriter, apiEmit, apiReference) })
     }
 
+    options.sdkValueDir?.let { dir ->
+        dir.mkdirs()
+        SdkFileWriter(codebase, dir).generate()
+    }
+
     options.stubsDir?.let { createStubFiles(it, codebase) }
     // Otherwise, if we've asked to write out a file list, write out the
     // input file list instead
diff --git a/src/main/java/com/android/tools/metalava/Options.kt b/src/main/java/com/android/tools/metalava/Options.kt
index 06fbbda3ab4a0c7e8ec9e97c463603c031a9e57a..5d72e52db7c0ed5b3befd7b9e02adc5c325a573d 100644
--- a/src/main/java/com/android/tools/metalava/Options.kt
+++ b/src/main/java/com/android/tools/metalava/Options.kt
@@ -17,6 +17,7 @@
 package com.android.tools.metalava
 
 import com.android.SdkConstants
+import com.android.sdklib.SdkVersionInfo
 import com.android.tools.lint.annotations.ApiDatabase
 import com.android.tools.metalava.doclava1.Errors
 import com.android.utils.SdkUtils.wrap
@@ -29,12 +30,15 @@ import java.io.IOException
 import java.io.OutputStreamWriter
 import java.io.PrintWriter
 import java.io.StringWriter
+import kotlin.reflect.KMutableProperty1
+import kotlin.reflect.full.memberProperties
 
 /** Global options for the metadata extraction tool */
 var options = Options(emptyArray())
 
 private const val MAX_LINE_WIDTH = 90
 
+private const val ARGS_COMPAT_OUTPUT = "--compatible-output"
 private const val ARG_HELP = "--help"
 private const val ARG_QUIET = "--quiet"
 private const val ARG_VERBOSE = "--verbose"
@@ -44,7 +48,7 @@ private const val ARG_SOURCE_FILES = "--source-files"
 private const val ARG_API = "--api"
 private const val ARG_PRIVATE_API = "--private-api"
 private const val ARG_PRIVATE_DEX_API = "--private-dex-api"
-private const val ARGS_COMPAT_OUTPUT = "--compatible-output"
+private const val ARG_SDK_VALUES = "--sdk-values"
 private const val ARG_REMOVED_API = "--removed-api"
 private const val ARG_MERGE_ANNOTATIONS = "--merge-annotations"
 private const val ARG_INPUT_API_JAR = "--input-api-jar"
@@ -93,6 +97,11 @@ private const val ARG_CURRENT_VERSION = "--current-version"
 private const val ARG_CURRENT_CODENAME = "--current-codename"
 private const val ARG_CURRENT_JAR = "--current-jar"
 private const val ARG_CHECK_KOTLIN_INTEROP = "--check-kotlin-interop"
+private const val ARG_PUBLIC = "--public"
+private const val ARG_PROTECTED = "--protected"
+private const val ARG_PACKAGE = "--package"
+private const val ARG_PRIVATE = "--private"
+private const val ARG_HIDDEN = "--hidden"
 
 class Options(
     args: Array<String>,
@@ -133,7 +142,7 @@ class Options(
      * for more fine grained control (which is not (currently) exposed as individual command line
      * flags.
      */
-    var compatOutput = COMPAT_MODE_BY_DEFAULT && !args.contains("$ARGS_COMPAT_OUTPUT=no")
+    var compatOutput = useCompatMode(args)
 
     /** Whether nullness annotations should be displayed as ?/!/empty instead of with @NonNull/@Nullable. */
     var outputKotlinStyleNulls = !compatOutput
@@ -216,6 +225,9 @@ class Options(
     /** If set, a file to write the private DEX signatures to. Corresponds to --private-dex-api. */
     var privateDexApiFile: File? = null
 
+    /** Path to directory to write SDK values to */
+    var sdkValueDir: File? = null
+
     /** If set, a file to write extracted annotations to. Corresponds to the --extract-annotations flag. */
     var externalAnnotations: File? = null
 
@@ -309,6 +321,9 @@ class Options(
     /** Reads API XML file to apply into documentation */
     var applyApiLevelsXml: File? = null
 
+    /** Level to include for javadoc */
+    var docLevel = DocLevel.PROTECTED
+
     init {
         // Pre-check whether --color/--no-color is present and use that to decide how
         // to emit the banner even before we emit errors
@@ -381,6 +396,8 @@ class Options(
                     )
                 )
 
+                "-sdkvalues", ARG_SDK_VALUES -> sdkValueDir = stringToNewDir(getValue(args, ++index))
+
                 ARG_API, "-api" -> apiFile = stringToNewFile(getValue(args, ++index))
 
                 ARG_PRIVATE_API, "-privateApi" -> privateApiFile = stringToNewFile(getValue(args, ++index))
@@ -409,6 +426,7 @@ class Options(
                 ARG_STUBS_SOURCE_LIST -> stubsSourceList = stringToNewFile(getValue(args, ++index))
 
                 ARG_EXCLUDE_ANNOTATIONS -> generateAnnotations = false
+                "--include-annotations" -> generateAnnotations = true // temporary for tests
 
                 ARG_PROGUARD, "-proguard" -> proguard = stringToNewFile(getValue(args, ++index))
 
@@ -437,6 +455,12 @@ class Options(
                     mutableSkipEmitPackages += packages.split(File.pathSeparatorChar)
                 }
 
+                ARG_PUBLIC, "-public" -> docLevel = DocLevel.PUBLIC
+                ARG_PROTECTED, "-protected" -> docLevel = DocLevel.PROTECTED
+                ARG_PACKAGE, "-package" -> docLevel = DocLevel.PACKAGE
+                ARG_PRIVATE, "-private" -> docLevel = DocLevel.PRIVATE
+                ARG_HIDDEN, "-hidden" -> docLevel = DocLevel.HIDDEN
+
                 ARG_INPUT_API_JAR -> apiJar = stringToExistingFile(getValue(args, ++index))
 
                 ARG_EXTRACT_ANNOTATIONS -> externalAnnotations = stringToNewFile(getValue(args, ++index))
@@ -561,6 +585,17 @@ class Options(
             // doclava1 doc-related flags: only supported here to make this command a drop-in
             // replacement
                 "-referenceonly",
+                "-devsite",
+                "-ignoreJdLinks",
+                "-nodefaultassets",
+                "-parsecomments",
+                "-offlinemode",
+                "-gcmref",
+                "-metadataDebug",
+                "-includePreview",
+                "-staticonly",
+                "-navtreeonly",
+                "-atLinksNavtree",
                 "-nodocs" -> {
                     javadoc(arg)
                 }
@@ -573,6 +608,16 @@ class Options(
                 "-knowntags",
                 "-resourcesdir",
                 "-resourcesoutdir",
+                "-yaml",
+                "-apidocsdir",
+                "-toroot",
+                "-samplegroup",
+                "-samplesdir",
+                "-dac_libraryroot",
+                "-dac_dataname",
+                "-title",
+                "-proofread",
+                "-todo",
                 "-overview" -> {
                     javadoc(arg)
                     index++
@@ -580,11 +625,19 @@ class Options(
 
             // doclava1 flags with two arguments
                 "-federate",
-                "-federationapi" -> {
+                "-federationapi",
+                "-artifact",
+                "-htmldir2" -> {
                     javadoc(arg)
                     index += 2
                 }
 
+            // doclava1 flags with three arguments
+                "-samplecode" -> {
+                    javadoc(arg)
+                    index += 3
+                }
+
             // doclava1 flag with variable number of arguments; skip everything until next arg
                 "-hdf" -> {
                     javadoc(arg)
@@ -635,8 +688,23 @@ class Options(
                         else
                             yesNo(arg.substring(ARGS_COMPAT_OUTPUT.length + 1))
                     } else if (arg.startsWith("-")) {
-                        val usage = getUsage(includeHeader = false, colorize = color)
-                        throw DriverException(stderr = "Invalid argument $arg\n\n$usage")
+                        // Compatibility flag; map to mutable properties in the Compatibility
+                        // class and assign it
+                        val compatibilityArg = findCompatibilityFlag(arg)
+                        if (compatibilityArg != null) {
+                            val dash = arg.indexOf('=')
+                            val value = if (dash == -1) {
+                                true
+                            } else {
+                                arg.substring(dash + 1).toBoolean()
+                            }
+                            compatibilityArg.set(compatibility, value)
+                        } else {
+                            // Some other argument: display usage info and exit
+
+                            val usage = getUsage(includeHeader = false, colorize = color)
+                            throw DriverException(stderr = "Invalid argument $arg\n\n$usage")
+                        }
                     } else {
                         // All args that don't start with "-" are taken to be filenames
                         mutableSources.addAll(stringToExistingFiles(arg))
@@ -685,6 +753,20 @@ class Options(
         checkFlagConsistency()
     }
 
+    private fun findCompatibilityFlag(arg: String): KMutableProperty1<Compatibility, Boolean>? {
+        val index = arg.indexOf('=')
+        val name = arg
+            .substring(0, if (index != -1) index else arg.length)
+            .removePrefix("--")
+            .replace('-', '_')
+        val propertyName = SdkVersionInfo.underlinesToCamelCase(name).decapitalize()
+        return Compatibility::class.memberProperties
+            .filterIsInstance<KMutableProperty1<Compatibility, Boolean>>()
+            .find {
+                it.name == propertyName
+            }
+    }
+
     private fun findAndroidJars(
         androidJarPatterns: List<String>, currentApiLevel: Int,
         currentCodeName: String?, currentJar: File?
@@ -802,7 +884,6 @@ class Options(
                         else -> ""
                     }
             reporter.report(Severity.WARNING, null as String?, message, color = color)
-
         }
     }
 
@@ -1011,6 +1092,13 @@ class Options(
             ARG_SHOW_ANNOTATION + " <annotation class>", "Include the given annotation in the API analysis",
             ARG_SHOW_UNANNOTATED, "Include un-annotated public APIs in the signature file as well",
 
+            "", "\nDocumentation:",
+            ARG_PUBLIC, "Only include elements that are public",
+            ARG_PROTECTED, "Only include elements that are public or protected",
+            ARG_PACKAGE, "Only include elements that are public, protected or package protected",
+            ARG_PRIVATE, "Include all elements except those that are marked hidden",
+            ARG_HIDDEN, "INclude all elements, including hidden",
+
             "", "\nExtracting Signature Files:",
             // TODO: Document --show-annotation!
             ARG_API + " <file>", "Generate a signature descriptor file",
@@ -1030,6 +1118,7 @@ class Options(
                     "@NonNull.",
 
             ARG_PROGUARD + " <file>", "Write a ProGuard keep file for the API",
+            ARG_SDK_VALUES + " <dir>", "Write SDK values files to the given directory",
 
             "", "\nGenerating Stubs:",
             ARG_STUBS + " <dir>", "Generate stub source files for the API",
@@ -1145,4 +1234,11 @@ class Options(
             i += 2
         }
     }
+
+    companion object {
+        /** Whether we should use [Compatibility] mode */
+        fun useCompatMode(args: Array<String>): Boolean {
+            return COMPAT_MODE_BY_DEFAULT && !args.contains("$ARGS_COMPAT_OUTPUT=no")
+        }
+    }
 }
diff --git a/src/main/java/com/android/tools/metalava/SdkFileWriter.kt b/src/main/java/com/android/tools/metalava/SdkFileWriter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c6980c5da2660010cc694e7f2697fefd2c7636d4
--- /dev/null
+++ b/src/main/java/com/android/tools/metalava/SdkFileWriter.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.metalava
+
+import com.android.tools.metalava.model.ClassItem
+import com.android.tools.metalava.model.Codebase
+import com.android.tools.metalava.model.FieldItem
+import java.io.BufferedWriter
+import java.io.File
+import java.io.FileWriter
+import java.io.IOException
+
+// Ported from doclava1
+
+private const val ANDROID_VIEW_VIEW = "android.view.View"
+private const val ANDROID_VIEW_VIEW_GROUP = "android.view.ViewGroup"
+private const val ANDROID_VIEW_VIEW_GROUP_LAYOUT_PARAMS = "android.view.ViewGroup.LayoutParams"
+private const val SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant"
+private const val SDK_CONSTANT_TYPE_ACTIVITY_ACTION =
+    "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION"
+private const val SDK_CONSTANT_TYPE_BROADCAST_ACTION =
+    "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION"
+private const val SDK_CONSTANT_TYPE_SERVICE_ACTION =
+    "android.annotation.SdkConstant.SdkConstantType.SERVICE_ACTION"
+private const val SDK_CONSTANT_TYPE_CATEGORY = "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY"
+private const val SDK_CONSTANT_TYPE_FEATURE = "android.annotation.SdkConstant.SdkConstantType.FEATURE"
+private const val SDK_WIDGET_ANNOTATION = "android.annotation.Widget"
+private const val SDK_LAYOUT_ANNOTATION = "android.annotation.Layout"
+
+private const val TYPE_NONE = 0
+private const val TYPE_WIDGET = 1
+private const val TYPE_LAYOUT = 2
+private const val TYPE_LAYOUT_PARAM = 3
+
+/**
+ * Writes various SDK metadata files packaged with the SDK, such as
+ * {@code platforms/android-27/data/features.txt}
+ */
+class SdkFileWriter(val codebase: Codebase, private val outputDir: java.io.File) {
+    /**
+     * Collect the values used by the Dev tools and write them in files packaged with the SDK
+     */
+    fun generate() {
+        val activityActions = mutableListOf<String>()
+        val broadcastActions = mutableListOf<String>()
+        val serviceActions = mutableListOf<String>()
+        val categories = mutableListOf<String>()
+        val features = mutableListOf<String>()
+        val layouts = mutableListOf<ClassItem>()
+        val widgets = mutableListOf<ClassItem>()
+        val layoutParams = mutableListOf<ClassItem>()
+
+        val classes = codebase.getPackages().allClasses()
+
+        // The topmost LayoutParams class - android.view.ViewGroup.LayoutParams
+        var topLayoutParams: ClassItem? = null
+
+        // Go through all the fields of all the classes, looking SDK stuff.
+        for (clazz in classes) {
+            // first check constant fields for the SdkConstant annotation.
+            val fields = clazz.fields()
+            for (field in fields) {
+                val value = field.initialValue() ?: continue
+                val annotations = field.modifiers.annotations()
+                for (annotation in annotations) {
+                    if (SDK_CONSTANT_ANNOTATION == annotation.qualifiedName()) {
+                        val resolved =
+                            annotation.findAttribute(null)?.leafValues()?.firstOrNull()?.resolve() as? FieldItem
+                                    ?: continue
+                        val type = resolved.containingClass().qualifiedName() + "." + resolved.name()
+                        when {
+                            SDK_CONSTANT_TYPE_ACTIVITY_ACTION == type -> activityActions.add(value.toString())
+                            SDK_CONSTANT_TYPE_BROADCAST_ACTION == type -> broadcastActions.add(value.toString())
+                            SDK_CONSTANT_TYPE_SERVICE_ACTION == type -> serviceActions.add(value.toString())
+                            SDK_CONSTANT_TYPE_CATEGORY == type -> categories.add(value.toString())
+                            SDK_CONSTANT_TYPE_FEATURE == type -> features.add(value.toString())
+                        }
+                    }
+                }
+            }
+
+            // Now check the class for @Widget or if its in the android.widget package
+            // (unless the class is hidden or abstract, or non public)
+            if (!clazz.isHiddenOrRemoved() && clazz.isPublic && !clazz.modifiers.isAbstract()) {
+                var annotated = false
+                val annotations = clazz.modifiers.annotations()
+                if (!annotations.isEmpty()) {
+                    for (annotation in annotations) {
+                        if (SDK_WIDGET_ANNOTATION == annotation.qualifiedName()) {
+                            widgets.add(clazz)
+                            annotated = true
+                            break
+                        } else if (SDK_LAYOUT_ANNOTATION == annotation.qualifiedName()) {
+                            layouts.add(clazz)
+                            annotated = true
+                            break
+                        }
+                    }
+                }
+
+                if (!annotated) {
+                    if (topLayoutParams == null && ANDROID_VIEW_VIEW_GROUP_LAYOUT_PARAMS == clazz.qualifiedName()) {
+                        topLayoutParams = clazz
+                    }
+                    // let's check if this is inside android.widget or android.view
+                    if (isIncludedPackage(clazz)) {
+                        // now we check what this class inherits either from android.view.ViewGroup
+                        // or android.view.View, or android.view.ViewGroup.LayoutParams
+                        val type = checkInheritance(clazz)
+                        when (type) {
+                            TYPE_WIDGET -> widgets.add(clazz)
+                            TYPE_LAYOUT -> layouts.add(clazz)
+                            TYPE_LAYOUT_PARAM -> layoutParams.add(clazz)
+                        }
+                    }
+                }
+            }
+        }
+
+        // now write the files, whether or not the list are empty.
+        // the SDK built requires those files to be present.
+
+        activityActions.sort()
+        writeValues("activity_actions.txt", activityActions)
+
+        broadcastActions.sort()
+        writeValues("broadcast_actions.txt", broadcastActions)
+
+        serviceActions.sort()
+        writeValues("service_actions.txt", serviceActions)
+
+        categories.sort()
+        writeValues("categories.txt", categories)
+
+        features.sort()
+        writeValues("features.txt", features)
+
+        // Before writing the list of classes, we do some checks, to make sure the layout params
+        // are enclosed by a layout class (and not one that has been declared as a widget)
+        var i = 0
+        while (i < layoutParams.size) {
+            var clazz: ClassItem? = layoutParams[i]
+            val containingClass = clazz?.containingClass()
+            var remove = containingClass == null || layouts.indexOf(containingClass) == -1
+            // Also ensure that super classes of the layout params are in android.widget or android.view.
+            while (!remove && clazz != null) {
+                clazz = clazz.superClass() ?: break
+                if (clazz == topLayoutParams) {
+                    break
+                }
+                remove = !isIncludedPackage(clazz)
+            }
+            if (remove) {
+                layoutParams.removeAt(i)
+            } else {
+                i++
+            }
+        }
+
+        writeClasses("widgets.txt", widgets, layouts, layoutParams)
+    }
+
+    /**
+     * Check if the clazz is in package android.view or android.widget
+     */
+    private fun isIncludedPackage(clazz: ClassItem): Boolean {
+        val pckg = clazz.containingPackage().qualifiedName()
+        return "android.widget" == pckg || "android.view" == pckg
+    }
+
+    /**
+     * Writes a list of values into a text files.
+     *
+     * @param name the name of the file to write in the SDK directory
+     * @param values the list of values to write.
+     */
+    private fun writeValues(name: String, values: List<String>) {
+        val pathname = File(outputDir, name)
+        var fw: FileWriter? = null
+        var bw: BufferedWriter? = null
+        try {
+            fw = FileWriter(pathname, false)
+            bw = BufferedWriter(fw)
+
+            for (value in values) {
+                bw.append(value).append('\n')
+            }
+        } catch (e: IOException) {
+            // pass for now
+        } finally {
+            try {
+                if (bw != null) bw.close()
+            } catch (e: IOException) {
+                // pass for now
+            }
+
+            try {
+                if (fw != null) fw.close()
+            } catch (e: IOException) {
+                // pass for now
+            }
+        }
+    }
+
+    /**
+     * Writes the widget/layout/layout param classes into a text files.
+     *
+     * @param name the name of the output file.
+     * @param widgets the list of widget classes to write.
+     * @param layouts the list of layout classes to write.
+     * @param layoutParams the list of layout param classes to write.
+     */
+    private fun writeClasses(
+        name: String,
+        widgets: List<ClassItem>,
+        layouts: List<ClassItem>,
+        layoutParams: List<ClassItem>
+    ) {
+        var fw: FileWriter? = null
+        var bw: BufferedWriter? = null
+        try {
+            val pathname = File(outputDir, name)
+            fw = FileWriter(pathname, false)
+            bw = BufferedWriter(fw)
+
+            // write the 3 types of classes.
+            for (clazz in widgets) {
+                writeClass(bw, clazz, 'W')
+            }
+            for (clazz in layoutParams) {
+                writeClass(bw, clazz, 'P')
+            }
+            for (clazz in layouts) {
+                writeClass(bw, clazz, 'L')
+            }
+        } catch (ignore: IOException) {
+        } finally {
+            bw?.close()
+            fw?.close()
+        }
+    }
+
+    /**
+     * Writes a class name and its super class names into a [BufferedWriter].
+     *
+     * @param writer the BufferedWriter to write into
+     * @param clazz the class to write
+     * @param prefix the prefix to put at the beginning of the line.
+     * @throws IOException
+     */
+    @Throws(IOException::class)
+    private fun writeClass(writer: BufferedWriter, clazz: ClassItem, prefix: Char) {
+        writer.append(prefix).append(clazz.qualifiedName())
+        var superClass: ClassItem? = clazz.superClass()
+        while (superClass != null) {
+            writer.append(' ').append(superClass.qualifiedName())
+            superClass = superClass.superClass()
+        }
+        writer.append('\n')
+    }
+
+    /**
+     * Checks the inheritance of [ClassItem] objects. This method return
+     *
+     *  * [.TYPE_LAYOUT]: if the class extends `android.view.ViewGroup`
+     *  * [.TYPE_WIDGET]: if the class extends `android.view.View`
+     *  * [.TYPE_LAYOUT_PARAM]: if the class extends
+     * `android.view.ViewGroup$LayoutParams`
+     *  * [.TYPE_NONE]: in all other cases
+     *
+     * @param clazz the [ClassItem] to check.
+     */
+    private fun checkInheritance(clazz: ClassItem): Int {
+        return when {
+            ANDROID_VIEW_VIEW_GROUP == clazz.qualifiedName() -> TYPE_LAYOUT
+            ANDROID_VIEW_VIEW == clazz.qualifiedName() -> TYPE_WIDGET
+            ANDROID_VIEW_VIEW_GROUP_LAYOUT_PARAMS == clazz.qualifiedName() -> TYPE_LAYOUT_PARAM
+            else -> {
+                val parent = clazz.superClass()
+                if (parent != null) {
+                    checkInheritance(parent)
+                } else TYPE_NONE
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/StubWriter.kt b/src/main/java/com/android/tools/metalava/StubWriter.kt
index a1a9534df289bfdb21ad1679ee04a5d7ab0534ae..6c2192350f95f1e68d995b6192dea5b70ee5adac 100644
--- a/src/main/java/com/android/tools/metalava/StubWriter.kt
+++ b/src/main/java/com/android/tools/metalava/StubWriter.kt
@@ -24,6 +24,7 @@ import com.android.tools.metalava.model.Codebase
 import com.android.tools.metalava.model.ConstructorItem
 import com.android.tools.metalava.model.FieldItem
 import com.android.tools.metalava.model.Item
+import com.android.tools.metalava.model.MemberItem
 import com.android.tools.metalava.model.MethodItem
 import com.android.tools.metalava.model.ModifierList
 import com.android.tools.metalava.model.PackageItem
@@ -133,7 +134,8 @@ class StubWriter(
             ModifierList.writeAnnotations(
                 list = pkg.modifiers,
                 separateLines = true,
-                writer = writer)
+                writer = writer
+            )
             writer.println("package ${pkg.qualifiedName()};")
 
 
@@ -194,8 +196,13 @@ class StubWriter(
             }
 
             compilationUnit?.getImportStatements(filterReference)?.let {
-                for (importedClass in it) {
-                    writer.println("import $importedClass;")
+                for (item in it) {
+                    when (item) {
+                        is ClassItem ->
+                            writer.println("import ${item.qualifiedName()};")
+                        is MemberItem ->
+                            writer.println("import static ${item.containingClass().qualifiedName()}.${item.name()};")
+                    }
                 }
                 writer.println()
             }
@@ -248,7 +255,7 @@ class StubWriter(
     }
 
     private fun appendDocumentation(item: Item, writer: PrintWriter) {
-        val documentation = item.fullyQualifiedDocumentation()
+        val documentation = item.documentation
         if (documentation.isNotBlank()) {
             val trimmed = trimDocIndent(documentation)
             writer.println(trimmed)
@@ -282,7 +289,7 @@ class StubWriter(
         removeFinal: Boolean = false,
         addPublic: Boolean = false
     ) {
-        if (item.deprecated) {
+        if (item.deprecated && generateAnnotations) {
             writer.write("@Deprecated ")
         }
 
@@ -463,7 +470,7 @@ class StubWriter(
 
         if (isEnum && (method.name() == "values" ||
                     method.name() == "valueOf" && method.parameters().size == 1 &&
-                    method.parameters()[0].type().toTypeString() == "java.lang.String")
+                    method.parameters()[0].type().toTypeString() == JAVA_LANG_STRING)
         ) {
             // Skip the values() and valueOf(String) methods in enums: these are added by
             // the compiler for enums anyway, but was part of the doclava1 signature files
@@ -482,7 +489,7 @@ class StubWriter(
         generateTypeParameterList(typeList = method.typeParameterList(), addSpace = true)
 
         val returnType = method.returnType()
-        writer.print(returnType?.toTypeString(outerAnnotations = false, innerAnnotations = true))
+        writer.print(returnType?.toTypeString(outerAnnotations = false, innerAnnotations = generateAnnotations))
 
         writer.print(' ')
         writer.print(method.name())
@@ -508,7 +515,7 @@ class StubWriter(
 
         appendDocumentation(field, writer)
         appendModifiers(field, false, false)
-        writer.print(field.type().toTypeString(outerAnnotations = false, innerAnnotations = true))
+        writer.print(field.type().toTypeString(outerAnnotations = false, innerAnnotations = generateAnnotations))
         writer.print(' ')
         writer.print(field.name())
         val needsInitialization =
@@ -539,7 +546,12 @@ class StubWriter(
                 writer.print(", ")
             }
             appendModifiers(parameter, false)
-            writer.print(parameter.type().toTypeString(outerAnnotations = false, innerAnnotations = true))
+            writer.print(
+                parameter.type().toTypeString(
+                    outerAnnotations = false,
+                    innerAnnotations = generateAnnotations
+                )
+            )
             writer.print(' ')
             val name = parameter.publicName() ?: parameter.name()
             writer.print(name)
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 d4b03f9a5df244ddd7acd53c87fd4f4b6b59fff2..2bc9cc0c849e2a3c247906661d28fdd6b54ecd76 100644
--- a/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
+++ b/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
@@ -36,6 +36,9 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
+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;
 
 //
@@ -192,7 +195,7 @@ public class ApiFile {
         } else if ("enum".equals(token)) {
             isEnum = true;
             fin = true;
-            ext = "java.lang.Enum";
+            ext = JAVA_LANG_ENUM;
             token = tokenizer.requireToken();
         } else {
             throw new ApiParseException("missing class or interface. got: " + token, tokenizer.getLine());
@@ -233,11 +236,11 @@ public class ApiFile {
                 }
             }
         }
-        if ("java.lang.Enum".equals(ext)) {
+        if (JAVA_LANG_ENUM.equals(ext)) {
             cl.setIsEnum(true);
         } else if (isAnnotation) {
-            api.mapClassToInterface(cl, "java.lang.annotation.Annotation");
-        } else if (api.implementsInterface(cl, "java.lang.annotation.Annotation")) {
+            api.mapClassToInterface(cl, JAVA_LANG_ANNOTATION);
+        } else if (api.implementsInterface(cl, JAVA_LANG_ANNOTATION)) {
             cl.setIsAnnotationType(true);
         }
         if (!"{".equals(token)) {
@@ -674,7 +677,7 @@ public class ApiFile {
                 }
             } else if ("char".equals(type)) {
                 return (char) Integer.parseInt(val);
-            } else if ("java.lang.String".equals(type)) {
+            } else if (JAVA_LANG_STRING.equals(type)) {
                 if ("null".equals(val)) {
                     return null;
                 } else {
@@ -751,7 +754,7 @@ public class ApiFile {
             if ("=".equals(token)) {
                 token = tokenizer.requireToken(false);
                 try {
-                    defaultValue = (String) parseValue("java.lang.String", token);
+                    defaultValue = (String) parseValue(JAVA_LANG_STRING, token);
                 } catch (ApiParseException ex) {
                     ex.line = tokenizer.getLine();
                     throw ex;
diff --git a/src/main/java/com/android/tools/metalava/doclava1/ApiInfo.kt b/src/main/java/com/android/tools/metalava/doclava1/ApiInfo.kt
index 1993aa541cb2486441040ec29715684ce4377643..c9b042a3af6f5d386da2e9bfe3842f2c584aa46f 100644
--- a/src/main/java/com/android/tools/metalava/doclava1/ApiInfo.kt
+++ b/src/main/java/com/android/tools/metalava/doclava1/ApiInfo.kt
@@ -19,6 +19,7 @@ package com.android.tools.metalava.doclava1
 import com.android.annotations.NonNull
 import com.android.tools.metalava.CodebaseComparator
 import com.android.tools.metalava.ComparisonVisitor
+import com.android.tools.metalava.JAVA_LANG_OBJECT
 import com.android.tools.metalava.model.AnnotationItem
 import com.android.tools.metalava.model.ClassItem
 import com.android.tools.metalava.model.Codebase
@@ -118,7 +119,7 @@ class ApiInfo : DefaultCodebase() {
             }
             var scName: String? = mClassToSuper[cl]
             if (scName == null) {
-                scName = "java.lang.Object"
+                scName = JAVA_LANG_OBJECT
             }
             var superclass: TextClassItem? = mAllClasses[scName]
             if (superclass == null) {
@@ -153,7 +154,7 @@ class ApiInfo : DefaultCodebase() {
             // java.lang.Object has no superclass
             var scName: String? = mClassToSuper[cl]
             if (scName == null) {
-                scName = "java.lang.Object"
+                scName = JAVA_LANG_OBJECT
             }
             var superclass: TextClassItem? = mAllClasses[scName]
             if (superclass == null) {
diff --git a/src/main/java/com/android/tools/metalava/doclava1/ApiPredicate.kt b/src/main/java/com/android/tools/metalava/doclava1/ApiPredicate.kt
index 4b5af4c9ba825d62ede1d382e5f39f4711e3ccfb..dfbcaeeda6ddba1153602f82f7d2269164fddd7f 100644
--- a/src/main/java/com/android/tools/metalava/doclava1/ApiPredicate.kt
+++ b/src/main/java/com/android/tools/metalava/doclava1/ApiPredicate.kt
@@ -56,7 +56,7 @@ class ApiPredicate(
             return false
         }
 
-        var visible = member.isPublic || member.isProtected
+        var visible = member.isPublic || member.isProtected // TODO: Should this use checkLevel instead?
         var hidden = member.hidden
         if (!visible || hidden) {
             return false
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 9e50d2d861982ff89d6273b37c6202d4a1b2061a..9f1bbd8cd7d0f79d0e23003b94b6f282d6ea5231 100644
--- a/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
@@ -18,6 +18,8 @@ package com.android.tools.metalava.model
 
 import com.android.SdkConstants
 import com.android.SdkConstants.ATTR_VALUE
+import com.android.tools.metalava.ANDROID_SUPPORT_ANNOTATION_PREFIX
+import com.android.tools.metalava.JAVA_LANG_PREFIX
 import com.android.tools.metalava.NEWLY_NONNULL
 import com.android.tools.metalava.NEWLY_NULLABLE
 import com.android.tools.metalava.Options
@@ -98,7 +100,7 @@ interface AnnotationItem {
     companion object {
         /** Whether the given annotation name is "significant", e.g. should be included in signature files */
         fun isSignificantAnnotation(qualifiedName: String?): Boolean {
-            return qualifiedName?.startsWith("android.support.annotation.") ?: false
+            return qualifiedName?.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX) ?: false
         }
 
         /** The simple name of an annotation, which is the annotation name (not qualified name) prefixed by @ */
@@ -184,7 +186,6 @@ interface AnnotationItem {
                     // These aren't support annotations
                 "android.annotation.AppIdInt",
                 "android.annotation.BroadcastBehavior",
-                "android.annotation.SdkConstant",
                 "android.annotation.SuppressAutoDoc",
                 "android.annotation.SystemApi",
                 "android.annotation.TestApi",
@@ -208,6 +209,8 @@ interface AnnotationItem {
                 }
 
             // Included for analysis, but should not be exported:
+                "android.annotation.SdkConstant",
+                "android.annotation.RequiresFeature",
                 "android.annotation.SystemService" -> return qualifiedName
 
             // Should not be mapped to a different package name:
@@ -230,8 +233,8 @@ interface AnnotationItem {
                         isNonNullAnnotation(qualifiedName) -> "android.support.annotation.NonNull"
 
                     // Support library annotations are all included, as is the built-in stuff like @Retention
-                        qualifiedName.startsWith("android.support.annotation.") -> return qualifiedName
-                        qualifiedName.startsWith("java.lang.") -> return qualifiedName
+                        qualifiedName.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX) -> return qualifiedName
+                        qualifiedName.startsWith(JAVA_LANG_PREFIX) -> return qualifiedName
 
                     // Unknown Android platform annotations
                         qualifiedName.startsWith("android.annotation.") -> {
@@ -276,7 +279,7 @@ interface AnnotationItem {
                 source.startsWith("android.annotation.", 1) -> {
                     "@" + source.substring("@android.annotation.".length)
                 }
-                source.startsWith("android.support.annotation.", 1) -> {
+                source.startsWith(ANDROID_SUPPORT_ANNOTATION_PREFIX, 1) -> {
                     "@" + source.substring("@android.support.annotation.".length)
                 }
                 else -> source
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 ecfa8cda85ea05601616e30faef616cb5f3cba5c..ae74cc15560b52106fdfc90c0ee3ac97ea82be68 100644
--- a/src/main/java/com/android/tools/metalava/model/ClassItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/ClassItem.kt
@@ -17,6 +17,7 @@
 package com.android.tools.metalava.model
 
 import com.android.tools.metalava.ApiAnalyzer
+import com.android.tools.metalava.JAVA_LANG_OBJECT
 import com.android.tools.metalava.compatibility
 import com.android.tools.metalava.model.visitors.ApiVisitor
 import com.android.tools.metalava.model.visitors.ItemVisitor
@@ -169,7 +170,7 @@ interface ClassItem : Item {
     fun typeArgumentClasses(): List<ClassItem> = TODO("Not yet implemented")
 
     fun isJavaLangObject(): Boolean {
-        return qualifiedName() == "java.lang.Object"
+        return qualifiedName() == JAVA_LANG_OBJECT
     }
 
     // Mutation APIs: Used to "fix up" the API hierarchy (in [ApiAnalyzer]) to only expose
@@ -296,7 +297,7 @@ interface ClassItem : Item {
     }
 
     companion object {
-        // Same as doclava1 (modulo the new handling when class names match
+        // Same as doclava1 (modulo the new handling when class names match)
         val comparator: Comparator<in ClassItem> = Comparator { o1, o2 ->
             val delta = o1.fullName().compareTo(o2.fullName())
             if (delta == 0) {
@@ -653,7 +654,6 @@ class VisitCandidate(private val cls: ClassItem, private val visitor: ApiVisitor
                 fields
             }
 
-
             for (constructor in sortedConstructors) {
                 constructor.accept(visitor)
             }
diff --git a/src/main/java/com/android/tools/metalava/model/CompilationUnit.kt b/src/main/java/com/android/tools/metalava/model/CompilationUnit.kt
index 6f7549322b69ba71a7766b806d194f1daf4b5f9b..37529de78d85bc2b245f3abf3f69b031c0395853 100644
--- a/src/main/java/com/android/tools/metalava/model/CompilationUnit.kt
+++ b/src/main/java/com/android/tools/metalava/model/CompilationUnit.kt
@@ -31,5 +31,5 @@ open class CompilationUnit(
 
     override fun toString(): String = "compilation unit ${file.virtualFile?.path}"
 
-    open fun getImportStatements(predicate: Predicate<Item>): Collection<String>? = null
+    open fun getImportStatements(predicate: Predicate<Item>): Collection<Item> = emptyList()
 }
\ No newline at end of file
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 1dc8fd1907512654ec43a1d54b674e62198833c2..028960f41c5cb13613a0cc15db94b93fe7af4cac 100644
--- a/src/main/java/com/android/tools/metalava/model/Item.kt
+++ b/src/main/java/com/android/tools/metalava/model/Item.kt
@@ -175,12 +175,8 @@ interface Item {
     fun hasShowAnnotation(): Boolean = modifiers.hasShowAnnotation()
     fun hasHideAnnotation(): Boolean = modifiers.hasHideAnnotations()
 
-    // TODO: Cache?
     fun checkLevel(): Boolean {
-        if (isHiddenOrRemoved()) {
-            return false
-        }
-        return modifiers.isPublic() || modifiers.isProtected()
+        return modifiers.checkLevel()
     }
 
     fun compilationUnit(): CompilationUnit? {
diff --git a/src/main/java/com/android/tools/metalava/model/ModifierList.kt b/src/main/java/com/android/tools/metalava/model/ModifierList.kt
index 6195682cef5df44c810028f0e8a04dbfbb864e44..edb325a0e216c2f124255758012488d74374b283 100644
--- a/src/main/java/com/android/tools/metalava/model/ModifierList.kt
+++ b/src/main/java/com/android/tools/metalava/model/ModifierList.kt
@@ -16,6 +16,12 @@
 
 package com.android.tools.metalava.model
 
+import com.android.tools.metalava.DocLevel
+import com.android.tools.metalava.DocLevel.HIDDEN
+import com.android.tools.metalava.DocLevel.PACKAGE
+import com.android.tools.metalava.DocLevel.PRIVATE
+import com.android.tools.metalava.DocLevel.PROTECTED
+import com.android.tools.metalava.DocLevel.PUBLIC
 import com.android.tools.metalava.Options
 import com.android.tools.metalava.compatibility
 import com.android.tools.metalava.options
@@ -118,6 +124,27 @@ interface ModifierList {
         }
     }
 
+    /** Returns true if this modifier list has adequate access */
+    fun checkLevel() = checkLevel(options.docLevel)
+
+    /**
+     * Returns true if this modifier list has access modifiers that
+     * are adequate for the given documentation level
+     */
+    fun checkLevel(level: DocLevel): Boolean {
+        if (level == HIDDEN) {
+            return true
+        } else if (owner().isHiddenOrRemoved()) {
+            return false
+        }
+        return when (level) {
+            PUBLIC -> isPublic()
+            PROTECTED -> isPublic() || isProtected()
+            PACKAGE -> !isPrivate()
+            PRIVATE, HIDDEN -> true
+        }
+    }
+
     companion object {
         fun write(
             writer: Writer,
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 937b5ec915a21af288b4663972cc827f65e8d012..072e4e871bdef3a3ba1b135ad625fade53234e18 100644
--- a/src/main/java/com/android/tools/metalava/model/TypeItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/TypeItem.kt
@@ -17,6 +17,8 @@
 package com.android.tools.metalava.model
 
 import com.android.tools.lint.detector.api.ClassContext
+import com.android.tools.metalava.JAVA_LANG_OBJECT
+import com.android.tools.metalava.JAVA_LANG_PREFIX
 import com.android.tools.metalava.compatibility
 import com.android.tools.metalava.options
 import java.util.function.Predicate
@@ -53,7 +55,7 @@ interface TypeItem {
     fun asClass(): ClassItem?
 
     fun toSimpleType(): String {
-        return toTypeString().replace("java.lang.", "")
+        return stripJavaLangPrefix(toTypeString())
     }
 
     val primitive: Boolean
@@ -76,7 +78,7 @@ interface TypeItem {
     }
 
     fun isJavaLangObject(): Boolean {
-        return toTypeString() == "java.lang.Object"
+        return toTypeString() == JAVA_LANG_OBJECT
     }
 
     fun defaultValue(): Any? {
@@ -115,21 +117,28 @@ interface TypeItem {
     fun isTypeParameter(): Boolean = toTypeString().length == 1 // heuristic; accurate implementation in PSI subclass
 
     companion object {
-        private const val JAVA_LANG_PREFIX = "java.lang."
-        private const val ANDROID_SUPPORT_ANNOTATION_PREFIX = "@android.support.annotation."
-
+        /** Shortens types, if configured */
         fun shortenTypes(type: String): String {
-            if (options.omitCommonPackages &&
-                (type.contains("java.lang.") ||
-                        type.contains("@android.support.annotation."))
-            ) {
+            if (options.omitCommonPackages) {
                 var cleaned = type
-                if (options.omitCommonPackages) {
-                    if (cleaned.contains(ANDROID_SUPPORT_ANNOTATION_PREFIX)) {
-                        cleaned = cleaned.replace(ANDROID_SUPPORT_ANNOTATION_PREFIX, "@")
-                    }
+                if (cleaned.contains("@android.support.annotation.")) {
+                    cleaned = cleaned.replace("@android.support.annotation.", "@")
                 }
 
+                return stripJavaLangPrefix(cleaned)
+            }
+
+            return type
+        }
+
+        /**
+         * Removes java.lang. prefixes from types, unless it's in a subpackage such
+         * as java.lang.reflect
+         */
+        fun stripJavaLangPrefix(type: String): String {
+            if (type.contains(JAVA_LANG_PREFIX)) {
+                var cleaned = type
+
                 // Replacing java.lang is harder, since we don't want to operate in sub packages,
                 // e.g. java.lang.String -> String, but java.lang.reflect.Method -> unchanged
                 var index = cleaned.indexOf(JAVA_LANG_PREFIX)
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiBasedCodebase.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiBasedCodebase.kt
index 83a90df1f4d9a09ca0d742b45da562a8ae0af6b6..02fe52defb3c76726db96d4a6e7b427ed388ad00 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiBasedCodebase.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiBasedCodebase.kt
@@ -558,6 +558,10 @@ open class PsiBasedCodebase(override var description: String = "Unknown") : Defa
         val name = top.name
         val fullName = top.qualifiedName ?: return ""
 
+        if (name == fullName) {
+            return ""
+        }
+
         return fullName.substring(0, fullName.length - 1 - name!!.length)
     }
 
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 c7524a0d7438585426c49dd20253c109c248ffde..7b949d3bb89a9ce7061a6e2b4755f4d434070f3e 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
@@ -28,20 +28,13 @@ import com.google.common.base.Splitter
 import com.intellij.lang.jvm.types.JvmReferenceType
 import com.intellij.psi.PsiClass
 import com.intellij.psi.PsiClassType
-import com.intellij.psi.PsiComment
 import com.intellij.psi.PsiCompiledFile
-import com.intellij.psi.PsiElement
-import com.intellij.psi.PsiJavaFile
 import com.intellij.psi.PsiModifier
 import com.intellij.psi.PsiModifierListOwner
 import com.intellij.psi.PsiType
 import com.intellij.psi.PsiTypeParameter
-import com.intellij.psi.PsiWhiteSpace
 import com.intellij.psi.impl.source.PsiClassReferenceType
 import com.intellij.psi.util.PsiUtil
-import org.jetbrains.kotlin.kdoc.psi.api.KDoc
-import org.jetbrains.kotlin.psi.KtFile
-import org.jetbrains.kotlin.psi.psiUtil.startOffset
 
 class PsiClassItem(
     override val codebase: PsiBasedCodebase,
@@ -206,34 +199,7 @@ class PsiClassItem(
             return null
         }
 
-        return object : CompilationUnit(containingFile) {
-            override fun getHeaderComments(): String? {
-                // https://youtrack.jetbrains.com/issue/KT-22135
-                if (file is PsiJavaFile) {
-                    val pkg = file.packageStatement ?: return null
-                    return file.text.substring(0, pkg.startOffset)
-                } else if (file is KtFile) {
-                    var curr: PsiElement? = file.firstChild
-                    var comment: String? = null
-                    while (curr != null) {
-                        if (curr is PsiComment || curr is KDoc) {
-                            val text = curr.text
-                            comment = if (comment != null) {
-                                comment + "\n" + text
-                            } else {
-                                text
-                            }
-                        } else if (curr !is PsiWhiteSpace) {
-                            break
-                        }
-                        curr = curr.nextSibling
-                    }
-                    return comment
-                }
-
-                return super.getHeaderComments()
-            }
-        }
+        return PsiCompilationUnit(codebase, containingFile)
     }
 
     fun findMethod(template: MethodItem): MethodItem? {
@@ -475,7 +441,7 @@ class PsiClassItem(
                 }
             }
 
-            if (hasImplicitDefaultConstructor ) {
+            if (hasImplicitDefaultConstructor) {
                 assert(constructors.isEmpty())
                 constructors.add(PsiConstructorItem.createDefaultConstructor(codebase, item, psiClass))
             }
@@ -553,8 +519,8 @@ class PsiClassItem(
             newClass.fields = classFilter.fields.asSequence()
                 // Preserve sorting order for enums
                 .sortedBy { it.sortingRank }.map {
-                PsiFieldItem.create(codebase, newClass, it as PsiFieldItem)
-            }.toMutableList()
+                    PsiFieldItem.create(codebase, newClass, it as PsiFieldItem)
+                }.toMutableList()
 
 
             newClass.innerClasses = classFilter.innerClasses.map {
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiCompilationUnit.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiCompilationUnit.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f70d21cb73b07134448b7fc2973ef306cfc3a50f
--- /dev/null
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiCompilationUnit.kt
@@ -0,0 +1,169 @@
+/*
+ * 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.tools.metalava.model.ClassItem
+import com.android.tools.metalava.model.CompilationUnit
+import com.android.tools.metalava.model.Item
+import com.android.tools.metalava.model.MemberItem
+import com.android.tools.metalava.model.visitors.ItemVisitor
+import com.google.common.collect.ArrayListMultimap
+import com.google.common.collect.Multimap
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiClassOwner
+import com.intellij.psi.PsiComment
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import com.intellij.psi.PsiJavaFile
+import com.intellij.psi.PsiWhiteSpace
+import org.jetbrains.kotlin.kdoc.psi.api.KDoc
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import java.util.function.Predicate
+
+class PsiCompilationUnit(val codebase: PsiBasedCodebase, containingFile: PsiFile) : CompilationUnit(containingFile) {
+    override fun getHeaderComments(): String? {
+        // https://youtrack.jetbrains.com/issue/KT-22135
+        if (file is PsiJavaFile) {
+            val pkg = file.packageStatement ?: return null
+            return file.text.substring(0, pkg.startOffset)
+        } else if (file is KtFile) {
+            var curr: PsiElement? = file.firstChild
+            var comment: String? = null
+            while (curr != null) {
+                if (curr is PsiComment || curr is KDoc) {
+                    val text = curr.text
+                    comment = if (comment != null) {
+                        comment + "\n" + text
+                    } else {
+                        text
+                    }
+                } else if (curr !is PsiWhiteSpace) {
+                    break
+                }
+                curr = curr.nextSibling
+            }
+            return comment
+        }
+
+        return super.getHeaderComments()
+    }
+
+    override fun getImportStatements(predicate: Predicate<Item>): Collection<Item> {
+        val imports = mutableListOf<Item>()
+
+        if (file is PsiJavaFile) {
+            // TODO: Do we need to deal with static imports?
+            val importList = file.importList
+            if (importList != null) {
+                for (importStatement in importList.importStatements) {
+                    val resolved = importStatement.resolve() ?: continue
+                    if (resolved is PsiClass) {
+                        val classItem = codebase.findClass(resolved) ?: continue
+                        if (predicate.test(classItem)) {
+                            imports.add(classItem)
+                        }
+                    }
+                }
+            }
+
+            for (psiClass in file.classes) {
+                val classItem = codebase.findClass(psiClass) ?: continue
+                if (predicate.test(classItem)) {
+
+                }
+
+            }
+        } else if (file is KtFile) {
+            for (importDirective in file.importDirectives) {
+                val resolved = importDirective.reference?.resolve() ?: continue
+                if (resolved is PsiClass) {
+                    val classItem = codebase.findClass(resolved) ?: continue
+                    if (predicate.test(classItem)) {
+                        imports.add(classItem)
+                    }
+                }
+            }
+        }
+
+        // Next only keep those that are present in any docs; those are the only ones
+        // we need to import
+        if (!imports.isEmpty()) {
+            val map: Multimap<String, Item> = ArrayListMultimap.create()
+            for (item in imports) {
+                if (item is ClassItem) {
+                    map.put(item.simpleName(), item)
+                } else if (item is MemberItem) {
+                    map.put(item.name(), item)
+                }
+            }
+
+            // Compute set of import statements that are actually referenced
+            // from the documentation (we do inexact matching here; we don't
+            // need to have an exact set of imports since it's okay to have
+            // some extras)
+            val result = mutableListOf<Item>()
+            for (cls in classes(predicate)) {
+                cls.accept(object : ItemVisitor() {
+                    override fun visitItem(item: Item) {
+                        val doc = item.documentation
+                        if (doc.isNotBlank()) {
+                            var found: MutableList<String>? = null
+                            for (name in map.keys()) {
+                                if (doc.contains(name)) {
+                                    if (found == null) {
+                                        found = mutableListOf()
+                                    }
+                                    found.add(name)
+                                }
+                            }
+                            found?.let {
+                                for (name in found) {
+                                    val all = map.get(name) ?: continue
+                                    for (referenced in all) {
+                                        if (!result.contains(referenced)) {
+                                            result.add(referenced)
+                                        }
+                                    }
+                                    map.removeAll(name)
+                                }
+                            }
+                        }
+                    }
+                })
+            }
+
+            return result
+        }
+
+        return emptyList()
+    }
+
+    private fun classes(predicate: Predicate<Item>): List<ClassItem> {
+        val topLevel = mutableListOf<ClassItem>()
+        if (file is PsiClassOwner) {
+            for (psiClass in file.classes) {
+                val classItem = codebase.findClass(psiClass) ?: continue
+                if (predicate.test(classItem)) {
+                    topLevel.add(classItem)
+                }
+            }
+        }
+
+        return topLevel
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiItem.kt
index 068d44ba6cdb0542daf8979d970ff8d3e79e213a..d8621a7a5ef3aa1b610f893d179664a771cd9161 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiItem.kt
@@ -215,7 +215,6 @@ abstract class PsiItem(
             }
 
             when (resolved) {
-            // TODO: If same package, do nothing
             // TODO: If not absolute, preserve syntax
                 is PsiClass -> {
                     if (samePackage(resolved)) {
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 89826e4da852964eb51bc8bf12407d514d7ca56e..d711446ed7df3662fc8025e9198e93c12d3ce0ed 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
@@ -291,6 +291,13 @@ open class PsiMethodItem(
             val name = psiMethod.name
             val commentText = javadoc(psiMethod)
             val modifiers = modifiers(codebase, psiMethod, commentText)
+            if (modifiers.isFinal() && containingClass.modifiers.isFinal()) {
+                // The containing class is final, so it is implied that every method is final as well.
+                // No need to apply 'final' to each method. (We do it here rather than just in the
+                // signature emit code since we want to make sure that the signature comparison
+                // 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)
             }
diff --git a/src/test/java/com/android/tools/metalava/AnnotationStatisticsTest.kt b/src/test/java/com/android/tools/metalava/AnnotationStatisticsTest.kt
index c9ba5018d614ece5c5f009e2ff4f6e5b50177a7e..dc749c31bc2a75dfd25d5495dcff98687c8427dc 100644
--- a/src/test/java/com/android/tools/metalava/AnnotationStatisticsTest.kt
+++ b/src/test/java/com/android/tools/metalava/AnnotationStatisticsTest.kt
@@ -138,15 +138,12 @@ class AnnotationStatisticsTest : DriverTest() {
                 6 methods and fields were missing nullness annotations out of 7 total API references.
                 API nullness coverage is 14%
 
-                |--------------------------------------------------------------|------------------|
                 | Qualified Class Name                                         |      Usage Count |
                 |--------------------------------------------------------------|-----------------:|
                 | test.pkg.ApiSurface                                          |                7 |
-                |--------------------------------------------------------------|------------------|
 
                 Top referenced un-annotated members:
 
-                |--------------------------------------------------------------|------------------|
                 | Member                                                       |      Usage Count |
                 |--------------------------------------------------------------|-----------------:|
                 | ApiSurface.missing1(Object)                                  |                2 |
@@ -155,7 +152,6 @@ class AnnotationStatisticsTest : DriverTest() {
                 | ApiSurface.missing3(int)                                     |                1 |
                 | ApiSurface.missing4(Object)                                  |                1 |
                 | ApiSurface.missing5(Object)                                  |                1 |
-                |--------------------------------------------------------------|------------------|
                 """,
             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 e0eb443457d4185eadb510e51ff393691dafd1b8..92a5a0c5bd046871002aa5c48ff343e19d23f213 100644
--- a/src/test/java/com/android/tools/metalava/ApiFileTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
@@ -168,7 +168,7 @@ class ApiFileTest : DriverTest() {
                 package test.pkg {
                   public final class Foo {
                     ctor public Foo();
-                    method public final void error(int p = "42", Integer? int2 = "null");
+                    method public void error(int p = "42", Integer? int2 = "null");
                   }
                 }
                 """,
@@ -216,10 +216,10 @@ class ApiFileTest : DriverTest() {
                 package test.pkg {
                   public final class Kotlin extends test.pkg.Parent {
                     ctor public Kotlin(java.lang.String property1, int arg2);
-                    method public final java.lang.String getProperty1();
-                    method public final java.lang.String getProperty2();
-                    method public final void otherMethod(boolean ok, int times);
-                    method public final void setProperty2(java.lang.String p);
+                    method public java.lang.String getProperty1();
+                    method public java.lang.String getProperty2();
+                    method public void otherMethod(boolean ok, int times);
+                    method public void setProperty2(java.lang.String p);
                     field public static final test.pkg.Kotlin.Companion Companion;
                     field public static final int MY_CONST = 42; // 0x2a
                     field public int someField2;
@@ -237,9 +237,9 @@ class ApiFileTest : DriverTest() {
             privateApi = """
                 package test.pkg {
                   public final class Kotlin extends test.pkg.Parent {
-                    method internal final boolean getMyHiddenVar${"$"}lintWithKotlin();
-                    method internal final void myHiddenMethod${"$"}lintWithKotlin();
-                    method internal final void setMyHiddenVar${"$"}lintWithKotlin(boolean p);
+                    method internal boolean getMyHiddenVar${"$"}lintWithKotlin();
+                    method internal void myHiddenMethod${"$"}lintWithKotlin();
+                    method internal void setMyHiddenVar${"$"}lintWithKotlin(boolean p);
                     field internal boolean myHiddenVar;
                     field private final java.lang.String property1;
                     field private java.lang.String property2;
@@ -250,7 +250,7 @@ class ApiFileTest : DriverTest() {
                   }
                    internal static final class Kotlin.myHiddenClass extends kotlin.Unit {
                     ctor public Kotlin.myHiddenClass();
-                    method internal final test.pkg.Kotlin.myHiddenClass copy();
+                    method internal test.pkg.Kotlin.myHiddenClass copy();
                   }
                 }
                 """,
@@ -396,8 +396,8 @@ class ApiFileTest : DriverTest() {
                   }
                   public final class NonNullableKotlinPair<F, S> {
                     ctor public NonNullableKotlinPair(F first, S second);
-                    method public final F getFirst();
-                    method public final S getSecond();
+                    method public F getFirst();
+                    method public S getSecond();
                   }
                   public class NullableJavaPair<F, S> {
                     ctor public NullableJavaPair(F?, S?);
@@ -406,8 +406,8 @@ class ApiFileTest : DriverTest() {
                   }
                   public final class NullableKotlinPair<F, S> {
                     ctor public NullableKotlinPair(F? first, S? second);
-                    method public final F? getFirst();
-                    method public final S? getSecond();
+                    method public F? getFirst();
+                    method public S? getSecond();
                   }
                   public class PlatformJavaPair<F, S> {
                     ctor public PlatformJavaPair(F!, S!);
@@ -416,7 +416,7 @@ class ApiFileTest : DriverTest() {
                   }
                   public final class TestKt {
                     ctor public TestKt();
-                    method public static final operator <F, S> F! component1(androidx.util.PlatformJavaPair<F,S>);
+                    method public static operator <F, S> F! component1(androidx.util.PlatformJavaPair<F,S>);
                   }
                 }
                 """,
@@ -704,6 +704,75 @@ class ApiFileTest : DriverTest() {
         )
     }
 
+    @Test
+    fun `Do not include inherited public methods from private parents in compat mode`() {
+        // Real life example: StringBuilder.setLength, in compat mode
+        check(
+            compatibilityMode = true,
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                    package test.pkg;
+                    public class MyStringBuilder extends AbstractMyStringBuilder {
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package test.pkg;
+                    class AbstractMyStringBuilder {
+                        public void setLength(int length) {
+                        }
+                    }
+                    """
+                )
+            ),
+            api = """
+                package test.pkg {
+                  public class MyStringBuilder {
+                    ctor public MyStringBuilder();
+                  }
+                }
+                """
+        )
+    }
+
+    @Test
+    fun `Include inherited public methods from private parents`() {
+        // In non-compat mode, include public methods from hidden parents too.
+        // Real life example: StringBuilder.setLength
+        // This is just like the above test, but with compat mode disabled.
+        check(
+            compatibilityMode = false,
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                    package test.pkg;
+                    public class MyStringBuilder extends AbstractMyStringBuilder {
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package test.pkg;
+                    class AbstractMyStringBuilder {
+                        public void setLength(int length) {
+                        }
+                    }
+                    """
+                )
+            ),
+            api = """
+                package test.pkg {
+                  public class MyStringBuilder {
+                    ctor public MyStringBuilder();
+                    method public void setLength(int);
+                  }
+                }
+                """
+        )
+    }
+
     @Test
     fun `Annotation class extraction, non-compat mode`() {
         @Suppress("ConstantConditionIf")
@@ -1368,7 +1437,53 @@ class ApiFileTest : DriverTest() {
     @Test
     fun `Inheriting from package private classes, package private class should be included`() {
         check(
-            checkDoclava1 = true,
+            checkDoclava1 = false, // doclava1 does not include method2, which it should
+            compatibilityMode = false,
+            sourceFiles =
+            *arrayOf(
+                java(
+                    """
+                    package test.pkg;
+                    @SuppressWarnings("ALL")
+                    public class MyClass extends HiddenParent {
+                        public void method1() { }
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package test.pkg;
+                    @SuppressWarnings("ALL")
+                    class HiddenParent {
+                        public static final String CONSTANT = "MyConstant";
+                        protected int mContext;
+                        public void method2() { }
+                    }
+                    """
+                )
+            ),
+            warnings = "",
+            api = """
+                    package test.pkg {
+                      public class MyClass {
+                        ctor public MyClass();
+                        method public void method1();
+                        method public void method2();
+                      }
+                    }
+            """
+        )
+    }
+
+    @Test
+    fun `Using compatibility flag manually`() {
+        // Like previous test, but using compatibility mode and explicitly turning on
+        // the hidden super class compatibility flag. This test is mostly intended
+        // to test the flag handling for individual compatibility flags.
+        check(
+            checkDoclava1 = false, // doclava1 does not include method2, which it should
+            compatibilityMode = true,
+            extraArguments = arrayOf("--include-public-methods-from-hidden-super-classes=true"),
             sourceFiles =
             *arrayOf(
                 java(
@@ -1398,6 +1513,7 @@ class ApiFileTest : DriverTest() {
                       public class MyClass {
                         ctor public MyClass();
                         method public void method1();
+                        method public void method2();
                       }
                     }
             """
@@ -1877,7 +1993,7 @@ class ApiFileTest : DriverTest() {
             api = """
                 package test.pkg {
                   public final class -Foo {
-                    method public static final void printHelloWorld(java.lang.String);
+                    method public static void printHelloWorld(java.lang.String);
                   }
                 }
                 """
diff --git a/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt b/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
index efa6646f332c4c788fe801f89becfa0669a9e980..4a1a86a6f32ec5a2fb4b3df984eaab7064b27edb 100644
--- a/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
+++ b/src/test/java/com/android/tools/metalava/DocAnalyzerTest.kt
@@ -184,6 +184,7 @@ class DocAnalyzerTest : DriverTest() {
             stubs = arrayOf(
                 """
                 package test.pkg;
+                import android.Manifest;
                 @SuppressWarnings({"unchecked", "deprecation", "all"})
                 public class PermissionTest {
                 public PermissionTest() { throw new RuntimeException("Stub!"); }
@@ -917,7 +918,7 @@ class DocAnalyzerTest : DriverTest() {
     }
 
     @Test
-    fun `Merge API levels)`() {
+    fun `Merge API levels`() {
         check(
             sourceFiles = *arrayOf(
                 java(
@@ -977,7 +978,7 @@ class DocAnalyzerTest : DriverTest() {
     }
 
     @Test
-    fun `Merge deprecation levels)`() {
+    fun `Merge deprecation levels`() {
         check(
             sourceFiles = *arrayOf(
                 java(
@@ -1041,7 +1042,7 @@ class DocAnalyzerTest : DriverTest() {
 
 
     @Test
-    fun `Generate overview html docs)`() {
+    fun `Generate overview html docs`() {
         // If a codebase provides overview.html files in the a public package,
         // make sure that we include this in the exported stubs folder as well!
         check(
@@ -1071,4 +1072,50 @@ class DocAnalyzerTest : DriverTest() {
             )
         )
     }
+
+    @Test
+    fun `Check RequiresFeature handling`() {
+        check(
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                    package test.pkg;
+                    import android.annotation.RequiresFeature;
+                    import android.content.pm.PackageManager;
+                    @SuppressWarnings("WeakerAccess")
+                    @RequiresFeature(PackageManager.FEATURE_LOCATION)
+                    public class LocationManager {
+                    }
+                    """
+                ),
+                java(
+                    """
+                    package android.content.pm;
+                    public abstract class PackageManager {
+                        public static final String FEATURE_LOCATION = "android.hardware.location";
+                    }
+                    """
+                ),
+
+                requiresFeatureSource
+            ),
+            checkCompilation = true,
+            checkDoclava1 = false,
+            stubs = arrayOf(
+                """
+                package test.pkg;
+                import android.content.pm.PackageManager;
+                /**
+                 * Requires the {@link android.content.pm.PackageManager#FEATURE_LOCATION PackageManager#FEATURE_LOCATION} feature which can be detected using {@link android.content.pm.PackageManager#hasSystemFeature(String) PackageManager.hasSystemFeature(String)}.
+                 */
+                @SuppressWarnings({"unchecked", "deprecation", "all"})
+                public class LocationManager {
+                public LocationManager() { throw new RuntimeException("Stub!"); }
+                }
+                """
+            )
+        )
+    }
+
+
 }
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/DriverTest.kt b/src/test/java/com/android/tools/metalava/DriverTest.kt
index 2a5d0e74660c1da1885338fbc0f974dc8d0908af..e8888781fae2f54f74119814d4f547d7694dc34c 100644
--- a/src/test/java/com/android/tools/metalava/DriverTest.kt
+++ b/src/test/java/com/android/tools/metalava/DriverTest.kt
@@ -187,7 +187,19 @@ abstract class DriverTest {
         /** Whether we should warn about super classes that are stripped because they are hidden */
         includeStrippedSuperclassWarnings: Boolean = false,
         /** Apply level to XML */
-        applyApiLevelsXml: String? = null
+        applyApiLevelsXml: String? = null,
+        /** Corresponds to SDK constants file broadcast_actions.txt */
+        sdk_broadcast_actions: String? = null,
+        /** Corresponds to SDK constants file activity_actions.txt */
+        sdk_activity_actions: String? = null,
+        /** Corresponds to SDK constants file service_actions.txt */
+        sdk_service_actions: String? = null,
+        /** Corresponds to SDK constants file categories.txt */
+        sdk_categories: String? = null,
+        /** Corresponds to SDK constants file features.txt */
+        sdk_features: String? = null,
+        /** Corresponds to SDK constants file widgets.txt */
+        sdk_widgets: String? = null
     ) {
         System.setProperty("METALAVA_TESTS_RUNNING", VALUE_TRUE)
 
@@ -425,6 +437,23 @@ abstract class DriverTest {
                 emptyArray()
             }
 
+        val sdkFilesDir: File?
+        val sdkFilesArgs: Array<String>
+        if (sdk_broadcast_actions != null ||
+            sdk_activity_actions != null ||
+            sdk_service_actions != null ||
+            sdk_categories != null ||
+            sdk_features != null ||
+            sdk_widgets != null
+        ) {
+            val dir = File(project, "sdk-files")
+            sdkFilesArgs = arrayOf("--sdk-values", dir.path)
+            sdkFilesDir = dir
+        } else {
+            sdkFilesArgs = emptyArray()
+            sdkFilesDir = null
+        }
+
         val actualOutput = runDriver(
             "--no-color",
             "--no-banner",
@@ -435,6 +464,10 @@ abstract class DriverTest {
             //"--unhide-classpath-classes",
             "--allow-referencing-unknown-classes",
 
+            // Annotation generation temporarily turned off by default while integrating with
+            // SDK builds; tests need these
+            "--include-annotations",
+
             "--sourcepath",
             sourcePath,
             "--classpath",
@@ -462,6 +495,7 @@ abstract class DriverTest {
             *applyApiLevelsXmlArgs,
             *showAnnotationArguments,
             *showUnannotatedArgs,
+            *sdkFilesArgs,
             *importedPackageArgs.toTypedArray(),
             *skipEmitPackagesArgs.toTypedArray(),
             *extraArguments,
@@ -524,6 +558,36 @@ abstract class DriverTest {
             assertEquals(stripComments(proguard, stripLineComments = false).trimIndent(), expectedProguard.trim())
         }
 
+        if (sdk_broadcast_actions != null) {
+            val actual = readFile(File(sdkFilesDir, "broadcast_actions.txt"), stripBlankLines, trim)
+            assertEquals(sdk_broadcast_actions.trimIndent().trim(), actual.trim())
+        }
+
+        if (sdk_activity_actions != null) {
+            val actual = readFile(File(sdkFilesDir, "activity_actions.txt"), stripBlankLines, trim)
+            assertEquals(sdk_activity_actions.trimIndent().trim(), actual.trim())
+        }
+
+        if (sdk_service_actions != null) {
+            val actual = readFile(File(sdkFilesDir, "service_actions.txt"), stripBlankLines, trim)
+            assertEquals(sdk_service_actions.trimIndent().trim(), actual.trim())
+        }
+
+        if (sdk_categories != null) {
+            val actual = readFile(File(sdkFilesDir, "categories.txt"), stripBlankLines, trim)
+            assertEquals(sdk_categories.trimIndent().trim(), actual.trim())
+        }
+
+        if (sdk_features != null) {
+            val actual = readFile(File(sdkFilesDir, "features.txt"), stripBlankLines, trim)
+            assertEquals(sdk_features.trimIndent().trim(), actual.trim())
+        }
+
+        if (sdk_widgets != null) {
+            val actual = readFile(File(sdkFilesDir, "widgets.txt"), stripBlankLines, trim)
+            assertEquals(sdk_widgets.trimIndent().trim(), actual.trim())
+        }
+
         if (warnings != null) {
             assertEquals(
                 warnings.trimIndent().trim(),
@@ -960,102 +1024,129 @@ val intRangeAnnotationSource: TestFile = java(
             long from() default Long.MIN_VALUE;
             long to() default Long.MAX_VALUE;
         }
-                """
+        """
 ).indented()
 
 val intDefAnnotationSource: TestFile = java(
     """
-package android.annotation;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-@Retention(SOURCE)
-@Target({ANNOTATION_TYPE})
-public @interface IntDef {
-    long[] value() default {};
-    boolean flag() default false;
-}
-"""
-)
+    package android.annotation;
+    import java.lang.annotation.Retention;
+    import java.lang.annotation.RetentionPolicy;
+    import java.lang.annotation.Target;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    @Retention(SOURCE)
+    @Target({ANNOTATION_TYPE})
+    public @interface IntDef {
+        long[] value() default {};
+        boolean flag() default false;
+    }
+    """
+).indented()
 
 val nonNullSource: TestFile = java(
     """
-package android.annotation;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
+    package android.annotation;
+    import java.lang.annotation.Retention;
+    import java.lang.annotation.Target;
+
+    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.RetentionPolicy.SOURCE;
+    /**
+     * Denotes that a parameter, field or method return value can never be null.
+     * @paramDoc This value must never be {@code null}.
+     * @returnDoc This value will never be {@code null}.
+     * @hide
+     */
+    @SuppressWarnings({"WeakerAccess", "JavaDoc"})
+    @Retention(SOURCE)
+    @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+    public @interface NonNull {
+    }
+    """
+).indented()
 
-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.RetentionPolicy.SOURCE;
-/**
- * Denotes that a parameter, field or method return value can never be null.
- * @paramDoc This value must never be {@code null}.
- * @returnDoc This value will never be {@code null}.
- * @hide
- */
-@SuppressWarnings({"WeakerAccess", "JavaDoc"})
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD, TYPE_USE})
-public @interface NonNull {
-}
+val requiresPermissionSource: TestFile = java(
+    """
+    package android.annotation;
+    import java.lang.annotation.*;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    @Retention(SOURCE)
+    @Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
+    public @interface RequiresPermission {
+        String value() default "";
+        String[] allOf() default {};
+        String[] anyOf() default {};
+        boolean conditional() default false;
+    }
+                    """
+).indented()
 
-"""
-)
+val requiresFeatureSource: TestFile = java(
+    """
+    package android.annotation;
+    import java.lang.annotation.*;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    @Retention(SOURCE)
+    @Target({TYPE,FIELD,METHOD,CONSTRUCTOR})
+    public @interface RequiresFeature {
+        String value();
+    }
+            """
+).indented()
 
-val requiresPermissionSource: TestFile = java(
+val sdkConstantSource: TestFile = java(
     """
-package android.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-@Retention(SOURCE)
-@Target({ANNOTATION_TYPE,METHOD,CONSTRUCTOR,FIELD,PARAMETER})
-public @interface RequiresPermission {
-    String value() default "";
-    String[] allOf() default {};
-    String[] anyOf() default {};
-    boolean conditional() default false;
-}
-                """
-)
+    package android.annotation;
+    import java.lang.annotation.*;
+    @Target({ ElementType.FIELD })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SdkConstant {
+        enum SdkConstantType {
+            ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION, INTENT_CATEGORY, FEATURE;
+        }
+        SdkConstantType value();
+    }
+        """
+).indented()
 
 val nullableSource: TestFile = java(
     """
-package android.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-/**
- * Denotes that a parameter, field or method return value can be null.
- * @paramDoc This value may be {@code null}.
- * @returnDoc This value may be {@code null}.
- * @hide
- */
-@SuppressWarnings({"WeakerAccess", "JavaDoc"})
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD, TYPE_USE})
-public @interface Nullable {
-}
-                """
-)
+    package android.annotation;
+    import java.lang.annotation.*;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    /**
+     * Denotes that a parameter, field or method return value can be null.
+     * @paramDoc This value may be {@code null}.
+     * @returnDoc This value may be {@code null}.
+     * @hide
+     */
+    @SuppressWarnings({"WeakerAccess", "JavaDoc"})
+    @Retention(SOURCE)
+    @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+    public @interface Nullable {
+    }
+    """
+).indented()
 
 val supportNonNullSource: TestFile = java(
     """
-package android.support.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-@SuppressWarnings("WeakerAccess")
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD, TYPE_USE})
-public @interface NonNull {
-}
-
-"""
-)
+    package android.support.annotation;
+    import java.lang.annotation.*;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    @SuppressWarnings("WeakerAccess")
+    @Retention(SOURCE)
+    @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+    public @interface NonNull {
+    }
+    """
+).indented()
 
 val supportNullableSource: TestFile = java(
     """
@@ -1073,80 +1164,78 @@ public @interface Nullable {
 
 val supportParameterName: TestFile = java(
     """
-package android.support.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-@SuppressWarnings("WeakerAccess")
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD})
-public @interface ParameterName {
-    String value();
-}
-
-"""
-)
+    package android.support.annotation;
+    import java.lang.annotation.*;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    @SuppressWarnings("WeakerAccess")
+    @Retention(SOURCE)
+    @Target({METHOD, PARAMETER, FIELD})
+    public @interface ParameterName {
+        String value();
+    }
+    """
+).indented()
 
 val supportDefaultValue: TestFile = java(
     """
-package android.support.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-@SuppressWarnings("WeakerAccess")
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD})
-public @interface DefaultValue {
-    String value();
-}
-
-"""
-)
+    package android.support.annotation;
+    import java.lang.annotation.*;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    @SuppressWarnings("WeakerAccess")
+    @Retention(SOURCE)
+    @Target({METHOD, PARAMETER, FIELD})
+    public @interface DefaultValue {
+        String value();
+    }
+    """
+).indented()
 
 val uiThreadSource: TestFile = java(
     """
-package android.support.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-/**
- * Denotes that the annotated method or constructor should only be called on the
- * UI thread. If the annotated element is a class, then all methods in the class
- * should be called on the UI thread.
- * @memberDoc This method must be called on the thread that originally created
- *            this UI element. This is typically the main thread of your app.
- * @classDoc Methods in this class must be called on the thread that originally created
- *            this UI element, unless otherwise noted. This is typically the
- *            main thread of your app. * @hide
- */
-@SuppressWarnings({"WeakerAccess", "JavaDoc"})
-@Retention(SOURCE)
-@Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
-public @interface UiThread {
-}
-                """
-)
+    package android.support.annotation;
+    import java.lang.annotation.*;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    /**
+     * Denotes that the annotated method or constructor should only be called on the
+     * UI thread. If the annotated element is a class, then all methods in the class
+     * should be called on the UI thread.
+     * @memberDoc This method must be called on the thread that originally created
+     *            this UI element. This is typically the main thread of your app.
+     * @classDoc Methods in this class must be called on the thread that originally created
+     *            this UI element, unless otherwise noted. This is typically the
+     *            main thread of your app. * @hide
+     */
+    @SuppressWarnings({"WeakerAccess", "JavaDoc"})
+    @Retention(SOURCE)
+    @Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
+    public @interface UiThread {
+    }
+    """
+).indented()
 
 val workerThreadSource: TestFile = java(
     """
-package android.support.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-/**
- * @memberDoc This method may take several seconds to complete, so it should
- *            only be called from a worker thread.
- * @classDoc Methods in this class may take several seconds to complete, so it should
- *            only be called from a worker thread unless otherwise noted.
- * @hide
- */
-@SuppressWarnings({"WeakerAccess", "JavaDoc"})
-@Retention(SOURCE)
-@Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
-public @interface WorkerThread {
-}
-                """
-)
+    package android.support.annotation;
+    import java.lang.annotation.*;
+    import static java.lang.annotation.ElementType.*;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    /**
+     * @memberDoc This method may take several seconds to complete, so it should
+     *            only be called from a worker thread.
+     * @classDoc Methods in this class may take several seconds to complete, so it should
+     *            only be called from a worker thread unless otherwise noted.
+     * @hide
+     */
+    @SuppressWarnings({"WeakerAccess", "JavaDoc"})
+    @Retention(SOURCE)
+    @Target({METHOD,CONSTRUCTOR,TYPE,PARAMETER})
+    public @interface WorkerThread {
+    }
+    """
+).indented()
 
 val suppressLintSource: TestFile = java(
     """
@@ -1164,27 +1253,38 @@ public @interface SuppressLint {
 
 val systemServiceSource: TestFile = java(
     """
-package android.annotation;
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-import java.lang.annotation.*;
-@Retention(SOURCE)
-@Target(TYPE)
-public @interface SystemService {
-    String value();
-}
-                """
-)
+    package android.annotation;
+    import static java.lang.annotation.ElementType.TYPE;
+    import static java.lang.annotation.RetentionPolicy.SOURCE;
+    import java.lang.annotation.*;
+    @Retention(SOURCE)
+    @Target(TYPE)
+    public @interface SystemService {
+        String value();
+    }
+    """
+).indented()
 
 val systemApiSource: TestFile = java(
     """
-package android.annotation;
-import static java.lang.annotation.ElementType.*;
-import java.lang.annotation.*;
-@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
-@Retention(RetentionPolicy.SOURCE)
-public @interface SystemApi {
-}
-"""
-)
+    package android.annotation;
+    import static java.lang.annotation.ElementType.*;
+    import java.lang.annotation.*;
+    @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemApi {
+    }
+    """
+).indented()
+
+val widgetSource: TestFile = java(
+    """
+    package android.annotation;
+    import java.lang.annotation.*;
+    @Target({ ElementType.TYPE })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Widget {
+    }
+    """
+).indented()
 
diff --git a/src/test/java/com/android/tools/metalava/OptionsTest.kt b/src/test/java/com/android/tools/metalava/OptionsTest.kt
index 6740f449bbbb36b1d8bf35eaf50fa7e55c449b2c..8e47973bb58565cbb5b95e7f924e0e55d4b5218a 100644
--- a/src/test/java/com/android/tools/metalava/OptionsTest.kt
+++ b/src/test/java/com/android/tools/metalava/OptionsTest.kt
@@ -62,6 +62,15 @@ API sources:
 --show-unannotated                     Include un-annotated public APIs in the signature
                                        file as well
 
+Documentation:
+--public                               Only include elements that are public
+--protected                            Only include elements that are public or protected
+--package                              Only include elements that are public, protected or
+                                       package protected
+--private                              Include all elements except those that are marked
+                                       hidden
+--hidden                               INclude all elements, including hidden
+
 Extracting Signature Files:
 --api <file>                           Generate a signature descriptor file
 --private-api <file>                   Generate a signature descriptor file listing the
@@ -86,6 +95,7 @@ Extracting Signature Files:
                                        for well known annotations like @Nullable and
                                        @NonNull.
 --proguard <file>                      Write a ProGuard keep file for the API
+--sdk-values <dir>                     Write SDK values files to the given directory
 
 Generating Stubs:
 --stubs <dir>                          Generate stub source files for the API
diff --git a/src/test/java/com/android/tools/metalava/SdkFileWriterTest.kt b/src/test/java/com/android/tools/metalava/SdkFileWriterTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e28f65fb1a8d6bb467f515fdc9f11c65627cd5b3
--- /dev/null
+++ b/src/test/java/com/android/tools/metalava/SdkFileWriterTest.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+@file:Suppress("ALL")
+
+package com.android.tools.metalava
+
+import org.junit.Test
+
+class SdkFileWriterTest : DriverTest() {
+    @Test
+    fun `Test generating broadcast actions`() {
+        check(
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                package android.telephony;
+
+                import android.annotation.SdkConstant;
+                import android.annotation.SdkConstant.SdkConstantType;
+
+                public class SubscriptionManager {
+                    /**
+                     * Broadcast Action: The default subscription has changed.
+                     */
+                    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+                    public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED
+                            = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
+                }
+            """
+                ),
+                sdkConstantSource
+            ),
+            sdk_broadcast_actions = """
+            android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED
+            """
+        )
+    }
+
+    @Test
+    fun `Test generating widgets`() {
+        check(
+            sourceFiles = *arrayOf(
+                java(
+                    """
+                package android.widget;
+
+                import android.content.Context;
+                import android.annotation.Widget;
+
+                @Widget
+                public class MyButton extends android.view.View {
+                    public MyButton(Context context) {
+                        super(context, null);
+                    }
+                }
+            """
+                ),
+                widgetSource
+            ),
+            sdk_widgets = """
+            Wandroid.view.View java.lang.Object
+            Wandroid.widget.MyButton android.view.View java.lang.Object
+            """
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/StubsTest.kt b/src/test/java/com/android/tools/metalava/StubsTest.kt
index d5f0df455be575cb35135b974b7b4605384785fd..924373b0e772d1adc661626876f36181c1ce3688 100644
--- a/src/test/java/com/android/tools/metalava/StubsTest.kt
+++ b/src/test/java/com/android/tools/metalava/StubsTest.kt
@@ -709,9 +709,10 @@ class StubsTest : DriverTest() {
     @Test
     fun `Check inheriting from package private class`() {
         checkStubs(
-            // Disabled because doclava1 includes fields here that it doesn't include in the
-            // signature file; not sure if it's a bug or intentional but it seems suspicious.
+            // Note that doclava1 includes fields here that it doesn't include in the
+            // signature file.
             //checkDoclava1 = true,
+            compatibilityMode = false,
             sourceFiles =
             *arrayOf(
                 java(
@@ -739,6 +740,7 @@ class StubsTest : DriverTest() {
                     public class MyClass {
                     public MyClass() { throw new RuntimeException("Stub!"); }
                     public void method1() { throw new RuntimeException("Stub!"); }
+                    public void method2() { throw new RuntimeException("Stub!"); }
                     }
                 """
         )
@@ -1261,12 +1263,12 @@ class StubsTest : DriverTest() {
                     public Kotlin(@android.support.annotation.NonNull java.lang.String property1, int arg2) { throw new RuntimeException("Stub!"); }
                     @android.support.annotation.NonNull public java.lang.String method() { throw new RuntimeException("Stub!"); }
                     /** My method doc */
-                    public final void otherMethod(boolean ok, int times) { throw new RuntimeException("Stub!"); }
+                    public void otherMethod(boolean ok, int times) { throw new RuntimeException("Stub!"); }
                     /** property doc */
-                    @android.support.annotation.Nullable public final java.lang.String getProperty2() { throw new RuntimeException("Stub!"); }
+                    @android.support.annotation.Nullable public java.lang.String getProperty2() { throw new RuntimeException("Stub!"); }
                     /** property doc */
-                    public final void setProperty2(@android.support.annotation.Nullable java.lang.String p) { throw new RuntimeException("Stub!"); }
-                    @android.support.annotation.NonNull public final java.lang.String getProperty1() { throw new RuntimeException("Stub!"); }
+                    public void setProperty2(@android.support.annotation.Nullable java.lang.String p) { throw new RuntimeException("Stub!"); }
+                    @android.support.annotation.NonNull public java.lang.String getProperty1() { throw new RuntimeException("Stub!"); }
                     public int someField2;
                     }
                 """,
@@ -1553,6 +1555,7 @@ class StubsTest : DriverTest() {
     fun `Check generating required stubs from hidden super classes and interfaces`() {
         checkStubs(
             checkDoclava1 = false,
+            compatibilityMode = false,
             sourceFiles =
             *arrayOf(
                 java(
@@ -1617,14 +1620,17 @@ class StubsTest : DriverTest() {
                       public class MyClass extends test.pkg.PublicSuperParent implements test.pkg.PublicInterface test.pkg.PublicInterface2 {
                         ctor public MyClass();
                         method public void myMethod();
+                        method public void publicInterfaceMethod();
                         method public void publicInterfaceMethod2();
+                        method public void publicMethod();
+                        method public void publicMethod2();
                         field public static final int MY_CONSTANT = 5; // 0x5
                       }
-                      public abstract interface PublicInterface {
-                        method public abstract void publicInterfaceMethod();
+                      public interface PublicInterface {
+                        method public void publicInterfaceMethod();
                       }
-                      public abstract interface PublicInterface2 {
-                        method public abstract void publicInterfaceMethod2();
+                      public interface PublicInterface2 {
+                        method public void publicInterfaceMethod2();
                       }
                       public abstract class PublicSuperParent {
                         ctor public PublicSuperParent();
@@ -1641,10 +1647,10 @@ class StubsTest : DriverTest() {
                 public MyClass() { throw new RuntimeException("Stub!"); }
                 public void myMethod() { throw new RuntimeException("Stub!"); }
                 public void publicInterfaceMethod2() { throw new RuntimeException("Stub!"); }
-                // Inlined stub from hidden parent class test.pkg.HiddenSuperClass
                 public void publicMethod() { throw new RuntimeException("Stub!"); }
-                // Inlined stub from hidden parent class test.pkg.HiddenSuperClass
+                public void publicMethod2() { throw new RuntimeException("Stub!"); }
                 public void publicInterfaceMethod() { throw new RuntimeException("Stub!"); }
+                public void inheritedMethod2() { throw new RuntimeException("Stub!"); }
                 public static final int MY_CONSTANT = 5; // 0x5
                 }
                 """
@@ -1835,19 +1841,16 @@ class StubsTest : DriverTest() {
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
                     public class MyClass<X extends java.lang.Number, Y> implements test.pkg.Generics.PublicParent<X,Y> {
                     public MyClass() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.Generics.HiddenParent2
                     public java.util.Map<X,java.util.Map<Y,java.lang.String>> createMap(java.util.List<X> list) { throw new RuntimeException("Stub!"); }
                     }
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
                     public class MyClass2<W> implements test.pkg.Generics.PublicParent<java.lang.Float,W> {
                     public MyClass2() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.Generics.HiddenParent2
                     public java.util.Map<java.lang.Float,java.util.Map<W,java.lang.String>> createMap(java.util.List<java.lang.Float> list) { throw new RuntimeException("Stub!"); }
                     }
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
                     public class MyClass3 implements test.pkg.Generics.PublicParent<java.lang.Float,java.lang.Double> {
                     public MyClass3() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.Generics.HiddenParent2
                     public java.util.Map<java.lang.Float,java.util.Map<java.lang.Double,java.lang.String>> createMap(java.util.List<java.lang.Float> list) { throw new RuntimeException("Stub!"); }
                     }
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
@@ -1911,6 +1914,8 @@ class StubsTest : DriverTest() {
                       }
                       public class Generics.MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent implements test.pkg.Generics.PublicInterface {
                         ctor public Generics.MyClass();
+                        method public java.util.Map<X, java.util.Map<Y, java.lang.String>> createMap(java.util.List<X>);
+                        method public java.util.List<X> foo();
                       }
                       public static abstract interface Generics.PublicInterface<A, B> {
                         method public abstract java.util.Map<A, java.util.Map<B, java.lang.String>> createMap(java.util.List<A>) throws java.io.IOException;
@@ -1929,9 +1934,7 @@ class StubsTest : DriverTest() {
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
                     public class MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent<X,Y> implements test.pkg.Generics.PublicInterface<X,Y> {
                     public MyClass() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.Generics.HiddenParent
                     public java.util.List<X> foo() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.Generics.HiddenParent
                     public java.util.Map<X,java.util.Map<Y,java.lang.String>> createMap(java.util.List<X> list) { throw new RuntimeException("Stub!"); }
                     }
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
@@ -1993,6 +1996,9 @@ class StubsTest : DriverTest() {
                       }
                       public static abstract class ConcurrentHashMap.KeySetView<K, V> implements java.util.Collection java.io.Serializable java.util.Set {
                         ctor public ConcurrentHashMap.KeySetView();
+                        method public int size();
+                        method public final java.lang.Object[] toArray();
+                        method public final <T> T[] toArray(T[]);
                       }
                     }
                     """,
@@ -2004,11 +2010,8 @@ class StubsTest : DriverTest() {
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
                     public abstract static class KeySetView<K, V> implements java.util.Collection<K>, java.io.Serializable, java.util.Set<K> {
                     public KeySetView() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.ConcurrentHashMap.CollectionView
                     public int size() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.ConcurrentHashMap.CollectionView
                     public final java.lang.Object[] toArray() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.ConcurrentHashMap.CollectionView
                     public final <T> T[] toArray(T[] a) { throw new RuntimeException("Stub!"); }
                     }
                     }
@@ -2612,7 +2615,6 @@ class StubsTest : DriverTest() {
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
                     public class SpannableString implements test.pkg.SpanTest.CharSequence, test.pkg.SpanTest.Spannable {
                     public SpannableString() { throw new RuntimeException("Stub!"); }
-                    // Inlined stub from hidden parent class test.pkg.SpanTest.SpannableStringInternal
                     public int nextSpanTransition(int start, int limit, java.lang.Class kind) { throw new RuntimeException("Stub!"); }
                     }
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
@@ -2705,10 +2707,12 @@ class StubsTest : DriverTest() {
                     public class SomeClass {
                        /**
                        * My method.
+                       * @param focus The focus to find. One of {@link OtherClass#FOCUS_INPUT} or
+                       *         {@link OtherClass#FOCUS_ACCESSIBILITY}.
                        * @throws IOException when blah blah blah
                        * @throws {@link RuntimeException} when blah blah blah
                        */
-                       public void baz() throws IOException;
+                       public void baz(int focus) throws IOException;
                        public boolean importance;
                     }
                     """
@@ -2719,6 +2723,8 @@ class StubsTest : DriverTest() {
 
                     @SuppressWarnings("all")
                     public class OtherClass {
+                        public static final int FOCUS_INPUT = 1;
+                        public static final int FOCUS_ACCESSIBILITY = 2;
                         public int foo;
                         public void bar(int baz, boolean bar);
                     }
@@ -2737,11 +2743,12 @@ class StubsTest : DriverTest() {
             warnings = "",
             source = """
                     package test.pkg1;
+                    import test.pkg2.OtherClass;
+                    import java.io.IOException;
                     /**
-                     *  Blah blah {@link test.pkg2.OtherClass OtherClass} blah blah.
-                     *  Referencing <b>field</b> {@link test.pkg2.OtherClass#foo OtherClass#foo},
-                     *  and referencing method {@link test.pkg2.OtherClass#bar(int,
-                     *   boolean) OtherClass#bar(int,
+                     *  Blah blah {@link OtherClass} blah blah.
+                     *  Referencing <b>field</b> {@link OtherClass#foo},
+                     *  and referencing method {@link OtherClass#bar(int,
                      *   boolean)}.
                      *  And relative method reference {@link #baz()}.
                      *  And relative field reference {@link #importance}.
@@ -2749,7 +2756,7 @@ class StubsTest : DriverTest() {
                      *  And here's one in the same package: {@link LocalClass}.
                      *
                      *  @deprecated For some reason
-                     *  @see test.pkg2.OtherClass
+                     *  @see OtherClass
                      *  @see OtherClass#bar(int, boolean)
                      */
                     @SuppressWarnings({"unchecked", "deprecation", "all"})
@@ -2757,10 +2764,12 @@ class StubsTest : DriverTest() {
                     public SomeClass() { throw new RuntimeException("Stub!"); }
                     /**
                      * My method.
-                     * @throws java.io.IOException when blah blah blah
-                     * @throws {java.lang.RuntimeExceptionk RuntimeException} when blah blah blah
+                     * @param focus The focus to find. One of {@link OtherClass#FOCUS_INPUT} or
+                     *         {@link OtherClass#FOCUS_ACCESSIBILITY}.
+                     * @throws IOException when blah blah blah
+                     * @throws {@link RuntimeException} when blah blah blah
                      */
-                    public void baz() throws java.io.IOException { throw new RuntimeException("Stub!"); }
+                    public void baz(int focus) throws java.io.IOException { throw new RuntimeException("Stub!"); }
                     public boolean importance;
                     }
                     """
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 29c919f043c71057fde959b04beff66d8b57ec1a..b7b586c8e1f1eed88a72c290875ce2b786351a40 100644
--- a/src/test/java/com/android/tools/metalava/model/TypeItemTest.kt
+++ b/src/test/java/com/android/tools/metalava/model/TypeItemTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.tools.metalava.model
 
+import com.android.tools.metalava.JAVA_LANG_STRING
 import com.android.tools.metalava.options
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -25,7 +26,7 @@ class TypeItemTest {
     fun test() {
         options.omitCommonPackages = true
         assertThat(TypeItem.shortenTypes("@android.support.annotation.Nullable")).isEqualTo("@Nullable")
-        assertThat(TypeItem.shortenTypes("java.lang.String")).isEqualTo("String")
+        assertThat(TypeItem.shortenTypes(JAVA_LANG_STRING)).isEqualTo("String")
         assertThat(TypeItem.shortenTypes("java.lang.reflect.Method")).isEqualTo("java.lang.reflect.Method")
         assertThat(TypeItem.shortenTypes("java.util.List<java.lang.String>")).isEqualTo("java.util.List<String>")
         assertThat(TypeItem.shortenTypes("java.util.List<@android.support.annotation.NonNull java.lang.String>")).isEqualTo(