Merge "Snap for 8296668 from 9bfcb851c99aca018ef8a97b410ee8610a376ea5 to androidx-mediarouter-release" into androidx-mediarouter-release
diff --git a/Android.bp b/Android.bp
index d5ee50e..4c81ca5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -12,6 +12,41 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["external_doclava_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "external_doclava_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+        "SPDX-license-identifier-CC-BY",
+        "SPDX-license-identifier-GPL",
+        "SPDX-license-identifier-GPL-2.0",
+        "SPDX-license-identifier-LGPL",
+        "SPDX-license-identifier-MIT",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 droiddoc_exported_dir {
     name: "droiddoc-templates-sdk",
     path: "res/assets/templates-sdk",
@@ -19,6 +54,7 @@
 
 java_library_host {
     name: "doclava",
+    java_version: "1.8",
     srcs: [
         "src/**/*.java",
     ],
@@ -34,6 +70,7 @@
 
 java_library_host {
     name: "doclava-no-guava",
+    java_version: "1.8",
     srcs: [
         "src/**/*.java",
     ],
diff --git a/METADATA b/METADATA
index d97975c..abbaf9d 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,4 @@
 third_party {
-  license_type: NOTICE
+  license_note: "would be NOTICE save for: templates/ and templates-sdk/"
+  license_type: RESTRICTED
 }
diff --git a/build.gradle b/build.gradle
index bb0ff5f..4f586df 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,79 +16,42 @@
 
 import javax.tools.ToolProvider
 
-apply plugin: 'java'
-apply plugin: 'maven'
+plugins {
+    id("java")
+    id("maven-publish")
+}
 
 group = 'com.android'
 version = '1.0.6'
 
-/*
- * With the build server you are given two env variables:
- * 1. The OUT_DIR is a temporary directory you can use to put things during the build.
- * 2. The DIST_DIR is where you want to save things from the build.
- *
- * The build server will copy the contents of DIST_DIR to somewhere and make it available.
- */
-if (System.env.DIST_DIR != null && System.env.OUT_DIR != null) {
-    buildDir = file("${System.env.OUT_DIR}/gradle/external/jdiff/build").getCanonicalFile()
-    ext.distDir = file(System.env.DIST_DIR).getCanonicalFile()
-
-    // The distDir is conveniently named after the build ID.
-    version = "${version}.${ext.distDir.name}"
+if (System.env.OUT_DIR != null) {
+    buildDir = file("${System.env.OUT_DIR}/gradle/external/doclava/build").getCanonicalFile()
 } else {
-    buildDir = file('../../out/host/gradle/external/jdiff/build')
-    ext.distDir = file('../../out/dist')
-
-    // Local builds are not public release candidates.
-    version = "${version}-SNAPSHOT"
+    buildDir = file('../../out/host/gradle/external/doclava/build')
 }
 
-/*
- * If prebuilts are available, use them. Else, if this is unbundled build use jcenter().
- * Finally, if none of that is true, attempt to compile against the full source trees.
- */
-File m2repo = file('../../prebuilts/androidx/external')
-boolean unbundleBuild = (new File("unbundled-build")).exists()
-
-if (m2repo.exists() || unbundleBuild) {
-    repositories {
-        maven { url m2repo.absolutePath }
-        if (unbundleBuild) {
-            jcenter()
-        }
-    }
-
-    dependencies {
-        compile 'org.antlr:antlr:3.5.2'
-        compile 'com.google.jsilver:jsilver:1.0.0'
-        compile 'org.ccil.cowan.tagsoup:tagsoup:1.2.1'
-        // Transitive dependency required by jsilver.
-        compile 'com.google.guava:guava:15.0'
-    }
-} else {
-    dependencies {
-        compile project(path: ':antlr', configuration: 'antlrRuntime')
-        compile project(':jsilver')
-        compile project(':tagsoup')
-    }
+repositories {
+    maven { url file('../../prebuilts/androidx/external').absolutePath }
 }
 
-
 dependencies {
-    testCompile 'junit:junit:4.12'
+    implementation("org.antlr:antlr:3.5.2")
+    implementation("com.google.jsilver:jsilver:1.0.0")
+    implementation("org.ccil.cowan.tagsoup:tagsoup:1.2.1")
 
     // tools.jar required for com.sun.javadoc
-    def currentJvmVersion = org.gradle.api.JavaVersion.current()
-    def toolsJar;
-    if (currentJvmVersion.getMajorVersion() == "8") {
+    def toolsJar
+    if (JavaVersion.current().getMajorVersion() == "8") {
         toolsJar = ((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs()
     } else if (System.env.JAVA_TOOLS_JAR != null) {
         toolsJar = System.env.JAVA_TOOLS_JAR
     } else {
         throw new Exception("If you are not using Java 8, JAVA_TOOLS_JAR env variable " +
-            "needs to be set to build Doclava")
+                "needs to be set to tools.jar from a Java 8 installation to build Doclava")
     }
-    compile files(toolsJar)
+    implementation(files(toolsJar))
+
+    testImplementation("junit:junit:4.12")
 }
 
 sourceSets {
@@ -102,27 +65,12 @@
     }
 }
 
-uploadArchives {
-    repositories {
-        mavenDeployer {
-            repository(url: uri("${buildDir}/repo"))
-        }
-    }
-}
-
-task dist(type: Zip, dependsOn: uploadArchives)  {
-    group = BasePlugin.BUILD_GROUP
-    description 'Builds distribution artifacts.'
-
-    from uploadArchives.artifacts
-    destinationDir distDir
-
-    doLast {
-        logger.lifecycle "Compressed maven artifacts to ${archivePath}"
-    }
-}
-
 tasks.withType(JavaCompile) {
     // Suppress build warnings that we're not interested in: b/154755010
     options.warnings = false
 }
+
+tasks.withType(Jar) { task ->
+    task.reproducibleFileOrder = true
+    task.preserveFileTimestamps = false
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 84939b4..27a6e95 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=../../../../tools/external/gradle/gradle-3.3-bin.zip
+distributionUrl=../../../../tools/external/gradle/gradle-7.4-bin.zip
diff --git a/res/assets/templates-sdk/compatchanges.cs b/res/assets/templates-sdk/compatchanges.cs
index 001b937..fba233a 100644
--- a/res/assets/templates-sdk/compatchanges.cs
+++ b/res/assets/templates-sdk/compatchanges.cs
@@ -24,9 +24,9 @@
             Disabled for all apps.
         <?cs else ?>
             Enabled for
-            <?cs if:change.enableAfterTargetSdk ?>
-                apps with a <code>targetSdkVersion</code> of greater than
-                <?cs var:change.enableAfterTargetSdk ?>.
+            <?cs if:change.enableSinceTargetSdk ?>
+                apps with a <code>targetSdkVersion</code> of greater than or equal to
+                <?cs var:change.enableSinceTargetSdk ?>.
             <?cs else ?>
                 all apps.
             <?cs /if ?>
diff --git a/src/com/google/doclava/CompatInfo.java b/src/com/google/doclava/CompatInfo.java
index 167f6ab..1999818 100644
--- a/src/com/google/doclava/CompatInfo.java
+++ b/src/com/google/doclava/CompatInfo.java
@@ -44,12 +44,12 @@
     public final int sourceLine;
     public final boolean disabled;
     public final boolean loggingOnly;
-    public final int enableAfterTargetSdk;
+    public final int enableSinceTargetSdk;
 
 
     CompatChange(String name, long id, String description, String definedInClass,
             String sourceFile, int sourceLine, boolean disabled, boolean loggingOnly,
-            int enableAfterTargetSdk) {
+            int enableAfterTargetSdk, int enableSinceTargetSdk) {
       this.name = name;
       this.id = id;
       this.description = description;
@@ -58,7 +58,14 @@
       this.sourceLine = sourceLine;
       this.disabled = disabled;
       this.loggingOnly = loggingOnly;
-      this.enableAfterTargetSdk = enableAfterTargetSdk;
+      if (enableSinceTargetSdk > 0) {
+        this.enableSinceTargetSdk = enableSinceTargetSdk;
+      } else if (enableAfterTargetSdk > 0) {
+        this.enableSinceTargetSdk = enableAfterTargetSdk + 1;
+      } else {
+        this.enableSinceTargetSdk = 0;
+      }
+
     }
 
     static class Builder {
@@ -71,11 +78,12 @@
       private boolean mDisabled;
       private boolean mLoggingOnly;
       private int mEnableAfterTargetSdk;
+      private int mEnableSinceTargetSdk;
 
       CompatChange build() {
         return new CompatChange(
             mName, mId, mDescription, mDefinedInClass, mSourceFile, mSourceLine,
-                mDisabled, mLoggingOnly, mEnableAfterTargetSdk);
+                mDisabled, mLoggingOnly, mEnableAfterTargetSdk, mEnableSinceTargetSdk);
       }
 
       Builder name(String name) {
@@ -144,6 +152,18 @@
         }
         return this;
       }
+      Builder enableSinceTargetSdk(String enableSince) throws SAXException {
+        if (enableSince == null) {
+          mEnableSinceTargetSdk = 0;
+        } else {
+          try {
+            mEnableSinceTargetSdk = Integer.parseInt(enableSince);
+          } catch (NumberFormatException nfe) {
+            throw new SAXException("Invalid SDK version int: " + enableSince, nfe);
+          }
+        }
+        return this;
+      }
     }
 
   }
@@ -166,6 +186,7 @@
         mCurrentChange.name(attributes.getValue("name"))
                 .description(attributes.getValue("description"))
                 .enableAfterTargetSdk(attributes.getValue("enableAfterTargetSdk"))
+                .enableSinceTargetSdk(attributes.getValue("enableSinceTargetSdk"))
                 .disabled(attributes.getValue("disabled"))
                 .loggingOnly(attributes.getValue("loggingOnly"));
 
@@ -232,16 +253,16 @@
         definedInContainer = Converter.obtainPackage("android");
       }
       if (change.description == null) {
-        throw new RuntimeException("No desriprion found for @ChangeId " + change.name);
+        throw new RuntimeException("No description found for @ChangeId " + change.name);
       }
       Comment comment = new Comment(change.description, definedInContainer, new SourcePositionInfo(
           change.sourceFile, change.sourceLine, 1));
       String path = "change." + i;
       hdf.setValue(path + ".id", Long.toString(change.id));
       hdf.setValue(path + ".name", change.name);
-      if (change.enableAfterTargetSdk != 0) {
-        hdf.setValue(path + ".enableAfterTargetSdk",
-            Integer.toString(change.enableAfterTargetSdk));
+      if (change.enableSinceTargetSdk != 0) {
+        hdf.setValue(path + ".enableSinceTargetSdk",
+            Integer.toString(change.enableSinceTargetSdk));
       }
       if (change.loggingOnly) {
         hdf.setValue(path + ".loggingOnly", Boolean.toString(true));
diff --git a/src/com/google/doclava/Converter.java b/src/com/google/doclava/Converter.java
index cf14237..2143875 100644
--- a/src/com/google/doclava/Converter.java
+++ b/src/com/google/doclava/Converter.java
@@ -23,6 +23,7 @@
 import com.sun.javadoc.AnnotationValue;
 import com.sun.javadoc.ClassDoc;
 import com.sun.javadoc.ConstructorDoc;
+import com.sun.javadoc.Doc;
 import com.sun.javadoc.ExecutableMemberDoc;
 import com.sun.javadoc.FieldDoc;
 import com.sun.javadoc.MemberDoc;
@@ -43,7 +44,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 
 public class Converter {
   private static RootDoc root;
@@ -288,7 +288,7 @@
   }
 
   private static ParameterInfo[] convertParameters(Parameter[] p, ExecutableMemberDoc m) {
-    SourcePosition pos = m.position();
+    SourcePosition pos = getPositionSafe(m);
     int len = p.length;
     ParameterInfo[] q = new ParameterInfo[len];
     for (int i = 0; i < len; i++) {
@@ -447,6 +447,27 @@
     return (MethodInfo) mMethods.obtain(o);
   }
 
+  /**
+   * Returns the result of a call to {@code f.position()} but avoids a {@link NullPointerException}
+   * if {@code f.position()} throws a {@link NullPointerException} due to the {@link Doc} element
+   * being related to an annotation.
+   *
+   * <p>This is consistent with the Java runtime where references to annotations that do not exist
+   * on the classpath are allowed. This method is used, for example, to allow Doclava to process
+   * JARs in the classpath that were generated by the Kotlin compiler, without requiring the Kotlin
+   * stdlib to exist on the classpath when Doclava is executed.
+   */
+  private static SourcePosition getPositionSafe(Doc f) {
+    try {
+      return f.position();
+    } catch (NullPointerException e) {
+      if (f.isAnnotationTypeElement() || mFieldDocElementsOnAnnotationClasses.contains(f)) {
+        return null;
+      }
+      throw e;
+    }
+  }
+
   private static Cache mMethods = new Cache() {
     @Override
     protected Object make(Object o) {
@@ -466,7 +487,7 @@
                     new ArrayList<ParameterInfo>(Arrays.asList(
                             Converter.convertParameters(m.parameters(), m))),
                     new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(
-                            m.thrownExceptions()))), Converter.convertSourcePosition(m.position()),
+                            m.thrownExceptions()))), Converter.convertSourcePosition(getPositionSafe(m)),
                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
                             Converter.convertAnnotationInstances(m.annotations()))));
         result.setVarargs(m.isVarArgs());
@@ -547,7 +568,7 @@
           .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f
           .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(),
           f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(),
-          f.constantValue(), Converter.convertSourcePosition(f.position()),
+          f.constantValue(), Converter.convertSourcePosition(getPositionSafe(f)),
           new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter
               .convertAnnotationInstances(f.annotations()))));
     }
@@ -760,6 +781,12 @@
       new HashMap<AnnotationValue, AnnotationValueInfo>();
   private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit =
       new HashSet<AnnotationValue>();
+  /**
+   * Stores the {@link FieldDoc} instances the are contained in annotation classes. This is used by
+   * {@link #getPositionSafe(Doc)} to allow {@link NullPointerException} for annotation classes to
+   * be ignored.
+   */
+  private static HashSet<FieldDoc> mFieldDocElementsOnAnnotationClasses = new HashSet<FieldDoc>();
 
   private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) {
     if (o == null) {
@@ -769,6 +796,9 @@
     if (v != null) return v;
     v = new AnnotationValueInfo(element);
     mAnnotationValues.put(o, v);
+    if (o.value() instanceof FieldDoc) {
+      mFieldDocElementsOnAnnotationClasses.add((FieldDoc) o.value());
+    }
     if (mAnnotationValuesNeedingInit != null) {
       mAnnotationValuesNeedingInit.add(o);
     } else {
diff --git a/src/com/google/doclava/Doclava.java b/src/com/google/doclava/Doclava.java
index 049e93e..45804a1 100644
--- a/src/com/google/doclava/Doclava.java
+++ b/src/com/google/doclava/Doclava.java
@@ -172,7 +172,7 @@
   }
 
   public static void main(String[] args) {
-    com.sun.tools.javadoc.Main.execute(args);
+    System.exit(com.sun.tools.javadoc.Main.execute(args));
   }
 
   public static boolean start(RootDoc r) {