Implement android_binary.instruments attribute.

android_binary now takes an android_binary in the `instruments` attribute. If the attribute is explicitly specified, an AndroidInstrumentationInfo provider is included in the RuleConfiguredTarget, which android_instrumentation_test can use.

Having the provider allows android_instrumentation_test to check and fail if it tries to depend on an android_binary that is not instrumenting anything.

RELNOTES: None.
PiperOrigin-RevId: 173547240
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 1344581..c48dc11 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
@@ -621,6 +621,21 @@
     RuleConfiguredTargetBuilder builder =
         new RuleConfiguredTargetBuilder(ruleContext);
 
+    // If this is an instrumentation APK, create the provider for android_instrumentation_test.
+    if (ruleContext.attributes().isAttributeValueExplicitlySpecified("instruments")) {
+      Artifact targetApk =
+          ruleContext
+              .getPrerequisite("instruments", Mode.TARGET)
+              .getProvider(ApkProvider.class)
+              .getApk();
+      Artifact instrumentationApk = zipAlignedApk;
+
+      AndroidInstrumentationInfo instrumentationProvider =
+          new AndroidInstrumentationInfo(targetApk, instrumentationApk);
+
+      builder.addNativeDeclaredProvider(instrumentationProvider);
+    }
+
     androidCommon.addTransitiveInfoProviders(
         builder,
         androidSemantics,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
index d1c6ed2..d3231d1 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
@@ -26,6 +26,7 @@
 import static com.google.devtools.build.lib.syntax.Type.STRING_DICT;
 import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
 import static com.google.devtools.build.lib.util.FileTypeSet.ANY_FILE;
+import static com.google.devtools.build.lib.util.FileTypeSet.NO_FILE;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -945,6 +946,11 @@
                   .cfg(HOST)
                   .exec()
                   .value(env.getToolsLabel("//tools/android:resource_extractor")))
+          .add(
+              attr("instruments", BuildType.LABEL)
+                  .undocumented("blocked by android_instrumentation_test")
+                  .allowedRuleClasses("android_binary")
+                  .allowedFileTypes(NO_FILE))
           .advertiseSkylarkProvider(SkylarkProviderIdentifier.forKey(JavaInfo.PROVIDER.getKey()))
           .build();
       }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
index c440200..8793c07 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/android/AndroidBinaryTest.java
@@ -3777,4 +3777,37 @@
 
     checkProguardLibJars(action, getAndroidJarPath());
   }
+
+  @Test
+  public void testInstrumentationInfoProviderHasApks() throws Exception {
+    scratch.file(
+        "java/com/google/android/instr/BUILD",
+        "android_binary(name = 'b1',",
+        "               srcs = ['b1.java'],",
+        "               instruments = ':b2',",
+        "               manifest = 'AndroidManifest.xml')",
+        "android_binary(name = 'b2',",
+        "               srcs = ['b2.java'],",
+        "               manifest = 'AndroidManifest.xml')");
+    ConfiguredTarget b1 = getConfiguredTarget("//java/com/google/android/instr:b1");
+    AndroidInstrumentationInfo provider = b1.get(AndroidInstrumentationInfo.PROVIDER);
+    assertThat(provider.getTargetApk()).isNotNull();
+    assertThat(provider.getTargetApk().prettyPrint())
+        .isEqualTo("java/com/google/android/instr/b2.apk");
+    assertThat(provider.getInstrumentationApk()).isNotNull();
+    assertThat(provider.getInstrumentationApk().prettyPrint())
+        .isEqualTo("java/com/google/android/instr/b1.apk");
+  }
+
+  @Test
+  public void testNoInstrumentationInfoProviderIfNotInstrumenting() throws Exception {
+    scratch.file(
+        "java/com/google/android/instr/BUILD",
+        "android_binary(name = 'b1',",
+        "               srcs = ['b1.java'],",
+        "               manifest = 'AndroidManifest.xml')");
+    ConfiguredTarget b1 = getConfiguredTarget("//java/com/google/android/instr:b1");
+    AndroidInstrumentationInfo provider = b1.get(AndroidInstrumentationInfo.PROVIDER);
+    assertThat(provider).isNull();
+  }
 }