Add InstallInRoot to allow modules to install into root partition

If InstallInRoot() returns true the module will be installed to
$OUT/root or $OUT/recovery/root.

Bug: 141877526
Test: m checkbuild
Test: no change to build.ninja or Android-${TARGET_PRODUCT}.mk
Test: TestPathForModuleInstall
Change-Id: Id6e435c6019f11eeb5806528fd464dbf220b88d9
diff --git a/android/module.go b/android/module.go
index a1a01a5..2d0c20d 100644
--- a/android/module.go
+++ b/android/module.go
@@ -157,6 +157,7 @@
 	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
 	InstallBypassMake() bool
 
 	RequiredModuleNames() []string
@@ -196,6 +197,7 @@
 	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
 	InstallBypassMake() bool
 	SkipInstall()
 	ExportedToMake() bool
@@ -846,6 +848,10 @@
 	return Bool(m.commonProperties.Recovery)
 }
 
+func (m *ModuleBase) InstallInRoot() bool {
+	return false
+}
+
 func (m *ModuleBase) InstallBypassMake() bool {
 	return false
 }
@@ -1522,6 +1528,10 @@
 	return m.module.InstallInRecovery()
 }
 
+func (m *moduleContext) InstallInRoot() bool {
+	return m.module.InstallInRoot()
+}
+
 func (m *moduleContext) InstallBypassMake() bool {
 	return m.module.InstallBypassMake()
 }
diff --git a/android/paths.go b/android/paths.go
index 8bd2c61..e8b08b5 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -47,6 +47,7 @@
 	InstallInTestcases() bool
 	InstallInSanitizerDir() bool
 	InstallInRecovery() bool
+	InstallInRoot() bool
 	InstallBypassMake() bool
 }
 
@@ -1159,8 +1160,12 @@
 	} else if ctx.InstallInTestcases() {
 		partition = "testcases"
 	} else if ctx.InstallInRecovery() {
-		// the layout of recovery partion is the same as that of system partition
-		partition = "recovery/root/system"
+		if ctx.InstallInRoot() {
+			partition = "recovery/root"
+		} else {
+			// the layout of recovery partion is the same as that of system partition
+			partition = "recovery/root/system"
+		}
 	} else if ctx.SocSpecific() {
 		partition = ctx.DeviceConfig().VendorPath()
 	} else if ctx.DeviceSpecific() {
@@ -1169,6 +1174,8 @@
 		partition = ctx.DeviceConfig().ProductPath()
 	} else if ctx.SystemExtSpecific() {
 		partition = ctx.DeviceConfig().SystemExtPath()
+	} else if ctx.InstallInRoot() {
+		partition = "root"
 	} else {
 		partition = "system"
 	}
diff --git a/android/paths_test.go b/android/paths_test.go
index b66eb1e..2e67272 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -204,6 +204,7 @@
 	inTestcases    bool
 	inSanitizerDir bool
 	inRecovery     bool
+	inRoot         bool
 }
 
 func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
@@ -232,6 +233,10 @@
 	return m.inRecovery
 }
 
+func (m moduleInstallPathContextImpl) InstallInRoot() bool {
+	return m.inRoot
+}
+
 func (m moduleInstallPathContextImpl) InstallBypassMake() bool {
 	return false
 }
@@ -313,6 +318,40 @@
 			in:  []string{"bin", "my_test"},
 			out: "target/product/test_device/system_ext/bin/my_test",
 		},
+		{
+			name: "root binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					target: deviceTarget,
+				},
+				inRoot: true,
+			},
+			in:  []string{"my_test"},
+			out: "target/product/test_device/root/my_test",
+		},
+		{
+			name: "recovery binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					target: deviceTarget,
+				},
+				inRecovery: true,
+			},
+			in:  []string{"bin/my_test"},
+			out: "target/product/test_device/recovery/root/system/bin/my_test",
+		},
+		{
+			name: "recovery root binary",
+			ctx: &moduleInstallPathContextImpl{
+				baseModuleContext: baseModuleContext{
+					target: deviceTarget,
+				},
+				inRecovery: true,
+				inRoot:     true,
+			},
+			in:  []string{"my_test"},
+			out: "target/product/test_device/recovery/root/my_test",
+		},
 
 		{
 			name: "system native test binary",