• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2019 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package apex
16
17import (
18	"encoding/json"
19	"fmt"
20	"path"
21	"path/filepath"
22	"runtime"
23	"slices"
24	"sort"
25	"strconv"
26	"strings"
27
28	"android/soong/aconfig"
29	"android/soong/android"
30	"android/soong/dexpreopt"
31	"android/soong/java"
32
33	"github.com/google/blueprint"
34	"github.com/google/blueprint/proptools"
35)
36
37var (
38	pctx = android.NewPackageContext("android/apex")
39)
40
41func init() {
42	pctx.Import("android/soong/aconfig")
43	pctx.Import("android/soong/android")
44	pctx.Import("android/soong/cc/config")
45	pctx.Import("android/soong/java")
46	pctx.HostBinToolVariable("apexer", "apexer")
47	pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
48
49	// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
50	// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
51	hostBinToolVariableWithPrebuilt := func(name, prebuiltDir, tool string) {
52		pctx.VariableFunc(name, func(ctx android.PackageVarContext) string {
53			if !ctx.Config().FrameworksBaseDirExists(ctx) {
54				return filepath.Join(prebuiltDir, runtime.GOOS, "bin", tool)
55			} else {
56				return ctx.Config().HostToolPath(ctx, tool).String()
57			}
58		})
59	}
60	hostBinToolVariableWithPrebuilt("aapt2", "prebuilts/sdk/tools", "aapt2")
61	pctx.HostBinToolVariable("avbtool", "avbtool")
62	pctx.HostBinToolVariable("e2fsdroid", "e2fsdroid")
63	pctx.HostBinToolVariable("merge_zips", "merge_zips")
64	pctx.HostBinToolVariable("mke2fs", "mke2fs")
65	pctx.HostBinToolVariable("resize2fs", "resize2fs")
66	pctx.HostBinToolVariable("sefcontext_compile", "sefcontext_compile")
67	pctx.HostBinToolVariable("soong_zip", "soong_zip")
68	pctx.HostBinToolVariable("zip2zip", "zip2zip")
69	pctx.HostBinToolVariable("zipalign", "zipalign")
70	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
71	pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest")
72	pctx.HostBinToolVariable("extract_apks", "extract_apks")
73	pctx.HostBinToolVariable("make_f2fs", "make_f2fs")
74	pctx.HostBinToolVariable("sload_f2fs", "sload_f2fs")
75	pctx.HostBinToolVariable("make_erofs", "mkfs.erofs")
76	pctx.HostBinToolVariable("apex_compression_tool", "apex_compression_tool")
77	pctx.HostBinToolVariable("dexdeps", "dexdeps")
78	pctx.HostBinToolVariable("apex_ls", "apex-ls")
79	pctx.HostBinToolVariable("apex_sepolicy_tests", "apex_sepolicy_tests")
80	pctx.HostBinToolVariable("deapexer", "deapexer")
81	pctx.HostBinToolVariable("debugfs_static", "debugfs_static")
82	pctx.HostBinToolVariable("fsck_erofs", "fsck.erofs")
83	pctx.SourcePathVariable("genNdkUsedbyApexPath", "build/soong/scripts/gen_ndk_usedby_apex.sh")
84	pctx.HostBinToolVariable("conv_linker_config", "conv_linker_config")
85	pctx.HostBinToolVariable("assemble_vintf", "assemble_vintf")
86	pctx.HostBinToolVariable("apex_elf_checker", "apex_elf_checker")
87	pctx.HostBinToolVariable("aconfig", "aconfig")
88	pctx.HostBinToolVariable("host_apex_verifier", "host_apex_verifier")
89}
90
91type createStorageStruct struct {
92	Output_file string
93	Desc        string
94	File_type   string
95}
96
97var createStorageInfo = []createStorageStruct{
98	{"package.map", "create_aconfig_package_map_file", "package_map"},
99	{"flag.map", "create_aconfig_flag_map_file", "flag_map"},
100	{"flag.val", "create_aconfig_flag_val_file", "flag_val"},
101	{"flag.info", "create_aconfig_flag_info_file", "flag_info"},
102}
103
104var (
105	apexManifestRule = pctx.StaticRule("apexManifestRule", blueprint.RuleParams{
106		Command: `rm -f $out && ${jsonmodify} $in ` +
107			`-a provideNativeLibs ${provideNativeLibs} ` +
108			`-a requireNativeLibs ${requireNativeLibs} ` +
109			`-se version 0 ${default_version} ` +
110			`${opt} ` +
111			`-o $out`,
112		CommandDeps: []string{"${jsonmodify}"},
113		Description: "prepare ${out}",
114	}, "provideNativeLibs", "requireNativeLibs", "default_version", "opt")
115
116	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
117		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
118		CommandDeps: []string{"${conv_apex_manifest}"},
119		Description: "strip ${in}=>${out}",
120	})
121
122	pbApexManifestRule = pctx.StaticRule("pbApexManifestRule", blueprint.RuleParams{
123		Command:     `rm -f $out && ${conv_apex_manifest} proto $in -o $out`,
124		CommandDeps: []string{"${conv_apex_manifest}"},
125		Description: "convert ${in}=>${out}",
126	})
127
128	// TODO(b/113233103): make sure that file_contexts is as expected, i.e., validate
129	// against the binary policy using sefcontext_compiler -p <policy>.
130
131	// TODO(b/114327326): automate the generation of file_contexts
132	apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{
133		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
134			`(. ${out}.copy_commands) && ` +
135			`APEXER_TOOL_PATH=${tool_path} ` +
136			`${apexer} --force --manifest ${manifest} ` +
137			`--file_contexts ${file_contexts} ` +
138			`--canned_fs_config ${canned_fs_config} ` +
139			`--include_build_info ` +
140			`--payload_type image ` +
141			`--key ${key} ${opt_flags} ${image_dir} ${out} `,
142		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
143			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}", "${sload_f2fs}", "${make_erofs}",
144			"${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"},
145		Rspfile:        "${out}.copy_commands",
146		RspfileContent: "${copy_commands}",
147		Description:    "APEX ${image_dir} => ${out}",
148	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
149		"opt_flags", "manifest")
150
151	DCLAApexRule = pctx.StaticRule("DCLAApexRule", blueprint.RuleParams{
152		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
153			`(. ${out}.copy_commands) && ` +
154			`APEXER_TOOL_PATH=${tool_path} ` +
155			`${apexer_with_DCLA_preprocessing} ` +
156			`--apexer ${apexer} ` +
157			`--canned_fs_config ${canned_fs_config} ` +
158			`${image_dir} ` +
159			`${out} ` +
160			`-- ` +
161			`--include_build_info ` +
162			`--force ` +
163			`--payload_type image ` +
164			`--key ${key} ` +
165			`--file_contexts ${file_contexts} ` +
166			`--manifest ${manifest} ` +
167			`${opt_flags} `,
168		CommandDeps: []string{"${apexer_with_DCLA_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
169			"${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
170			"${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
171			"prebuilts/sdk/current/public/android.jar"},
172		Rspfile:        "${out}.copy_commands",
173		RspfileContent: "${copy_commands}",
174		Description:    "APEX ${image_dir} => ${out}",
175	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
176		"opt_flags", "manifest", "is_DCLA")
177
178	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
179		blueprint.RuleParams{
180			Command:     `${aapt2} convert --output-format proto $in -o $out`,
181			CommandDeps: []string{"${aapt2}"},
182		})
183
184	apexBundleRule = pctx.StaticRule("apexBundleRule", blueprint.RuleParams{
185		Command: `${zip2zip} -i $in -o $out.base ` +
186			`apex_payload.img:apex/${abi}.img ` +
187			`apex_build_info.pb:apex/${abi}.build_info.pb ` +
188			`apex_manifest.json:root/apex_manifest.json ` +
189			`apex_manifest.pb:root/apex_manifest.pb ` +
190			`AndroidManifest.xml:manifest/AndroidManifest.xml ` +
191			`assets/NOTICE.html.gz:assets/NOTICE.html.gz &&` +
192			`${soong_zip} -o $out.config -C $$(dirname ${config}) -f ${config} && ` +
193			`${merge_zips} $out $out.base $out.config`,
194		CommandDeps: []string{"${zip2zip}", "${soong_zip}", "${merge_zips}"},
195		Description: "app bundle",
196	}, "abi", "config")
197
198	diffApexContentRule = pctx.StaticRule("diffApexContentRule", blueprint.RuleParams{
199		Command: `diff --unchanged-group-format='' \` +
200			`--changed-group-format='%<' \` +
201			`${image_content_file} ${allowed_files_file} || (` +
202			`echo "New unexpected files were added to ${apex_module_name}." ` +
203			` "To fix the build run following command:" && ` +
204			`echo "system/apex/tools/update_allowed_list.sh ${allowed_files_file} ${image_content_file}" && ` +
205			`exit 1); touch ${out}`,
206		Description: "Diff ${image_content_file} and ${allowed_files_file}",
207	}, "image_content_file", "allowed_files_file", "apex_module_name")
208
209	generateAPIsUsedbyApexRule = pctx.StaticRule("generateAPIsUsedbyApexRule", blueprint.RuleParams{
210		Command:     "$genNdkUsedbyApexPath ${image_dir} ${readelf} ${out}",
211		CommandDeps: []string{"${genNdkUsedbyApexPath}"},
212		Description: "Generate symbol list used by Apex",
213	}, "image_dir", "readelf")
214
215	apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{
216		Command: `${apex_ls} -Z ${in} > ${out}.fc` +
217			` && ${apex_sepolicy_tests} -f ${out}.fc --partition ${partition_tag} && touch ${out}`,
218		CommandDeps: []string{"${apex_sepolicy_tests}", "${apex_ls}"},
219		Description: "run apex_sepolicy_tests",
220	}, "partition_tag")
221
222	apexLinkerconfigValidationRule = pctx.StaticRule("apexLinkerconfigValidationRule", blueprint.RuleParams{
223		Command:     `${conv_linker_config} validate --type apex ${image_dir} && touch ${out}`,
224		CommandDeps: []string{"${conv_linker_config}"},
225		Description: "run apex_linkerconfig_validation",
226	}, "image_dir")
227
228	apexHostVerifierRule = pctx.StaticRule("apexHostVerifierRule", blueprint.RuleParams{
229		Command: `${host_apex_verifier} --deapexer=${deapexer} --debugfs=${debugfs_static} ` +
230			`--fsckerofs=${fsck_erofs} --apex=${in} --partition_tag=${partition_tag} && touch ${out}`,
231		CommandDeps: []string{"${host_apex_verifier}", "${deapexer}", "${debugfs_static}", "${fsck_erofs}"},
232		Description: "run host_apex_verifier",
233	}, "partition_tag")
234
235	assembleVintfRule = pctx.StaticRule("assembleVintfRule", blueprint.RuleParams{
236		Command:     `rm -f $out && VINTF_IGNORE_TARGET_FCM_VERSION=true ${assemble_vintf} -i $in -o $out`,
237		CommandDeps: []string{"${assemble_vintf}"},
238		Description: "run assemble_vintf",
239	})
240
241	apexElfCheckerUnwantedRule = pctx.StaticRule("apexElfCheckerUnwantedRule", blueprint.RuleParams{
242		Command:     `${apex_elf_checker} --tool_path ${tool_path} --unwanted ${unwanted} ${in} && touch ${out}`,
243		CommandDeps: []string{"${apex_elf_checker}", "${deapexer}", "${debugfs_static}", "${fsck_erofs}", "${config.ClangBin}/llvm-readelf"},
244		Description: "run apex_elf_checker --unwanted",
245	}, "tool_path", "unwanted")
246)
247
248func (a *apexBundle) buildAconfigFiles(ctx android.ModuleContext) []apexFile {
249	var aconfigFiles android.Paths
250	for _, file := range a.filesInfo {
251		if file.module == nil {
252			continue
253		}
254		if dep, ok := android.OtherModuleProvider(ctx, file.module, android.AconfigPropagatingProviderKey); ok {
255			if len(dep.AconfigFiles) > 0 && dep.AconfigFiles[ctx.ModuleName()] != nil {
256				aconfigFiles = append(aconfigFiles, dep.AconfigFiles[ctx.ModuleName()]...)
257			}
258		}
259
260		validationFlag := ctx.DeviceConfig().AconfigContainerValidation()
261		if validationFlag == "error" || validationFlag == "warning" {
262			android.VerifyAconfigBuildMode(ctx, ctx.ModuleName(), file.module, validationFlag == "error")
263		}
264	}
265	aconfigFiles = android.FirstUniquePaths(aconfigFiles)
266
267	var files []apexFile
268	if len(aconfigFiles) > 0 {
269		apexAconfigFile := android.PathForModuleOut(ctx, "aconfig_flags.pb")
270		ctx.Build(pctx, android.BuildParams{
271			Rule:        aconfig.AllDeclarationsRule,
272			Inputs:      aconfigFiles,
273			Output:      apexAconfigFile,
274			Description: "combine_aconfig_declarations",
275			Args: map[string]string{
276				"cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
277			},
278		})
279		files = append(files, newApexFile(ctx, apexAconfigFile, "aconfig_flags", "etc", etc, nil))
280
281		// To enable fingerprint, we need to have v2 storage files. The default version is 1.
282		storageFilesVersion := 1
283		if ctx.Config().ReleaseFingerprintAconfigPackages() {
284			storageFilesVersion = 2
285		}
286
287		for _, info := range createStorageInfo {
288			outputFile := android.PathForModuleOut(ctx, info.Output_file)
289			ctx.Build(pctx, android.BuildParams{
290				Rule:        aconfig.CreateStorageRule,
291				Inputs:      aconfigFiles,
292				Output:      outputFile,
293				Description: info.Desc,
294				Args: map[string]string{
295					"container":   ctx.ModuleName(),
296					"file_type":   info.File_type,
297					"cache_files": android.JoinPathsWithPrefix(aconfigFiles, "--cache "),
298					"version":     strconv.Itoa(storageFilesVersion),
299				},
300			})
301			files = append(files, newApexFile(ctx, outputFile, info.File_type, "etc", etc, nil))
302		}
303	}
304	return files
305}
306
307// buildManifest creates buile rules to modify the input apex_manifest.json to add information
308// gathered by the build system such as provided/required native libraries. Two output files having
309// different formats are generated. a.manifestJsonOut is JSON format for Q devices, and
310// a.manifest.PbOut is protobuf format for R+ devices.
311// TODO(jiyong): make this to return paths instead of directly storing the paths to apexBundle
312func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) {
313	src := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
314
315	// Put dependency({provide|require}NativeLibs) in apex_manifest.json
316	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
317	requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
318
319	// VNDK APEX name is determined at runtime, so update "name" in apex_manifest
320	optCommands := []string{}
321	if a.vndkApex {
322		apexName := vndkApexNamePrefix + a.vndkVersion()
323		optCommands = append(optCommands, "-v name "+apexName)
324	}
325
326	// Collect jniLibs. Notice that a.filesInfo is already sorted
327	var jniLibs []string
328	for _, fi := range a.filesInfo {
329		if fi.isJniLib && !android.InList(fi.stem(), jniLibs) {
330			jniLibs = append(jniLibs, fi.stem())
331		}
332	}
333	if len(jniLibs) > 0 {
334		optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
335	}
336
337	if android.InList(":vndk", requireNativeLibs) {
338		if _, vndkVersion := a.getImageVariationPair(); vndkVersion != "" {
339			optCommands = append(optCommands, "-v vndkVersion "+vndkVersion)
340		}
341	}
342
343	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
344	defaultVersion := ctx.Config().ReleaseDefaultUpdatableModuleVersion()
345	if a.properties.Variant_version != nil {
346		defaultVersionInt, err := strconv.Atoi(defaultVersion)
347		if err != nil {
348			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to be an int, but got %s", defaultVersion)
349		}
350		if defaultVersionInt%10 != 0 && defaultVersionInt%10 != 9 {
351			ctx.ModuleErrorf("expected RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION to end in a zero or a nine, but got %s", defaultVersion)
352		}
353		variantVersion := []rune(*a.properties.Variant_version)
354		if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' {
355			ctx.PropertyErrorf("variant_version", "expected an integer between 0-9; got %s", *a.properties.Variant_version)
356		}
357		defaultVersionRunes := []rune(defaultVersion)
358		defaultVersionRunes[len(defaultVersion)-1] = []rune(variantVersion)[0]
359		defaultVersion = string(defaultVersionRunes)
360	}
361	if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" {
362		defaultVersion = override
363	}
364	ctx.Build(pctx, android.BuildParams{
365		Rule:   apexManifestRule,
366		Input:  src,
367		Output: manifestJsonFullOut,
368		Args: map[string]string{
369			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
370			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
371			"default_version":   defaultVersion,
372			"opt":               strings.Join(optCommands, " "),
373		},
374	})
375
376	// b/143654022 Q apexd can't understand newly added keys in apex_manifest.json prepare
377	// stripped-down version so that APEX modules built from R+ can be installed to Q
378	minSdkVersion := a.minSdkVersion(ctx)
379	if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
380		a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json")
381		ctx.Build(pctx, android.BuildParams{
382			Rule:   stripApexManifestRule,
383			Input:  manifestJsonFullOut,
384			Output: a.manifestJsonOut,
385		})
386	}
387
388	// From R+, protobuf binary format (.pb) is the standard format for apex_manifest
389	a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb")
390	ctx.Build(pctx, android.BuildParams{
391		Rule:   pbApexManifestRule,
392		Input:  manifestJsonFullOut,
393		Output: a.manifestPbOut,
394	})
395}
396
397// buildFileContexts create build rules to append an entry for apex_manifest.pb to the file_contexts
398// file for this APEX which is either from /systme/sepolicy/apex/<apexname>-file_contexts or from
399// the file_contexts property of this APEX. This is to make sure that the manifest file is correctly
400// labeled as system_file or vendor_apex_metadata_file.
401func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.Path {
402	var fileContexts android.Path
403	var fileContextsDir string
404	isFileContextsModule := false
405	if a.properties.File_contexts == nil {
406		fileContexts = android.PathForSource(ctx, "system/sepolicy/apex", ctx.ModuleName()+"-file_contexts")
407	} else {
408		if m, t := android.SrcIsModuleWithTag(*a.properties.File_contexts); m != "" {
409			isFileContextsModule = true
410			otherModule := android.GetModuleProxyFromPathDep(ctx, m, t)
411			if otherModule != nil {
412				fileContextsDir = ctx.OtherModuleDir(*otherModule)
413			}
414		}
415		fileContexts = android.PathForModuleSrc(ctx, *a.properties.File_contexts)
416	}
417	if fileContextsDir == "" {
418		fileContextsDir = filepath.Dir(fileContexts.String())
419	}
420	fileContextsDir += string(filepath.Separator)
421
422	if a.Platform() {
423		if !strings.HasPrefix(fileContextsDir, "system/sepolicy/") {
424			ctx.PropertyErrorf("file_contexts", "should be under system/sepolicy, but found in  %q", fileContextsDir)
425		}
426	}
427	if !isFileContextsModule && !android.ExistentPathForSource(ctx, fileContexts.String()).Valid() {
428		ctx.PropertyErrorf("file_contexts", "cannot find file_contexts file: %q", fileContexts.String())
429	}
430
431	output := android.PathForModuleOut(ctx, "file_contexts")
432	rule := android.NewRuleBuilder(pctx, ctx)
433
434	labelForRoot := "u:object_r:system_file:s0"
435	labelForManifest := "u:object_r:system_file:s0"
436	if a.SocSpecific() && !a.vndkApex {
437		// APEX on /vendor should label ./ and ./apex_manifest.pb as vendor file.
438		labelForRoot = "u:object_r:vendor_file:s0"
439		labelForManifest = "u:object_r:vendor_apex_metadata_file:s0"
440	}
441	// remove old file
442	rule.Command().Text("rm").FlagWithOutput("-f ", output)
443	// copy file_contexts
444	rule.Command().Text("cat").Input(fileContexts).Text(">>").Output(output)
445	// new line
446	rule.Command().Text("echo").Text(">>").Output(output)
447	// force-label /apex_manifest.pb and /
448	rule.Command().Text("echo").Text("/apex_manifest\\\\.pb").Text(labelForManifest).Text(">>").Output(output)
449	rule.Command().Text("echo").Text("/").Text(labelForRoot).Text(">>").Output(output)
450
451	rule.Build("file_contexts."+a.Name(), "Generate file_contexts")
452	return output
453}
454
455// buildInstalledFilesFile creates a build rule for the installed-files.txt file where the list of
456// files included in this APEX is shown. The text file is dist'ed so that people can see what's
457// included in the APEX without actually downloading and extracting it.
458func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.Path {
459	output := android.PathForModuleOut(ctx, "installed-files.txt")
460	rule := android.NewRuleBuilder(pctx, ctx)
461	rule.Command().
462		Implicit(builtApex).
463		Text("(cd " + imageDir.String() + " ; ").
464		Text("find . \\( -type f -o -type l \\) -printf \"%s %p\\n\") ").
465		Text(" | sort -nr > ").
466		Output(output)
467	rule.Build("installed-files."+a.Name(), "Installed files")
468	return output
469}
470
471// buildBundleConfig creates a build rule for the bundle config file that will control the bundle
472// creation process.
473func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.Path {
474	output := android.PathForModuleOut(ctx, "bundle_config.json")
475
476	type ApkConfig struct {
477		Package_name string `json:"package_name"`
478		Apk_path     string `json:"path"`
479	}
480	config := struct {
481		Compression struct {
482			Uncompressed_glob []string `json:"uncompressed_glob"`
483		} `json:"compression"`
484		Apex_config struct {
485			Apex_embedded_apk_config []ApkConfig `json:"apex_embedded_apk_config,omitempty"`
486		} `json:"apex_config,omitempty"`
487	}{}
488
489	config.Compression.Uncompressed_glob = []string{
490		"apex_payload.img",
491		"apex_manifest.*",
492	}
493
494	// Collect the manifest names and paths of android apps if their manifest names are
495	// overridden.
496	for _, fi := range a.filesInfo {
497		if fi.class != app && fi.class != appSet {
498			continue
499		}
500		packageName := fi.overriddenPackageName
501		if packageName != "" {
502			config.Apex_config.Apex_embedded_apk_config = append(
503				config.Apex_config.Apex_embedded_apk_config,
504				ApkConfig{
505					Package_name: packageName,
506					Apk_path:     fi.path(),
507				})
508		}
509	}
510
511	j, err := json.Marshal(config)
512	if err != nil {
513		panic(fmt.Errorf("error while marshalling to %q: %#v", output, err))
514	}
515
516	android.WriteFileRule(ctx, output, string(j))
517
518	return output
519}
520
521func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path {
522	return java.ManifestFixer(ctx, androidManifestFile, java.ManifestFixerParams{
523		TestOnly: true,
524	})
525}
526
527func shouldApplyAssembleVintf(fi apexFile) bool {
528	isVintfFragment, _ := path.Match("etc/vintf/*", fi.path())
529	_, fromVintfFragmentModule := fi.module.(*android.VintfFragmentModule)
530	return isVintfFragment && !fromVintfFragmentModule
531}
532
533func runAssembleVintf(ctx android.ModuleContext, vintfFragment android.Path) android.Path {
534	processed := android.PathForModuleOut(ctx, "vintf", vintfFragment.Base())
535	ctx.Build(pctx, android.BuildParams{
536		Rule:        assembleVintfRule,
537		Input:       vintfFragment,
538		Output:      processed,
539		Description: "run assemble_vintf for VINTF in APEX",
540	})
541	return processed
542}
543
544// installApexSystemServerFiles installs dexpreopt and dexjar files for system server classpath entries
545// provided by the apex.  They are installed here instead of in library module because there may be multiple
546// variants of the library, generally one for the "main" apex and another with a different min_sdk_version
547// for the Android Go version of the apex.  Both variants would attempt to install to the same locations,
548// and the library variants cannot determine which one should.  The apex module is better equipped to determine
549// if it is "selected".
550// This assumes that the jars produced by different min_sdk_version values are identical, which is currently
551// true but may not be true if the min_sdk_version difference between the variants spans version that changed
552// the dex format.
553func (a *apexBundle) installApexSystemServerFiles(ctx android.ModuleContext) {
554	// If performInstalls is set this module is responsible for creating the install rules.
555	performInstalls := a.GetOverriddenBy() == "" && !a.testApex && a.installable()
556	// TODO(b/234351700): Remove once ART does not have separated debug APEX, or make the selection
557	// explicit in the ART Android.bp files.
558	if ctx.Config().UseDebugArt() {
559		if ctx.ModuleName() == "com.android.art" {
560			performInstalls = false
561		}
562	} else {
563		if ctx.ModuleName() == "com.android.art.debug" {
564			performInstalls = false
565		}
566	}
567
568	psi := android.PrebuiltSelectionInfoMap{}
569	ctx.VisitDirectDeps(func(am android.Module) {
570		if info, exists := android.OtherModuleProvider(ctx, am, android.PrebuiltSelectionInfoProvider); exists {
571			psi = info
572		}
573	})
574
575	if len(psi.GetSelectedModulesForApiDomain(ctx.ModuleName())) > 0 {
576		performInstalls = false
577	}
578
579	for _, fi := range a.filesInfo {
580		for _, install := range fi.systemServerDexpreoptInstalls {
581			var installedFile android.InstallPath
582			if performInstalls {
583				installedFile = ctx.InstallFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
584			} else {
585				// Another module created the install rules, but this module should still depend on
586				// the installed locations.
587				installedFile = install.InstallDirOnDevice.Join(ctx, install.InstallFileOnDevice)
588			}
589			a.extraInstalledFiles = append(a.extraInstalledFiles, installedFile)
590			a.extraInstalledPairs = append(a.extraInstalledPairs, installPair{install.OutputPathOnHost, installedFile})
591			ctx.PackageFile(install.InstallDirOnDevice, install.InstallFileOnDevice, install.OutputPathOnHost)
592		}
593		if performInstalls {
594			for _, dexJar := range fi.systemServerDexJars {
595				// Copy the system server dex jar to a predefined location where dex2oat will find it.
596				android.CopyFileRule(ctx, dexJar,
597					android.PathForOutput(ctx, dexpreopt.SystemServerDexjarsDir, dexJar.Base()))
598			}
599		}
600	}
601}
602
603// buildApex creates build rules to build an APEX using apexer.
604func (a *apexBundle) buildApex(ctx android.ModuleContext) {
605	suffix := imageApexSuffix
606	apexName := a.BaseModuleName()
607
608	////////////////////////////////////////////////////////////////////////////////////////////
609	// Step 1: copy built files to appropriate directories under the image directory
610
611	imageDir := android.PathForModuleOut(ctx, "image"+suffix)
612
613	installSymbolFiles := a.ExportedToMake() && a.installable()
614
615	// set of dependency module:location mappings
616	installMapSet := make(map[string]bool)
617
618	// TODO(jiyong): use the RuleBuilder
619	var copyCommands []string
620	var implicitInputs []android.Path
621	apexDir := android.PathForModuleInPartitionInstall(ctx, "apex", apexName)
622	for _, fi := range a.filesInfo {
623		destPath := imageDir.Join(ctx, fi.path()).String()
624		// Prepare the destination path
625		destPathDir := filepath.Dir(destPath)
626		if fi.class == appSet {
627			copyCommands = append(copyCommands, "rm -rf "+destPathDir)
628		}
629		copyCommands = append(copyCommands, "mkdir -p "+destPathDir)
630
631		installMapPath := fi.builtFile
632
633		// Copy the built file to the directory. But if the symlink optimization is turned
634		// on, place a symlink to the corresponding file in /system partition instead.
635		if a.linkToSystemLib && fi.transitiveDep && fi.availableToPlatform() {
636			pathOnDevice := filepath.Join("/", fi.partition, fi.path())
637			copyCommands = append(copyCommands, "ln -sfn "+pathOnDevice+" "+destPath)
638		} else {
639			// Copy the file into APEX
640			if !a.testApex && shouldApplyAssembleVintf(fi) {
641				// copy the output of assemble_vintf instead of the original
642				vintfFragment := runAssembleVintf(ctx, fi.builtFile)
643				copyCommands = append(copyCommands, "cp -f "+vintfFragment.String()+" "+destPath)
644				implicitInputs = append(implicitInputs, vintfFragment)
645			} else {
646				copyCommands = append(copyCommands, "cp -f "+fi.builtFile.String()+" "+destPath)
647				implicitInputs = append(implicitInputs, fi.builtFile)
648			}
649
650			var installedPath android.InstallPath
651			if fi.class == appSet {
652				// In case of AppSet, we need to copy additional APKs as well. They
653				// are zipped. So we need to unzip them.
654				copyCommands = append(copyCommands,
655					fmt.Sprintf("unzip -qDD -d %s %s", destPathDir,
656						fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs().String()))
657				if installSymbolFiles {
658					installedPath = ctx.InstallFileWithExtraFilesZip(apexDir.Join(ctx, fi.installDir),
659						fi.stem(), fi.builtFile, fi.module.(*java.AndroidAppSet).PackedAdditionalOutputs())
660				}
661			} else {
662				if installSymbolFiles {
663					// store installedPath. symlinks might be created if required.
664					installedPath = ctx.InstallFile(apexDir.Join(ctx, fi.installDir), fi.stem(), fi.builtFile)
665				}
666			}
667
668			// Create additional symlinks pointing the file inside the APEX (if any). Note that
669			// this is independent from the symlink optimization.
670			for _, symlinkPath := range fi.symlinkPaths() {
671				symlinkDest := imageDir.Join(ctx, symlinkPath).String()
672				copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
673				if installSymbolFiles {
674					ctx.InstallSymlink(apexDir.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
675				}
676			}
677
678			installMapPath = installedPath
679		}
680
681		// Copy the test files (if any)
682		for _, d := range fi.dataPaths {
683			// TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible
684			relPath := d.ToRelativeInstallPath()
685			dataDest := imageDir.Join(ctx, fi.apexRelativePath(relPath)).String()
686
687			copyCommands = append(copyCommands, "cp -f "+d.SrcPath.String()+" "+dataDest)
688			implicitInputs = append(implicitInputs, d.SrcPath)
689		}
690
691		installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
692	}
693
694	implicitInputs = append(implicitInputs, a.manifestPbOut)
695
696	if len(installMapSet) > 0 {
697		var installs []string
698		installs = append(installs, android.SortedKeys(installMapSet)...)
699		ctx.SetLicenseInstallMap(installs)
700	}
701
702	////////////////////////////////////////////////////////////////////////////////////////////
703	// Step 1.a: Write the list of files in this APEX to a txt file and compare it against
704	// the allowed list given via the allowed_files property. Build fails when the two lists
705	// differ.
706	//
707	// TODO(jiyong): consider removing this. Nobody other than com.android.apex.cts.shim.* seems
708	// to be using this at this moment. Furthermore, this looks very similar to what
709	// buildInstalledFilesFile does. At least, move this to somewhere else so that this doesn't
710	// hurt readability.
711	if a.overridableProperties.Allowed_files != nil {
712		// Build content.txt
713		var contentLines []string
714		imageContentFile := android.PathForModuleOut(ctx, "content.txt")
715		contentLines = append(contentLines, "./apex_manifest.pb")
716		minSdkVersion := a.minSdkVersion(ctx)
717		if minSdkVersion.EqualTo(android.SdkVersion_Android10) {
718			contentLines = append(contentLines, "./apex_manifest.json")
719		}
720		for _, fi := range a.filesInfo {
721			contentLines = append(contentLines, "./"+fi.path())
722		}
723		sort.Strings(contentLines)
724		android.WriteFileRule(ctx, imageContentFile, strings.Join(contentLines, "\n"))
725		implicitInputs = append(implicitInputs, imageContentFile)
726
727		// Compare content.txt against allowed_files.
728		allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.overridableProperties.Allowed_files))
729		phonyOutput := android.PathForModuleOut(ctx, a.Name()+"-diff-phony-output")
730		ctx.Build(pctx, android.BuildParams{
731			Rule:        diffApexContentRule,
732			Implicits:   implicitInputs,
733			Output:      phonyOutput,
734			Description: "diff apex image content",
735			Args: map[string]string{
736				"allowed_files_file": allowedFilesFile.String(),
737				"image_content_file": imageContentFile.String(),
738				"apex_module_name":   a.Name(),
739			},
740		})
741		implicitInputs = append(implicitInputs, phonyOutput)
742	}
743
744	unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
745	outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
746	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
747
748	////////////////////////////////////////////////////////////////////////////////////
749	// Step 2: create canned_fs_config which encodes filemode,uid,gid of each files
750	// in this APEX. The file will be used by apexer in later steps.
751	cannedFsConfig := a.buildCannedFsConfig(ctx)
752	implicitInputs = append(implicitInputs, cannedFsConfig)
753
754	////////////////////////////////////////////////////////////////////////////////////
755	// Step 3: Prepare option flags for apexer and invoke it to create an unsigned APEX.
756	// TODO(jiyong): use the RuleBuilder
757	optFlags := []string{}
758
759	fileContexts := a.buildFileContexts(ctx)
760	implicitInputs = append(implicitInputs, fileContexts)
761
762	implicitInputs = append(implicitInputs, a.privateKeyFile, a.publicKeyFile)
763	optFlags = append(optFlags, "--pubkey "+a.publicKeyFile.String())
764
765	manifestPackageName := a.getOverrideManifestPackageName(ctx)
766	if manifestPackageName != "" {
767		optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName)
768	}
769
770	androidManifest := a.properties.AndroidManifest.GetOrDefault(ctx, "")
771	if androidManifest != "" {
772		androidManifestFile := android.PathForModuleSrc(ctx, androidManifest)
773
774		if a.testApex {
775			androidManifestFile = markManifestTestOnly(ctx, androidManifestFile)
776		}
777
778		implicitInputs = append(implicitInputs, androidManifestFile)
779		optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String())
780	} else if a.testApex {
781		optFlags = append(optFlags, "--test_only")
782	}
783
784	// Determine target/min sdk version from the context
785	// TODO(jiyong): make this as a function
786	moduleMinSdkVersion := a.minSdkVersion(ctx)
787	minSdkVersion := moduleMinSdkVersion.String()
788
789	// bundletool doesn't understand what "current" is. We need to transform it to
790	// codename
791	if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() {
792		minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
793
794		if useApiFingerprint, fingerprintMinSdkVersion, fingerprintDeps :=
795			java.UseApiFingerprint(ctx); useApiFingerprint {
796			minSdkVersion = fingerprintMinSdkVersion
797			implicitInputs = append(implicitInputs, fingerprintDeps)
798		}
799	}
800	// apex module doesn't have a concept of target_sdk_version, hence for the time
801	// being targetSdkVersion == default targetSdkVersion of the branch.
802	targetSdkVersion := strconv.Itoa(ctx.Config().DefaultAppTargetSdk(ctx).FinalOrFutureInt())
803
804	if useApiFingerprint, fingerprintTargetSdkVersion, fingerprintDeps :=
805		java.UseApiFingerprint(ctx); useApiFingerprint {
806		targetSdkVersion = fingerprintTargetSdkVersion
807		implicitInputs = append(implicitInputs, fingerprintDeps)
808	}
809	optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
810	optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion)
811
812	if a.overridableProperties.Logging_parent != "" {
813		optFlags = append(optFlags, "--logging_parent ", a.overridableProperties.Logging_parent)
814	}
815
816	// Create a NOTICE file, and embed it as an asset file in the APEX.
817	htmlGzNotice := android.PathForModuleOut(ctx, "NOTICE.html.gz")
818	android.BuildNoticeHtmlOutputFromLicenseMetadata(
819		ctx, htmlGzNotice, "", "",
820		[]string{
821			android.PathForModuleInstall(ctx).String() + "/",
822			android.PathForModuleInPartitionInstall(ctx, "apex").String() + "/",
823		})
824	noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
825	builder := android.NewRuleBuilder(pctx, ctx)
826	builder.Command().Text("cp").
827		Input(htmlGzNotice).
828		Output(noticeAssetPath)
829	builder.Build("notice_dir", "Building notice dir")
830	implicitInputs = append(implicitInputs, noticeAssetPath)
831	optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeAssetPath.String()))
832
833	if a.testOnlyShouldSkipPayloadSign() {
834		optFlags = append(optFlags, "--unsigned_payload")
835	}
836
837	if moduleMinSdkVersion == android.SdkVersion_Android10 {
838		implicitInputs = append(implicitInputs, a.manifestJsonOut)
839		optFlags = append(optFlags, "--manifest_json "+a.manifestJsonOut.String())
840	}
841
842	optFlags = append(optFlags, "--payload_fs_type "+a.payloadFsType.string())
843
844	if a.dynamic_common_lib_apex() {
845		ctx.Build(pctx, android.BuildParams{
846			Rule:        DCLAApexRule,
847			Implicits:   implicitInputs,
848			Output:      unsignedOutputFile,
849			Description: "apex",
850			Args: map[string]string{
851				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
852				"image_dir":        imageDir.String(),
853				"copy_commands":    strings.Join(copyCommands, " && "),
854				"manifest":         a.manifestPbOut.String(),
855				"file_contexts":    fileContexts.String(),
856				"canned_fs_config": cannedFsConfig.String(),
857				"key":              a.privateKeyFile.String(),
858				"opt_flags":        strings.Join(optFlags, " "),
859			},
860		})
861	} else {
862		ctx.Build(pctx, android.BuildParams{
863			Rule:        apexRule,
864			Implicits:   implicitInputs,
865			Output:      unsignedOutputFile,
866			Description: "apex",
867			Args: map[string]string{
868				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
869				"image_dir":        imageDir.String(),
870				"copy_commands":    strings.Join(copyCommands, " && "),
871				"manifest":         a.manifestPbOut.String(),
872				"file_contexts":    fileContexts.String(),
873				"canned_fs_config": cannedFsConfig.String(),
874				"key":              a.privateKeyFile.String(),
875				"opt_flags":        strings.Join(optFlags, " "),
876			},
877		})
878	}
879
880	// TODO(jiyong): make the two rules below as separate functions
881	apexProtoFile := android.PathForModuleOut(ctx, a.Name()+".pb"+suffix)
882	bundleModuleFile := android.PathForModuleOut(ctx, a.Name()+suffix+"-base.zip")
883	a.bundleModuleFile = bundleModuleFile
884
885	ctx.Build(pctx, android.BuildParams{
886		Rule:        apexProtoConvertRule,
887		Input:       unsignedOutputFile,
888		Output:      apexProtoFile,
889		Description: "apex proto convert",
890	})
891
892	implicitInputs = append(implicitInputs, unsignedOutputFile)
893
894	// Run coverage analysis
895	apisUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.txt")
896	ctx.Build(pctx, android.BuildParams{
897		Rule:        generateAPIsUsedbyApexRule,
898		Implicits:   implicitInputs,
899		Description: "coverage",
900		Output:      apisUsedbyOutputFile,
901		Args: map[string]string{
902			"image_dir": imageDir.String(),
903			"readelf":   "${config.ClangBin}/llvm-readelf",
904		},
905	})
906	a.nativeApisUsedByModuleFile = apisUsedbyOutputFile
907
908	var nativeLibNames []string
909	for _, f := range a.filesInfo {
910		if f.class == nativeSharedLib {
911			nativeLibNames = append(nativeLibNames, f.stem())
912		}
913	}
914	apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt")
915	rb := android.NewRuleBuilder(pctx, ctx)
916	rb.Command().
917		Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")).
918		Output(apisBackedbyOutputFile).
919		Flags(nativeLibNames)
920	rb.Build("ndk_backedby_list", "Generate API libraries backed by Apex")
921	a.nativeApisBackedByModuleFile = apisBackedbyOutputFile
922
923	var javaLibOrApkPath []android.Path
924	for _, f := range a.filesInfo {
925		if f.class == javaSharedLib || f.class == app {
926			javaLibOrApkPath = append(javaLibOrApkPath, f.builtFile)
927		}
928	}
929	javaApiUsedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_using.xml")
930	javaUsedByRule := android.NewRuleBuilder(pctx, ctx)
931	javaUsedByRule.Command().
932		Tool(android.PathForSource(ctx, "build/soong/scripts/gen_java_usedby_apex.sh")).
933		BuiltTool("dexdeps").
934		Output(javaApiUsedbyOutputFile).
935		Inputs(javaLibOrApkPath)
936	javaUsedByRule.Build("java_usedby_list", "Generate Java APIs used by Apex")
937	a.javaApisUsedByModuleFile = javaApiUsedbyOutputFile
938
939	bundleConfig := a.buildBundleConfig(ctx)
940
941	var abis []string
942	for _, target := range ctx.MultiTargets() {
943		if len(target.Arch.Abi) > 0 {
944			abis = append(abis, target.Arch.Abi[0])
945		}
946	}
947
948	abis = android.FirstUniqueStrings(abis)
949
950	ctx.Build(pctx, android.BuildParams{
951		Rule:        apexBundleRule,
952		Input:       apexProtoFile,
953		Implicit:    bundleConfig,
954		Output:      a.bundleModuleFile,
955		Description: "apex bundle module",
956		Args: map[string]string{
957			"abi":    strings.Join(abis, "."),
958			"config": bundleConfig.String(),
959		},
960	})
961
962	////////////////////////////////////////////////////////////////////////////////////
963	// Step 4: Sign the APEX using signapk
964	signedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix)
965
966	pem, key := a.getCertificateAndPrivateKey(ctx)
967	rule := java.Signapk
968	args := map[string]string{
969		"certificates": pem.String() + " " + key.String(),
970		"flags":        "-a 4096 --align-file-size", //alignment
971	}
972	implicits := android.Paths{pem, key}
973	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
974		rule = java.SignapkRE
975		args["implicits"] = strings.Join(implicits.Strings(), ",")
976		args["outCommaList"] = signedOutputFile.String()
977	}
978	var validations android.Paths
979	validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile, imageDir))
980	if !a.skipValidation(apexSepolicyTests) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
981		validations = append(validations, runApexSepolicyTests(ctx, a, unsignedOutputFile))
982	}
983	if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 {
984		validations = append(validations,
985			runApexElfCheckerUnwanted(ctx, unsignedOutputFile, a.properties.Unwanted_transitive_deps))
986	}
987	if !a.skipValidation(hostApexVerifier) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) {
988		validations = append(validations, runApexHostVerifier(ctx, a, unsignedOutputFile))
989	}
990	ctx.Build(pctx, android.BuildParams{
991		Rule:        rule,
992		Description: "signapk",
993		Output:      signedOutputFile,
994		Input:       unsignedOutputFile,
995		Implicits:   implicits,
996		Args:        args,
997		Validations: validations,
998	})
999	if suffix == imageApexSuffix {
1000		a.outputApexFile = signedOutputFile
1001	}
1002	a.outputFile = signedOutputFile
1003
1004	if ctx.ModuleDir() != "system/apex/apexd/apexd_testdata" && a.testOnlyShouldForceCompression() {
1005		ctx.PropertyErrorf("test_only_force_compression", "not available")
1006		return
1007	}
1008
1009	installSuffix := suffix
1010	a.setCompression(ctx)
1011	if a.isCompressed {
1012		unsignedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix+".unsigned")
1013
1014		compressRule := android.NewRuleBuilder(pctx, ctx)
1015		compressRule.Command().
1016			Text("rm").
1017			FlagWithOutput("-f ", unsignedCompressedOutputFile)
1018		compressRule.Command().
1019			BuiltTool("apex_compression_tool").
1020			Flag("compress").
1021			FlagWithArg("--apex_compression_tool ", outHostBinDir+":"+prebuiltSdkToolsBinDir).
1022			FlagWithInput("--input ", signedOutputFile).
1023			FlagWithOutput("--output ", unsignedCompressedOutputFile)
1024		compressRule.Build("compressRule", "Generate unsigned compressed APEX file")
1025
1026		signedCompressedOutputFile := android.PathForModuleOut(ctx, a.Name()+imageCapexSuffix)
1027		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
1028			args["outCommaList"] = signedCompressedOutputFile.String()
1029		}
1030		ctx.Build(pctx, android.BuildParams{
1031			Rule:        rule,
1032			Description: "sign compressedApex",
1033			Output:      signedCompressedOutputFile,
1034			Input:       unsignedCompressedOutputFile,
1035			Implicits:   implicits,
1036			Args:        args,
1037		})
1038		a.outputFile = signedCompressedOutputFile
1039		installSuffix = imageCapexSuffix
1040	}
1041
1042	if !a.installable() {
1043		a.SkipInstall()
1044	}
1045
1046	installDeps := slices.Concat(a.compatSymlinks, a.extraInstalledFiles)
1047	// Install to $OUT/soong/{target,host}/.../apex.
1048	a.installedFile = ctx.InstallFile(a.installDir, a.Name()+installSuffix, a.outputFile, installDeps...)
1049
1050	// installed-files.txt is dist'ed
1051	a.installedFilesFile = a.buildInstalledFilesFile(ctx, a.outputFile, imageDir)
1052
1053	a.apexKeysPath = writeApexKeys(ctx, a)
1054}
1055
1056// getCertificateAndPrivateKey retrieves the cert and the private key that will be used to sign
1057// the zip container of this APEX. See the description of the 'certificate' property for how
1058// the cert and the private key are found.
1059func (a *apexBundle) getCertificateAndPrivateKey(ctx android.PathContext) (pem, key android.Path) {
1060	if a.containerCertificateFile != nil {
1061		return a.containerCertificateFile, a.containerPrivateKeyFile
1062	}
1063
1064	cert := String(a.overridableProperties.Certificate)
1065	if cert == "" {
1066		return ctx.Config().DefaultAppCertificate(ctx)
1067	}
1068
1069	defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
1070	pem = defaultDir.Join(ctx, cert+".x509.pem")
1071	key = defaultDir.Join(ctx, cert+".pk8")
1072	return pem, key
1073}
1074
1075func (a *apexBundle) getOverrideManifestPackageName(ctx android.ModuleContext) string {
1076	// For VNDK APEXes, check "com.android.vndk" in PRODUCT_MANIFEST_PACKAGE_NAME_OVERRIDES
1077	// to see if it should be overridden because their <apex name> is dynamically generated
1078	// according to its VNDK version.
1079	if a.vndkApex {
1080		overrideName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(vndkApexName)
1081		if overridden {
1082			return overrideName + ".v" + a.vndkVersion()
1083		}
1084		return ""
1085	}
1086	packageNameFromProp := a.overridableProperties.Package_name.GetOrDefault(ctx, "")
1087	if packageNameFromProp != "" {
1088		return packageNameFromProp
1089	}
1090	manifestPackageName, overridden := ctx.DeviceConfig().OverrideManifestPackageNameFor(ctx.ModuleName())
1091	if overridden {
1092		return manifestPackageName
1093	}
1094	return ""
1095}
1096
1097func (a *apexBundle) buildApexDependencyInfo(ctx android.ModuleContext) {
1098	if a.properties.IsCoverageVariant {
1099		// Otherwise, we will have duplicated rules for coverage and
1100		// non-coverage variants of the same APEX
1101		return
1102	}
1103
1104	depInfos := android.DepNameToDepInfoMap{}
1105	a.WalkPayloadDeps(ctx, func(ctx android.BaseModuleContext, from, to android.ModuleProxy, externalDep bool) bool {
1106		if from.Name() == to.Name() {
1107			// This can happen for cc.reuseObjTag. We are not interested in tracking this.
1108			// As soon as the dependency graph crosses the APEX boundary, don't go further.
1109			return !externalDep
1110		}
1111
1112		// Skip dependencies that are only available to APEXes; they are developed with updatability
1113		// in mind and don't need manual approval.
1114		if android.OtherModulePointerProviderOrDefault(ctx, to, android.CommonModuleInfoProvider).NotAvailableForPlatform {
1115			return !externalDep
1116		}
1117
1118		depTag := ctx.OtherModuleDependencyTag(to)
1119		// Check to see if dependency been marked to skip the dependency check
1120		if skipDepCheck, ok := depTag.(android.SkipApexAllowedDependenciesCheck); ok && skipDepCheck.SkipApexAllowedDependenciesCheck() {
1121			return !externalDep
1122		}
1123
1124		if info, exists := depInfos[to.Name()]; exists {
1125			if !android.InList(from.Name(), info.From) {
1126				info.From = append(info.From, from.Name())
1127			}
1128			info.IsExternal = info.IsExternal && externalDep
1129			depInfos[to.Name()] = info
1130		} else {
1131			toMinSdkVersion := "(no version)"
1132			if info, ok := android.OtherModuleProvider(ctx, to, android.CommonModuleInfoProvider); ok &&
1133				!info.MinSdkVersion.IsPlatform && info.MinSdkVersion.ApiLevel != nil {
1134				toMinSdkVersion = info.MinSdkVersion.ApiLevel.String()
1135			}
1136			depInfos[to.Name()] = android.ApexModuleDepInfo{
1137				To:            to.Name(),
1138				From:          []string{from.Name()},
1139				IsExternal:    externalDep,
1140				MinSdkVersion: toMinSdkVersion,
1141			}
1142		}
1143
1144		// As soon as the dependency graph crosses the APEX boundary, don't go further.
1145		return !externalDep
1146	})
1147
1148	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(ctx).String(), depInfos)
1149
1150	ctx.Build(pctx, android.BuildParams{
1151		Rule:   android.Phony,
1152		Output: android.PathForPhony(ctx, a.Name()+"-deps-info"),
1153		Inputs: []android.Path{
1154			a.ApexBundleDepsInfo.FullListPath(),
1155			a.ApexBundleDepsInfo.FlatListPath(),
1156		},
1157	})
1158}
1159
1160func (a *apexBundle) buildLintReports(ctx android.ModuleContext) {
1161	depSetsBuilder := java.NewLintDepSetBuilder()
1162	for _, fi := range a.filesInfo {
1163		if fi.lintInfo != nil {
1164			depSetsBuilder.Transitive(fi.lintInfo)
1165		}
1166	}
1167
1168	depSets := depSetsBuilder.Build()
1169	var validations android.Paths
1170
1171	if a.checkStrictUpdatabilityLinting(ctx) {
1172		baselines := depSets.Baseline.ToList()
1173		if len(baselines) > 0 {
1174			outputFile := java.VerifyStrictUpdatabilityChecks(ctx, baselines)
1175			validations = append(validations, outputFile)
1176		}
1177	}
1178
1179	a.lintReports = java.BuildModuleLintReportZips(ctx, depSets, validations)
1180}
1181
1182func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext) android.Path {
1183	var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
1184	var executablePaths []string // this also includes dirs
1185	var appSetDirs []string
1186	appSetFiles := make(map[string]android.Path)
1187	for _, f := range a.filesInfo {
1188		pathInApex := f.path()
1189		if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
1190			executablePaths = append(executablePaths, pathInApex)
1191			for _, d := range f.dataPaths {
1192				rel := d.ToRelativeInstallPath()
1193				readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, rel))
1194			}
1195			for _, s := range f.symlinks {
1196				executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
1197			}
1198		} else if f.class == appSet {
1199			// base APK
1200			readOnlyPaths = append(readOnlyPaths, pathInApex)
1201			// Additional APKs
1202			appSetDirs = append(appSetDirs, f.installDir)
1203			appSetFiles[f.installDir] = f.module.(*java.AndroidAppSet).PackedAdditionalOutputs()
1204		} else {
1205			readOnlyPaths = append(readOnlyPaths, pathInApex)
1206		}
1207		dir := f.installDir
1208		for !android.InList(dir, executablePaths) && dir != "" {
1209			executablePaths = append(executablePaths, dir)
1210			dir, _ = filepath.Split(dir) // move up to the parent
1211			if len(dir) > 0 {
1212				// remove trailing slash
1213				dir = dir[:len(dir)-1]
1214			}
1215		}
1216	}
1217	sort.Strings(readOnlyPaths)
1218	sort.Strings(executablePaths)
1219	sort.Strings(appSetDirs)
1220
1221	cannedFsConfig := android.PathForModuleOut(ctx, "canned_fs_config")
1222	builder := android.NewRuleBuilder(pctx, ctx)
1223	cmd := builder.Command()
1224	cmd.Text("(")
1225	cmd.Text("echo '/ 1000 1000 0755';")
1226	for _, p := range readOnlyPaths {
1227		cmd.Textf("echo '/%s 1000 1000 0644';", p)
1228	}
1229	for _, p := range executablePaths {
1230		cmd.Textf("echo '/%s 0 2000 0755';", p)
1231	}
1232	for _, dir := range appSetDirs {
1233		cmd.Textf("echo '/%s 0 2000 0755';", dir)
1234		file := appSetFiles[dir]
1235		cmd.Text("zipinfo -1").Input(file).Textf(`| sed "s:\(.*\):/%s/\1 1000 1000 0644:";`, dir)
1236	}
1237	// Custom fs_config is "appended" to the last so that entries from the file are preferred
1238	// over default ones set above.
1239	customFsConfig := a.properties.Canned_fs_config.GetOrDefault(ctx, "")
1240	if customFsConfig != "" {
1241		cmd.Text("cat").Input(android.PathForModuleSrc(ctx, customFsConfig))
1242	}
1243	cmd.Text(")").FlagWithOutput("> ", cannedFsConfig)
1244	builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName()))
1245
1246	return cannedFsConfig
1247}
1248
1249func runApexLinkerconfigValidation(ctx android.ModuleContext, apexFile android.Path, imageDir android.Path) android.Path {
1250	timestamp := android.PathForModuleOut(ctx, "apex_linkerconfig_validation.timestamp")
1251	ctx.Build(pctx, android.BuildParams{
1252		Rule:   apexLinkerconfigValidationRule,
1253		Input:  apexFile,
1254		Output: timestamp,
1255		Args: map[string]string{
1256			"image_dir": imageDir.String(),
1257		},
1258	})
1259	return timestamp
1260}
1261
1262// Can't use PartitionTag() because PartitionTag() returns the partition this module is actually
1263// installed (e.g. PartitionTag() may return "system" for vendor apex when vendor is linked to /system/vendor)
1264func (a *apexBundle) partition() string {
1265	if a.SocSpecific() {
1266		return "vendor"
1267	} else if a.DeviceSpecific() {
1268		return "odm"
1269	} else if a.ProductSpecific() {
1270		return "product"
1271	} else if a.SystemExtSpecific() {
1272		return "system_ext"
1273	} else {
1274		return "system"
1275	}
1276}
1277
1278// Runs apex_sepolicy_tests
1279//
1280// $ apex-ls -Z {apex_file} > {file_contexts}
1281// $ apex_sepolicy_tests -f {file_contexts}
1282func runApexSepolicyTests(ctx android.ModuleContext, a *apexBundle, apexFile android.Path) android.Path {
1283	timestamp := android.PathForModuleOut(ctx, "apex_sepolicy_tests.timestamp")
1284	ctx.Build(pctx, android.BuildParams{
1285		Rule:   apexSepolicyTestsRule,
1286		Input:  apexFile,
1287		Output: timestamp,
1288		Args: map[string]string{
1289			"partition_tag": a.partition(),
1290		},
1291	})
1292	return timestamp
1293}
1294
1295func runApexElfCheckerUnwanted(ctx android.ModuleContext, apexFile android.Path, unwanted []string) android.Path {
1296	timestamp := android.PathForModuleOut(ctx, "apex_elf_unwanted.timestamp")
1297	ctx.Build(pctx, android.BuildParams{
1298		Rule:   apexElfCheckerUnwantedRule,
1299		Input:  apexFile,
1300		Output: timestamp,
1301		Args: map[string]string{
1302			"unwanted":  android.JoinWithSuffixAndSeparator(unwanted, ".so", ":"),
1303			"tool_path": ctx.Config().HostToolPath(ctx, "").String() + ":${config.ClangBin}",
1304		},
1305	})
1306	return timestamp
1307}
1308
1309func runApexHostVerifier(ctx android.ModuleContext, a *apexBundle, apexFile android.Path) android.Path {
1310	timestamp := android.PathForModuleOut(ctx, "host_apex_verifier.timestamp")
1311	ctx.Build(pctx, android.BuildParams{
1312		Rule:   apexHostVerifierRule,
1313		Input:  apexFile,
1314		Output: timestamp,
1315		Args: map[string]string{
1316			"partition_tag": a.partition(),
1317		},
1318	})
1319	return timestamp
1320}
1321