Add "legacy_android10_support" to "apex"

APEXes with "legacy_android10_support" will have apex_manifest.json for
compatibility as well as apex_manifest.pb.

Bug: 143951586
Test: m (soong tests)
Change-Id: I019252aee5a9423f4b180ba1026e6e99c9961437
diff --git a/apex/apex.go b/apex/apex.go
index 81c1230..0fa3b2a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -343,6 +343,10 @@
 	// Whenever apex_payload.img of the APEX should include dm-verity hashtree.
 	// Should be only used in tests#.
 	Test_only_no_hashtree *bool
+
+	// Whether this APEX should support Android10. Default is false. If this is set true, then apex_manifest.json is bundled as well
+	// because Android10 requires legacy apex_manifest.json instead of apex_manifest.pb
+	Legacy_android10_support *bool
 }
 
 type apexTargetBundleProperties struct {
@@ -507,10 +511,8 @@
 	artApex         bool
 	primaryApexType bool
 
-	// intermediate path for apex_manifest.json
-	manifestJsonOut     android.WritablePath
-	manifestJsonFullOut android.WritablePath
-	manifestPbOut       android.WritablePath
+	manifestJsonOut 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
diff --git a/apex/apex_test.go b/apex/apex_test.go
index ee5d0f2..d2884b9 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -140,8 +140,8 @@
 		ctx.BottomUp("prebuilts", android.PrebuiltMutator).Parallel()
 	})
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
 		ctx.BottomUp("vndk", cc.VndkMutator).Parallel()
+		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
 		ctx.BottomUp("test_per_src", cc.TestPerSrcMutator).Parallel()
 		ctx.BottomUp("version", cc.VersionMutator).Parallel()
 		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
@@ -547,9 +547,10 @@
 	`)
 
 	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
-	module.Output("apex_manifest.pb")
-	module.Output("apex_manifest.json")
-	module.Output("apex_manifest_full.json")
+	args := module.Rule("apexRule").Args
+	if manifest := args["manifest"]; manifest != module.Output("apex_manifest.pb").Output.String() {
+		t.Error("manifest should be apex_manifest.pb, but " + manifest)
+	}
 }
 
 func TestBasicZipApex(t *testing.T) {
@@ -2452,7 +2453,7 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE := mytest1.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := mytest2.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := mytest3.myapex\n")
-	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.json.myapex\n")
+	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
 }
@@ -3011,6 +3012,26 @@
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
 }
 
+func TestLegacyAndroid10Support(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			legacy_android10_support: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	args := module.Rule("apexRule").Args
+	ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String())
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/builder.go b/apex/builder.go
index 55db934..213f9ea 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -103,7 +103,6 @@
 			`(. ${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 ` +
@@ -114,22 +113,20 @@
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags",
-		"manifest", "manifest_json", "manifest_json_full",
-	)
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags", "manifest")
 
 	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} --manifest_json_full ${manifest_json_full} ` +
+			`${apexer} --force --manifest ${manifest} ` +
 			`--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", "manifest_json_full")
+	}, "tool_path", "image_dir", "copy_commands", "manifest")
 
 	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
 		blueprint.RuleParams{
@@ -169,7 +166,7 @@
 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")
+	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
 
 	// put dependency({provide|require}NativeLibs) in apex_manifest.json
 	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
@@ -184,7 +181,7 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
 		Input:  manifestSrc,
-		Output: a.manifestJsonFullOut,
+		Output: manifestJsonFullOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
 			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
@@ -192,20 +189,22 @@
 		},
 	})
 
-	// b/143654022 Q apexd can't understand newly added keys in apex_manifest.json
-	// prepare stripped-down 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,
-	})
+	if proptools.Bool(a.properties.Legacy_android10_support) {
+		// b/143654022 Q apexd can't understand newly added keys in apex_manifest.json
+		// prepare stripped-down 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:  manifestJsonFullOut,
+			Output: a.manifestJsonOut,
+		})
+	}
 
 	// 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,
+		Input:  manifestJsonFullOut,
 		Output: a.manifestPbOut,
 	})
 }
@@ -254,7 +253,10 @@
 	copyCommands := []string{}
 	emitCommands := []string{}
 	imageContentFile := android.PathForModuleOut(ctx, a.Name()+"-content.txt")
-	emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
+	emitCommands = append(emitCommands, "echo ./apex_manifest.pb >> "+imageContentFile.String())
+	if proptools.Bool(a.properties.Legacy_android10_support) {
+		emitCommands = append(emitCommands, "echo ./apex_manifest.json >> "+imageContentFile.String())
+	}
 	for i, src := range filesToCopy {
 		dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
 		emitCommands = append(emitCommands, "echo './"+dest+"' >> "+imageContentFile.String())
@@ -269,7 +271,7 @@
 	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
 
 	implicitInputs := append(android.Paths(nil), filesToCopy...)
-	implicitInputs = append(implicitInputs, a.manifestPbOut, a.manifestJsonFullOut, a.manifestJsonOut)
+	implicitInputs = append(implicitInputs, a.manifestPbOut)
 
 	if a.properties.Whitelisted_files != nil {
 		ctx.Build(pctx, android.BuildParams{
@@ -392,22 +394,25 @@
 			optFlags = append(optFlags, "--do_not_check_keyname")
 		}
 
+		if proptools.Bool(a.properties.Legacy_android10_support) {
+			implicitInputs = append(implicitInputs, a.manifestJsonOut)
+			optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
+		}
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        apexRule,
 			Implicits:   implicitInputs,
 			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_json_full": a.manifestJsonFullOut.String(),
-				"manifest_json":      a.manifestJsonOut.String(),
-				"manifest":           a.manifestPbOut.String(),
-				"file_contexts":      a.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":         a.manifestPbOut.String(),
+				"file_contexts":    a.fileContexts.String(),
+				"canned_fs_config": cannedFsConfig.String(),
+				"key":              a.private_key_file.String(),
+				"opt_flags":        strings.Join(optFlags, " "),
 			},
 		})
 
@@ -438,11 +443,10 @@
 			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.manifestPbOut.String(),
-				"manifest_json_full": a.manifestJsonFullOut.String(),
+				"tool_path":     outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":     android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"copy_commands": strings.Join(copyCommands, " && "),
+				"manifest":      a.manifestPbOut.String(),
 			},
 		})
 	}
@@ -503,9 +507,8 @@
 
 func (a *apexBundle) buildFilesInfo(ctx android.ModuleContext) {
 	if a.installable() {
-		// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
+		// For flattened APEX, do nothing but make sure that APEX manifest and apex_pubkey are also copied along
 		// with other ordinary files.
-		a.filesInfo = append(a.filesInfo, newApexFile(a.manifestJsonOut, "apex_manifest.json."+a.Name()+a.suffix, ".", etc, nil))
 		a.filesInfo = append(a.filesInfo, newApexFile(a.manifestPbOut, "apex_manifest.pb."+a.Name()+a.suffix, ".", etc, nil))
 
 		// rename to apex_pubkey