Install android_app_set modules in Soong

Add support for installing extra files from a zip file when installing
a primary file, and use it to support installing android_app_set modules,
which install a primary APK and then an unknown set of additional splits
APKs.

Test: app_set_test.go
Test: install test android_app_set
Bug: 204136549
Change-Id: Ia92f7e5c427adcef3bcf59c82a2f83450905c01d
diff --git a/android/module.go b/android/module.go
index c778078..e100330 100644
--- a/android/module.go
+++ b/android/module.go
@@ -381,6 +381,16 @@
 	// for which IsInstallDepNeeded returns true.
 	InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath
 
+	// InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath
+	// directory, and also unzip a zip file containing extra files to install into the same
+	// directory.
+	//
+	// The installed file will be returned by FilesToInstall(), and the PackagingSpec for the
+	// installed file will be returned by PackagingSpecs() on this module or by
+	// TransitivePackagingSpecs() on modules that depend on this module through dependency tags
+	// for which IsInstallDepNeeded returns true.
+	InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...Path) InstallPath
+
 	// InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath
 	// directory.
 	//
@@ -2235,10 +2245,16 @@
 	implicitDeps  Paths
 	orderOnlyDeps Paths
 	executable    bool
+	extraFiles    *extraFilesZip
 
 	absFrom string
 }
 
+type extraFilesZip struct {
+	zip Path
+	dir InstallPath
+}
+
 type katiInstalls []katiInstall
 
 // BuiltInstalled returns the katiInstalls in the form used by $(call copy-many-files) in Make, a
@@ -2852,12 +2868,20 @@
 
 func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
 	deps ...Path) InstallPath {
-	return m.installFile(installPath, name, srcPath, deps, false)
+	return m.installFile(installPath, name, srcPath, deps, false, nil)
 }
 
 func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path,
 	deps ...Path) InstallPath {
-	return m.installFile(installPath, name, srcPath, deps, true)
+	return m.installFile(installPath, name, srcPath, deps, true, nil)
+}
+
+func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path,
+	extraZip Path, deps ...Path) InstallPath {
+	return m.installFile(installPath, name, srcPath, deps, false, &extraFilesZip{
+		zip: extraZip,
+		dir: installPath,
+	})
 }
 
 func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec {
@@ -2878,7 +2902,8 @@
 	return spec
 }
 
-func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, executable bool) InstallPath {
+func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path,
+	executable bool, extraZip *extraFilesZip) InstallPath {
 
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
@@ -2906,6 +2931,7 @@
 				implicitDeps:  implicitDeps,
 				orderOnlyDeps: orderOnlyDeps,
 				executable:    executable,
+				extraFiles:    extraZip,
 			})
 		} else {
 			rule := Cp
@@ -2913,6 +2939,13 @@
 				rule = CpExecutable
 			}
 
+			extraCmds := ""
+			if extraZip != nil {
+				extraCmds += fmt.Sprintf(" && unzip -qDD -d '%s' '%s'",
+					extraZip.dir.String(), extraZip.zip.String())
+				implicitDeps = append(implicitDeps, extraZip.zip)
+			}
+
 			m.Build(pctx, BuildParams{
 				Rule:        rule,
 				Description: "install " + fullInstallPath.Base(),
@@ -2921,6 +2954,9 @@
 				Implicits:   implicitDeps,
 				OrderOnly:   orderOnlyDeps,
 				Default:     !m.Config().KatiEnabled(),
+				Args: map[string]string{
+					"extraCmds": extraCmds,
+				},
 			})
 		}