APEXs are signed with apk signer

The entire APEX (which is a zip file) is signed with the apk signer.
Certificate can be specified via the 'certificate' property just like
ordinary apps. Note: multiple additional certificates are not supported.

Bug: 115721587
Test: m apex.test
Test: jarsigner -verify -verbose -certs .../apex.test.apex shows the
certificate info

Change-Id: Ia4c898d3427779a3809fdc683b85d7661ca65137
diff --git a/apex/apex.go b/apex/apex.go
index 41c7a97..e6c4d88 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -71,15 +71,17 @@
 }
 
 var (
-	sharedLibTag  = dependencyTag{name: "sharedLib"}
-	executableTag = dependencyTag{name: "executable"}
-	javaLibTag    = dependencyTag{name: "javaLib"}
-	prebuiltTag   = dependencyTag{name: "prebuilt"}
-	keyTag        = dependencyTag{name: "key"}
+	sharedLibTag   = dependencyTag{name: "sharedLib"}
+	executableTag  = dependencyTag{name: "executable"}
+	javaLibTag     = dependencyTag{name: "javaLib"}
+	prebuiltTag    = dependencyTag{name: "prebuilt"}
+	keyTag         = dependencyTag{name: "key"}
+	certificateTag = dependencyTag{name: "certificate"}
 )
 
 func init() {
 	pctx.Import("android/soong/common")
+	pctx.Import("android/soong/java")
 	pctx.HostBinToolVariable("apexer", "apexer")
 	// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
 	// projects, and hence cannot built 'aapt2'. Use the SDK prebuilt instead.
@@ -188,6 +190,10 @@
 	// Name of the apex_key module that provides the private key to sign APEX
 	Key *string
 
+	// The name of a certificate in the default certificate directory, blank to use the default product certificate,
+	// or an android_app_certificate module name in the form ":module".
+	Certificate *string
+
 	Multilib struct {
 		First struct {
 			// List of native libraries whose compile_multilib is "first"
@@ -324,6 +330,11 @@
 		return
 	}
 	ctx.AddDependency(ctx.Module(), keyTag, String(a.properties.Key))
+
+	cert := android.SrcIsModule(String(a.properties.Certificate))
+	if cert != "" {
+		ctx.AddDependency(ctx.Module(), certificateTag, cert)
+	}
 }
 
 func getCopyManifestForNativeLibrary(cc *cc.Module) (fileToCopy android.Path, dirInApex string) {
@@ -366,6 +377,7 @@
 	copyManifest := make(map[android.Path]string)
 
 	var keyFile android.Path
+	var certificate java.Certificate
 
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		if _, ok := parent.(*apexBundle); ok {
@@ -412,6 +424,13 @@
 				} else {
 					ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
 				}
+			case certificateTag:
+				if dep, ok := child.(*java.AndroidAppCertificate); ok {
+					certificate = dep.Certificate
+					return false
+				} else {
+					ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
+				}
 			}
 		} else {
 			// indirect dependencies
@@ -426,6 +445,18 @@
 		return false
 	})
 
+	cert := String(a.properties.Certificate)
+	if cert != "" && android.SrcIsModule(cert) == "" {
+		defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
+		certificate = java.Certificate{
+			defaultDir.Join(ctx, cert+".x509.pem"),
+			defaultDir.Join(ctx, cert+".pk8"),
+		}
+	} else if cert == "" {
+		pem, key := ctx.Config().DefaultAppCertificate(ctx)
+		certificate = java.Certificate{pem, key}
+	}
+
 	// files and dirs that will be created in apex
 	var readOnlyPaths []string
 	var executablePaths []string // this also includes dirs
@@ -455,7 +486,7 @@
 	manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "manifest.json"))
 	fileContexts := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.File_contexts, "file_contexts"))
 
-	a.outputFile = android.PathForModuleOut(ctx, a.ModuleBase.Name()+apexSuffix)
+	unsignedOutputFile := android.PathForModuleOut(ctx, a.ModuleBase.Name()+apexSuffix+".unsigned")
 
 	filesToCopy := []android.Path{}
 	for file := range copyManifest {
@@ -479,7 +510,7 @@
 	ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 		Rule:      apexRule,
 		Implicits: implicitInputs,
-		Output:    a.outputFile,
+		Output:    unsignedOutputFile,
 		Args: map[string]string{
 			"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
 			"image_dir":        android.PathForModuleOut(ctx, "image").String(),
@@ -491,6 +522,17 @@
 		},
 	})
 
+	a.outputFile = android.PathForModuleOut(ctx, a.ModuleBase.Name()+apexSuffix)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        java.Signapk,
+		Description: "signapk",
+		Output:      a.outputFile,
+		Input:       unsignedOutputFile,
+		Args: map[string]string{
+			"certificates": strings.Join([]string{certificate.Pem.String(), certificate.Key.String()}, " "),
+		},
+	})
+
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 }