Add support for android_app_certificate modules

Some android_app modules need certificates located outside their
directory.  Instead of requiring paths from the root of the tree,
add an android_app_certificate module that exports the certificate
files.

Test: m checkbuild
Change-Id: Icbf3898894f3eb857e2d907e3e58dd072c6fabe9
diff --git a/java/app.go b/java/app.go
index 843ced6..b9088c3 100644
--- a/java/app.go
+++ b/java/app.go
@@ -30,17 +30,18 @@
 func init() {
 	android.RegisterModuleType("android_app", AndroidAppFactory)
 	android.RegisterModuleType("android_test", AndroidTestFactory)
+	android.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
 }
 
 // AndroidManifest.xml merging
 // package splits
 
 type appProperties struct {
-	// path to a certificate, or the name of a certificate in the default
-	// certificate directory, or blank to use the default product certificate
+	// 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
 
-	// paths to extra certificates to sign the apk with
+	// Names of extra android_app_certificate modules to sign the apk with in the form ":module".
 	Additional_certificates []string
 
 	// If set, create package-export.apk, which other packages can
@@ -116,6 +117,21 @@
 		}
 		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
 	}
+
+	cert := android.SrcIsModule(String(a.appProperties.Certificate))
+	if cert != "" {
+		ctx.AddDependency(ctx.Module(), certificateTag, cert)
+	}
+
+	for _, cert := range a.appProperties.Additional_certificates {
+		cert = android.SrcIsModule(cert)
+		if cert != "" {
+			ctx.AddDependency(ctx.Module(), certificateTag, cert)
+		} else {
+			ctx.PropertyErrorf("additional_certificates",
+				`must be names of android_app_certificate modules in the form ":module"`)
+		}
+	}
 }
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -171,36 +187,12 @@
 		a.Module.compile(ctx, a.aaptSrcJar)
 	}
 
-	c := String(a.appProperties.Certificate)
-	switch {
-	case c == "":
-		pem, key := ctx.Config().DefaultAppCertificate(ctx)
-		a.certificate = certificate{pem, key}
-	case strings.ContainsRune(c, '/'):
-		a.certificate = certificate{
-			android.PathForSource(ctx, c+".x509.pem"),
-			android.PathForSource(ctx, c+".pk8"),
-		}
-	default:
-		defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
-		a.certificate = certificate{
-			defaultDir.Join(ctx, c+".x509.pem"),
-			defaultDir.Join(ctx, c+".pk8"),
-		}
-	}
-
-	certificates := []certificate{a.certificate}
-	for _, c := range a.appProperties.Additional_certificates {
-		certificates = append(certificates, certificate{
-			android.PathForSource(ctx, c+".x509.pem"),
-			android.PathForSource(ctx, c+".pk8"),
-		})
-	}
-
 	packageFile := android.PathForModuleOut(ctx, "package.apk")
 
+	var certificates []certificate
+
 	var jniJarFile android.WritablePath
-	jniLibs := a.collectJniDeps(ctx)
+	jniLibs, certificateDeps := a.collectAppDeps(ctx)
 	if len(jniLibs) > 0 {
 		embedJni := ctx.Config().UnbundledBuild() || a.appProperties.EmbedJNI
 		if embedJni {
@@ -211,6 +203,28 @@
 		}
 	}
 
+	if ctx.Failed() {
+		return
+	}
+
+	cert := String(a.appProperties.Certificate)
+	certModule := android.SrcIsModule(cert)
+	if certModule != "" {
+		a.certificate = certificateDeps[0]
+		certificateDeps = certificateDeps[1:]
+	} else if cert != "" {
+		defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
+		a.certificate = certificate{
+			defaultDir.Join(ctx, cert+".x509.pem"),
+			defaultDir.Join(ctx, cert+".pk8"),
+		}
+	} else {
+		pem, key := ctx.Config().DefaultAppCertificate(ctx)
+		a.certificate = certificate{pem, key}
+	}
+
+	certificates = append([]certificate{a.certificate}, certificateDeps...)
+
 	CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, a.outputFile, certificates)
 
 	a.outputFile = packageFile
@@ -225,8 +239,9 @@
 	}
 }
 
-func (a *AndroidApp) collectJniDeps(ctx android.ModuleContext) []jniLib {
+func (a *AndroidApp) collectAppDeps(ctx android.ModuleContext) ([]jniLib, []certificate) {
 	var jniLibs []jniLib
+	var certificates []certificate
 
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
@@ -248,10 +263,16 @@
 				ctx.ModuleErrorf("jni_libs dependency %q must be a cc library", otherName)
 
 			}
+		} else if tag == certificateTag {
+			if dep, ok := module.(*AndroidAppCertificate); ok {
+				certificates = append(certificates, dep.certificate)
+			} else {
+				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
+			}
 		}
 	})
 
-	return jniLibs
+	return jniLibs, certificates
 }
 
 func AndroidAppFactory() android.Module {
@@ -337,3 +358,32 @@
 	android.InitDefaultableModule(module)
 	return module
 }
+
+type AndroidAppCertificate struct {
+	android.ModuleBase
+	properties  AndroidAppCertificateProperties
+	certificate certificate
+}
+
+type AndroidAppCertificateProperties struct {
+	// Name of the certificate files.  Extensions .x509.pem and .pk8 will be added to the name.
+	Certificate *string
+}
+
+func AndroidAppCertificateFactory() android.Module {
+	module := &AndroidAppCertificate{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	return module
+}
+
+func (c *AndroidAppCertificate) DepsMutator(ctx android.BottomUpMutatorContext) {
+}
+
+func (c *AndroidAppCertificate) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	cert := String(c.properties.Certificate)
+	c.certificate = certificate{
+		android.PathForModuleSrc(ctx, cert+".x509.pem"),
+		android.PathForModuleSrc(ctx, cert+".pk8"),
+	}
+}