@Override
  public <A extends Arg> BuildRule createBuildRule(
      TargetGraph targetGraph,
      BuildRuleParams originalBuildRuleParams,
      BuildRuleResolver resolver,
      A args) {

    UnflavoredBuildTarget originalBuildTarget =
        originalBuildRuleParams.getBuildTarget().checkUnflavored();
    SourcePathResolver pathResolver = new SourcePathResolver(resolver);
    ImmutableList.Builder<BuildRule> aarExtraDepsBuilder =
        ImmutableList.<BuildRule>builder().addAll(originalBuildRuleParams.getExtraDeps().get());

    /* android_manifest */
    AndroidManifestDescription.Arg androidManifestArgs =
        androidManifestDescription.createUnpopulatedConstructorArg();
    androidManifestArgs.skeleton = args.manifestSkeleton;
    androidManifestArgs.deps = args.deps;

    BuildRuleParams androidManifestParams =
        originalBuildRuleParams.copyWithChanges(
            BuildTargets.createFlavoredBuildTarget(
                originalBuildTarget, AAR_ANDROID_MANIFEST_FLAVOR),
            originalBuildRuleParams.getDeclaredDeps(),
            originalBuildRuleParams.getExtraDeps());

    AndroidManifest manifest =
        androidManifestDescription.createBuildRule(
            targetGraph, androidManifestParams, resolver, androidManifestArgs);
    aarExtraDepsBuilder.add(resolver.addToIndex(manifest));

    /* assemble dirs */
    AndroidPackageableCollector collector =
        new AndroidPackageableCollector(
            originalBuildRuleParams.getBuildTarget(),
            /* buildTargetsToExcludeFromDex */ ImmutableSet.<BuildTarget>of(),
            /* resourcesToExclude */ ImmutableSet.<BuildTarget>of());
    collector.addPackageables(
        AndroidPackageableCollector.getPackageableRules(originalBuildRuleParams.getDeps()));
    AndroidPackageableCollection packageableCollection = collector.build();

    ImmutableSortedSet<BuildRule> androidResourceDeclaredDeps =
        AndroidResourceHelper.androidResOnly(originalBuildRuleParams.getDeclaredDeps().get());
    ImmutableSortedSet<BuildRule> androidResourceExtraDeps =
        AndroidResourceHelper.androidResOnly(originalBuildRuleParams.getExtraDeps().get());

    BuildRuleParams assembleAssetsParams =
        originalBuildRuleParams.copyWithChanges(
            BuildTargets.createFlavoredBuildTarget(originalBuildTarget, AAR_ASSEMBLE_ASSETS_FLAVOR),
            Suppliers.ofInstance(androidResourceDeclaredDeps),
            Suppliers.ofInstance(androidResourceExtraDeps));
    ImmutableCollection<SourcePath> assetsDirectories =
        packageableCollection.getAssetsDirectories();
    AssembleDirectories assembleAssetsDirectories =
        new AssembleDirectories(assembleAssetsParams, pathResolver, assetsDirectories);
    aarExtraDepsBuilder.add(resolver.addToIndex(assembleAssetsDirectories));

    BuildRuleParams assembleResourceParams =
        originalBuildRuleParams.copyWithChanges(
            BuildTargets.createFlavoredBuildTarget(
                originalBuildTarget, AAR_ASSEMBLE_RESOURCE_FLAVOR),
            Suppliers.ofInstance(androidResourceDeclaredDeps),
            Suppliers.ofInstance(androidResourceExtraDeps));
    ImmutableCollection<SourcePath> resDirectories =
        packageableCollection.getResourceDetails().getResourceDirectories();
    MergeAndroidResourceSources assembleResourceDirectories =
        new MergeAndroidResourceSources(assembleResourceParams, pathResolver, resDirectories);
    aarExtraDepsBuilder.add(resolver.addToIndex(assembleResourceDirectories));

    /* android_resource */
    BuildRuleParams androidResourceParams =
        originalBuildRuleParams.copyWithChanges(
            BuildTargets.createFlavoredBuildTarget(
                originalBuildTarget, AAR_ANDROID_RESOURCE_FLAVOR),
            Suppliers.ofInstance(
                ImmutableSortedSet.<BuildRule>of(
                    manifest, assembleAssetsDirectories, assembleResourceDirectories)),
            Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));

    AndroidResource androidResource =
        new AndroidResource(
            androidResourceParams,
            pathResolver,
            /* deps */ ImmutableSortedSet.<BuildRule>naturalOrder()
                .add(assembleAssetsDirectories)
                .add(assembleResourceDirectories)
                .addAll(originalBuildRuleParams.getDeclaredDeps().get())
                .build(),
            new BuildTargetSourcePath(assembleResourceDirectories.getBuildTarget()),
            /* resSrcs */ ImmutableSortedSet.<SourcePath>of(),
            Optional.<SourcePath>absent(),
            /* rDotJavaPackage */ null,
            new BuildTargetSourcePath(assembleAssetsDirectories.getBuildTarget()),
            /* assetsSrcs */ ImmutableSortedSet.<SourcePath>of(),
            Optional.<SourcePath>absent(),
            new BuildTargetSourcePath(manifest.getBuildTarget()),
            /* hasWhitelistedStrings */ false);
    aarExtraDepsBuilder.add(resolver.addToIndex(androidResource));

    /* native_libraries */
    AndroidNativeLibsPackageableGraphEnhancer packageableGraphEnhancer =
        new AndroidNativeLibsPackageableGraphEnhancer(
            resolver,
            originalBuildRuleParams,
            nativePlatforms,
            ImmutableSet.<NdkCxxPlatforms.TargetCpuType>of());
    Optional<CopyNativeLibraries> nativeLibrariesOptional =
        packageableGraphEnhancer.getCopyNativeLibraries(targetGraph, packageableCollection);
    if (nativeLibrariesOptional.isPresent()) {
      aarExtraDepsBuilder.add(resolver.addToIndex(nativeLibrariesOptional.get()));
    }

    Optional<Path> assembledNativeLibsDir =
        nativeLibrariesOptional.transform(
            new Function<CopyNativeLibraries, Path>() {
              @Override
              public Path apply(CopyNativeLibraries input) {
                return input.getPathToNativeLibsDir();
              }
            });
    BuildRuleParams androidAarParams =
        originalBuildRuleParams.copyWithExtraDeps(
            Suppliers.ofInstance(ImmutableSortedSet.copyOf(aarExtraDepsBuilder.build())));
    return new AndroidAar(
        androidAarParams,
        pathResolver,
        manifest,
        androidResource,
        assembleResourceDirectories.getPathToOutput(),
        assembleAssetsDirectories.getPathToOutput(),
        assembledNativeLibsDir,
        packageableCollection.getNativeLibAssetsDirectories());
  }
  AndroidGraphEnhancementResult createAdditionalBuildables() {
    ImmutableSortedSet.Builder<BuildRule> enhancedDeps = ImmutableSortedSet.naturalOrder();
    enhancedDeps.addAll(originalDeps);

    AndroidPackageableCollector collector =
        new AndroidPackageableCollector(
            originalBuildTarget, buildTargetsToExcludeFromDex, resourcesToExclude);
    collector.addPackageables(AndroidPackageableCollector.getPackageableRules(originalDeps));
    AndroidPackageableCollection packageableCollection = collector.build();
    AndroidPackageableCollection.ResourceDetails resourceDetails =
        packageableCollection.getResourceDetails();

    ImmutableSortedSet<BuildRule> resourceRules =
        getTargetsAsRules(resourceDetails.getResourcesWithNonEmptyResDir());

    FilteredResourcesProvider filteredResourcesProvider;
    boolean needsResourceFiltering =
        resourceFilter.isEnabled()
            || resourceCompressionMode.isStoreStringsAsAssets()
            || !locales.isEmpty();

    if (needsResourceFiltering) {
      BuildRuleParams paramsForResourcesFilter =
          buildRuleParams.copyWithChanges(
              createBuildTargetWithFlavor(RESOURCES_FILTER_FLAVOR),
              Suppliers.ofInstance(
                  ImmutableSortedSet.<BuildRule>naturalOrder()
                      .addAll(resourceRules)
                      .addAll(
                          pathResolver.filterBuildRuleInputs(
                              resourceDetails.getResourceDirectories()))
                      .build()),
              /* extraDeps */ Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));
      ResourcesFilter resourcesFilter =
          new ResourcesFilter(
              paramsForResourcesFilter,
              pathResolver,
              resourceDetails.getResourceDirectories(),
              ImmutableSet.copyOf(resourceDetails.getWhitelistedStringDirectories()),
              locales,
              resourceCompressionMode,
              resourceFilter);
      ruleResolver.addToIndex(resourcesFilter);

      filteredResourcesProvider = resourcesFilter;
      enhancedDeps.add(resourcesFilter);
      resourceRules = ImmutableSortedSet.<BuildRule>of(resourcesFilter);
    } else {
      filteredResourcesProvider =
          new IdentityResourcesProvider(
              pathResolver.getAllPaths(resourceDetails.getResourceDirectories()));
    }

    // Create the AaptPackageResourcesBuildable.
    BuildTarget buildTargetForAapt = createBuildTargetWithFlavor(AAPT_PACKAGE_FLAVOR);
    BuildRuleParams paramsForAaptPackageResources =
        buildRuleParams.copyWithChanges(
            buildTargetForAapt,
            Suppliers.ofInstance(
                ImmutableSortedSet.<BuildRule>naturalOrder()
                    // Add all deps with non-empty res dirs, since we at least need the R.txt file
                    // (even if we're filtering).
                    .addAll(getTargetsAsRules(resourceDetails.getResourcesWithNonEmptyResDir()))
                    .addAll(
                        pathResolver.filterBuildRuleInputs(
                            resourceDetails.getResourceDirectories()))
                    .addAll(
                        getAdditionalAaptDeps(pathResolver, resourceRules, packageableCollection))
                    .build()),
            /* extraDeps */ Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));
    AaptPackageResources aaptPackageResources =
        new AaptPackageResources(
            paramsForAaptPackageResources,
            pathResolver,
            manifest,
            filteredResourcesProvider,
            getTargetsAsResourceDeps(resourceDetails.getResourcesWithNonEmptyResDir()),
            packageableCollection.getAssetsDirectories(),
            packageType,
            javacOptions,
            shouldPreDex,
            shouldBuildStringSourceMap,
            locales.isEmpty(),
            skipCrunchPngs);
    ruleResolver.addToIndex(aaptPackageResources);
    enhancedDeps.add(aaptPackageResources);

    Optional<PackageStringAssets> packageStringAssets = Optional.absent();
    if (resourceCompressionMode.isStoreStringsAsAssets()) {
      BuildTarget buildTargetForPackageStringAssets =
          createBuildTargetWithFlavor(PACKAGE_STRING_ASSETS_FLAVOR);
      BuildRuleParams paramsForPackageStringAssets =
          buildRuleParams.copyWithChanges(
              buildTargetForPackageStringAssets,
              Suppliers.ofInstance(
                  ImmutableSortedSet.<BuildRule>naturalOrder()
                      .add(aaptPackageResources)
                      // Model the dependency on the presence of res directories, which, in the case
                      // of resource filtering, is cached by the `ResourcesFilter` rule.
                      .addAll(
                          Iterables.filter(
                              ImmutableList.of(filteredResourcesProvider), BuildRule.class))
                      .build()),
              /* extraDeps */ Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));
      packageStringAssets =
          Optional.of(
              new PackageStringAssets(
                  paramsForPackageStringAssets,
                  pathResolver,
                  locales,
                  filteredResourcesProvider,
                  aaptPackageResources));
      ruleResolver.addToIndex(packageStringAssets.get());
      enhancedDeps.add(packageStringAssets.get());
    }

    // TODO(natthu): Try to avoid re-building the collection by passing UberRDotJava directly.
    if (packageableCollection.getResourceDetails().hasResources()) {
      collector.addClasspathEntry(
          aaptPackageResources,
          new BuildTargetSourcePath(
              aaptPackageResources.getBuildTarget(),
              aaptPackageResources.getPathToCompiledRDotJavaFiles()));
    }

    // BuildConfig deps should not be added for instrumented APKs because BuildConfig.class has
    // already been added to the APK under test.
    ImmutableList<DexProducedFromJavaLibrary> preDexBuildConfigs;
    ImmutableList<Path> buildConfigJarFiles;
    if (packageType == PackageType.INSTRUMENTED) {
      preDexBuildConfigs = ImmutableList.of();
      buildConfigJarFiles = ImmutableList.of();
    } else {
      ImmutableList.Builder<DexProducedFromJavaLibrary> preDexBuildConfigsBuilder =
          ImmutableList.builder();
      ImmutableList.Builder<Path> buildConfigJarFilesBuilder = ImmutableList.builder();
      addBuildConfigDeps(
          shouldPreDex,
          packageableCollection,
          enhancedDeps,
          preDexBuildConfigsBuilder,
          buildConfigJarFilesBuilder);
      preDexBuildConfigs = preDexBuildConfigsBuilder.build();
      buildConfigJarFiles = buildConfigJarFilesBuilder.build();
    }

    packageableCollection = collector.build();

    Optional<PreDexMerge> preDexMerge = Optional.absent();
    if (shouldPreDex) {
      preDexMerge =
          Optional.of(
              createPreDexMergeRule(
                  aaptPackageResources, preDexBuildConfigs, packageableCollection));
      enhancedDeps.add(preDexMerge.get());
    } else {
      enhancedDeps.addAll(getTargetsAsRules(packageableCollection.getJavaLibrariesToDex()));
    }

    // Add dependencies on all the build rules generating third-party JARs.  This is mainly to
    // correctly capture deps when a prebuilt_jar forwards the output from another build rule.
    enhancedDeps.addAll(
        pathResolver.filterBuildRuleInputs(packageableCollection.getPathsToThirdPartyJars()));

    Optional<CopyNativeLibraries> copyNativeLibraries =
        nativeLibsEnhancer.getCopyNativeLibraries(targetGraph, packageableCollection);
    if (copyNativeLibraries.isPresent()) {
      ruleResolver.addToIndex(copyNativeLibraries.get());
      enhancedDeps.add(copyNativeLibraries.get());
    }

    Optional<ComputeExopackageDepsAbi> computeExopackageDepsAbi = Optional.absent();
    if (!exopackageModes.isEmpty()) {
      BuildRuleParams paramsForComputeExopackageAbi =
          buildRuleParams.copyWithChanges(
              createBuildTargetWithFlavor(CALCULATE_ABI_FLAVOR),
              Suppliers.ofInstance(enhancedDeps.build()),
              /* extraDeps */ Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of()));
      computeExopackageDepsAbi =
          Optional.of(
              new ComputeExopackageDepsAbi(
                  paramsForComputeExopackageAbi,
                  pathResolver,
                  exopackageModes,
                  packageableCollection,
                  aaptPackageResources,
                  copyNativeLibraries,
                  packageStringAssets,
                  preDexMerge,
                  keystore));
      ruleResolver.addToIndex(computeExopackageDepsAbi.get());
      enhancedDeps.add(computeExopackageDepsAbi.get());
    }

    return AndroidGraphEnhancementResult.builder()
        .setPackageableCollection(packageableCollection)
        .setAaptPackageResources(aaptPackageResources)
        .setCopyNativeLibraries(copyNativeLibraries)
        .setPackageStringAssets(packageStringAssets)
        .setPreDexMerge(preDexMerge)
        .setComputeExopackageDepsAbi(computeExopackageDepsAbi)
        .setClasspathEntriesToDex(
            ImmutableSet.<Path>builder()
                .addAll(pathResolver.getAllPaths(packageableCollection.getClasspathEntriesToDex()))
                .addAll(buildConfigJarFiles)
                .build())
        .setFinalDeps(enhancedDeps.build())
        .build();
  }