Add `assets` property for Android apps.

This property is similar to `asset_dirs`, but for individual files. This
is especially useful when the user wants to pack generated asset files
into the APK.

Bug: 257532944
Test: m nothing
Change-Id: If3062124e9dedb41315dcaf5d379803e5419b8cd
diff --git a/java/aar.go b/java/aar.go
index 85a6fbb..368f241 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -66,6 +66,9 @@
 	// ones.
 	Aapt_include_all_resources *bool
 
+	// list of files to use as assets.
+	Assets []string `android:"path"`
+
 	// list of directories relative to the Blueprints file containing assets.
 	// Defaults to ["assets"] if a directory called assets exists.  Set to []
 	// to disable the default.
@@ -192,6 +195,11 @@
 	linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...)
 
 	// Find implicit or explicit asset and resource dirs
+	assets := android.PathsRelativeToModuleSourceDir(android.SourceInput{
+		Context:     ctx,
+		Paths:       a.aaptProperties.Assets,
+		IncludeDirs: false,
+	})
 	assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
 	resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
 	resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
@@ -226,6 +234,28 @@
 		assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
 		assetDeps = append(assetDeps, a.noticeFile.Path())
 	}
+	if len(assets) > 0 {
+		// aapt2 doesn't support adding individual asset files. Create a temp directory to hold asset
+		// files and pass it to aapt2.
+		tmpAssetDir := android.PathForModuleOut(ctx, "tmp_asset_dir")
+
+		rule := android.NewRuleBuilder(pctx, ctx)
+		rule.Command().
+			Text("rm -rf").Text(tmpAssetDir.String()).
+			Text("&&").
+			Text("mkdir -p").Text(tmpAssetDir.String())
+
+		for _, asset := range assets {
+			output := tmpAssetDir.Join(ctx, asset.Rel())
+			assetDeps = append(assetDeps, output)
+			rule.Command().Text("mkdir -p").Text(filepath.Dir(output.String()))
+			rule.Command().Text("cp").Input(asset).Output(output)
+		}
+
+		rule.Build("tmp_asset_dir", "tmp_asset_dir")
+
+		assetDirStrings = append(assetDirStrings, tmpAssetDir.String())
+	}
 
 	linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
 	linkDeps = append(linkDeps, manifestPath)
diff --git a/java/app_test.go b/java/app_test.go
index e38a724..4d3b2dc 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -608,6 +608,15 @@
 				asset_dirs: ["assets_b"],
 			}
 
+			android_library {
+				name: "lib5",
+				sdk_version: "current",
+				assets: [
+					"path/to/asset_file_1",
+					"path/to/asset_file_2",
+				],
+			}
+
 			android_library_import {
 				name: "import",
 				sdk_version: "current",
@@ -616,9 +625,11 @@
 		`
 
 	testCases := []struct {
-		name          string
-		assetFlag     string
-		assetPackages []string
+		name               string
+		assetFlag          string
+		assetPackages      []string
+		tmpAssetDirInputs  []string
+		tmpAssetDirOutputs []string
 	}{
 		{
 			name: "foo",
@@ -644,6 +655,18 @@
 			name:      "lib4",
 			assetFlag: "-A assets_b",
 		},
+		{
+			name:      "lib5",
+			assetFlag: "-A out/soong/.intermediates/lib5/android_common/tmp_asset_dir",
+			tmpAssetDirInputs: []string{
+				"path/to/asset_file_1",
+				"path/to/asset_file_2",
+			},
+			tmpAssetDirOutputs: []string{
+				"out/soong/.intermediates/lib5/android_common/tmp_asset_dir/path/to/asset_file_1",
+				"out/soong/.intermediates/lib5/android_common/tmp_asset_dir/path/to/asset_file_2",
+			},
+		},
 	}
 	ctx := testApp(t, bp)
 
@@ -671,6 +694,14 @@
 				mergeAssets := m.Output("package-res.apk")
 				android.AssertPathsRelativeToTopEquals(t, "mergeAssets inputs", test.assetPackages, mergeAssets.Inputs)
 			}
+
+			if len(test.tmpAssetDirInputs) > 0 {
+				rule := m.Rule("tmp_asset_dir")
+				inputs := rule.Implicits
+				outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Paths()
+				android.AssertPathsRelativeToTopEquals(t, "tmp_asset_dir inputs", test.tmpAssetDirInputs, inputs)
+				android.AssertPathsRelativeToTopEquals(t, "tmp_asset_dir outputs", test.tmpAssetDirOutputs, outputs)
+			}
 		})
 	}
 }