Merge "Remove unnecessary constraint on previous_api"
diff --git a/apex/apex.go b/apex/apex.go
index 08ba293..d77670a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -41,7 +41,6 @@
 	// TODO(b/113082813) make this configurable using config.fs syntax
 	generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
 		Command: `echo '/ 1000 1000 0755' > ${out} && ` +
-			`echo '/apex_manifest.json 1000 1000 0644' >> ${out} && ` +
 			`echo ${ro_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 1000 1000 0644"}' >> ${out} && ` +
 			`echo ${exec_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 0 2000 0755"}' >> ${out}`,
 		Description: "fs_config ${out}",
@@ -57,6 +56,18 @@
 		Description: "prepare ${out}",
 	}, "provideNativeLibs", "requireNativeLibs", "opt")
 
+	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
+		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
+		CommandDeps: []string{"${conv_apex_manifest}"},
+		Description: "strip ${in}=>${out}",
+	})
+
+	pbApexManifestRule = pctx.StaticRule("pbApexManifestRule", blueprint.RuleParams{
+		Command:     `rm -f $out && ${conv_apex_manifest} proto $in -o $out`,
+		CommandDeps: []string{"${conv_apex_manifest}"},
+		Description: "convert ${in}=>${out}",
+	})
+
 	// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
 	// against the binary policy using sefcontext_compiler -p <policy>.
 
@@ -66,6 +77,7 @@
 			`(. ${out}.copy_commands) && ` +
 			`APEXER_TOOL_PATH=${tool_path} ` +
 			`${apexer} --force --manifest ${manifest} ` +
+			`--manifest_json ${manifest_json} --manifest_json_full ${manifest_json_full} ` +
 			`--file_contexts ${file_contexts} ` +
 			`--canned_fs_config ${canned_fs_config} ` +
 			`--payload_type image ` +
@@ -76,20 +88,22 @@
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "manifest", "file_contexts", "canned_fs_config", "key", "opt_flags")
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags",
+		"manifest", "manifest_json", "manifest_json_full",
+	)
 
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
 			`(. ${out}.copy_commands) && ` +
 			`APEXER_TOOL_PATH=${tool_path} ` +
-			`${apexer} --force --manifest ${manifest} ` +
+			`${apexer} --force --manifest ${manifest} --manifest_json_full ${manifest_json_full} ` +
 			`--payload_type zip ` +
 			`${image_dir} ${out} `,
 		CommandDeps:    []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "ZipAPEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "manifest")
+	}, "tool_path", "image_dir", "copy_commands", "manifest", "manifest_json_full")
 
 	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
 		blueprint.RuleParams{
@@ -181,6 +195,7 @@
 	pctx.HostBinToolVariable("zip2zip", "zip2zip")
 	pctx.HostBinToolVariable("zipalign", "zipalign")
 	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
+	pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest")
 
 	android.RegisterModuleType("apex", BundleFactory)
 	android.RegisterModuleType("apex_test", testApexBundleFactory)
@@ -640,7 +655,9 @@
 	primaryApexType bool
 
 	// intermediate path for apex_manifest.json
-	manifestOut android.WritablePath
+	manifestJsonOut     android.WritablePath
+	manifestJsonFullOut android.WritablePath
+	manifestPbOut       android.WritablePath
 
 	// list of commands to create symlinks for backward compatibility
 	// these commands will be attached as LOCAL_POST_INSTALL_CMD to
@@ -1291,9 +1308,24 @@
 	a.filesInfo = filesInfo
 
 	// prepare apex_manifest.json
-	a.manifestOut = android.PathForModuleOut(ctx, "apex_manifest.json")
+	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
+
+	a.setCertificateAndPrivateKey(ctx)
+	if a.properties.ApexType == flattenedApex {
+		a.buildFlattenedApex(ctx)
+	} else {
+		a.buildUnflattenedApex(ctx)
+	}
+
+	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
+	a.compatSymlinks = makeCompatSymlinks(apexName, ctx)
+}
+
+func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) {
 	manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
 
+	a.manifestJsonFullOut = android.PathForModuleOut(ctx, "apex_manifest_full.json")
+
 	// put dependency({provide|require}NativeLibs) in apex_manifest.json
 	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
 	requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
@@ -1307,7 +1339,7 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
 		Input:  manifestSrc,
-		Output: a.manifestOut,
+		Output: a.manifestJsonFullOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
 			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
@@ -1315,15 +1347,22 @@
 		},
 	})
 
-	a.setCertificateAndPrivateKey(ctx)
-	if a.properties.ApexType == flattenedApex {
-		a.buildFlattenedApex(ctx)
-	} else {
-		a.buildUnflattenedApex(ctx)
-	}
+	// b/143654022 Q apexd can't understand newly added keys in apex_manifest.json
+	// prepare stripp-downed version so that APEX modules built from R+ can be installed to Q
+	a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   stripApexManifestRule,
+		Input:  a.manifestJsonFullOut,
+		Output: a.manifestJsonOut,
+	})
 
-	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
-	a.compatSymlinks = makeCompatSymlinks(apexName, ctx)
+	// from R+, protobuf binary format (.pb) is the standard format for apex_manifest
+	a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   pbApexManifestRule,
+		Input:  a.manifestJsonFullOut,
+		Output: a.manifestPbOut,
+	})
 }
 
 func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
@@ -1385,7 +1424,7 @@
 	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
 
 	implicitInputs := append(android.Paths(nil), filesToCopy...)
-	implicitInputs = append(implicitInputs, a.manifestOut)
+	implicitInputs = append(implicitInputs, a.manifestPbOut, a.manifestJsonFullOut, a.manifestJsonOut)
 
 	if a.properties.Whitelisted_files != nil {
 		ctx.Build(pctx, android.BuildParams{
@@ -1421,7 +1460,7 @@
 
 	if apexType == imageApex {
 		// files and dirs that will be created in APEX
-		var readOnlyPaths []string
+		var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
 		var executablePaths []string // this also includes dirs
 		for _, f := range a.filesInfo {
 			pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
@@ -1519,14 +1558,16 @@
 			Output:      unsignedOutputFile,
 			Description: "apex (" + apexType.name() + ")",
 			Args: map[string]string{
-				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":        android.PathForModuleOut(ctx, "image"+suffix).String(),
-				"copy_commands":    strings.Join(copyCommands, " && "),
-				"manifest":         a.manifestOut.String(),
-				"file_contexts":    fileContexts.String(),
-				"canned_fs_config": cannedFsConfig.String(),
-				"key":              a.private_key_file.String(),
-				"opt_flags":        strings.Join(optFlags, " "),
+				"tool_path":          outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":          android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"copy_commands":      strings.Join(copyCommands, " && "),
+				"manifest_json_full": a.manifestJsonFullOut.String(),
+				"manifest_json":      a.manifestJsonOut.String(),
+				"manifest":           a.manifestPbOut.String(),
+				"file_contexts":      fileContexts.String(),
+				"canned_fs_config":   cannedFsConfig.String(),
+				"key":                a.private_key_file.String(),
+				"opt_flags":          strings.Join(optFlags, " "),
 			},
 		})
 
@@ -1557,10 +1598,11 @@
 			Output:      unsignedOutputFile,
 			Description: "apex (" + apexType.name() + ")",
 			Args: map[string]string{
-				"tool_path":     outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":     android.PathForModuleOut(ctx, "image"+suffix).String(),
-				"copy_commands": strings.Join(copyCommands, " && "),
-				"manifest":      a.manifestOut.String(),
+				"tool_path":          outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":          android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"copy_commands":      strings.Join(copyCommands, " && "),
+				"manifest":           a.manifestPbOut.String(),
+				"manifest_json_full": a.manifestJsonFullOut.String(),
 			},
 		})
 	}
@@ -1618,7 +1660,8 @@
 	if a.installable() {
 		// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
 		// with other ordinary files.
-		a.filesInfo = append(a.filesInfo, apexFile{a.manifestOut, "apex_manifest.json." + ctx.ModuleName() + a.suffix, ".", etc, nil, nil})
+		a.filesInfo = append(a.filesInfo, apexFile{a.manifestJsonOut, "apex_manifest.json." + ctx.ModuleName() + a.suffix, ".", etc, nil, nil})
+		a.filesInfo = append(a.filesInfo, apexFile{a.manifestPbOut, "apex_manifest.pb." + ctx.ModuleName() + a.suffix, ".", etc, nil, nil})
 
 		// rename to apex_pubkey
 		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -1755,7 +1798,7 @@
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
 			// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
-			if a.primaryApexType && fi.builtFile.Base() == "apex_manifest.json" && len(a.compatSymlinks) > 0 {
+			if a.primaryApexType && fi.builtFile == a.manifestPbOut && len(a.compatSymlinks) > 0 {
 				fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(a.compatSymlinks, " && "))
 			}
 			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
diff --git a/apex/apex_test.go b/apex/apex_test.go
index d6b5a04..ac5c6fc 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -520,6 +520,26 @@
 	ensureListContains(t, noticeInputs, "custom_notice")
 }
 
+func TestApexManifest(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	module.Output("apex_manifest.pb")
+	module.Output("apex_manifest.json")
+	module.Output("apex_manifest_full.json")
+}
+
 func TestBasicZipApex(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 91a3c99..ff181d8 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -327,14 +327,15 @@
 			filepath.Dir(fuzz.config.String())+":config.json")
 	}
 
-	if len(fuzzFiles) > 0 {
-		ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-			fmt.Fprintln(w, "LOCAL_TEST_DATA := "+strings.Join(fuzzFiles, " "))
-		})
-	}
-
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		fmt.Fprintln(w, "LOCAL_IS_FUZZ_TARGET := true")
+		if len(fuzzFiles) > 0 {
+			fmt.Fprintln(w, "LOCAL_TEST_DATA := "+strings.Join(fuzzFiles, " "))
+		}
+		if fuzz.installedSharedDeps != nil {
+			fmt.Fprintln(w, "LOCAL_FUZZ_INSTALLED_SHARED_DEPS :="+
+				strings.Join(fuzz.installedSharedDeps, " "))
+		}
 	})
 }
 
diff --git a/cc/cc.go b/cc/cc.go
index 036135c..840fe24 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -892,7 +892,7 @@
 
 func isBionic(name string) bool {
 	switch name {
-	case "libc", "libm", "libdl", "libdl_android", "linker":
+	case "libc", "libm", "libdl", "linker":
 		return true
 	}
 	return false
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 71bea42..1b6744e 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -139,9 +139,7 @@
 
 	pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
 		// Enable clang's thread-safety annotations in libcxx.
-		// Turn off -Wthread-safety-negative, to avoid breaking projects that use -Weverything.
 		"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
-		"-Wno-thread-safety-negative",
 
 		// libc++'s math.h has an #include_next outside of system_headers.
 		"-Wno-gnu-include-next",
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 577fa70..1f06fb0 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -17,10 +17,9 @@
 import (
 	"encoding/json"
 	"path/filepath"
+	"sort"
 	"strings"
 
-	"github.com/google/blueprint/proptools"
-
 	"android/soong/android"
 	"android/soong/cc/config"
 )
@@ -82,6 +81,7 @@
 	corpus                android.Paths
 	corpusIntermediateDir android.Path
 	config                android.Path
+	installedSharedDeps   []string
 }
 
 func (fuzz *fuzzBinary) linkerProps() []interface{} {
@@ -91,21 +91,6 @@
 }
 
 func (fuzz *fuzzBinary) linkerInit(ctx BaseModuleContext) {
-	// Add ../lib[64] to rpath so that out/host/linux-x86/fuzz/<fuzzer> can
-	// find out/host/linux-x86/lib[64]/library.so
-	runpaths := []string{"../lib"}
-	for _, runpath := range runpaths {
-		if ctx.toolchain().Is64Bit() {
-			runpath += "64"
-		}
-		fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths = append(
-			fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths, runpath)
-	}
-
-	// add "" to rpath so that fuzzer binaries can find libraries in their own fuzz directory
-	fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths = append(
-		fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths, "")
-
 	fuzz.binaryDecorator.linkerInit(ctx)
 }
 
@@ -118,9 +103,80 @@
 
 func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = fuzz.binaryDecorator.linkerFlags(ctx, flags)
+	// RunPaths on devices isn't instantiated by the base linker.
+	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
 	return flags
 }
 
+// This function performs a breadth-first search over the provided module's
+// dependencies using `visitDirectDeps` to enumerate all shared library
+// dependencies. We require breadth-first expansion, as otherwise we may
+// incorrectly use the core libraries (sanitizer runtimes, libc, libdl, etc.)
+// from a dependency. This may cause issues when dependencies have explicit
+// sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
+func collectAllSharedDependencies(
+	module android.Module,
+	sharedDeps map[string]android.Path,
+	ctx android.SingletonContext) {
+	var fringe []android.Module
+
+	// Enumerate the first level of dependencies, as we discard all non-library
+	// modules in the BFS loop below.
+	ctx.VisitDirectDeps(module, func(dep android.Module) {
+		fringe = append(fringe, dep)
+	})
+
+	for i := 0; i < len(fringe); i++ {
+		module := fringe[i]
+		if !isValidSharedDependency(module, sharedDeps) {
+			continue
+		}
+
+		ccModule := module.(*Module)
+		sharedDeps[ccModule.Name()] = ccModule.UnstrippedOutputFile()
+		ctx.VisitDirectDeps(module, func(dep android.Module) {
+			fringe = append(fringe, dep)
+		})
+	}
+}
+
+// This function takes a module and determines if it is a unique shared library
+// that should be installed in the fuzz target output directories. This function
+// returns true, unless:
+//  - The module already exists in `sharedDeps`, or
+//  - The module is not a shared library, or
+//  - The module is a header, stub, or vendor-linked library.
+func isValidSharedDependency(
+	dependency android.Module,
+	sharedDeps map[string]android.Path) bool {
+	// TODO(b/144090547): We should be parsing these modules using
+	// ModuleDependencyTag instead of the current brute-force checking.
+
+	if linkable, ok := dependency.(LinkableInterface); !ok || // Discard non-linkables.
+		!linkable.CcLibraryInterface() || !linkable.Shared() || // Discard static libs.
+		linkable.UseVndk() || // Discard vendor linked libraries.
+		!linkable.CcLibrary() || linkable.BuildStubs() { // Discard stubs libs (only CCLibrary variants).
+		return false
+	}
+
+	// If this library has already been traversed, we don't need to do any more work.
+	if _, exists := sharedDeps[dependency.Name()]; exists {
+		return false
+	}
+	return true
+}
+
+func sharedLibraryInstallLocation(
+	libraryPath android.Path, isHost bool, archString string) string {
+	installLocation := "$(PRODUCT_OUT)/data"
+	if isHost {
+		installLocation = "$(HOST_OUT)"
+	}
+	installLocation = filepath.Join(
+		installLocation, "fuzz", archString, "lib", libraryPath.Base())
+	return installLocation
+}
+
 func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
 	fuzz.binaryDecorator.baseInstaller.dir = filepath.Join(
 		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
@@ -160,6 +216,22 @@
 		})
 		fuzz.config = configPath
 	}
+
+	// Grab the list of required shared libraries.
+	sharedLibraries := make(map[string]android.Path)
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if isValidSharedDependency(child, sharedLibraries) {
+			sharedLibraries[child.Name()] = child.(*Module).UnstrippedOutputFile()
+			return true
+		}
+		return false
+	})
+
+	for _, lib := range sharedLibraries {
+		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+			sharedLibraryInstallLocation(
+				lib, ctx.Host(), ctx.Arch().ArchType.String()))
+	}
 }
 
 func NewFuzz(hod android.HostOrDeviceSupported) *Module {
@@ -193,28 +265,15 @@
 		ctx.AppendProperties(&disableDarwinAndLinuxBionic)
 	})
 
-	// Statically link the STL. This allows fuzz target deployment to not have to
-	// include the STL.
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		staticStlLinkage := struct {
-			Target struct {
-				Linux_glibc struct {
-					Stl *string
-				}
-			}
-		}{}
-
-		staticStlLinkage.Target.Linux_glibc.Stl = proptools.StringPtr("libc++_static")
-		ctx.AppendProperties(&staticStlLinkage)
-	})
-
 	return module
 }
 
 // Responsible for generating GNU Make rules that package fuzz targets into
 // their architecture & target/host specific zip file.
 type fuzzPackager struct {
-	packages android.Paths
+	packages                android.Paths
+	sharedLibInstallStrings []string
+	fuzzTargets             map[string]bool
 }
 
 func fuzzPackagingFactory() android.Singleton {
@@ -226,18 +285,31 @@
 	DestinationPathPrefix string
 }
 
+type archAndLibraryKey struct {
+	ArchDir android.OutputPath
+	Library android.Path
+}
+
 func (s *fuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
 	// Map between each architecture + host/device combination, and the files that
 	// need to be packaged (in the tuple of {source file, destination folder in
 	// archive}).
 	archDirs := make(map[android.OutputPath][]fileToZip)
 
+	// List of shared library dependencies for each architecture + host/device combo.
+	archSharedLibraryDeps := make(map[archAndLibraryKey]bool)
+
+	// List of individual fuzz targets, so that 'make fuzz' also installs the targets
+	// to the correct output directories as well.
+	s.fuzzTargets = make(map[string]bool)
+
 	ctx.VisitAllModules(func(module android.Module) {
 		// Discard non-fuzz targets.
 		ccModule, ok := module.(*Module)
 		if !ok {
 			return
 		}
+
 		fuzzModule, ok := ccModule.compiler.(*fuzzBinary)
 		if !ok {
 			return
@@ -249,6 +321,8 @@
 			return
 		}
 
+		s.fuzzTargets[module.Name()] = true
+
 		hostOrTargetString := "target"
 		if ccModule.Host() {
 			hostOrTargetString = "host"
@@ -257,6 +331,29 @@
 		archString := ccModule.Arch().ArchType.String()
 		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
 
+		// Grab the list of required shared libraries.
+		sharedLibraries := make(map[string]android.Path)
+		collectAllSharedDependencies(module, sharedLibraries, ctx)
+
+		for _, library := range sharedLibraries {
+			if _, exists := archSharedLibraryDeps[archAndLibraryKey{archDir, library}]; exists {
+				continue
+			}
+
+			// For each architecture-specific shared library dependency, we need to
+			// install it to the output directory. Setup the install destination here,
+			// which will be used by $(copy-many-files) in the Make backend.
+			archSharedLibraryDeps[archAndLibraryKey{archDir, library}] = true
+			installDestination := sharedLibraryInstallLocation(
+				library, ccModule.Host(), archString)
+			// Escape all the variables, as the install destination here will be called
+			// via. $(eval) in Make.
+			installDestination = strings.ReplaceAll(
+				installDestination, "$", "$$")
+			s.sharedLibInstallStrings = append(s.sharedLibInstallStrings,
+				library.String()+":"+installDestination)
+		}
+
 		// The executable.
 		archDirs[archDir] = append(archDirs[archDir],
 			fileToZip{ccModule.UnstrippedOutputFile(), ccModule.Name()})
@@ -280,6 +377,12 @@
 		}
 	})
 
+	// Add the shared library deps for packaging.
+	for key, _ := range archSharedLibraryDeps {
+		archDirs[key.ArchDir] = append(archDirs[key.ArchDir],
+			fileToZip{key.Library, "lib"})
+	}
+
 	for archDir, filesToZip := range archDirs {
 		arch := archDir.Base()
 		hostOrTarget := filepath.Base(filepath.Dir(archDir.String()))
@@ -302,9 +405,22 @@
 }
 
 func (s *fuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+	packages := s.packages.Strings()
+	sort.Strings(packages)
+	sort.Strings(s.sharedLibInstallStrings)
 	// TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's
 	// ready to handle phony targets created in Soong. In the meantime, this
 	// exports the phony 'fuzz' target and dependencies on packages to
 	// core/main.mk so that we can use dist-for-goals.
-	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(s.packages.Strings(), " "))
+	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
+	ctx.Strict("FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+		strings.Join(s.sharedLibInstallStrings, " "))
+
+	// Preallocate the slice of fuzz targets to minimise memory allocations.
+	fuzzTargets := make([]string, 0, len(s.fuzzTargets))
+	for target, _ := range s.fuzzTargets {
+		fuzzTargets = append(fuzzTargets, target)
+	}
+	sort.Strings(fuzzTargets)
+	ctx.Strict("ALL_FUZZ_TARGETS", strings.Join(fuzzTargets, " "))
 }