Implement support for android_library and android_binary in AndroidStudioInfoAspect.

--
MOS_MIGRATED_REVID=103635637
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidSemantics.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidSemantics.java
index 366bfcf..73b3a5c 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidSemantics.java
@@ -21,6 +21,7 @@
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.actions.SpawnAction;
 import com.google.devtools.build.lib.rules.android.AndroidCommon;
+import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider;
 import com.google.devtools.build.lib.rules.android.AndroidSemantics;
 import com.google.devtools.build.lib.rules.android.ApplicationManifest;
 import com.google.devtools.build.lib.rules.android.ResourceApk;
@@ -39,11 +40,18 @@
   }
 
   @Override
-  public void addTransitiveInfoProviders(RuleConfiguredTargetBuilder builder,
-      RuleContext ruleContext, JavaCommon javaCommon, AndroidCommon androidCommon,
-      Artifact jarWithAllClasses, ResourceApk resourceApk, Artifact zipAlignedApk,
-      Iterable<Artifact> apksUnderTest) {
-  }
+  public void addNonLocalResources(
+      RuleContext ruleContext,
+      ResourceApk resourceApk,
+      AndroidIdeInfoProvider.Builder ideInfoProviderBuilder) {}
+
+  @Override
+  public void addTransitiveInfoProviders(
+      RuleConfiguredTargetBuilder builder,
+      RuleContext ruleContext,
+      JavaCommon javaCommon,
+      AndroidCommon androidCommon,
+      Artifact jarWithAllClasses) {}
 
   @Override
   public ApplicationManifest getManifestForRule(RuleContext ruleContext) {
diff --git a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java
index 5a32d0a..ebf01e3 100644
--- a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java
+++ b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspect.java
@@ -33,6 +33,7 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.AndroidRuleIdeInfo;
 import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.AndroidSdkRuleInfo;
 import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.ArtifactLocation;
 import com.google.devtools.build.lib.ideinfo.androidstudio.AndroidStudioIdeInfo.JavaRuleIdeInfo;
@@ -43,10 +44,14 @@
 import com.google.devtools.build.lib.packages.AspectParameters;
 import com.google.devtools.build.lib.packages.BuildType;
 import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.rules.android.AndroidCommon;
+import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider;
+import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider.SourceDirectory;
 import com.google.devtools.build.lib.rules.android.AndroidSdkProvider;
 import com.google.devtools.build.lib.rules.java.JavaExportsProvider;
 import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
 import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider;
+import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.protobuf.MessageLite;
@@ -81,7 +86,6 @@
   @Override
   public AspectDefinition getDefinition() {
     return new AspectDefinition.Builder(NAME)
-        .requireProvider(JavaSourceInfoProvider.class)
         .attributeAspect("deps", AndroidStudioInfoAspect.class)
         .build();
   }
@@ -94,6 +98,7 @@
     // Collect ide build files and calculate dependencies.
     NestedSetBuilder<Label> transitiveDependenciesBuilder = NestedSetBuilder.stableOrder();
     NestedSetBuilder<Label> dependenciesBuilder = NestedSetBuilder.stableOrder();
+    NestedSetBuilder<SourceDirectory> transitiveResourcesBuilder = NestedSetBuilder.stableOrder();
 
     NestedSetBuilder<Artifact> ideBuildFilesBuilder = NestedSetBuilder.stableOrder();
 
@@ -106,6 +111,7 @@
       for (AndroidStudioInfoFilesProvider depProvider : androidStudioInfoFilesProviders) {
         ideBuildFilesBuilder.addTransitive(depProvider.getIdeBuildFiles());
         transitiveDependenciesBuilder.addTransitive(depProvider.getTransitiveDependencies());
+        transitiveResourcesBuilder.addTransitive(depProvider.getTransitiveResources());
       }
       List<? extends TransitiveInfoCollection> deps =
           ruleContext.getPrerequisites("deps", Mode.TARGET);
@@ -126,12 +132,20 @@
 
     RuleIdeInfo.Kind ruleKind = getRuleKind(ruleContext.getRule(), base);
 
+    NestedSet<SourceDirectory> transitiveResources;
     if (ruleKind != RuleIdeInfo.Kind.UNRECOGNIZED) {
-      Artifact ideBuildFile =
-          createIdeBuildArtifact(base, ruleContext, ruleKind,
+      Pair<Artifact, NestedSet<SourceDirectory>> ideBuildFile =
+          createIdeBuildArtifact(
+              base,
+              ruleContext,
+              ruleKind,
               directDependencies,
-              transitiveDependencies);
-      ideBuildFilesBuilder.add(ideBuildFile);
+              transitiveDependencies,
+              transitiveResourcesBuilder);
+      ideBuildFilesBuilder.add(ideBuildFile.first);
+      transitiveResources = ideBuildFile.second;
+    } else {
+      transitiveResources = transitiveResourcesBuilder.build();
     }
 
     NestedSet<Artifact> ideBuildFiles = ideBuildFilesBuilder.build();
@@ -139,7 +153,8 @@
         .addOutputGroup(IDE_BUILD, ideBuildFiles)
         .addProvider(
             AndroidStudioInfoFilesProvider.class,
-            new AndroidStudioInfoFilesProvider(ideBuildFiles, transitiveDependencies));
+            new AndroidStudioInfoFilesProvider(
+                ideBuildFiles, transitiveDependencies, transitiveResources));
 
     return builder.build();
   }
@@ -160,11 +175,13 @@
     return sdkInfoBuilder.build();
   }
 
-  private Artifact createIdeBuildArtifact(
+  private Pair<Artifact, NestedSet<SourceDirectory>> createIdeBuildArtifact(
       ConfiguredTarget base,
       RuleContext ruleContext,
       Kind ruleKind,
-      NestedSet<Label> directDependencies, NestedSet<Label> transitiveDependencies) {
+      NestedSet<Label> directDependencies,
+      NestedSet<Label> transitiveDependencies,
+      NestedSetBuilder<SourceDirectory> transitiveResourcesBuilder) {
     PathFragment ideBuildFilePath = getOutputFilePath(base, ruleContext);
     Root genfilesDirectory = ruleContext.getConfiguration().getGenfilesDirectory();
     Artifact ideBuildFile =
@@ -189,22 +206,69 @@
     outputBuilder.addAllDependencies(transform(directDependencies, LABEL_TO_STRING));
     outputBuilder.addAllTransitiveDependencies(transform(transitiveDependencies, LABEL_TO_STRING));
 
+    NestedSet<SourceDirectory> transitiveResources = null;
     if (ruleKind == Kind.JAVA_LIBRARY
         || ruleKind == Kind.JAVA_IMPORT
         || ruleKind == Kind.JAVA_TEST
         || ruleKind == Kind.JAVA_BINARY) {
       outputBuilder.setJavaRuleIdeInfo(makeJavaRuleIdeInfo(base));
+    } else if (ruleKind == Kind.ANDROID_LIBRARY || ruleKind == Kind.ANDROID_BINARY) {
+      outputBuilder.setJavaRuleIdeInfo(makeJavaRuleIdeInfo(base));
+      Pair<AndroidRuleIdeInfo, NestedSet<SourceDirectory>> androidRuleIdeInfo =
+          makeAndroidRuleIdeInfo(ruleContext, base, transitiveResourcesBuilder);
+      outputBuilder.setAndroidRuleIdeInfo(androidRuleIdeInfo.first);
+      transitiveResources = androidRuleIdeInfo.second;
     } else if (ruleKind == Kind.ANDROID_SDK) {
       outputBuilder.setAndroidSdkRuleInfo(
           makeAndroidSdkRuleInfo(ruleContext, base.getProvider(AndroidSdkProvider.class)));
     }
 
+    if (transitiveResources == null) {
+      transitiveResources = transitiveResourcesBuilder.build();
+    }
 
     final RuleIdeInfo ruleIdeInfo = outputBuilder.build();
     ruleContext.registerAction(
         makeProtoWriteAction(ruleContext.getActionOwner(), ruleIdeInfo, ideBuildFile));
 
-    return ideBuildFile;
+    return Pair.of(ideBuildFile, transitiveResources);
+  }
+
+  private static Pair<AndroidRuleIdeInfo, NestedSet<SourceDirectory>> makeAndroidRuleIdeInfo(
+      RuleContext ruleContext,
+      ConfiguredTarget base,
+      NestedSetBuilder<SourceDirectory> transitiveResourcesBuilder) {
+    AndroidRuleIdeInfo.Builder builder = AndroidRuleIdeInfo.newBuilder();
+    AndroidIdeInfoProvider provider = base.getProvider(AndroidIdeInfoProvider.class);
+    if (provider.getSignedApk() != null) {
+      builder.setApk(makeArtifactLocation(provider.getSignedApk()));
+    }
+
+    if (provider.getManifest() != null) {
+      builder.setManifest(makeArtifactLocation(provider.getManifest()));
+    }
+
+    if (provider.getGeneratedManifest() != null) {
+      builder.setGeneratedManifest(makeArtifactLocation(provider.getGeneratedManifest()));
+    }
+
+    for (Artifact artifact : provider.getApksUnderTest()) {
+      builder.addDependencyApk(makeArtifactLocation(artifact));
+    }
+    for (SourceDirectory resourceDir : provider.getResourceDirs()) {
+      ArtifactLocation artifactLocation = makeArtifactLocation(resourceDir);
+      builder.addResources(artifactLocation);
+      transitiveResourcesBuilder.add(resourceDir);
+    }
+
+    builder.setJavaPackage(AndroidCommon.getJavaPackage(ruleContext));
+
+    NestedSet<SourceDirectory> transitiveResources = transitiveResourcesBuilder.build();
+    for (SourceDirectory transitiveResource : transitiveResources) {
+      builder.addTransitiveResources(makeArtifactLocation(transitiveResource));
+    }
+
+    return Pair.of(builder.build(), transitiveResources);
   }
 
   private static BinaryFileWriteAction makeProtoWriteAction(
@@ -228,6 +292,13 @@
         .build();
   }
 
+  private static ArtifactLocation makeArtifactLocation(SourceDirectory resourceDir) {
+    return ArtifactLocation.newBuilder()
+        .setRootPath(resourceDir.getRootPath().toString())
+        .setRelativePath(resourceDir.getRelativePath().toString())
+        .build();
+  }
+
   private static JavaRuleIdeInfo makeJavaRuleIdeInfo(ConfiguredTarget base) {
     JavaRuleIdeInfo.Builder builder = JavaRuleIdeInfo.newBuilder();
     JavaRuleOutputJarsProvider outputJarsProvider =
@@ -325,21 +396,27 @@
   }
 
   private RuleIdeInfo.Kind getRuleKind(Rule rule, ConfiguredTarget base) {
-    RuleIdeInfo.Kind kind;
-    String ruleClassName = rule.getRuleClassObject().getName();
-    if ("java_library".equals(ruleClassName)) {
-      kind = RuleIdeInfo.Kind.JAVA_LIBRARY;
-    } else if ("java_import".equals(ruleClassName)) {
-      kind = Kind.JAVA_IMPORT;
-    } else if ("java_test".equals(ruleClassName)) {
-      kind = Kind.JAVA_TEST;
-    } else if ("java_binary".equals(ruleClassName)) {
-      kind = Kind.JAVA_BINARY;
-    } else if (base.getProvider(AndroidSdkProvider.class) != null) {
-      kind = RuleIdeInfo.Kind.ANDROID_SDK;
-    } else {
-      kind = RuleIdeInfo.Kind.UNRECOGNIZED;
+    switch (rule.getRuleClassObject().getName()) {
+      case "java_library":
+        return Kind.JAVA_LIBRARY;
+      case "java_import":
+        return Kind.JAVA_IMPORT;
+      case "java_test":
+        return Kind.JAVA_TEST;
+      case "java_binary":
+        return Kind.JAVA_BINARY;
+      case "android_library":
+        return Kind.ANDROID_LIBRARY;
+      case "android_binary":
+        return Kind.ANDROID_BINARY;
+      default:
+        {
+          if (base.getProvider(AndroidSdkProvider.class) != null) {
+            return RuleIdeInfo.Kind.ANDROID_SDK;
+          } else {
+            return RuleIdeInfo.Kind.UNRECOGNIZED;
+          }
+        }
     }
-    return kind;
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java
index 0cb61a8..5d93531 100644
--- a/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoFilesProvider.java
@@ -19,6 +19,8 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider;
+import com.google.devtools.build.lib.rules.android.AndroidIdeInfoProvider.SourceDirectory;
 
 /**
  * File provider for Android Studio ide build files.
@@ -27,11 +29,15 @@
 public final class AndroidStudioInfoFilesProvider implements TransitiveInfoProvider {
   private final NestedSet<Artifact> ideBuildFiles;
   private final NestedSet<Label> transitiveDependencies;
+  private final NestedSet<AndroidIdeInfoProvider.SourceDirectory> transitiveResources;
 
   public AndroidStudioInfoFilesProvider(
-      NestedSet<Artifact> ideBuildFiles, NestedSet<Label> transitiveDependencies) {
+      NestedSet<Artifact> ideBuildFiles,
+      NestedSet<Label> transitiveDependencies,
+      NestedSet<SourceDirectory> transitiveResources) {
     this.ideBuildFiles = ideBuildFiles;
     this.transitiveDependencies = transitiveDependencies;
+    this.transitiveResources = transitiveResources;
   }
 
   public NestedSet<Artifact> getIdeBuildFiles() {
@@ -41,4 +47,8 @@
   public NestedSet<Label> getTransitiveDependencies() {
     return transitiveDependencies;
   }
+
+  public NestedSet<SourceDirectory> getTransitiveResources() {
+    return transitiveResources;
+  }
 }
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 73e3b12..81db9ac 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
@@ -57,6 +57,7 @@
 import com.google.devtools.build.lib.rules.java.JavaCommon;
 import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
 import com.google.devtools.build.lib.rules.java.JavaSemantics;
+import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider;
 import com.google.devtools.build.lib.rules.java.JavaTargetAttributes;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -265,7 +266,9 @@
     Artifact deployJar = createDeployJar(ruleContext, javaSemantics, androidCommon, resourceClasses,
         ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_BINARY_DEPLOY_JAR));
 
-    return createAndroidBinary(ruleContext,
+
+    return createAndroidBinary(
+        ruleContext,
         filesBuilder,
         deployJar,
         javaCommon,
@@ -506,9 +509,10 @@
         .add(splitDeployMarker)
         .build();
 
-    androidCommon.addTransitiveInfoProviders(builder);
-    androidSemantics.addTransitiveInfoProviders(builder, ruleContext, javaCommon, androidCommon,
-        jarToDex, resourceApk, zipAlignedApk, apksUnderTest);
+    androidCommon.addTransitiveInfoProviders(
+        builder, androidSemantics, resourceApk, zipAlignedApk, apksUnderTest);
+    androidSemantics.addTransitiveInfoProviders(
+        builder, ruleContext, javaCommon, androidCommon, jarToDex);
 
     if (proguardOutput.mapping != null) {
       builder.add(ProguardMappingProvider.class,
@@ -517,14 +521,20 @@
 
     return builder
         .setFilesToBuild(filesToBuild)
-        .add(RunfilesProvider.class, RunfilesProvider.simple(
-            new Runfiles.Builder(ruleContext.getWorkspaceName())
-            .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES)
-            .addTransitiveArtifacts(filesToBuild)
-            .build()))
-        .add(ApkProvider.class,
-            new ApkProvider(NestedSetBuilder.create(Order.STABLE_ORDER, zipAlignedApk),
-                coverageMetadata))
+        .add(
+            RunfilesProvider.class,
+            RunfilesProvider.simple(
+                new Runfiles.Builder(ruleContext.getWorkspaceName())
+                    .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES)
+                    .addTransitiveArtifacts(filesToBuild)
+                    .build()))
+        .add(
+            JavaSourceInfoProvider.class,
+            JavaSourceInfoProvider.fromJavaTargetAttributes(resourceClasses, javaSemantics))
+        .add(
+            ApkProvider.class,
+            new ApkProvider(
+                NestedSetBuilder.create(Order.STABLE_ORDER, zipAlignedApk), coverageMetadata))
         .add(AndroidPreDexJarProvider.class, new AndroidPreDexJarProvider(jarToDex))
         .addOutputGroup("mobile_install_full", fullDeployMarker)
         .addOutputGroup("mobile_install_incremental", incrementalDeployMarker)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
index 86aa0c8..8a26cbf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
@@ -42,6 +42,7 @@
 import com.google.devtools.build.lib.packages.BuildType;
 import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction;
 import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceContainer;
+import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceType;
 import com.google.devtools.build.lib.rules.android.AndroidRuleClasses.MultidexMode;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParams;
 import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider;
@@ -58,10 +59,12 @@
 import com.google.devtools.build.lib.rules.java.JavaCompilationArtifacts;
 import com.google.devtools.build.lib.rules.java.JavaCompilationHelper;
 import com.google.devtools.build.lib.rules.java.JavaNativeLibraryProvider;
+import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
 import com.google.devtools.build.lib.rules.java.JavaRuntimeJarProvider;
 import com.google.devtools.build.lib.rules.java.JavaSemantics;
 import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
 import com.google.devtools.build.lib.rules.java.JavaTargetAttributes;
+import com.google.devtools.build.lib.rules.java.JavaUtil;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -207,6 +210,55 @@
     ruleContext.registerAction(builder.build(ruleContext));
   }
 
+  public static AndroidIdeInfoProvider createAndroidIdeInfoProvider(
+      RuleContext ruleContext,
+      AndroidSemantics semantics,
+      ResourceApk resourceApk,
+      Artifact zipAlignedApk,
+      Iterable<Artifact> apksUnderTest) {
+    AndroidIdeInfoProvider.Builder ideInfoProviderBuilder =
+        new AndroidIdeInfoProvider.Builder()
+            .addIdlParcelables(getIdlParcelables(ruleContext))
+            .addIdlSrcs(getIdlSrcs(ruleContext))
+            .addAllApksUnderTest(apksUnderTest);
+
+    if (zipAlignedApk != null) {
+      ideInfoProviderBuilder.setApk(zipAlignedApk);
+    }
+
+    // If the rule defines resources, put those in the IDE info. Otherwise, proxy the data coming
+    // from the android_resources rule in its direct dependencies, if such a thing exists.
+    if (LocalResourceContainer.definesAndroidResources(ruleContext.attributes())) {
+      ideInfoProviderBuilder
+          .addResourceSources(resourceApk.getPrimaryResource().getArtifacts(ResourceType.RESOURCES))
+          .addAssetSources(
+              resourceApk.getPrimaryResource().getArtifacts(ResourceType.ASSETS),
+              getAssetDir(ruleContext))
+          // Sets the possibly merged manifest and the raw manifest.
+          .setGeneratedManifest(resourceApk.getPrimaryResource().getManifest())
+          .setManifest(ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET));
+    } else {
+      semantics.addNonLocalResources(ruleContext, resourceApk, ideInfoProviderBuilder);
+    }
+
+    return ideInfoProviderBuilder.build();
+  }
+
+  public static String getJavaPackage(RuleContext ruleContext) {
+    if (ruleContext.attributes().isAttributeValueExplicitlySpecified("custom_package")) {
+      return ruleContext.attributes().get("custom_package", Type.STRING);
+    } else {
+      PathFragment nameFragment = ruleContext.getRule().getPackage().getNameFragment();
+      String packageName = JavaUtil.getJavaFullClassname(nameFragment);
+      if (packageName != null) {
+        return packageName;
+      } else {
+        // This is a workaround for libraries that don't follow the standard Bazel package format
+        return nameFragment.getPathString().replace('/', '.');
+      }
+    }
+  }
+
   Artifact compileDexWithJack(
       MultidexMode mode, Optional<Artifact> mainDexList, Collection<Artifact> proguardSpecs) {
     return jackCompilationHelper.compileAsDex(mode, mainDexList, proguardSpecs);
@@ -546,7 +598,11 @@
   }
 
   public RuleConfiguredTargetBuilder addTransitiveInfoProviders(
-      RuleConfiguredTargetBuilder builder) {
+      RuleConfiguredTargetBuilder builder,
+      AndroidSemantics androidSemantics,
+      ResourceApk resourceApk,
+      Artifact zipAlignedApk,
+      Iterable<Artifact> apksUnderTest) {
     if (!idls.isEmpty()) {
       generateAndroidIdlActions(
           ruleContext, idls, transitiveIdlImportData, translatedIdlSources);
@@ -557,6 +613,9 @@
         .build();
 
     javaCommon.addTransitiveInfoProviders(builder, filesToBuild, classJar);
+    builder.add(
+        JavaRuleOutputJarsProvider.class,
+        new JavaRuleOutputJarsProvider(classJar, srcJar, genJar, gensrcJar));
 
     return builder
         .setFilesToBuild(filesToBuild)
@@ -567,6 +626,10 @@
         .add(
             AndroidResourcesProvider.class,
             new AndroidResourcesProvider(ruleContext.getLabel(), transitiveResources))
+        .add(
+            AndroidIdeInfoProvider.class,
+            createAndroidIdeInfoProvider(
+                ruleContext, androidSemantics, resourceApk, zipAlignedApk, apksUnderTest))
         .add(AndroidIdlProvider.class, transitiveIdlImportData)
         .add(
             JavaCompilationArgsProvider.class,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdeInfoProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdeInfoProvider.java
new file mode 100644
index 0000000..5474ccb
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidIdeInfoProvider.java
@@ -0,0 +1,338 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// 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.google.devtools.build.lib.rules.android;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.Root;
+import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import javax.annotation.Nullable;
+
+/**
+ * An Android target provider to provide Android-specific info to IDEs.
+ */
+@Immutable
+public final class AndroidIdeInfoProvider implements TransitiveInfoProvider {
+  /** Represents a directory that contains sources, generated or otherwise, for an IDE.*/
+  @Immutable
+  public static class SourceDirectory {
+    final PathFragment relativePath;
+    final PathFragment rootPath;
+    final boolean isSource;
+
+    public SourceDirectory(PathFragment rootPath, PathFragment relativePath, boolean isSource) {
+      this.rootPath = rootPath;
+      this.relativePath = relativePath;
+      this.isSource = isSource;
+    }
+
+   /**
+    * The root relative path, {@link Artifact#getRootRelativePath()}.
+    */
+    public PathFragment getRelativePath() {
+      return relativePath;
+    }
+
+    /**
+     * The absolute path of the root that contains this directory, {@link Root#getPath()}.
+     */
+    public PathFragment getRootPath() {
+      return rootPath;
+    }
+
+    /** Indicates if the directory is in the gen files tree. */
+    boolean isSource() {
+      return isSource;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(relativePath, rootPath, isSource);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (other instanceof SourceDirectory) {
+        SourceDirectory otherDir = (SourceDirectory) other;
+        return Objects.equals(rootPath, otherDir.rootPath)
+            && Objects.equals(relativePath, otherDir.relativePath)
+            && Objects.equals(isSource, otherDir.isSource);
+      }
+      return false;
+    }
+
+    @Override
+    public String toString() {
+      return "SourceDirectory [relativePath=" + relativePath + ", rootPath=" + rootPath
+          + ", isSource=" + isSource + "]";
+    }
+  }
+
+  /**
+   * Builder for {@link AndroidIdeInfoProvider}
+   */
+  public static class Builder {
+    private Artifact manifest = null;
+    private Artifact generatedManifest = null;
+    private Artifact apk = null;
+    private final Set<SourceDirectory> resourceDirs = new LinkedHashSet<>();
+    private final Set<SourceDirectory> assetDirs = new LinkedHashSet<>();
+    private final Set<SourceDirectory> idlDirs = new LinkedHashSet<>();
+    private final Set<Artifact> idlSrcs = new LinkedHashSet<>();
+    private final Set<Artifact> apksUnderTest = new LinkedHashSet<>();
+
+    public AndroidIdeInfoProvider build() {
+      return new AndroidIdeInfoProvider(
+          manifest,
+          generatedManifest,
+          apk,
+          ImmutableList.copyOf(assetDirs),
+          ImmutableList.copyOf(resourceDirs),
+          ImmutableList.copyOf(idlDirs),
+          ImmutableList.copyOf(idlSrcs),
+          ImmutableList.copyOf(apksUnderTest));
+    }
+
+    public Builder setApk(Artifact apk) {
+      Preconditions.checkState(this.apk == null);
+      this.apk = apk;
+      return this;
+    }
+
+    public Builder setManifest(Artifact manifest) {
+      Preconditions.checkState(this.manifest == null);
+      this.manifest = manifest;
+      return this;
+    }
+
+    public Builder setGeneratedManifest(Artifact manifest) {
+      Preconditions.checkState(this.generatedManifest == null);
+      this.generatedManifest = manifest;
+      return this;
+    }
+
+    /**
+     * Add "idl_srcs" contents.
+     */
+    public Builder addIdlSrcs(Collection<Artifact> idlSrcs) {
+      this.idlSrcs.addAll(idlSrcs);
+      addIdlDirs(idlSrcs);
+      return this;
+    }
+
+    /**
+     * Add "idl_parcelables" contents.
+     */
+    public Builder addIdlParcelables(Collection<Artifact> idlParcelables) {
+      addIdlDirs(idlParcelables);
+      return this;
+    }
+
+    private void addIdlDirs(Collection<Artifact> idlArtifacts) {
+      for (Artifact idl : idlArtifacts) {
+        this.idlDirs.add(
+            new SourceDirectory(
+                idl.getRoot().getPath().asFragment(),
+                idl.getRootRelativePath().getParentDirectory(),
+                idl.isSourceArtifact()));
+      }
+    }
+
+    public Builder addAllResources(Collection<SourceDirectory> resources) {
+      resourceDirs.addAll(resources);
+      return this;
+    }
+
+    public Builder addAllAssets(Collection<SourceDirectory> assets) {
+      assetDirs.addAll(assets);
+      return this;
+    }
+
+    public Builder addResourceSource(Artifact resource) {
+      resourceDirs.add(
+          new SourceDirectory(
+              resource.getRoot().getPath().asFragment(),
+              trimTo(
+                  resource.getRootRelativePath(),
+                  LocalResourceContainer.Builder.findResourceDir(resource)),
+              resource.isSourceArtifact()));
+      return this;
+    }
+
+    public Builder addResourceSources(Collection<Artifact> resources) {
+      for (Artifact resource : resources) {
+        addResourceSource(resource);
+      }
+      return this;
+    }
+
+    public Builder addAssetSources(Collection<Artifact> assets, PathFragment assetDir) {
+      for (Artifact asset : assets) {
+        addAssetSource(asset, assetDir);
+      }
+      return this;
+    }
+
+    public Builder addAssetSource(Artifact asset, PathFragment assetDir) {
+      assetDirs.add(
+          new SourceDirectory(
+              asset.getRoot().getPath().asFragment(),
+              trimTo(asset.getRootRelativePath(), assetDir),
+              asset.isSourceArtifact()));
+      return this;
+    }
+
+    public Builder addAllApksUnderTest(Iterable<Artifact> apks) {
+      Iterables.addAll(apksUnderTest, apks);
+      return this;
+    }
+
+    /**
+     * Finds the rightmost occurrence of the needle and returns subfragment of the haystack from
+     * left to the end of the occurrence inclusive of the needle.
+     *
+     * <pre>
+     * `Example:
+     *   Given the haystack:
+     *     res/research/handwriting/res/values/strings.xml
+     *   And the needle:
+     *     res
+     *   Returns:
+     *     res/research/handwriting/res
+     * </pre>
+     */
+    private static PathFragment trimTo(PathFragment haystack, PathFragment needle) {
+      if (needle.equals(PathFragment.EMPTY_FRAGMENT)) {
+        return haystack;
+      }
+      // Compute the overlap offset for duplicated parts of the needle.
+      int[] overlap = new int[needle.segmentCount() + 1];
+      // Start overlap at -1, as it will cancel out the increment in the search.
+      // See http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm for the
+      // details.
+      overlap[0] = -1;
+      for (int i = 0, j = -1; i < needle.segmentCount(); j++, i++, overlap[i] = j) {
+        while (j >= 0 && !needle.getSegment(i).equals(needle.getSegment(j))) {
+          // Walk the overlap until the bound is found.
+          j = overlap[j];
+        }
+      }
+      // TODO(corysmith): reverse the search algorithm.
+      // Keep the index of the found so that the rightmost index is taken.
+      int found = -1;
+      for (int i = 0, j = 0; i < haystack.segmentCount(); i++) {
+
+        while (j >= 0 && !haystack.getSegment(i).equals(needle.getSegment(j))) {
+          // Not matching, walk the needle index to attempt another match.
+          j = overlap[j];
+        }
+        j++;
+        // Needle index is exhausted, so the needle must match.
+        if (j == needle.segmentCount()) {
+          // Record the found index + 1 to be inclusive of the end index.
+          found = i + 1;
+          // Subtract one from the needle index to restart the search process
+          j = j - 1;
+        }
+      }
+      if (found != -1) {
+        // Return the subsection of the haystack.
+        return haystack.subFragment(0, found);
+      }
+      throw new IllegalArgumentException(String.format("%s was not found in %s", needle, haystack));
+    }
+  }
+
+  private final Artifact manifest;
+  private final Artifact generatedManifest;
+  private final Artifact signedApk;
+  private final ImmutableCollection<SourceDirectory> resourceDirs;
+  private final ImmutableCollection<SourceDirectory> assetDirs;
+  private final ImmutableCollection<SourceDirectory> idlImports;
+  private final ImmutableCollection<Artifact> idlSrcs;
+  private final ImmutableCollection<Artifact> apksUnderTest;
+
+  AndroidIdeInfoProvider(@Nullable Artifact manifest,
+      @Nullable Artifact generatedManifest,
+      @Nullable Artifact signedApk,
+      ImmutableCollection<SourceDirectory> assetDirs,
+      ImmutableCollection<SourceDirectory> resourceDirs,
+      ImmutableCollection<SourceDirectory> idlImports,
+      ImmutableCollection<Artifact> idlSrcs,
+      ImmutableCollection<Artifact> apksUnderTest) {
+    this.manifest = manifest;
+    this.generatedManifest = generatedManifest;
+    this.signedApk = signedApk;
+    this.assetDirs = assetDirs;
+    this.resourceDirs = resourceDirs;
+    this.idlImports = idlImports;
+    this.idlSrcs = idlSrcs;
+    this.apksUnderTest = apksUnderTest;
+  }
+
+  /** Returns the direct AndroidManifest. */
+  @Nullable
+  public Artifact getManifest() {
+    return manifest;
+  }
+
+  /** Returns the direct generated AndroidManifest. */
+  @Nullable
+  public Artifact getGeneratedManifest() {
+    return generatedManifest;
+  }
+
+
+  /** Returns the direct debug key signed apk, if there is one. */
+  @Nullable
+  public Artifact getSignedApk() {
+    return signedApk;
+  }
+
+  /** A list of the direct Resource directories. */
+  public ImmutableCollection<SourceDirectory> getResourceDirs() {
+    return resourceDirs;
+  }
+
+  /** A list of the direct Asset directories. */
+  public ImmutableCollection<SourceDirectory> getAssetDirs() {
+    return assetDirs;
+  }
+
+  /** A list of direct idl directories. */
+  public ImmutableCollection<SourceDirectory> getIdlImports() {
+    return idlImports;
+  }
+
+  /** A list of sources from the "idl_srcs" attribute. */
+  public ImmutableCollection<Artifact> getIdlSrcs() {
+    return idlSrcs;
+  }
+
+  /** A list of the APKs related to the app under test, if any. */
+  public ImmutableCollection<Artifact> getApksUnderTest() {
+    return apksUnderTest;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
index 6ab6579..1c69f38 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLibrary.java
@@ -14,7 +14,6 @@
 package com.google.devtools.build.lib.rules.android;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
@@ -35,8 +34,10 @@
 import com.google.devtools.build.lib.rules.cpp.LinkerInput;
 import com.google.devtools.build.lib.rules.java.JavaCommon;
 import com.google.devtools.build.lib.rules.java.JavaNeverlinkInfoProvider;
+import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
 import com.google.devtools.build.lib.rules.java.JavaSemantics;
 import com.google.devtools.build.lib.rules.java.JavaSkylarkApiProvider;
+import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider;
 import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
 import com.google.devtools.build.lib.rules.java.JavaTargetAttributes;
 import com.google.devtools.build.lib.rules.java.JavaUtil;
@@ -78,12 +79,13 @@
     JavaCommon javaCommon = new JavaCommon(ruleContext, javaSemantics);
     AndroidCommon androidCommon = new AndroidCommon(ruleContext, javaCommon);
 
-    boolean definesLocalResources = 
+
+    boolean definesLocalResources =
       LocalResourceContainer.definesAndroidResources(ruleContext.attributes());
     if (definesLocalResources && !LocalResourceContainer.validateRuleContext(ruleContext)) {
       return null;
     }
-        
+
     final ResourceApk resourceApk;
     if (definesLocalResources) {
       ApplicationManifest applicationManifest = androidSemantics.getManifestForRule(ruleContext);
@@ -101,6 +103,7 @@
             null /* versionName */,
             false,
             null /* proguardCfgOut */);
+
       } catch (RuleConfigurationException e) {
         // RuleConfigurations exceptions will only be thrown after the RuleContext is updated.
         // So, exit.
@@ -117,7 +120,7 @@
         transitiveIdlImportData,
         false /* addCoverageSupport */,
         true /* collectJavaCompilationArgs */,
-        AndroidRuleClasses.ANDROID_LIBRARY_GEN_JAR); 
+        AndroidRuleClasses.ANDROID_LIBRARY_GEN_JAR);
     if (javaTargetAttributes == null) {
       return null;
     }
@@ -147,17 +150,7 @@
       Artifact apk = ruleContext.getImplicitOutputArtifact(
           AndroidRuleClasses.ANDROID_RESOURCES_APK);
 
-      String javaPackage;
-      if (apk.getExecPath().getFirstSegment(ImmutableSet.of("java", "javatests"))
-          != PathFragment.INVALID_SEGMENT) {
-        javaPackage = JavaUtil.getJavaPackageName(apk.getExecPath());
-      } else {
-        // This is a workaround for libraries that don't follow the standard Bazel package format
-        javaPackage = apk.getRootRelativePath().getPathString().replace('/', '.');
-      }
-      if (ruleContext.attributes().isAttributeValueExplicitlySpecified("custom_package")) {
-        javaPackage = ruleContext.attributes().get("custom_package", Type.STRING);
-      }
+      String javaPackage = AndroidCommon.getJavaPackage(ruleContext);
 
       ResourceContainer resourceContainer = new ResourceContainer(ruleContext.getLabel(),
           javaPackage, null /* renameManifestPackage */, false /* inlinedConstants */,
@@ -190,11 +183,10 @@
       .build(ruleContext);
 
     RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext);
-    androidCommon.addTransitiveInfoProviders(builder);
+    androidCommon.addTransitiveInfoProviders(builder, androidSemantics,
+        definesLocalResources ? resourceApk : null, null, ImmutableList.<Artifact>of());
     androidSemantics.addTransitiveInfoProviders(
-            builder, ruleContext, javaCommon, androidCommon,
-            null, definesLocalResources ? resourceApk : null, 
-            null, ImmutableList.<Artifact>of());
+        builder, ruleContext, javaCommon, androidCommon, null);
 
     return builder
       .add(AndroidNativeLibraryProvider.class,
@@ -202,6 +194,15 @@
       .addSkylarkTransitiveInfo(JavaSkylarkApiProvider.NAME, new JavaSkylarkApiProvider())
       .add(JavaNeverlinkInfoProvider.class,
           new JavaNeverlinkInfoProvider(androidCommon.isNeverLink()))
+      // TODO(ahumesky): The gensrcJar is passed in for the srcJar -- is this a mistake?
+      .add(JavaRuleOutputJarsProvider.class,
+          new JavaRuleOutputJarsProvider(
+              classesJar,
+              androidCommon.getGensrcJar(),
+              androidCommon.getGenJar(),
+              androidCommon.getGensrcJar()))
+      .add(JavaSourceInfoProvider.class,
+           JavaSourceInfoProvider.fromJavaTargetAttributes(javaTargetAttributes, javaSemantics))
       .add(JavaSourceJarsProvider.class, androidCommon.getJavaSourceJarsProvider())
       .add(AndroidCcLinkParamsProvider.class,
           new AndroidCcLinkParamsProvider(androidCommon.getCcLinkParamsStore()))
@@ -367,3 +368,4 @@
     return ruleContext.getPrerequisiteArtifacts("proguard_specs", Mode.TARGET).list();
   }
 }
+
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java
index f86fd57..a25ba93 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidSemantics.java
@@ -23,6 +23,8 @@
 import com.google.devtools.build.lib.rules.java.JavaSemantics;
 import com.google.devtools.build.lib.rules.java.JavaTargetAttributes;
 
+import javax.annotation.Nullable;
+
 /**
  * Pluggable semantics for Android rules.
  *
@@ -32,12 +34,27 @@
 public interface AndroidSemantics {
   /**
    * Adds transitive info providers for {@code android_binary} and {@code android_library} rules.
-   * @throws InterruptedException 
+   * @throws InterruptedException
    */
-  void addTransitiveInfoProviders(RuleConfiguredTargetBuilder builder,
-      RuleContext ruleContext, JavaCommon javaCommon, AndroidCommon androidCommon,
-      Artifact jarWithAllClasses, ResourceApk resourceApk, Artifact zipAlignedApk,
-      Iterable<Artifact> apksUnderTest) throws InterruptedException;
+  void addTransitiveInfoProviders(
+      RuleConfiguredTargetBuilder builder,
+      RuleContext ruleContext,
+      JavaCommon javaCommon,
+      AndroidCommon androidCommon,
+      Artifact jarWithAllClasses)
+      throws InterruptedException;
+
+  /**
+   * Add additional resources to IDE info for {@code android_binary} and {@code android_library}
+   *
+   * @param ruleContext rule context for target rule
+   * @param resourceApk resource apk directly provided by the rule
+   * @param ideInfoProviderBuilder
+   */
+  void addNonLocalResources(
+      RuleContext ruleContext,
+      @Nullable ResourceApk resourceApk,
+      AndroidIdeInfoProvider.Builder ideInfoProviderBuilder);
 
   /**
    * Returns the manifest to be used when compiling a given rule.
diff --git a/src/main/protobuf/android_studio_ide_info.proto b/src/main/protobuf/android_studio_ide_info.proto
index e81db55..ade3460 100644
--- a/src/main/protobuf/android_studio_ide_info.proto
+++ b/src/main/protobuf/android_studio_ide_info.proto
@@ -37,6 +37,17 @@
   repeated ArtifactLocation sources = 4;
 }
 
+message AndroidRuleIdeInfo {
+  repeated ArtifactLocation resources = 1;
+  repeated ArtifactLocation transitive_resources = 2;
+  ArtifactLocation apk = 3;
+  repeated ArtifactLocation dependency_apk = 4;
+  ArtifactLocation manifest = 5;
+  ArtifactLocation generated_manifest = 6;
+  string java_package = 7;
+  bool has_idl_sources = 8;
+}
+
 message AndroidSdkRuleInfo {
   string android_sdk_path = 1;
   string genfiles_path = 2;
@@ -63,9 +74,10 @@
   repeated string dependencies = 4;
   repeated string transitive_dependencies = 5;
 
-  // kind is one of JAVA_LIBRARY, JAVA_TEST, JAVA_IMPORT
-  JavaRuleIdeInfo java_rule_ide_info = 6;
-
   // kind is ANDROID_SDK
-  AndroidSdkRuleInfo android_sdk_rule_info = 7;
+  AndroidSdkRuleInfo android_sdk_rule_info = 6;
+
+  // kind is one of {JAVA,ANDROID}_{LIBRARY,BINARY,TEST} and JAVA_IMPORT
+  JavaRuleIdeInfo java_rule_ide_info = 7;
+  AndroidRuleIdeInfo android_rule_ide_info = 8;
 }
diff --git a/src/test/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspectTest.java b/src/test/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspectTest.java
index 0071a6b..792be89 100644
--- a/src/test/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspectTest.java
+++ b/src/test/java/com/google/devtools/build/lib/ideinfo/AndroidStudioInfoAspectTest.java
@@ -416,6 +416,109 @@
             "<jar:com/google/example/foobar-exe.jar><source:com/google/example/foobar-exe-src.jar>");
   }
 
+  public void testAndroidLibrary() throws Exception {
+    scratch.file(
+        "com/google/example/BUILD",
+        "android_library(",
+        "  name = \"l1\",",
+        "  manifest = \"Manifesto.xml\",",
+        "  custom_package = \"com.google.example\",",
+        "  resource_files = [\"r1/values/a.xml\"],",
+        ")",
+        "android_library(",
+        "  name = \"l\",",
+        "  srcs = [\"Main.java\"],",
+        "  deps = [\":l1\"],",
+        "  manifest = \"Abracadabra.xml\",",
+        "  custom_package = \"com.google.example\",",
+        "  resource_files = [\"res/drawable/a.png\", \"res/drawable/b.png\"],",
+        ")");
+    String target = "//com/google/example:l";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    RuleIdeInfo ruleInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+    assertThat(ruleInfo.getKind()).isEqualTo(Kind.ANDROID_LIBRARY);
+    assertThat(relativePathsForSourcesOf(ruleInfo)).containsExactly("com/google/example/Main.java");
+    assertThat(transform(ruleInfo.getJavaRuleIdeInfo().getJarsList(), LIBRARY_ARTIFACT_TO_STRING))
+        .containsExactly("<jar:com/google/example/libl.jar>");
+    assertThat(
+            transform(
+                ruleInfo.getAndroidRuleIdeInfo().getResourcesList(), ARTIFACT_TO_RELATIVE_PATH))
+        .containsExactly("com/google/example/res");
+    assertThat(ruleInfo.getAndroidRuleIdeInfo().getManifest().getRelativePath())
+        .isEqualTo("com/google/example/Abracadabra.xml");
+    assertThat(ruleInfo.getAndroidRuleIdeInfo().getJavaPackage()).isEqualTo("com.google.example");
+
+    assertThat(ruleInfo.getDependenciesList()).containsExactly("//com/google/example:l1");
+    assertThat(
+            transform(
+                ruleInfo.getAndroidRuleIdeInfo().getTransitiveResourcesList(),
+                ARTIFACT_TO_RELATIVE_PATH))
+        .containsExactly("com/google/example/res", "com/google/example/r1");
+  }
+
+  public void testAndroidBinary() throws Exception {
+    scratch.file(
+        "com/google/example/BUILD",
+        "android_library(",
+        "  name = \"l1\",",
+        "  manifest = \"Manifesto.xml\",",
+        "  custom_package = \"com.google.example\",",
+        "  resource_files = [\"r1/values/a.xml\"],",
+        ")",
+        "android_binary(",
+        "  name = \"b\",",
+        "  srcs = [\"Main.java\"],",
+        "  deps = [\":l1\"],",
+        "  manifest = \"Abracadabra.xml\",",
+        "  custom_package = \"com.google.example\",",
+        "  resource_files = [\"res/drawable/a.png\", \"res/drawable/b.png\"],",
+        ")");
+    String target = "//com/google/example:b";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    RuleIdeInfo ruleInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+    assertThat(ruleInfo.getKind()).isEqualTo(Kind.ANDROID_BINARY);
+    assertThat(relativePathsForSourcesOf(ruleInfo)).containsExactly("com/google/example/Main.java");
+    assertThat(transform(ruleInfo.getJavaRuleIdeInfo().getJarsList(), LIBRARY_ARTIFACT_TO_STRING))
+        .containsExactly(
+            "<jar:com/google/example/libb.jar><source:com/google/example/libb-src.jar>");
+    assertThat(
+            transform(
+                ruleInfo.getAndroidRuleIdeInfo().getResourcesList(), ARTIFACT_TO_RELATIVE_PATH))
+        .containsExactly("com/google/example/res");
+    assertThat(ruleInfo.getAndroidRuleIdeInfo().getManifest().getRelativePath())
+        .isEqualTo("com/google/example/Abracadabra.xml");
+    assertThat(ruleInfo.getAndroidRuleIdeInfo().getJavaPackage()).isEqualTo("com.google.example");
+
+    assertThat(ruleInfo.getDependenciesList()).containsExactly("//com/google/example:l1");
+    assertThat(
+            transform(
+                ruleInfo.getAndroidRuleIdeInfo().getTransitiveResourcesList(),
+                ARTIFACT_TO_RELATIVE_PATH))
+        .containsExactly("com/google/example/res", "com/google/example/r1");
+  }
+
+  public void testAndroidInferredPackage() throws Exception {
+    scratch.file(
+        "java/com/google/example/BUILD",
+        "android_library(",
+        "  name = \"l\",",
+        "  manifest = \"Manifesto.xml\",",
+        ")",
+        "android_binary(",
+        "  name = \"b\",",
+        "  srcs = [\"Main.java\"],",
+        "  deps = [\":l\"],",
+        "  manifest = \"Abracadabra.xml\",",
+        ")");
+    String target = "//java/com/google/example:b";
+    Map<String, RuleIdeInfo> ruleIdeInfos = buildRuleIdeInfo(target);
+    RuleIdeInfo lRuleInfo = getRuleInfoAndVerifyLabel("//java/com/google/example:l", ruleIdeInfos);
+    RuleIdeInfo bRuleInfo = getRuleInfoAndVerifyLabel(target, ruleIdeInfos);
+
+    assertThat(bRuleInfo.getAndroidRuleIdeInfo().getJavaPackage()).isEqualTo("com.google.example");
+    assertThat(lRuleInfo.getAndroidRuleIdeInfo().getJavaPackage()).isEqualTo("com.google.example");
+  }
+
   private Map<String, RuleIdeInfo> buildRuleIdeInfo(String target) throws Exception {
     AnalysisResult analysisResult =
         update(