Support recovery and recovery_available

`recovery: true` installs a module to the recovery partition.
`recovery_available: true` makes a module to be available to other
`recovery:true` or `recovery_available: true` modules.

These to are very similar to vendor, vendor_available properties, except
for the target partition.

Bug: 67916654
Bug: 64960723
Test: m -j, toybox_recovery is installed to the recovery/root/sbin
Change-Id: Iaebe0593de16c69fa70de251a61f4d018a251509
diff --git a/Android.bp b/Android.bp
index cb63d27..2daa958 100644
--- a/Android.bp
+++ b/Android.bp
@@ -336,6 +336,7 @@
     name: "libatomic",
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
+    recovery_available: true,
     arch: {
         arm: {
             instruction_set: "arm",
@@ -347,6 +348,7 @@
     name: "libgcc",
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
+    recovery_available: true,
     arch: {
         arm: {
             instruction_set: "arm",
diff --git a/android/module.go b/android/module.go
index fba1917..3316a44 100644
--- a/android/module.go
+++ b/android/module.go
@@ -124,6 +124,7 @@
 
 	InstallInData() bool
 	InstallInSanitizerDir() bool
+	InstallInRecovery() bool
 
 	RequiredModuleNames() []string
 
@@ -176,6 +177,7 @@
 	Target() Target
 	InstallInData() bool
 	InstallInSanitizerDir() bool
+	InstallInRecovery() bool
 	SkipInstall()
 	ExportedToMake() bool
 
@@ -237,6 +239,9 @@
 	// /system/product if product partition does not exist).
 	Product_specific *bool
 
+	// Whether this module is installed to recovery partition
+	Recovery *bool
+
 	// init.rc files to be installed if this module is installed
 	Init_rc []string
 
@@ -560,6 +565,10 @@
 	return false
 }
 
+func (p *ModuleBase) InstallInRecovery() bool {
+	return Bool(p.commonProperties.Recovery)
+}
+
 func (a *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	allInstalledFiles := Paths{}
 	allCheckbuildFiles := Paths{}
@@ -1008,6 +1017,10 @@
 	return a.module.InstallInSanitizerDir()
 }
 
+func (a *androidModuleContext) InstallInRecovery() bool {
+	return a.module.InstallInRecovery()
+}
+
 func (a *androidModuleContext) skipInstall(fullInstallPath OutputPath) bool {
 	if a.module.base().commonProperties.SkipInstall {
 		return true
diff --git a/android/paths.go b/android/paths.go
index 91dd9a6..8cc3182 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -47,6 +47,7 @@
 
 	InstallInData() bool
 	InstallInSanitizerDir() bool
+	InstallInRecovery() bool
 }
 
 var _ ModuleInstallPathContext = ModuleContext(nil)
@@ -948,6 +949,8 @@
 		var partition string
 		if ctx.InstallInData() {
 			partition = "data"
+		} else if ctx.InstallInRecovery() {
+			partition = "recovery/root"
 		} else if ctx.SocSpecific() {
 			partition = ctx.DeviceConfig().VendorPath()
 		} else if ctx.DeviceSpecific() {
diff --git a/android/paths_test.go b/android/paths_test.go
index cd9fbfd..b3dc9de 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -201,6 +201,7 @@
 
 	inData         bool
 	inSanitizerDir bool
+	inRecovery     bool
 }
 
 func (moduleInstallPathContextImpl) Fs() pathtools.FileSystem {
@@ -221,6 +222,10 @@
 	return m.inSanitizerDir
 }
 
+func (m moduleInstallPathContextImpl) InstallInRecovery() bool {
+	return m.inRecovery
+}
+
 func TestPathForModuleInstall(t *testing.T) {
 	testConfig := TestConfig("", nil)
 
diff --git a/cc/androidmk.go b/cc/androidmk.go
index cdd4a5a..f417de0 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -24,7 +24,8 @@
 )
 
 var (
-	vendorSuffix = ".vendor"
+	vendorSuffix   = ".vendor"
+	recoverySuffix = ".recovery"
 )
 
 type AndroidMkContext interface {
@@ -99,6 +100,8 @@
 		// .vendor suffix is added only when we will have two variants: core and vendor.
 		// The suffix is not added for vendor-only module.
 		ret.SubName += vendorSuffix
+	} else if c.inRecovery() && !c.onlyInRecovery() {
+		ret.SubName += recoverySuffix
 	}
 
 	return ret
@@ -345,7 +348,7 @@
 
 func (c *llndkStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ret.Class = "SHARED_LIBRARIES"
-	ret.SubName = ".vendor"
+	ret.SubName = vendorSuffix
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		c.libraryDecorator.androidMkWriteExportedFlags(w)
diff --git a/cc/binary.go b/cc/binary.go
index 9e7b70b..00fda06 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -353,6 +353,11 @@
 }
 
 func (binary *binaryDecorator) install(ctx ModuleContext, file android.Path) {
+	// <recovery>/bin is a symlink to /system/bin. Recovery binaries are all in /sbin.
+	if ctx.inRecovery() {
+		binary.baseInstaller.dir = "sbin"
+	}
+
 	binary.baseInstaller.install(ctx, file)
 	for _, symlink := range binary.Properties.Symlinks {
 		binary.symlinks = append(binary.symlinks,
diff --git a/cc/cc.go b/cc/cc.go
index 9c3de09..a885c91 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -34,7 +34,7 @@
 	android.RegisterModuleType("cc_defaults", defaultsFactory)
 
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", vendorMutator).Parallel()
+		ctx.BottomUp("image", imageMutator).Parallel()
 		ctx.BottomUp("link", linkageMutator).Parallel()
 		ctx.BottomUp("vndk", vndkMutator).Parallel()
 		ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
@@ -175,6 +175,11 @@
 	// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
 	// file
 	Logtags []string
+
+	// Make this module available when building for recovery
+	Recovery_available *bool
+
+	InRecovery bool `blueprint:"mutated"`
 }
 
 type VendorProperties struct {
@@ -217,6 +222,7 @@
 	isVndk() bool
 	isVndkSp() bool
 	isVndkExt() bool
+	inRecovery() bool
 	createVndkSourceAbiDump() bool
 	selectedStl() string
 	baseModuleName() string
@@ -453,6 +459,14 @@
 	return c.isVndk() || Bool(c.VendorProperties.Vendor_available)
 }
 
+func (c *Module) inRecovery() bool {
+	return c.Properties.InRecovery || c.ModuleBase.InstallInRecovery()
+}
+
+func (c *Module) onlyInRecovery() bool {
+	return c.ModuleBase.InstallInRecovery()
+}
+
 type baseModuleContext struct {
 	android.BaseContext
 	moduleContextImpl
@@ -500,7 +514,7 @@
 }
 
 func (ctx *moduleContextImpl) useSdk() bool {
-	if ctx.ctx.Device() && !ctx.useVndk() {
+	if ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRecovery() {
 		return String(ctx.mod.Properties.Sdk_version) != ""
 	}
 	return false
@@ -544,6 +558,10 @@
 	return ctx.mod.isVndkExt()
 }
 
+func (ctx *moduleContextImpl) inRecovery() bool {
+	return ctx.mod.inRecovery()
+}
+
 // Create source abi dumps if the module belongs to the list of VndkLibraries.
 func (ctx *moduleContextImpl) createVndkSourceAbiDump() bool {
 	skipAbiChecks := ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS")
@@ -1069,6 +1087,10 @@
 		// Platform code can link to anything
 		return
 	}
+	if from.inRecovery() {
+		// Recovery code is not NDK
+		return
+	}
 	if _, ok := to.linker.(*toolchainLibraryDecorator); ok {
 		// These are always allowed
 		return
@@ -1360,6 +1382,8 @@
 				return libName + vendorSuffix
 			} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
 				return libName + vendorPublicLibrarySuffix
+			} else if ccDep.inRecovery() && !ccDep.onlyInRecovery() {
+				return libName + recoverySuffix
 			} else {
 				return libName
 			}
@@ -1411,6 +1435,10 @@
 	return c.installer.inSanitizerDir()
 }
 
+func (c *Module) InstallInRecovery() bool {
+	return c.inRecovery()
+}
+
 func (c *Module) HostToolPath() android.OptionalPath {
 	if c.installer == nil {
 		return android.OptionalPath{}
@@ -1496,6 +1524,8 @@
 	// vendorMode is the variant used for /vendor code that compiles
 	// against the VNDK.
 	vendorMode = "vendor"
+
+	recoveryMode = "recovery"
 )
 
 func squashVendorSrcs(m *Module) {
@@ -1508,7 +1538,17 @@
 	}
 }
 
-func vendorMutator(mctx android.BottomUpMutatorContext) {
+func squashRecoverySrcs(m *Module) {
+	if lib, ok := m.compiler.(*libraryDecorator); ok {
+		lib.baseCompiler.Properties.Srcs = append(lib.baseCompiler.Properties.Srcs,
+			lib.baseCompiler.Properties.Target.Recovery.Srcs...)
+
+		lib.baseCompiler.Properties.Exclude_srcs = append(lib.baseCompiler.Properties.Exclude_srcs,
+			lib.baseCompiler.Properties.Target.Recovery.Exclude_srcs...)
+	}
+}
+
+func imageMutator(mctx android.BottomUpMutatorContext) {
 	if mctx.Os() != android.Android {
 		return
 	}
@@ -1577,43 +1617,70 @@
 		}
 	}
 
+	var coreVariantNeeded bool = false
+	var vendorVariantNeeded bool = false
+	var recoveryVariantNeeded bool = false
+
 	if mctx.DeviceConfig().VndkVersion() == "" {
 		// If the device isn't compiling against the VNDK, we always
 		// use the core mode.
-		mctx.CreateVariations(coreMode)
+		coreVariantNeeded = true
 	} else if _, ok := m.linker.(*llndkStubDecorator); ok {
 		// LL-NDK stubs only exist in the vendor variant, since the
 		// real libraries will be used in the core variant.
-		mctx.CreateVariations(vendorMode)
+		vendorVariantNeeded = true
 	} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
 		// ... and LL-NDK headers as well
-		mod := mctx.CreateVariations(vendorMode)
-		vendor := mod[0].(*Module)
-		vendor.Properties.UseVndk = true
+		vendorVariantNeeded = true
 	} else if _, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok {
 		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
 		// PRODUCT_EXTRA_VNDK_VERSIONS.
-		mod := mctx.CreateVariations(vendorMode)
-		vendor := mod[0].(*Module)
-		vendor.Properties.UseVndk = true
+		vendorVariantNeeded = true
 	} else if m.hasVendorVariant() && !vendorSpecific {
 		// This will be available in both /system and /vendor
 		// or a /system directory that is available to vendor.
-		mod := mctx.CreateVariations(coreMode, vendorMode)
-		vendor := mod[1].(*Module)
-		vendor.Properties.UseVndk = true
-		squashVendorSrcs(vendor)
+		coreVariantNeeded = true
+		vendorVariantNeeded = true
 	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
 		// This will be available in /vendor (or /odm) only
-		mod := mctx.CreateVariations(vendorMode)
-		vendor := mod[0].(*Module)
-		vendor.Properties.UseVndk = true
-		squashVendorSrcs(vendor)
+		vendorVariantNeeded = true
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// modules built with the NDK. Modules built with the NDK
 		// will be restricted using the existing link type checks.
-		mctx.CreateVariations(coreMode)
+		coreVariantNeeded = true
+	}
+
+	if Bool(m.Properties.Recovery_available) {
+		recoveryVariantNeeded = true
+	}
+
+	if m.ModuleBase.InstallInRecovery() {
+		recoveryVariantNeeded = true
+		coreVariantNeeded = false
+	}
+
+	var variants []string
+	if coreVariantNeeded {
+		variants = append(variants, coreMode)
+	}
+	if vendorVariantNeeded {
+		variants = append(variants, vendorMode)
+	}
+	if recoveryVariantNeeded {
+		variants = append(variants, recoveryMode)
+	}
+	mod := mctx.CreateVariations(variants...)
+	for i, v := range variants {
+		if v == vendorMode {
+			m := mod[i].(*Module)
+			m.Properties.UseVndk = true
+			squashVendorSrcs(m)
+		} else if v == recoveryMode {
+			m := mod[i].(*Module)
+			m.Properties.InRecovery = true
+			squashRecoverySrcs(m)
+		}
 	}
 }
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 4f26827..1b12ad4 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -63,7 +63,7 @@
 	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(objectFactory))
 	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("image", vendorMutator).Parallel()
+		ctx.BottomUp("image", imageMutator).Parallel()
 		ctx.BottomUp("link", linkageMutator).Parallel()
 		ctx.BottomUp("vndk", vndkMutator).Parallel()
 		ctx.BottomUp("begin", beginMutator).Parallel()
diff --git a/cc/compiler.go b/cc/compiler.go
index 2ba19f1..b410115 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -142,6 +142,19 @@
 			// variant of the C/C++ module.
 			Cflags []string
 		}
+		Recovery struct {
+			// list of source files that should only be used in the
+			// recovery variant of the C/C++ module.
+			Srcs []string
+
+			// list of source files that should not be used to
+			// build the recovery variant of the C/C++ module.
+			Exclude_srcs []string
+
+			// List of additional cflags that should be used to build the recovery
+			// variant of the C/C++ module.
+			Cflags []string
+		}
 	}
 
 	Proto struct {
diff --git a/cc/library.go b/cc/library.go
index b31fee2..c83e89a 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -477,6 +477,11 @@
 		deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Vendor.Exclude_shared_libs)
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Vendor.Exclude_static_libs)
 	}
+	if ctx.inRecovery() {
+		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.SharedLibs = removeListFromList(deps.SharedLibs, library.baseLinker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.StaticLibs = removeListFromList(deps.StaticLibs, library.baseLinker.Properties.Target.Recovery.Exclude_static_libs)
+	}
 
 	android.ExtractSourceDeps(ctx, library.Properties.Version_script)
 	android.ExtractSourceDeps(ctx, library.Properties.Unexported_symbols_list)
@@ -745,7 +750,7 @@
 	}
 
 	if Bool(library.Properties.Static_ndk_lib) && library.static() &&
-		!ctx.useVndk() && ctx.Device() &&
+		!ctx.useVndk() && !ctx.inRecovery() && ctx.Device() &&
 		library.sanitize.isUnsanitizedVariant() {
 		installPath := getNdkSysrootBase(ctx).Join(
 			ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
diff --git a/cc/linker.go b/cc/linker.go
index 71da09e..3e2ecc6 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -107,6 +107,15 @@
 			// variant of the C/C++ module.
 			Exclude_runtime_libs []string
 		}
+		Recovery struct {
+			// list of shared libs that should not be used to build
+			// the recovery variant of the C/C++ module.
+			Exclude_shared_libs []string
+
+			// list of static libs that should not be used to build
+			// the recovery variant of the C/C++ module.
+			Exclude_static_libs []string
+		}
 	}
 
 	// make android::build:GetBuildNumber() available containing the build ID.
@@ -166,6 +175,14 @@
 		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs)
 	}
 
+	if ctx.inRecovery() {
+		deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+	}
+
 	if ctx.ModuleName() != "libcompiler_rt-extras" {
 		deps.LateStaticLibs = append(deps.LateStaticLibs, "libcompiler_rt-extras")
 	}