Port uses-shared library verification and dexpreopting to Soong

Ports 09f3b97f4b488cd3a7b7d72038b173575b02c162 (Add support for
preopt with uses-libraries) from Make to Soong to support verifying
and preopting shared libraries.

This reapplies Id25f55f07a55120bebe2a9b32c094209efc85c8b with fixes
for unbundled builds and builds with ALLOW_MISSING_DEPENDENCIES=true
set.

Bug: 132357300
Test: app_test.go
Test: m checkbuild
Change-Id: I964309a68ec4ed081f3f3154879c71048ecb5455
diff --git a/java/app.go b/java/app.go
index f7f08a8..2d817fe 100644
--- a/java/app.go
+++ b/java/app.go
@@ -17,12 +17,13 @@
 // This file contains the module types for compiling Android apps.
 
 import (
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
 	"path/filepath"
 	"reflect"
 	"strings"
 
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/tradefed"
@@ -119,6 +120,8 @@
 	aapt
 	android.OverridableModuleBase
 
+	usesLibrary usesLibrary
+
 	certificate Certificate
 
 	appProperties appProperties
@@ -176,6 +179,8 @@
 			}
 		}
 	}
+
+	a.usesLibrary.deps(ctx, Bool(a.properties.No_framework_libs))
 }
 
 func (a *AndroidApp) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
@@ -276,6 +281,7 @@
 	aaptLinkFlags = append(aaptLinkFlags, a.additionalAaptFlags...)
 
 	a.aapt.splitNames = a.appProperties.Package_splits
+	a.aapt.sdkLibraries = a.exportedSdkLibs
 
 	a.aapt.buildActions(ctx, sdkContext(a), aaptLinkFlags...)
 
@@ -308,9 +314,17 @@
 	} else {
 		installDir = filepath.Join("app", a.installApkName)
 	}
+
 	a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
 	a.dexpreopter.isInstallable = Bool(a.properties.Installable)
 	a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
+
+	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
+	a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
+	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
+	a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
+	a.dexpreopter.manifestFile = a.mergedManifestFile
+
 	a.deviceProperties.UncompressDex = a.dexpreopter.uncompressedDex
 
 	if ctx.ModuleName() != "framework-res" {
@@ -368,12 +382,19 @@
 }
 
 func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
+	var apkDeps android.Paths
+
 	// Check if the install APK name needs to be overridden.
 	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
 
 	// Process all building blocks, from AAPT to certificates.
 	a.aaptBuildActions(ctx)
 
+	if a.usesLibrary.enforceUsesLibraries() {
+		manifestCheckFile := a.usesLibrary.verifyUsesLibrariesManifest(ctx, a.mergedManifestFile)
+		apkDeps = append(apkDeps, manifestCheckFile)
+	}
+
 	a.proguardBuildActions(ctx)
 
 	dexJarFile := a.dexBuildActions(ctx)
@@ -391,13 +412,13 @@
 	// Build a final signed app package.
 	// TODO(jungjw): Consider changing this to installApkName.
 	packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".apk")
-	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates)
+	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps)
 	a.outputFile = packageFile
 
 	for _, split := range a.aapt.splits {
 		// Sign the split APKs
 		packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"_"+split.suffix+".apk")
-		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates)
+		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps)
 		a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
 	}
 
@@ -483,7 +504,8 @@
 		&module.Module.protoProperties,
 		&module.aaptProperties,
 		&module.appProperties,
-		&module.overridableAppProperties)
+		&module.overridableAppProperties,
+		&module.usesLibrary.usesLibraryProperties)
 
 	module.Prefer32(func(ctx android.BaseModuleContext, base *android.ModuleBase, class android.OsClass) bool {
 		return class == android.Device && ctx.Config().DevicePrefer32BitApps()
@@ -559,6 +581,7 @@
 		&module.appProperties,
 		&module.appTestProperties,
 		&module.overridableAppProperties,
+		&module.usesLibrary.usesLibraryProperties,
 		&module.testProperties)
 
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
@@ -599,7 +622,8 @@
 		&module.aaptProperties,
 		&module.appProperties,
 		&module.appTestHelperAppProperties,
-		&module.overridableAppProperties)
+		&module.overridableAppProperties,
+		&module.usesLibrary.usesLibraryProperties)
 
 	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
@@ -666,6 +690,8 @@
 	certificate *Certificate
 
 	dexpreopter
+
+	usesLibrary usesLibrary
 }
 
 type AndroidAppImportProperties struct {
@@ -753,6 +779,8 @@
 	if cert != "" {
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
+
+	a.usesLibrary.deps(ctx, false)
 }
 
 func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
@@ -808,7 +836,12 @@
 	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
 	// TODO: LOCAL_PACKAGE_SPLITS
 
-	srcApk := android.PathForModuleSrc(ctx, a.getSrcApkPath(ctx))
+	var srcApk android.Path
+	srcApk = android.PathForModuleSrc(ctx, a.getSrcApkPath(ctx))
+
+	if a.usesLibrary.enforceUsesLibraries() {
+		srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
+	}
 
 	// TODO: Install or embed JNI libraries
 
@@ -821,6 +854,12 @@
 	a.dexpreopter.isInstallable = true
 	a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
 	a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
+
+	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
+	a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
+	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
+	a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
+
 	dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
 	if a.dexpreopter.uncompressedDex {
 		dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
@@ -866,9 +905,129 @@
 	module.properties.Dpi_variants = reflect.New(dpiVariantsStruct).Interface()
 	module.AddProperties(&module.properties)
 	module.AddProperties(&module.dexpreoptProperties)
+	module.AddProperties(&module.usesLibrary.usesLibraryProperties)
 
 	InitJavaModule(module, android.DeviceSupported)
 	android.InitSingleSourcePrebuiltModule(module, &module.properties.Apk)
 
 	return module
 }
+
+type UsesLibraryProperties struct {
+	// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file.
+	Uses_libs []string
+
+	// A list of shared library modules that will be listed in uses-library tags in the AndroidManifest.xml file with
+	// required=false.
+	Optional_uses_libs []string
+
+	// If true, the list of uses_libs and optional_uses_libs modules must match the AndroidManifest.xml file.  Defaults
+	// to true if either uses_libs or optional_uses_libs is set.  Will unconditionally default to true in the future.
+	Enforce_uses_libs *bool
+}
+
+// usesLibrary provides properties and helper functions for AndroidApp and AndroidAppImport to verify that the
+// <uses-library> tags that end up in the manifest of an APK match the ones known to the build system through the
+// uses_libs and optional_uses_libs properties.  The build system's values are used by dexpreopt to preopt apps
+// with knowledge of their shared libraries.
+type usesLibrary struct {
+	usesLibraryProperties UsesLibraryProperties
+}
+
+func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, noFrameworkLibs bool) {
+	ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
+	ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...)
+	if !noFrameworkLibs {
+		// dexpreopt/dexpreopt.go needs the paths to the dex jars of these libraries in case construct_context.sh needs
+		// to pass them to dex2oat.  Add them as a dependency so we can determine the path to the dex jar of each
+		// library to dexpreopt.
+		ctx.AddVariationDependencies(nil, usesLibTag,
+			"org.apache.http.legacy",
+			"android.hidl.base-V1.0-java",
+			"android.hidl.manager-V1.0-java")
+	}
+}
+
+// presentOptionalUsesLibs returns optional_uses_libs after filtering out MissingUsesLibraries, which don't exist in the
+// build.
+func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []string {
+	optionalUsesLibs, _ := android.FilterList(u.usesLibraryProperties.Optional_uses_libs, ctx.Config().MissingUsesLibraries())
+	return optionalUsesLibs
+}
+
+// usesLibraryPaths returns a map of module names of shared library dependencies to the paths to their dex jars.
+func (u *usesLibrary) usesLibraryPaths(ctx android.ModuleContext) map[string]android.Path {
+	usesLibPaths := make(map[string]android.Path)
+
+	if !ctx.Config().UnbundledBuild() {
+		ctx.VisitDirectDepsWithTag(usesLibTag, func(m android.Module) {
+			if lib, ok := m.(Dependency); ok {
+				if dexJar := lib.DexJar(); dexJar != nil {
+					usesLibPaths[ctx.OtherModuleName(m)] = dexJar
+				} else {
+					ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must produce a dex jar, does it have installable: true?",
+						ctx.OtherModuleName(m))
+				}
+			} else if ctx.Config().AllowMissingDependencies() {
+				ctx.AddMissingDependencies([]string{ctx.OtherModuleName(m)})
+			} else {
+				ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library",
+					ctx.OtherModuleName(m))
+			}
+		})
+	}
+
+	return usesLibPaths
+}
+
+// enforceUsesLibraries returns true of <uses-library> tags should be checked against uses_libs and optional_uses_libs
+// properties.  Defaults to true if either of uses_libs or optional_uses_libs is specified.  Will default to true
+// unconditionally in the future.
+func (u *usesLibrary) enforceUsesLibraries() bool {
+	defaultEnforceUsesLibs := len(u.usesLibraryProperties.Uses_libs) > 0 ||
+		len(u.usesLibraryProperties.Optional_uses_libs) > 0
+	return BoolDefault(u.usesLibraryProperties.Enforce_uses_libs, defaultEnforceUsesLibs)
+}
+
+// verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against the ones specified
+// in the uses_libs and optional_uses_libs properties.  It returns the path to a copy of the manifest.
+func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
+	outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
+
+	rule := android.NewRuleBuilder()
+	cmd := rule.Command().Tool(ctx.Config().HostToolPath(ctx, "manifest_check")).
+		Flag("--enforce-uses-libraries").
+		Input(manifest).
+		FlagWithOutput("-o ", outputFile)
+
+	for _, lib := range u.usesLibraryProperties.Uses_libs {
+		cmd.FlagWithArg("--uses-library ", lib)
+	}
+
+	for _, lib := range u.usesLibraryProperties.Optional_uses_libs {
+		cmd.FlagWithArg("--optional-uses-library ", lib)
+	}
+
+	rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>")
+
+	return outputFile
+}
+
+// verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the ones specified
+// in the uses_libs and optional_uses_libs properties.  It returns the path to a copy of the APK.
+func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path {
+	outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
+
+	rule := android.NewRuleBuilder()
+	aapt := ctx.Config().HostToolPath(ctx, "aapt")
+	rule.Command().
+		Textf("aapt_binary=%s", aapt.String()).Implicit(aapt).
+		Textf(`uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Uses_libs, " ")).
+		Textf(`optional_uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Optional_uses_libs, " ")).
+		Tool(android.PathForSource(ctx, "build/make/core/verify_uses_libraries.sh")).Input(apk)
+	rule.Command().Text("cp -f").Input(apk).Output(outputFile)
+
+	rule.Build(pctx, ctx, "verify_uses_libraries", "verify <uses-library>")
+
+	return outputFile
+}
diff --git a/java/app_builder.go b/java/app_builder.go
index 82a390f..fa77bbf 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -63,7 +63,7 @@
 	})
 
 func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
-	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate) {
+	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths) {
 
 	unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk"
 	unsignedApk := android.PathForModuleOut(ctx, unsignedApkName)
@@ -78,9 +78,10 @@
 	}
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:   combineApk,
-		Inputs: inputs,
-		Output: unsignedApk,
+		Rule:      combineApk,
+		Inputs:    inputs,
+		Output:    unsignedApk,
+		Implicits: deps,
 	})
 
 	SignAppPackage(ctx, outputFile, unsignedApk, certificates)
diff --git a/java/app_test.go b/java/app_test.go
index 40a64af..559afcc 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1239,3 +1239,83 @@
 		})
 	}
 }
+
+func TestUsesLibraries(t *testing.T) {
+	bp := `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+		}
+
+		java_sdk_library {
+			name: "bar",
+			srcs: ["a.java"],
+			api_packages: ["bar"],
+		}
+
+		android_app {
+			name: "app",
+			srcs: ["a.java"],
+			uses_libs: ["foo"],
+			optional_uses_libs: [
+				"bar",
+				"baz",
+			],
+		}
+
+		android_app_import {
+			name: "prebuilt",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "platform",
+			uses_libs: ["foo"],
+			optional_uses_libs: [
+				"bar",
+				"baz",
+			],
+		}
+	`
+
+	config := testConfig(nil)
+	config.TestProductVariables.MissingUsesLibraries = []string{"baz"}
+
+	ctx := testAppContext(config, bp, nil)
+
+	run(t, ctx, config)
+
+	app := ctx.ModuleForTests("app", "android_common")
+	prebuilt := ctx.ModuleForTests("prebuilt", "android_common")
+
+	// Test that all libraries are verified
+	cmd := app.Rule("verify_uses_libraries").RuleParams.Command
+	if w := "--uses-library foo"; !strings.Contains(cmd, w) {
+		t.Errorf("wanted %q in %q", w, cmd)
+	}
+
+	if w := "--optional-uses-library bar --optional-uses-library baz"; !strings.Contains(cmd, w) {
+		t.Errorf("wanted %q in %q", w, cmd)
+	}
+
+	cmd = prebuilt.Rule("verify_uses_libraries").RuleParams.Command
+
+	if w := `uses_library_names="foo"`; !strings.Contains(cmd, w) {
+		t.Errorf("wanted %q in %q", w, cmd)
+	}
+
+	if w := `optional_uses_library_names="bar baz"`; !strings.Contains(cmd, w) {
+		t.Errorf("wanted %q in %q", w, cmd)
+	}
+
+	// Test that only present libraries are preopted
+	cmd = app.Rule("dexpreopt").RuleParams.Command
+
+	if w := `dex_preopt_target_libraries="/system/framework/foo.jar /system/framework/bar.jar"`; !strings.Contains(cmd, w) {
+		t.Errorf("wanted %q in %q", w, cmd)
+	}
+
+	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
+
+	if w := `dex_preopt_target_libraries="/system/framework/foo.jar /system/framework/bar.jar"`; !strings.Contains(cmd, w) {
+		t.Errorf("wanted %q in %q", w, cmd)
+	}
+}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 08fd06e..23d2aa6 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -29,6 +29,12 @@
 	isInstallable       bool
 	isPresignedPrebuilt bool
 
+	manifestFile     android.Path
+	usesLibs         []string
+	optionalUsesLibs []string
+	enforceUsesLibs  bool
+	libraryPaths     map[string]android.Path
+
 	builtInstalled string
 }
 
@@ -154,6 +160,7 @@
 		DexLocation:     dexLocation,
 		BuildPath:       android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath,
 		DexPath:         dexJarFile,
+		ManifestPath:    d.manifestFile,
 		UncompressedDex: d.uncompressedDex,
 		HasApkLibraries: false,
 		PreoptFlags:     nil,
@@ -161,10 +168,10 @@
 		ProfileClassListing:  profileClassListing,
 		ProfileIsTextListing: profileIsTextListing,
 
-		EnforceUsesLibraries:  false,
-		OptionalUsesLibraries: nil,
-		UsesLibraries:         nil,
-		LibraryPaths:          nil,
+		EnforceUsesLibraries:         d.enforceUsesLibs,
+		PresentOptionalUsesLibraries: d.optionalUsesLibs,
+		UsesLibraries:                d.usesLibs,
+		LibraryPaths:                 d.libraryPaths,
 
 		Archs:           archs,
 		DexPreoptImages: images,
diff --git a/java/java.go b/java/java.go
index 8dbdfb6..84518a1 100644
--- a/java/java.go
+++ b/java/java.go
@@ -420,6 +420,7 @@
 	proguardRaiseTag      = dependencyTag{name: "proguard-raise"}
 	certificateTag        = dependencyTag{name: "certificate"}
 	instrumentationForTag = dependencyTag{name: "instrumentation_for"}
+	usesLibTag            = dependencyTag{name: "uses-library"}
 )
 
 type sdkDep struct {
diff --git a/java/java_test.go b/java/java_test.go
index 50b1c34..3a7ed4e 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -174,6 +174,8 @@
 
 		"build/soong/scripts/jar-wrapper.sh": nil,
 
+		"build/make/core/verify_uses_libraries.sh": nil,
+
 		"build/make/core/proguard.flags":             nil,
 		"build/make/core/proguard_basic_keeps.flags": nil,
 
diff --git a/java/testing.go b/java/testing.go
index 22831c9..fc7842d 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -77,6 +77,33 @@
 			name: "framework-res",
 			no_framework_libs: true,
 		}
+
+		java_library {
+			name: "android.hidl.base-V1.0-java",
+			srcs: ["a.java"],
+			no_standard_libs: true,
+			sdk_version: "core_current",
+			system_modules: "core-platform-api-stubs-system-modules",
+			installable: true,
+		}
+
+		java_library {
+			name: "android.hidl.manager-V1.0-java",
+			srcs: ["a.java"],
+			no_standard_libs: true,
+			sdk_version: "core_current",
+			system_modules: "core-platform-api-stubs-system-modules",
+			installable: true,
+		}
+
+		java_library {
+			name: "org.apache.http.legacy",
+			srcs: ["a.java"],
+			no_standard_libs: true,
+			sdk_version: "core_current",
+			system_modules: "core-platform-api-stubs-system-modules",
+			installable: true,
+		}
 	`
 
 	systemModules := []string{