Expose process_test_data (for android_test) to Skylark

Explicitly work with rele attributes at (or closer to) the top level rather
than deep in the code.

RELNOTES: none
PiperOrigin-RevId: 196004542
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidAssets.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidAssets.java
index 32c5989..11ddc1b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidAssets.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidAssets.java
@@ -61,12 +61,13 @@
     }
   }
 
-  /** Collects this rule's android assets. */
+  /** Collects this rule's android assets, based on rule attributes. */
   public static AndroidAssets from(RuleContext ruleContext) throws RuleErrorException {
     return from(ruleContext, getAssetTargets(ruleContext), getAssetDir(ruleContext));
   }
 
-  static AndroidAssets from(
+  /** Collects Android assets from the specified values */
+  public static AndroidAssets from(
       RuleErrorConsumer errorConsumer,
       @Nullable Iterable<? extends TransitiveInfoCollection> assetTargets,
       @Nullable PathFragment assetsDir)
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 eed11af..4619862 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
@@ -202,13 +202,16 @@
       applicationManifest =
           ApplicationManifest.fromExplicitManifest(ruleContext, manifest.getManifest());
 
+      AndroidAaptVersion aaptVersion = AndroidAaptVersion.chooseTargetAaptVersion(ruleContext);
       resourceApk =
           ProcessedAndroidData.processBinaryDataFrom(
                   ruleContext,
                   manifest,
                   /* conditionalKeepRules = */ shouldShrinkResourceCycles(
-                      ruleContext, shrinkResources))
-              .generateRClass(ruleContext);
+                      ruleContext, shrinkResources),
+                  applicationManifest.getManifestValues(),
+                  aaptVersion)
+              .generateRClass(ruleContext, aaptVersion);
     } else {
       applicationManifest =
           androidSemantics.getManifestForRule(ruleContext).mergeWith(ruleContext, resourceDeps);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMobileInstall.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMobileInstall.java
index 64d0cd7..1042e2c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMobileInstall.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinaryMobileInstall.java
@@ -76,7 +76,8 @@
                   manifest.addMobileInstallStubApplication(ruleContext),
                   ruleContext.getImplicitOutputArtifact(
                       AndroidRuleClasses.ANDROID_INCREMENTAL_RESOURCES_APK),
-                  "incremental")
+                  "incremental",
+                  applicationManifest.getManifestValues())
               // Intentionally skip building an R class JAR - incremental binaries handle this
               // separately.
               .withValidatedResources(null);
@@ -86,7 +87,8 @@
                   ruleContext,
                   manifest.createSplitManifest(ruleContext, "android_resources", false),
                   getMobileInstallArtifact(ruleContext, "android_resources.ap_"),
-                  "incremental_split")
+                  "incremental_split",
+                  applicationManifest.getManifestValues())
               // Intentionally skip building an R class JAR - incremental binaries handle this
               // separately.
               .withValidatedResources(null);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
index d9f1fee..c88e369 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
@@ -36,6 +36,7 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion;
 import com.google.devtools.build.lib.rules.java.ClasspathConfiguredFragment;
 import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder;
 import com.google.devtools.build.lib.rules.java.JavaCommon;
@@ -93,9 +94,14 @@
       StampedAndroidManifest manifest =
           AndroidManifest.from(ruleContext).mergeWithDeps(ruleContext);
 
+      AndroidAaptVersion aaptVersion = AndroidAaptVersion.chooseTargetAaptVersion(ruleContext);
       resourceApk =
-          ProcessedAndroidData.processLocalTestDataFrom(ruleContext, manifest)
-              .generateRClass(ruleContext);
+          ProcessedAndroidData.processLocalTestDataFrom(
+                  ruleContext,
+                  manifest,
+                  ApplicationManifest.getManifestValues(ruleContext),
+                  aaptVersion)
+              .generateRClass(ruleContext, aaptVersion);
     } else {
       // Create the final merged manifest
       ResourceDependencies resourceDependencies =
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidManifest.java
index dfe7a01..b90d28b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidManifest.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidManifest.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.rules.android;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
@@ -21,6 +20,7 @@
 import com.google.devtools.build.lib.rules.java.JavaUtil;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.Map;
 import java.util.Objects;
 import javax.annotation.Nullable;
 
@@ -91,8 +91,11 @@
     this(manifest, other.pkg, other.exported);
   }
 
-  @VisibleForTesting
-  AndroidManifest(Artifact manifest, @Nullable String pkg, boolean exported) {
+  /**
+   * Creates a manifest wrapper without doing any processing. From within a rule, use {@link
+   * #from(RuleContext, AndroidSemantics)} instead.
+   */
+  public AndroidManifest(Artifact manifest, @Nullable String pkg, boolean exported) {
     this.manifest = manifest;
     this.pkg = pkg;
     this.exported = exported;
@@ -112,11 +115,15 @@
    * <p>If no manifest values are specified, the manifest will remain unstamped.
    */
   public StampedAndroidManifest stampWithManifestValues(RuleContext ruleContext) {
-    return mergeWithDeps(ruleContext, ResourceDependencies.empty());
+    return mergeWithDeps(
+        ruleContext,
+        ResourceDependencies.empty(),
+        ApplicationManifest.getManifestValues(ruleContext),
+        ApplicationManifest.useLegacyMerging(ruleContext));
   }
 
   /**
-   * Merges the manifest with any dependent manifests.
+   * Merges the manifest with any dependent manifests, extracted from rule attributes.
    *
    * <p>The manifest will also be stamped with any manifest values specified in the target's
    * attributes
@@ -126,17 +133,20 @@
    */
   public StampedAndroidManifest mergeWithDeps(RuleContext ruleContext) {
     return mergeWithDeps(
-        ruleContext, ResourceDependencies.fromRuleDeps(ruleContext, /* neverlink = */ false));
+        ruleContext,
+        ResourceDependencies.fromRuleDeps(ruleContext, /* neverlink = */ false),
+        ApplicationManifest.getManifestValues(ruleContext),
+        ApplicationManifest.useLegacyMerging(ruleContext));
   }
 
-  private StampedAndroidManifest mergeWithDeps(
-      RuleContext ruleContext, ResourceDependencies resourceDeps) {
+  public StampedAndroidManifest mergeWithDeps(
+      RuleContext ruleContext,
+      ResourceDependencies resourceDeps,
+      Map<String, String> manifestValues,
+      boolean useLegacyMerger) {
     Artifact newManifest =
         ApplicationManifest.maybeMergeWith(
-                ruleContext,
-                manifest,
-                resourceDeps,
-                ApplicationManifest.getManifestValues(ruleContext))
+            ruleContext, manifest, resourceDeps, manifestValues, useLegacyMerger, pkg)
             .orElse(manifest);
 
     return new StampedAndroidManifest(newManifest, pkg, exported);
@@ -165,7 +175,7 @@
   }
 
   /** Gets the default Java package */
-  static String getDefaultPackage(RuleContext ruleContext) {
+  public static String getDefaultPackage(RuleContext ruleContext) {
     PathFragment dummyJar = ruleContext.getPackageDirectory().getChild("Dummy.jar");
     return getJavaPackageFromPath(ruleContext, dummyJar);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java
index 7858091..842b4ed 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidResources.java
@@ -138,7 +138,7 @@
         resourcesAttr);
   }
 
-  static AndroidResources from(
+  public static AndroidResources from(
       RuleErrorConsumer errorConsumer,
       Iterable<FileProvider> resourcesTargets,
       String resourcesAttr)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java
index cb13f6a..389115a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSkylarkData.java
@@ -19,6 +19,7 @@
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.FileProvider;
 import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext;
+import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Location;
@@ -396,16 +397,7 @@
                   aaptVersion)
               .validate(ctx.getRuleContext(), aaptVersion);
 
-      JavaInfo javaInfo =
-          JavaInfo.Builder.create()
-              .setNeverlink(true)
-              .addProvider(
-                  JavaCompilationInfoProvider.class,
-                  new JavaCompilationInfoProvider.Builder()
-                      .setCompilationClasspath(
-                          NestedSetBuilder.create(Order.NAIVE_LINK_ORDER, validated.getClassJar()))
-                      .build())
-              .build();
+      JavaInfo javaInfo = getJavaInfoForRClassJar(validated.getClassJar());
 
       return SkylarkDict.of(
           /* env = */ null,
@@ -742,8 +734,35 @@
             .build());
   }
 
+  public static SkylarkDict<NativeProvider<?>, NativeInfo> getNativeInfosFrom(
+      ResourceApk resourceApk, Label label) {
+    ImmutableMap.Builder<NativeProvider<?>, NativeInfo> builder = ImmutableMap.builder();
+
+    builder.put(AndroidResourcesInfo.PROVIDER, resourceApk.toResourceInfo(label));
+
+    resourceApk
+        .toAssetsInfo(label)
+        .ifPresent(info -> builder.put(AndroidAssetsInfo.PROVIDER, info));
+    resourceApk.toManifestInfo().ifPresent(info -> builder.put(AndroidManifestInfo.PROVIDER, info));
+
+    builder.put(JavaInfo.PROVIDER, getJavaInfoForRClassJar(resourceApk.getResourceJavaClassJar()));
+
+    return SkylarkDict.copyOf(/* env = */ null, builder.build());
+  }
+
+  private static JavaInfo getJavaInfoForRClassJar(Artifact rClassJar) {
+    return JavaInfo.Builder.create()
+        .setNeverlink(true)
+        .addProvider(
+            JavaCompilationInfoProvider.class,
+            new JavaCompilationInfoProvider.Builder()
+                .setCompilationClasspath(NestedSetBuilder.create(Order.NAIVE_LINK_ORDER, rClassJar))
+                .build())
+        .build();
+  }
+
   /** Checks if a "Noneable" object passed by Skylark is "None", which Java should treat as null. */
-  private static boolean isNone(Object object) {
+  public static boolean isNone(Object object) {
     return object == Runtime.NONE;
   }
 
@@ -760,7 +779,7 @@
    * @return {@code null}, if the noneable argument was None, or the cast object, otherwise.
    */
   @Nullable
-  private static <T> T fromNoneable(Object object, Class<T> clazz) {
+  public static <T> T fromNoneable(Object object, Class<T> clazz) {
     if (isNone(object)) {
       return null;
     }
@@ -768,7 +787,7 @@
     return clazz.cast(object);
   }
 
-  private static <T> T fromNoneableOrDefault(Object object, Class<T> clazz, T defaultValue) {
+  public static <T> T fromNoneableOrDefault(Object object, Class<T> clazz, T defaultValue) {
     T value = fromNoneable(object, clazz);
     if (value == null) {
       return defaultValue;
@@ -784,7 +803,7 @@
    * casts it to a list with the appropriate generic.
    */
   @Nullable
-  private static <T> List<T> listFromNoneable(Object object, Class<T> clazz) throws EvalException {
+  public static <T> List<T> listFromNoneable(Object object, Class<T> clazz) throws EvalException {
     SkylarkList<?> asList = fromNoneable(object, SkylarkList.class);
     if (asList == null) {
       return null;
@@ -803,7 +822,7 @@
     return SkylarkList.createImmutable(value);
   }
 
-  private static <T extends NativeInfo> SkylarkList<T> getProviders(
+  public static <T extends NativeInfo> SkylarkList<T> getProviders(
       SkylarkList<ConfiguredTarget> targets, NativeProvider<T> provider) {
     return SkylarkList.createImmutable(
         targets
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
index 783f0f2..deb2cb5 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
@@ -192,7 +192,7 @@
    *
    * @return an artifact for the generated manifest
    */
-  static Artifact generateManifest(RuleContext ruleContext, String manifestPackage) {
+  public static Artifact generateManifest(RuleContext ruleContext, String manifestPackage) {
     Artifact generatedManifest =
         ruleContext.getUniqueDirectoryArtifact(
             ruleContext.getRule().getName() + "_generated",
@@ -216,19 +216,35 @@
     return generatedManifest;
   }
 
+  /** Gets a map of manifest values from this rule's 'manifest_values' attribute */
   static ImmutableMap<String, String> getManifestValues(RuleContext context) {
+    return getManifestValues(
+        context,
+        context.attributes().isAttributeValueExplicitlySpecified("manifest_values")
+            ? context.attributes().get("manifest_values", Type.STRING_DICT)
+            : null);
+  }
+
+  /** Gets and expands an expanded map of manifest values from some raw map of manifest values. */
+  static ImmutableMap<String, String> getManifestValues(
+      RuleContext ruleContext, @Nullable Map<String, String> rawMap) {
     Map<String, String> manifestValues = new TreeMap<>();
-    if (context.attributes().isAttributeValueExplicitlySpecified("manifest_values")) {
-      manifestValues.putAll(context.attributes().get("manifest_values", Type.STRING_DICT));
+    if (rawMap != null) {
+      manifestValues.putAll(rawMap);
     }
 
     for (String variable : manifestValues.keySet()) {
       manifestValues.put(
-          variable, context.getExpander().expand("manifest_values", manifestValues.get(variable)));
+          variable,
+          ruleContext.getExpander().expand("manifest_values", manifestValues.get(variable)));
     }
     return ImmutableMap.copyOf(manifestValues);
   }
 
+  public ImmutableMap<String, String> getManifestValues() {
+    return manifestValues;
+  }
+
   private final Artifact manifest;
   private final ImmutableMap<String, String> manifestValues;
   private final AndroidAaptVersion targetAaptVersion;
@@ -241,7 +257,13 @@
   }
 
   public ApplicationManifest mergeWith(RuleContext ruleContext, ResourceDependencies resourceDeps) {
-    return maybeMergeWith(ruleContext, manifest, resourceDeps, manifestValues)
+    return maybeMergeWith(
+            ruleContext,
+            manifest,
+            resourceDeps,
+            manifestValues,
+            useLegacyMerging(ruleContext),
+            AndroidCommon.getJavaPackage(ruleContext))
         .map(merged -> new ApplicationManifest(ruleContext, merged, targetAaptVersion))
         .orElse(this);
   }
@@ -250,11 +272,14 @@
       RuleContext ruleContext,
       Artifact primaryManifest,
       ResourceDependencies resourceDeps,
-      Map<String, String> manifestValues) {
+      Map<String, String> manifestValues,
+      boolean useLegacyMerging,
+      String customPackage) {
     Map<Artifact, Label> mergeeManifests = getMergeeManifests(resourceDeps.getResourceContainers());
 
-    if (useLegacyMerging(ruleContext)) {
+    if (useLegacyMerging) {
       if (!mergeeManifests.isEmpty()) {
+
         Artifact outputManifest =
             ruleContext.getUniqueDirectoryArtifact(
                 ruleContext.getRule().getName() + "_merged",
@@ -285,7 +310,7 @@
             .setMergeeManifests(mergeeManifests)
             .setLibrary(false)
             .setManifestValues(manifestValues)
-            .setCustomPackage(AndroidCommon.getJavaPackage(ruleContext))
+            .setCustomPackage(customPackage)
             .setManifestOutput(outputManifest)
             .setLogOut(mergeLog)
             .build(ruleContext);
@@ -295,24 +320,30 @@
     return Optional.empty();
   }
 
-  private static boolean useLegacyMerging(RuleContext ruleContext) {
-    boolean legacy = false;
-    if (ruleContext.isLegalFragment(AndroidConfiguration.class)
-        && ruleContext.getRule().isAttrDefined("manifest_merger", STRING)) {
-      AndroidManifestMerger merger =
-          AndroidManifestMerger.fromString(ruleContext.attributes().get("manifest_merger", STRING));
-      if (merger == null) {
-        merger = ruleContext.getFragment(AndroidConfiguration.class).getManifestMerger();
-      }
-      if (merger == AndroidManifestMerger.LEGACY) {
-        ruleContext.ruleWarning(
-            "manifest_merger 'legacy' is deprecated. Please update to 'android'.\n"
-                + "See https://developer.android.com/studio/build/manifest-merge.html for more "
-                + "information about the manifest merger.");
-      }
-      legacy = merger == AndroidManifestMerger.LEGACY;
+  /** Checks if the legacy manifest merger should be used, based on a rule attribute */
+  static boolean useLegacyMerging(RuleContext ruleContext) {
+    return ruleContext.isLegalFragment(AndroidConfiguration.class)
+        && ruleContext.getRule().isAttrDefined("manifest_merger", STRING)
+        && useLegacyMerging(ruleContext, ruleContext.attributes().get("manifest_merger", STRING));
+  }
+
+  /**
+   * Checks if the legacy manifest merger should be used, based on an optional string specifying the
+   * merger to use.
+   */
+  public static boolean useLegacyMerging(RuleContext ruleContext, @Nullable String mergerString) {
+    AndroidManifestMerger merger = AndroidManifestMerger.fromString(mergerString);
+    if (merger == null) {
+      merger = ruleContext.getFragment(AndroidConfiguration.class).getManifestMerger();
     }
-    return legacy;
+    if (merger == AndroidManifestMerger.LEGACY) {
+      ruleContext.ruleWarning(
+          "manifest_merger 'legacy' is deprecated. Please update to 'android'.\n"
+              + "See https://developer.android.com/studio/build/manifest-merge.html for more "
+              + "information about the manifest merger.");
+    }
+
+    return merger == AndroidManifestMerger.LEGACY;
   }
 
   private static Map<Artifact, Label> getMergeeManifests(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AssetDependencies.java b/src/main/java/com/google/devtools/build/lib/rules/android/AssetDependencies.java
index cc9c8dc..1cfe929 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AssetDependencies.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AssetDependencies.java
@@ -38,14 +38,15 @@
   private final NestedSet<Artifact> transitiveAssets;
   private final NestedSet<Artifact> transitiveSymbols;
 
-  static AssetDependencies fromRuleDeps(RuleContext ruleContext, boolean neverlink) {
+  public static AssetDependencies fromRuleDeps(RuleContext ruleContext, boolean neverlink) {
     return fromProviders(
         AndroidCommon.getTransitivePrerequisites(
             ruleContext, Mode.TARGET, AndroidAssetsInfo.PROVIDER),
         neverlink);
   }
 
-  static AssetDependencies fromProviders(Iterable<AndroidAssetsInfo> providers, boolean neverlink) {
+  public static AssetDependencies fromProviders(
+      Iterable<AndroidAssetsInfo> providers, boolean neverlink) {
     NestedSetBuilder<ParsedAndroidAssets> direct = NestedSetBuilder.naiveLinkOrder();
     NestedSetBuilder<ParsedAndroidAssets> transitive = NestedSetBuilder.naiveLinkOrder();
     NestedSetBuilder<Artifact> assets = NestedSetBuilder.naiveLinkOrder();
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ProcessedAndroidData.java b/src/main/java/com/google/devtools/build/lib/rules/android/ProcessedAndroidData.java
index 8a0d0e6..438f12c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ProcessedAndroidData.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ProcessedAndroidData.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package com.google.devtools.build.lib.rules.android;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.config.CompilationMode;
@@ -52,7 +53,11 @@
 
   /** Processes Android data (assets, resources, and manifest) for android_binary targets. */
   public static ProcessedAndroidData processBinaryDataFrom(
-      RuleContext ruleContext, StampedAndroidManifest manifest, boolean conditionalKeepRules)
+      RuleContext ruleContext,
+      StampedAndroidManifest manifest,
+      boolean conditionalKeepRules,
+      Map<String, String> manifestValues,
+      AndroidAaptVersion aaptVersion)
       throws RuleErrorException, InterruptedException {
     if (conditionalKeepRules
         && AndroidAaptVersion.chooseTargetAaptVersion(ruleContext) != AndroidAaptVersion.AAPT2) {
@@ -61,7 +66,7 @@
     }
 
     AndroidResourcesProcessorBuilder builder =
-        builderForNonIncrementalTopLevelTarget(ruleContext, manifest)
+        builderForNonIncrementalTopLevelTarget(ruleContext, manifest, manifestValues, aaptVersion)
             .setUseCompiledResourcesForMerge(
                 AndroidAaptVersion.chooseTargetAaptVersion(ruleContext) == AndroidAaptVersion.AAPT2
                     && AndroidCommon.getAndroidConfig(ruleContext).skipParsingAction())
@@ -95,11 +100,13 @@
       RuleContext ruleContext,
       StampedAndroidManifest manifest,
       Artifact apkOut,
-      String proguardPrefix)
+      String proguardPrefix,
+      Map<String, String> manifestValues)
       throws RuleErrorException {
 
     AndroidResourcesProcessorBuilder builder =
-        builderForTopLevelTarget(ruleContext, manifest, proguardPrefix).setApkOut(apkOut);
+        builderForTopLevelTarget(ruleContext, manifest, proguardPrefix, manifestValues)
+            .setApkOut(apkOut);
 
     return buildActionForBinary(ruleContext, builder, manifest);
   }
@@ -135,10 +142,14 @@
 
   /** Processes Android data (assets, resources, and manifest) for android_local_test targets. */
   public static ProcessedAndroidData processLocalTestDataFrom(
-      RuleContext ruleContext, StampedAndroidManifest manifest)
+      RuleContext ruleContext,
+      StampedAndroidManifest manifest,
+      Map<String, String> manifestValues,
+      AndroidAaptVersion aaptVersion)
       throws RuleErrorException, InterruptedException {
 
-    return builderForNonIncrementalTopLevelTarget(ruleContext, manifest)
+    return builderForNonIncrementalTopLevelTarget(
+            ruleContext, manifest, manifestValues, aaptVersion)
         .setUseCompiledResourcesForMerge(
             AndroidAaptVersion.chooseTargetAaptVersion(ruleContext) == AndroidAaptVersion.AAPT2
                 && AndroidCommon.getAndroidConfig(ruleContext).skipParsingAction())
@@ -161,27 +172,24 @@
       RuleContext ruleContext,
       StampedAndroidManifest manifest,
       String packageUnderTest,
-      boolean hasLocalResourceFiles)
-      throws InterruptedException, RuleErrorException {
+      boolean hasLocalResourceFiles,
+      AndroidAaptVersion aaptVersion,
+      AndroidResources resources,
+      ResourceDependencies resourceDeps,
+      AndroidAssets assets,
+      AssetDependencies assetDeps)
+      throws InterruptedException {
 
     AndroidResourcesProcessorBuilder builder =
-        builderForNonIncrementalTopLevelTarget(ruleContext, manifest)
+        builderForNonIncrementalTopLevelTarget(
+                ruleContext, manifest, ImmutableMap.of(), aaptVersion)
             .setMainDexProguardOut(AndroidBinary.createMainDexProguardSpec(ruleContext))
             .setPackageUnderTest(packageUnderTest)
-            .setIsTestWithResources(hasLocalResourceFiles);
+            .setIsTestWithResources(hasLocalResourceFiles)
+            .withResourceDependencies(resourceDeps)
+            .withAssetDependencies(assetDeps);
 
-    if (hasLocalResourceFiles) {
-      builder
-          .withResourceDependencies(
-              ResourceDependencies.fromRuleDeps(ruleContext, /* neverlink = */ false))
-          .withAssetDependencies(
-              AssetDependencies.fromRuleDeps(ruleContext, /* neverlink = */ false));
-    }
-
-    return builder.build(
-        AndroidResources.from(ruleContext, "local_resource_files"),
-        AndroidAssets.from(ruleContext),
-        manifest);
+    return builder.build(resources, assets, manifest);
   }
 
   /**
@@ -190,18 +198,22 @@
    * <p>The builder will be populated with commonly-used settings and outputs.
    */
   private static AndroidResourcesProcessorBuilder builderForNonIncrementalTopLevelTarget(
-      RuleContext ruleContext, StampedAndroidManifest manifest)
-      throws InterruptedException, RuleErrorException {
+      RuleContext ruleContext,
+      StampedAndroidManifest manifest,
+      Map<String, String> manifestValues,
+      AndroidAaptVersion aaptVersion)
+      throws InterruptedException {
 
-    return builderForTopLevelTarget(ruleContext, manifest, "")
-        .targetAaptVersion(AndroidAaptVersion.chooseTargetAaptVersion(ruleContext))
+    return builderForTopLevelTarget(ruleContext, manifest, "", manifestValues)
+        .targetAaptVersion(aaptVersion)
 
         // Outputs
         .setApkOut(ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_APK))
         .setRTxtOut(ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_R_TXT))
         .setSourceJarOut(
             ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_JAVA_SOURCE_JAR))
-        .setSymbols(ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_MERGED_SYMBOLS));
+        .setSymbols(
+            ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_MERGED_SYMBOLS));
   }
 
   /**
@@ -210,9 +222,10 @@
    * <p>The builder will be populated with commonly-used settings and outputs.
    */
   private static AndroidResourcesProcessorBuilder builderForTopLevelTarget(
-      RuleContext ruleContext, StampedAndroidManifest manifest, String proguardPrefix) {
-    Map<String, String> manifestValues = ApplicationManifest.getManifestValues(ruleContext);
-
+      RuleContext ruleContext,
+      StampedAndroidManifest manifest,
+      String proguardPrefix,
+      Map<String, String> manifestValues) {
     return new AndroidResourcesProcessorBuilder(ruleContext)
         // Settings
         .setDebug(ruleContext.getConfiguration().getCompilationMode() != CompilationMode.OPT)
@@ -272,10 +285,10 @@
    * <p>Registers an action to run R class generation, the last step needed in resource processing.
    * Returns the fully processed data, including validated resources, wrapped in a ResourceApk.
    */
-  public ResourceApk generateRClass(RuleContext ruleContext)
-      throws RuleErrorException, InterruptedException {
+  public ResourceApk generateRClass(RuleContext ruleContext, AndroidAaptVersion aaptVersion)
+      throws InterruptedException {
     return new RClassGeneratorActionBuilder(ruleContext)
-        .targetAaptVersion(AndroidAaptVersion.chooseTargetAaptVersion(ruleContext))
+        .targetAaptVersion(aaptVersion)
         .withDependencies(resourceDeps)
         .setClassJarOut(
             ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR))
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceApk.java b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceApk.java
index 324766d..7b12859 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ResourceApk.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ResourceApk.java
@@ -20,6 +20,7 @@
 import com.google.devtools.build.lib.analysis.config.CompilationMode;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import java.util.Optional;
 import javax.annotation.Nullable;
 
 /**
@@ -228,35 +229,49 @@
     return resourceDeps.toInfo(validatedResources);
   }
 
+  // TODO(b/77574966): Stop returning an Optional once we get rid of ResourceContainer and can
+  // guarantee that only properly merged assets are passed into this object.
+  Optional<AndroidAssetsInfo> toAssetsInfo(Label label) {
+    if (primaryAssets instanceof MergedAndroidAssets) {
+      MergedAndroidAssets merged = (MergedAndroidAssets) primaryAssets;
+      AndroidAssetsInfo assetsInfo = merged.toProvider();
+      return Optional.of(assetsInfo);
+    } else if (primaryAssets == null) {
+      return Optional.of(assetDeps.toInfo(label));
+    } else {
+      return Optional.empty();
+    }
+  }
+
+  // TODO(b/77574966): Remove this cast once we get rid of ResourceContainer and can guarantee
+  // that only properly merged resources are passed into this object.
+  Optional<AndroidManifestInfo> toManifestInfo() {
+    if (validatedResources instanceof ValidatedAndroidResources) {
+      ValidatedAndroidResources validated = (ValidatedAndroidResources) validatedResources;
+
+      return Optional.of(validated.getStampedManifest().toProvider());
+    }
+
+    return Optional.empty();
+  }
+
   public void addToConfiguredTargetBuilder(
       RuleConfiguredTargetBuilder builder, Label label, boolean includeSkylarkApiProvider) {
     AndroidResourcesInfo resourceInfo = toResourceInfo(label);
     builder.addNativeDeclaredProvider(resourceInfo);
 
-    // TODO(b/77574966): Remove this cast once we get rid of ResourceContainer and can guarantee
-    // that only properly merged resources are passed into this object.
-    if (validatedResources instanceof ValidatedAndroidResources) {
-      ValidatedAndroidResources validated = (ValidatedAndroidResources) validatedResources;
+    Optional<AndroidManifestInfo> manifestInfo = toManifestInfo();
+    manifestInfo.ifPresent(builder::addNativeDeclaredProvider);
 
-      builder.addNativeDeclaredProvider(validated.getStampedManifest().toProvider());
-    }
-
-    // TODO(b/77574966): Remove this cast once we get rid of ResourceContainer and can guarantee
-    // that only properly merged resources are passed into this object.
-    if (primaryAssets instanceof MergedAndroidAssets) {
-      MergedAndroidAssets merged = (MergedAndroidAssets) primaryAssets;
-      AndroidAssetsInfo assetsInfo = merged.toProvider();
-      builder.addNativeDeclaredProvider(assetsInfo);
-
-      if (assetsInfo.getValidationResult() != null) {
-        // Asset merging output isn't consumed by anything. Require it to be run by top-level
-        // targets
-        // so we can validate there are no asset merging conflicts.
-        builder.addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, assetsInfo.getValidationResult());
+    Optional<AndroidAssetsInfo> assetsInfo = toAssetsInfo(label);
+    if (assetsInfo.isPresent()) {
+      builder.addNativeDeclaredProvider(assetsInfo.get());
+      if (assetsInfo.get().getValidationResult() != null) {
+      // Asset merging output isn't consumed by anything. Require it to be run by top-level targets
+      // so we can validate there are no asset merging conflicts.
+        builder.addOutputGroup(
+            OutputGroupInfo.HIDDEN_TOP_LEVEL, assetsInfo.get().getValidationResult());
       }
-
-    } else if (primaryAssets == null) {
-      builder.addNativeDeclaredProvider(assetDeps.toInfo(label));
     }
 
     if (includeSkylarkApiProvider) {
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidResourcesTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidResourcesTest.java
index db71439..a7bee24 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidResourcesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidResourcesTest.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Artifact;
@@ -448,7 +449,9 @@
             null);
 
     ValidatedAndroidData validated =
-        processedData.generateRClass(ruleContext).getValidatedResources();
+        processedData
+            .generateRClass(ruleContext, AndroidAaptVersion.chooseTargetAaptVersion(ruleContext))
+            .getValidatedResources();
 
     // An action to generate the R.class file should be registered.
     assertActionArtifacts(
@@ -462,8 +465,9 @@
     RuleContext ruleContext = getRuleContext("android_binary", "manifest='AndroidManifest.xml',");
 
     ResourceApk resourceApk =
-        ProcessedAndroidData.processBinaryDataFrom(ruleContext, getManifest(), false)
-            .generateRClass(ruleContext);
+        ProcessedAndroidData.processBinaryDataFrom(
+                ruleContext, getManifest(), false, ImmutableMap.of(), AndroidAaptVersion.AUTO)
+            .generateRClass(ruleContext, AndroidAaptVersion.AUTO);
 
     assertThat(resourceApk.getResourceProguardConfig()).isNotNull();
     assertThat(resourceApk.getMainDexProguardConfig()).isNotNull();