Add a validation action to check that the manifest of `android_binary`s sets `minSdkVersion` to 21+ when `multidex='native'` is enabled

PiperOrigin-RevId: 441316783
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
index 0a83b54..7426d65 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
@@ -17,6 +17,8 @@
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.devtools.build.lib.collect.nestedset.Order.STABLE_ORDER;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
 import static com.google.devtools.build.lib.packages.Type.STRING;
 
 import com.google.auto.value.AutoValue;
@@ -260,6 +262,30 @@
                       : null);
     }
 
+    Artifact manifestValidation = null;
+    if (Allowlist.hasAllowlist(ruleContext, "android_multidex_native_min_sdk_allowlist")
+        && !Allowlist.isAvailable(ruleContext, "android_multidex_native_min_sdk_allowlist")
+        && getMultidexMode(ruleContext) == MultidexMode.NATIVE
+        && ruleContext.isAttrDefined("$validate_manifest", LABEL)) {
+      manifestValidation =
+          ruleContext.getPackageRelativeArtifact(
+              ruleContext.getLabel().getName() + "_manifest_validation_output",
+              ruleContext.getBinOrGenfilesDirectory());
+      ruleContext.registerAction(
+          createSpawnActionBuilder(ruleContext)
+              .setExecutable(ruleContext.getExecutablePrerequisite("$validate_manifest"))
+              .setProgressMessage("Validating %{input}")
+              .setMnemonic("ValidateManifest")
+              .addInput(manifest.getManifest())
+              .addOutput(manifestValidation)
+              .addCommandLine(
+                  CustomCommandLine.builder()
+                      .addExecPath("--manifest", manifest.getManifest())
+                      .addExecPath("--output", manifestValidation)
+                      .build())
+              .build(ruleContext));
+    }
+
     boolean shrinkResourceCycles =
         shouldShrinkResourceCycles(
             dataContext.getAndroidConfig(), ruleContext, dataContext.isResourceShrinkingEnabled());
@@ -405,7 +431,8 @@
         ImmutableList.of(),
         ImmutableList.of(),
         proguardMapping,
-        oneVersionOutputArtifact);
+        oneVersionOutputArtifact,
+        manifestValidation);
   }
 
   public static RuleConfiguredTargetBuilder createAndroidBinary(
@@ -425,7 +452,8 @@
       ImmutableList<Artifact> apksUnderTest,
       ImmutableList<Artifact> additionalMergedManifests,
       Artifact proguardMapping,
-      @Nullable Artifact oneVersionEnforcementArtifact)
+      @Nullable Artifact oneVersionEnforcementArtifact,
+      @Nullable Artifact manifestValidation)
       throws InterruptedException, RuleErrorException {
 
     List<ProguardSpecProvider> proguardDeps = new ArrayList<>();
@@ -769,6 +797,11 @@
           additionalMergedManifests);
     }
 
+    if (manifestValidation != null) {
+      builder.addOutputGroup(
+          OutputGroupInfo.VALIDATION, NestedSetBuilder.create(STABLE_ORDER, manifestValidation));
+    }
+
     // First propagate validations from most rule attributes as usual; then handle "deps" separately
     // to propagate validations from each config split but avoid known-redundant Android Lint
     // validations (b/168038145, b/180746622).