Use no_full_install: true instead of installable: false

So far, we have used `instalable: false` to avoid collision with the
other modules that are installed to the same path. A typical example was
<foo> and <foo>.microdroid. The latter is a modified version of the
former for the inclusion of the microdroid image. They however both have
the same instalation path (ex: system/bin) and stem (ex: foo) so that we
can reference them using the same path regardless of whether we are in
Android or microdroid.

However, the use of `installable: false` for the purpose is actually
incorrect, because `installable: false` also means, obviously, "this
module shouldn't be installed". The only reason this incorrect way has
worked is simply because packaging modules (ex: android_filesystem)
didn't respect the property when gathering the modules.

As packaging modules are now fixed to respect `installable: false`, we
need a correct way of avoiding the collision. `no_full_install: true` is
it.

If a module has this property set to true, it is never installed to the
full instal path like out/target/product/<partition>/... It can be
installed only via packaging modules.

Bug: 338160898
Test: m
Change-Id: Iee9be674951d0bf3d5e26432fcbae9afebb6007b
diff --git a/android/androidmk.go b/android/androidmk.go
index 07f7c58..53f0609 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -36,6 +36,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/pathtools"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -541,6 +542,11 @@
 		a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", base.katiInstalls[len(base.katiInstalls)-1].to)
 		a.SetString("LOCAL_SOONG_INSTALL_PAIRS", base.katiInstalls.BuiltInstalled())
 		a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths())
+	} else {
+		// Soong may not have generated the install rule also when `no_full_install: true`.
+		// Mark this module as uninstallable in order to prevent Make from creating an
+		// install rule there.
+		a.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", proptools.Bool(base.commonProperties.No_full_install))
 	}
 
 	if len(base.testData) > 0 {
diff --git a/android/module.go b/android/module.go
index effca03..f8fec3a 100644
--- a/android/module.go
+++ b/android/module.go
@@ -484,6 +484,11 @@
 	// Set by osMutator.
 	CommonOSVariant bool `blueprint:"mutated"`
 
+	// When set to true, this module is not installed to the full install path (ex: under
+	// out/target/product/<name>/<partition>). It can be installed only to the packaging
+	// modules like android_filesystem.
+	No_full_install *bool
+
 	// When HideFromMake is set to true, no entry for this variant will be emitted in the
 	// generated Android.mk file.
 	HideFromMake bool `blueprint:"mutated"`
diff --git a/android/module_context.go b/android/module_context.go
index dea22ba..605d3ba 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -444,6 +444,21 @@
 	return false
 }
 
+// Tells whether this module is installed to the full install path (ex:
+// out/target/product/<name>/<partition>) or not. If this returns false, the install build rule is
+// not created and this module can only be installed to packaging modules like android_filesystem.
+func (m *moduleContext) requiresFullInstall() bool {
+	if m.skipInstall() {
+		return false
+	}
+
+	if proptools.Bool(m.module.base().commonProperties.No_full_install) {
+		return false
+	}
+
+	return true
+}
+
 func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path,
 	deps ...InstallPath) InstallPath {
 	return m.installFile(installPath, name, srcPath, deps, false, true, nil)
@@ -490,7 +505,7 @@
 		m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
 	}
 
-	if !m.skipInstall() {
+	if m.requiresFullInstall() {
 		deps = append(deps, InstallPaths(m.module.base().installFilesDepSet.ToList())...)
 		deps = append(deps, m.module.base().installedInitRcPaths...)
 		deps = append(deps, m.module.base().installedVintfFragmentsPaths...)
@@ -563,7 +578,7 @@
 	if err != nil {
 		panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
 	}
-	if !m.skipInstall() {
+	if m.requiresFullInstall() {
 
 		if m.Config().KatiEnabled() {
 			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
@@ -612,7 +627,7 @@
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
 
-	if !m.skipInstall() {
+	if m.requiresFullInstall() {
 		if m.Config().KatiEnabled() {
 			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
 			// makefile instead of directly to the ninja file so that main.mk can add the