1# Copyright (C) 2021 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
15load("@bazel_skylib//lib:paths.bzl", "paths")
16load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
17load("@soong_injection//apex_toolchain:constants.bzl", "default_manifest_version")
18load("//build/bazel/platforms:platform_utils.bzl", "platforms")
19load("//build/bazel/rules:build_fingerprint.bzl", "BuildFingerprintInfo")
20load("//build/bazel/rules:common.bzl", "get_dep_targets")
21load("//build/bazel/rules:metadata.bzl", "MetadataFileInfo")
22load("//build/bazel/rules:prebuilt_file.bzl", "PrebuiltFileInfo")
23load("//build/bazel/rules:sh_binary.bzl", "ShBinaryInfo")
24load("//build/bazel/rules:toolchain_utils.bzl", "verify_toolchain_exists")
25load("//build/bazel/rules/android:android_app_certificate.bzl", "AndroidAppCertificateInfo", "NoAndroidAppCertificateInfo", "android_app_certificate_with_default_cert")
26load("//build/bazel/rules/android:manifest_fixer.bzl", "manifest_fixer")
27load("//build/bazel/rules/apex:cc.bzl", "ApexCcInfo", "ApexCcMkInfo", "apex_cc_aspect")
28load("//build/bazel/rules/apex:sdk_versions.bzl", "maybe_override_min_sdk_version")
29load("//build/bazel/rules/apex:transition.bzl", "apex_transition", "shared_lib_transition_32", "shared_lib_transition_64")
30load("//build/bazel/rules/cc:clang_tidy.bzl", "collect_deps_clang_tidy_info")
31load("//build/bazel/rules/cc:stripped_cc_common.bzl", "CcUnstrippedInfo", "StrippedCcBinaryInfo")
32load("//build/bazel/rules/common:api.bzl", "api")
33load(
34    "//build/bazel/rules/license:license_aspect.bzl",
35    "RuleLicensedDependenciesInfo",
36    "license_aspect",
37    "license_map",
38    "license_map_notice_files",
39    "license_map_to_json",
40)
41load(":apex_available.bzl", "ApexAvailableInfo", "apex_available_aspect")
42load(":apex_deps_validation.bzl", "ApexDepsInfo", "apex_deps_validation_aspect", "validate_apex_deps")
43load(":apex_info.bzl", "ApexInfo", "ApexMkInfo")
44load(":apex_key.bzl", "ApexKeyInfo")
45load(":bundle.bzl", "apex_zip_files")
46
47def _create_file_mapping(ctx):
48    """Create a file mapping for the APEX filesystem image.
49
50    This returns a Dict[File, str] where the dictionary keys are paths in the
51    apex staging dir / filesystem image, and the values are the files and other
52    metadata that should be installed there.
53
54    It also returns other data structures, such as:
55    - requires: libs that this apex depend on from other apex or the platform
56    - provides: libs that this apex provide to other apex or the platform
57    - make_modules_to_install: make module names of libs that needs to be installed onto the platform in a bundled build (LOCAL_REQUIRED_MODULES)
58    - make_files_info: metadata about this apex's payload to be used for other packaging steps.
59    """
60
61    # Dictionary mapping from paths in the apex to the files and associated metadata to be put there
62    file_mapping = {}
63    requires = {}
64    provides = {}
65    make_modules_to_install = {}
66    metadata_file_mapping = {}
67
68    # Generate a str -> str dictionary to define Make modules and variables for the
69    # packaging step in a mixed build. This is necessary as long as there are
70    # Make-derived actions that operate on bazel's outputs. If we move all Make
71    # packaging actions to Bazel, there's no need for this data flow.
72    make_files_info = {}
73
74    arch = platforms.get_target_arch(ctx.attr._platform_utils)
75    is_target_64_bit = platforms.get_target_bitness(ctx.attr._platform_utils) == 64
76
77    def add_file_mapping(install_dir, basename, bazel_file, klass, owner, arch = None, unstripped = None, metadata_file = None):
78        installed_path = paths.join(install_dir, basename)
79        if installed_path in file_mapping and file_mapping[installed_path] != bazel_file:
80            # TODO: we should figure this out and make it a failure
81            print("Warning: %s in this apex is already installed to %s, overwriting it with %s" %
82                  (file_mapping[installed_path].path, installed_path, bazel_file.path))
83        file_mapping[installed_path] = bazel_file
84        metadata_file_mapping[installed_path] = metadata_file
85
86        files_info = {
87            "built_file": bazel_file.path,
88            "class": klass,
89            "install_dir": install_dir,
90            "basename": basename,
91            "package": owner.package,
92            "make_module_name": owner.name,
93            "arch": arch,
94        }
95        if unstripped:
96            files_info["unstripped_built_file"] = unstripped.path
97        make_files_info[installed_path] = files_info
98
99    def _add_lib_files(directory, libs, arch):
100        for dep in libs:
101            apex_cc_info = dep[ApexCcInfo]
102            for lib in apex_cc_info.requires_native_libs.to_list():
103                requires[lib] = True
104            for lib in apex_cc_info.provides_native_libs.to_list():
105                provides[lib] = True
106            for lib_file in apex_cc_info.transitive_shared_libs.to_list():
107                stripped = lib_file.stripped
108                unstripped = lib_file.unstripped
109                add_file_mapping(
110                    directory,
111                    stripped.basename,
112                    stripped,
113                    "nativeSharedLib",
114                    lib_file.generating_rule_owner,
115                    arch = arch,
116                    unstripped = unstripped,
117                    metadata_file = lib_file.metadata_file,
118                )
119
120            # For bundled builds.
121            apex_cc_mk_info = dep[ApexCcMkInfo]
122            for mk_module in apex_cc_mk_info.make_modules_to_install.to_list():
123                make_modules_to_install[mk_module] = True
124
125    if is_target_64_bit:
126        _add_lib_files("lib64", ctx.attr.native_shared_libs_64, arch)
127
128        secondary_arch = platforms.get_target_secondary_arch(ctx.attr._platform_utils)
129        if secondary_arch:
130            _add_lib_files("lib", ctx.attr.native_shared_libs_32, secondary_arch)
131    else:
132        _add_lib_files("lib", ctx.attr.native_shared_libs_32, arch)
133
134    backing_libs = []
135    for lib in file_mapping.values():
136        if lib.basename not in backing_libs:
137            backing_libs.append(lib.basename)
138    backing_libs = sorted(backing_libs)
139
140    # Handle prebuilts
141    for dep in ctx.attr.prebuilts:
142        prebuilt_file_info = dep[PrebuiltFileInfo]
143        if prebuilt_file_info.filename:
144            filename = prebuilt_file_info.filename
145        else:
146            filename = dep.label.name
147        add_file_mapping(
148            prebuilt_file_info.dir,
149            filename,
150            prebuilt_file_info.src,
151            "etc",
152            dep.label,
153            arch = arch,
154            metadata_file = dep[MetadataFileInfo].metadata_file,
155        )
156
157    # Handle binaries
158    for dep in ctx.attr.binaries:
159        if ShBinaryInfo in dep:
160            # sh_binary requires special handling on directory/filename construction.
161            sh_binary_info = dep[ShBinaryInfo]
162            if sh_binary_info:
163                directory = "bin"
164                if sh_binary_info.sub_dir:
165                    directory = paths.join("bin", sh_binary_info.sub_dir)
166
167                filename = dep.label.name
168                if sh_binary_info.filename:
169                    filename = sh_binary_info.filename
170
171                add_file_mapping(
172                    directory,
173                    filename,
174                    dep[DefaultInfo].files_to_run.executable,
175                    "shBinary",
176                    dep.label,
177                    arch = arch,
178                    metadata_file = dep[MetadataFileInfo].metadata_file,
179                )
180        elif ApexCcInfo in dep:
181            # cc_binary just takes the final executable from the runfiles.
182            add_file_mapping(
183                "bin",
184                dep.label.name,
185                dep[DefaultInfo].files_to_run.executable,
186                "nativeExecutable",
187                dep.label,
188                arch,
189                unstripped = dep[CcUnstrippedInfo].unstripped[0].files.to_list()[0],
190                metadata_file = dep[MetadataFileInfo].metadata_file,
191            )
192
193            # Add transitive shared lib deps of apex binaries to the apex.
194            if is_target_64_bit:
195                _add_lib_files("lib64", [dep], arch)
196            else:
197                _add_lib_files("lib", [dep], arch)
198
199    return (
200        file_mapping,
201        sorted(requires.keys(), key = lambda x: x.name),  # sort on just the name of the target, not package
202        sorted(provides.keys(), key = lambda x: x.name),
203        backing_libs,
204        sorted(make_modules_to_install),
205        sorted(make_files_info.values(), key = lambda x: ":".join([x["package"], x["make_module_name"], x["arch"]])),
206        metadata_file_mapping,
207    )
208
209def _add_so(label):
210    return label.name + ".so"
211
212def _add_apex_manifest_information(
213        ctx,
214        apex_toolchain,
215        requires_native_libs,
216        provides_native_libs):
217    apex_manifest_json = ctx.file.manifest
218    apex_manifest_full_json = ctx.actions.declare_file(ctx.attr.name + "_apex_manifest_full.json")
219
220    args = ctx.actions.args()
221    args.add(apex_manifest_json)
222    args.add_all(["-a", "requireNativeLibs"])
223    args.add_all(requires_native_libs, map_each = _add_so)  # e.g. turn "//foo/bar:baz" to "baz.so"
224    args.add_all(["-a", "provideNativeLibs"])
225    args.add_all(provides_native_libs, map_each = _add_so)
226
227    manifest_version = default_manifest_version
228    if ctx.attr.variant_version != "":
229        manifest_version = manifest_version + int(ctx.attr.variant_version)
230    override_manifest_version = ctx.attr._override_apex_manifest_default_version[BuildSettingInfo].value
231    if override_manifest_version:
232        manifest_version = override_manifest_version
233    args.add_all(["-se", "version", "0", manifest_version])
234
235    # TODO: support other optional flags like -v name and -a jniLibs
236    args.add_all(["-o", apex_manifest_full_json])
237
238    ctx.actions.run(
239        inputs = [apex_manifest_json],
240        outputs = [apex_manifest_full_json],
241        executable = apex_toolchain.jsonmodify[DefaultInfo].files_to_run,
242        arguments = [args],
243        mnemonic = "ApexManifestModify",
244    )
245
246    return apex_manifest_full_json
247
248# conv_apex_manifest - Convert the JSON APEX manifest to protobuf, which is needed by apexer.
249def _convert_apex_manifest_json_to_pb(ctx, apex_toolchain, apex_manifest_json):
250    apex_manifest_pb = ctx.actions.declare_file(ctx.attr.name + "_apex_manifest.pb")
251
252    ctx.actions.run(
253        outputs = [apex_manifest_pb],
254        inputs = [apex_manifest_json],
255        executable = apex_toolchain.conv_apex_manifest[DefaultInfo].files_to_run,
256        arguments = [
257            "proto",
258            apex_manifest_json.path,
259            "-o",
260            apex_manifest_pb.path,
261        ],
262        mnemonic = "ConvApexManifest",
263    )
264
265    return apex_manifest_pb
266
267def _generate_canned_fs_config(ctx, filepaths):
268    """Generate filesystem config.
269
270    This encodes the filemode, uid, and gid of each file in the APEX,
271    including apex_manifest.json and apex_manifest.pb.
272    NOTE: every file must have an entry.
273    """
274
275    # Ensure all paths don't start with / and are normalized
276    filepaths = [paths.normalize(f).lstrip("/") for f in filepaths]
277
278    # Soong also sorts the config lines to be consistent with bazel
279    filepaths = sorted([f for f in filepaths if f])
280
281    # First, collect a set of all the directories in the apex
282    apex_subdirs_set = {}
283    for f in filepaths:
284        d = paths.dirname(f)
285        if d != "":  # The root dir is handled manually below
286            # Make sure all the parent dirs of the current subdir are in the set, too
287            dirs = d.split("/")
288            for i in range(1, len(dirs) + 1):
289                apex_subdirs_set["/".join(dirs[:i])] = True
290
291    config_lines = []
292    config_lines.append("/ 1000 1000 0755")
293    config_lines.append("/apex_manifest.json 1000 1000 0644")
294    config_lines.append("/apex_manifest.pb 1000 1000 0644")
295
296    # Readonly if not executable. filepaths is already sorted.
297    config_lines += ["/" + f + " 1000 1000 0644" for f in filepaths if not f.startswith("bin/")]
298
299    # Mark all binaries as executable. filepaths is already sorted.
300    config_lines += ["/" + f + " 0 2000 0755" for f in filepaths if f.startswith("bin/")]
301
302    # All directories have the same permission.
303    config_lines += ["/" + d + " 0 2000 0755" for d in sorted(apex_subdirs_set.keys())]
304
305    output = ctx.actions.declare_file(ctx.attr.name + "_canned_fs_config.txt")
306
307    config_lines = "\n".join(config_lines) + "\n"
308    ctx.actions.write(output, config_lines)
309
310    if ctx.attr.canned_fs_config:
311        # Append the custom fs config content to the existing file
312        combined_output = ctx.actions.declare_file(ctx.attr.name + "_combined_canned_fs_config.txt")
313        ctx.actions.run_shell(
314            inputs = [ctx.file.canned_fs_config, output],
315            outputs = [combined_output],
316            mnemonic = "AppendCustomFsConfig",
317            command = "cat {i} {canned_fs_config} > {o}".format(
318                i = output.path,
319                o = combined_output.path,
320                canned_fs_config = ctx.file.canned_fs_config.path,
321            ),
322        )
323        output = combined_output
324
325    return output
326
327# Append an entry for apex_manifest.pb to the file_contexts file for this APEX,
328# which is either from /system/sepolicy/apex/<apexname>-file_contexts (set in
329# the apex macro) or custom file_contexts attribute value of this APEX. This
330# ensures that the manifest file is correctly labeled as system_file.
331def _generate_file_contexts(ctx):
332    file_contexts = ctx.actions.declare_file(ctx.attr.name + "-file_contexts")
333
334    ctx.actions.run_shell(
335        inputs = [ctx.file.file_contexts],
336        outputs = [file_contexts],
337        mnemonic = "GenerateApexFileContexts",
338        command = "cat {i} > {o} && echo >> {o} && echo /apex_manifest\\\\.pb u:object_r:system_file:s0 >> {o} && echo / u:object_r:system_file:s0 >> {o}"
339            .format(i = ctx.file.file_contexts.path, o = file_contexts.path),
340    )
341
342    return file_contexts
343
344def _mark_manifest_as_test_only(ctx, apex_toolchain):
345    android_manifest = ctx.file.android_manifest
346    dir_name = android_manifest.dirname
347    base_name = android_manifest.basename
348    android_manifest_fixed = ctx.actions.declare_file(paths.join(dir_name, "manifest_fixer", base_name))
349    manifest_fixer.fix(
350        ctx,
351        manifest_fixer = apex_toolchain.manifest_fixer[DefaultInfo].files_to_run,
352        in_manifest = android_manifest,
353        out_manifest = android_manifest_fixed,
354        mnemonic = "MarkAndroidManifestTestOnly",
355        test_only = True,
356    )
357    return android_manifest_fixed
358
359# Generate <APEX>_backing.txt file which lists all libraries used by the APEX.
360def _generate_apex_backing_file(ctx, backing_libs):
361    backing_file = ctx.actions.declare_file(ctx.attr.name + "_backing.txt")
362    ctx.actions.write(
363        output = backing_file,
364        content = " ".join(backing_libs) + "\n",
365    )
366    return backing_file
367
368# Generate installed-files.txt which lists all installed files by the APEX.
369def _generate_installed_files_list(ctx, file_mapping):
370    installed_files = ctx.actions.declare_file(ctx.attr.name + "-installed-files.txt")
371    command = []
372    for device_path, bazel_file in file_mapping.items():
373        command.append("echo $(stat -L -c %%s %s) ./%s" % (bazel_file.path, device_path))
374    ctx.actions.run_shell(
375        inputs = file_mapping.values(),
376        outputs = [installed_files],
377        mnemonic = "GenerateApexInstalledFileList",
378        command = "(" + "; ".join(command) + ") | sort -nr > " + installed_files.path,
379    )
380    return installed_files
381
382def _generate_notices(ctx, apex_toolchain):
383    licensees = license_map(ctx.attr.binaries + ctx.attr.prebuilts + ctx.attr.native_shared_libs_32 + ctx.attr.native_shared_libs_64)
384    licenses_file = ctx.actions.declare_file(ctx.attr.name + "_licenses.json")
385    ctx.actions.write(licenses_file, "[\n%s\n]\n" % ",\n".join(license_map_to_json(licensees)))
386
387    # Run HTML notice file generator.
388    notice_file = ctx.actions.declare_file(ctx.attr.name + "_notice_dir/NOTICE.html.gz")
389    notice_generator = apex_toolchain.notice_generator[DefaultInfo].files_to_run
390
391    args = ctx.actions.args()
392    args.add_all(["-o", notice_file, licenses_file])
393
394    # TODO(asmundak): should we extend it with license info for self
395    # (the case when APEX itself has applicable_licenses attribute)?
396    inputs = license_map_notice_files(licensees) + [licenses_file]
397    ctx.actions.run(
398        mnemonic = "GenerateNoticeFile",
399        inputs = inputs,
400        outputs = [notice_file],
401        executable = notice_generator,
402        tools = [notice_generator],
403        arguments = [args],
404    )
405    return notice_file
406
407def _use_api_fingerprint(ctx):
408    if not ctx.attr._unbundled_build[BuildSettingInfo].value:
409        return False
410    if ctx.attr._always_use_prebuilt_sdks[BuildSettingInfo].value:
411        return False
412    if not ctx.attr._unbundled_build_target_sdk_with_api_fingerprint[BuildSettingInfo].value:
413        return False
414    return True
415
416# apexer - generate the APEX file.
417def _run_apexer(ctx, apex_toolchain):
418    # Inputs
419    apex_key_info = ctx.attr.key[ApexKeyInfo]
420    privkey = apex_key_info.private_key
421    pubkey = apex_key_info.public_key
422    android_jar = apex_toolchain.android_jar
423
424    file_mapping, requires_native_libs, provides_native_libs, backing_libs, make_modules_to_install, make_files_info, metadata_file_mapping = _create_file_mapping(ctx)
425    canned_fs_config = _generate_canned_fs_config(ctx, file_mapping.keys())
426    file_contexts = _generate_file_contexts(ctx)
427    full_apex_manifest_json = _add_apex_manifest_information(ctx, apex_toolchain, requires_native_libs, provides_native_libs)
428    apex_manifest_pb = _convert_apex_manifest_json_to_pb(ctx, apex_toolchain, full_apex_manifest_json)
429    notices_file = _generate_notices(ctx, apex_toolchain)
430    api_fingerprint_file = None
431
432    staging_dir_builder_options_file = ctx.actions.declare_file(ctx.attr.name + "_staging_dir_builder_options.json")
433    ctx.actions.write(staging_dir_builder_options_file, json.encode({
434        "file_mapping": {k: v.path for k, v in file_mapping.items()},
435    }))
436
437    # Outputs
438    apex_output_file = ctx.actions.declare_file(ctx.attr.name + ".apex.unsigned")
439
440    apexer_files = apex_toolchain.apexer[DefaultInfo].files_to_run
441
442    # Arguments
443    command = [ctx.executable._staging_dir_builder.path, staging_dir_builder_options_file.path]
444
445    # start of apexer cmd
446    command.append(apexer_files.executable.path)
447
448    command.append("--force")
449    command.append("--include_build_info")
450    command.extend(["--canned_fs_config", canned_fs_config.path])
451    command.extend(["--manifest", apex_manifest_pb.path])
452    command.extend(["--file_contexts", file_contexts.path])
453    command.extend(["--key", privkey.path])
454    command.extend(["--pubkey", pubkey.path])
455    command.extend(["--payload_type", "image"])
456    command.extend(["--payload_fs_type", "ext4"])
457    command.extend(["--assets_dir", notices_file.dirname])
458
459    # Override the package name, if it's expicitly specified
460    if ctx.attr.package_name:
461        command.extend(["--override_apk_package_name", ctx.attr.package_name])
462    else:
463        override_package_name = _override_manifest_package_name(ctx)
464        if override_package_name:
465            command.extend(["--override_apk_package_name", override_package_name])
466
467    if ctx.attr.logging_parent:
468        command.extend(["--logging_parent", ctx.attr.logging_parent])
469
470    use_api_fingerprint = _use_api_fingerprint(ctx)
471
472    target_sdk_version = str(api.final_or_future(api.default_app_target_sdk()))
473    if use_api_fingerprint:
474        api_fingerprint_file = ctx.file._api_fingerprint_txt
475        sdk_version_suffix = ".$(cat {})".format(api_fingerprint_file.path)
476        target_sdk_version = ctx.attr._platform_sdk_codename[BuildSettingInfo].value + sdk_version_suffix
477
478    command.extend(["--target_sdk_version", target_sdk_version])
479
480    # TODO(b/215339575): This is a super rudimentary way to convert "current" to a numerical number.
481    # Generalize this to API level handling logic in a separate Starlark utility, preferably using
482    # API level maps dumped from api_levels.go
483    min_sdk_version = ctx.attr.min_sdk_version
484    if min_sdk_version == "current":
485        min_sdk_version = "10000"
486
487    override_min_sdk_version = ctx.attr._apex_global_min_sdk_version_override[BuildSettingInfo].value
488    min_sdk_version = str(maybe_override_min_sdk_version(min_sdk_version, override_min_sdk_version))
489
490    if min_sdk_version == "10000" and use_api_fingerprint:
491        min_sdk_version = ctx.attr._platform_sdk_codename[BuildSettingInfo].value + sdk_version_suffix
492        command.append(api_fingerprint_file.path)
493    command.extend(["--min_sdk_version", min_sdk_version])
494
495    # apexer needs the list of directories containing all auxilliary tools invoked during
496    # the creation of an apex
497    avbtool_files = apex_toolchain.avbtool[DefaultInfo].files_to_run
498    e2fsdroid_files = apex_toolchain.e2fsdroid[DefaultInfo].files_to_run
499    mke2fs_files = apex_toolchain.mke2fs[DefaultInfo].files_to_run
500    resize2fs_files = apex_toolchain.resize2fs[DefaultInfo].files_to_run
501    sefcontext_compile_files = apex_toolchain.sefcontext_compile[DefaultInfo].files_to_run
502    staging_dir_builder_files = ctx.attr._staging_dir_builder[DefaultInfo].files_to_run
503    apexer_tool_paths = [
504        apex_toolchain.aapt2.dirname,
505        apexer_files.executable.dirname,
506        avbtool_files.executable.dirname,
507        e2fsdroid_files.executable.dirname,
508        mke2fs_files.executable.dirname,
509        resize2fs_files.executable.dirname,
510        sefcontext_compile_files.executable.dirname,
511    ]
512
513    command.extend(["--apexer_tool_path", ":".join(apexer_tool_paths)])
514
515    android_manifest = ctx.file.android_manifest
516    if android_manifest != None:
517        if ctx.attr.testonly:
518            android_manifest = _mark_manifest_as_test_only(ctx, apex_toolchain)
519        command.extend(["--android_manifest", android_manifest.path])
520    elif ctx.attr.testonly:
521        command.append("--test_only")
522
523    command.append("STAGING_DIR_PLACEHOLDER")
524    command.append(apex_output_file.path)
525
526    inputs = [
527        ctx.executable._staging_dir_builder,
528        staging_dir_builder_options_file,
529        canned_fs_config,
530        apex_manifest_pb,
531        file_contexts,
532        notices_file,
533        privkey,
534        pubkey,
535        android_jar,
536    ] + file_mapping.values()
537    if use_api_fingerprint:
538        inputs.append(api_fingerprint_file)
539
540    if android_manifest != None:
541        inputs.append(android_manifest)
542
543    # This is run_shell instead of run because --target_sdk_version may
544    # use the API fingerprinting file contents using bash expansion,
545    # and only run_shell can support that by executing the whole command with
546    # /bin/bash -c. Regular run would quote the --target_sdk_version value with
547    # single quotes ('--target_sdk_version=ABC.$(cat version.txt)'), preventing
548    # bash expansion.
549    ctx.actions.run_shell(
550        inputs = inputs,
551        tools = [
552            apexer_files,
553            avbtool_files,
554            e2fsdroid_files,
555            mke2fs_files,
556            resize2fs_files,
557            sefcontext_compile_files,
558            apex_toolchain.aapt2,
559            staging_dir_builder_files,
560        ],
561        outputs = [apex_output_file],
562        command = " ".join(command),
563        mnemonic = "Apexer",
564    )
565    return struct(
566        unsigned_apex = apex_output_file,
567        requires_native_libs = requires_native_libs,
568        provides_native_libs = provides_native_libs,
569        backing_libs = _generate_apex_backing_file(ctx, backing_libs),
570        symbols_used_by_apex = _generate_symbols_used_by_apex(ctx, apex_toolchain, file_mapping),
571        java_symbols_used_by_apex = _generate_java_symbols_used_by_apex(ctx, apex_toolchain),
572        installed_files = _generate_installed_files_list(ctx, file_mapping),
573        make_modules_to_install = make_modules_to_install,
574        make_files_info = make_files_info,
575        file_mapping = file_mapping,
576        metadata_file_mapping = metadata_file_mapping,
577    )
578
579def _run_signapk(ctx, unsigned_file, signed_file, private_key, public_key, mnemonic):
580    """Sign a file with signapk."""
581
582    # Arguments
583    args = ctx.actions.args()
584    args.add_all(["-a", 4096])
585    args.add_all(["--align-file-size"])
586    args.add_all([public_key, private_key])
587    args.add_all([unsigned_file, signed_file])
588
589    ctx.actions.run(
590        inputs = [
591            unsigned_file,
592            private_key,
593            public_key,
594            ctx.executable._signapk,
595        ],
596        outputs = [signed_file],
597        executable = ctx.executable._signapk,
598        arguments = [args],
599        mnemonic = mnemonic,
600    )
601
602    return signed_file
603
604# See also getOverrideManifestPackageName
605# https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/builder.go;l=1000;drc=241e738c7156d928e9a993b15993cb3297face45
606def _override_manifest_package_name(ctx):
607    apex_name = ctx.attr.name
608    overrides = ctx.attr._manifest_package_name_overrides[BuildSettingInfo].value
609    if not overrides:
610        return None
611
612    matches = [o for o in overrides if o.split(":")[0] == apex_name]
613
614    if not matches:
615        return None
616
617    if len(matches) > 1:
618        fail("unexpected multiple manifest package overrides for %s, %s" % (apex_name, matches))
619
620    return matches[0].split(":")[1]
621
622# https://cs.android.com/android/platform/superproject/+/master:build/soong/android/config.go;drc=5ca657189aac546af0aafaba11bbc9c5d889eab3;l=1501
623# In Soong, we don't check whether the current apex is part of Unbundled_apps.
624# Hence, we might simplify the logic by just checking product_vars["Unbundled_build"]
625# TODO(b/271474456): Eventually we might default to unbundled mode in bazel-only mode
626# so that we don't need to check Unbundled_apps.
627def _compression_enabled(ctx):
628    compressed_apex = ctx.attr._compressed_apex[BuildSettingInfo].value
629    unbundled_apps = ctx.attr._unbundled_build_apps[BuildSettingInfo].value
630
631    return compressed_apex and len(unbundled_apps) == 0
632
633# Compress a file with apex_compression_tool.
634def _run_apex_compression_tool(ctx, apex_toolchain, input_file, output_file_name):
635    avbtool_files = apex_toolchain.avbtool[DefaultInfo].files_to_run
636    apex_compression_tool_files = apex_toolchain.apex_compression_tool[DefaultInfo].files_to_run
637
638    # Outputs
639    compressed_file = ctx.actions.declare_file(output_file_name)
640
641    # Arguments
642    args = ctx.actions.args()
643    args.add_all(["compress"])
644    tool_dirs = [apex_toolchain.soong_zip.dirname, avbtool_files.executable.dirname]
645    args.add_all(["--apex_compression_tool", ":".join(tool_dirs)])
646    args.add_all(["--input", input_file])
647    args.add_all(["--output", compressed_file])
648
649    ctx.actions.run(
650        inputs = [input_file],
651        tools = [
652            avbtool_files,
653            apex_compression_tool_files,
654            apex_toolchain.soong_zip,
655        ],
656        outputs = [compressed_file],
657        executable = apex_compression_tool_files,
658        arguments = [args],
659        mnemonic = "BazelApexCompressing",
660    )
661    return compressed_file
662
663# Generate <module>_using.txt, which contains a list of versioned NDK symbols
664# dynamically linked to by this APEX's contents. This is used for coverage
665# checks.
666def _generate_symbols_used_by_apex(ctx, apex_toolchain, file_mapping):
667    staging_dir_builder_options_file = ctx.actions.declare_file(ctx.attr.name + "_generate_symbols_used_by_apex_staging_dir_builder_options.json")
668    ctx.actions.write(staging_dir_builder_options_file, json.encode({
669        "file_mapping": {k: v.path for k, v in file_mapping.items()},
670    }))
671    symbols_used_by_apex = ctx.actions.declare_file(ctx.attr.name + "_using.txt")
672    ctx.actions.run(
673        outputs = [symbols_used_by_apex],
674        inputs = [staging_dir_builder_options_file] + file_mapping.values(),
675        tools = [
676            apex_toolchain.readelf.files_to_run,
677            apex_toolchain.gen_ndk_usedby_apex.files_to_run,
678        ],
679        executable = ctx.executable._staging_dir_builder,
680        arguments = [
681            staging_dir_builder_options_file.path,
682            apex_toolchain.gen_ndk_usedby_apex.files_to_run.executable.path,
683            "STAGING_DIR_PLACEHOLDER",
684            apex_toolchain.readelf.files_to_run.executable.path,
685            symbols_used_by_apex.path,
686        ],
687        progress_message = "Generating dynamic NDK symbol list used by the %s apex" % ctx.attr.name,
688        mnemonic = "ApexUsingNDKSymbolsForCoverage",
689    )
690    return symbols_used_by_apex
691
692# Generate <module>_using.xml, which contains a list of java API metadata used
693# by this APEX's contents. This is used for coverage checks.
694#
695# TODO(b/257954111): Add JARs and APKs as inputs to this action when we start
696# building Java mainline modules.
697def _generate_java_symbols_used_by_apex(ctx, apex_toolchain):
698    java_symbols_used_by_apex = ctx.actions.declare_file(ctx.attr.name + "_using.xml")
699    ctx.actions.run(
700        outputs = [java_symbols_used_by_apex],
701        inputs = [],
702        tools = [
703            apex_toolchain.dexdeps.files_to_run,
704            apex_toolchain.gen_java_usedby_apex.files_to_run,
705        ],
706        executable = apex_toolchain.gen_java_usedby_apex.files_to_run,
707        arguments = [
708            apex_toolchain.dexdeps.files_to_run.executable.path,
709            java_symbols_used_by_apex.path,
710        ],
711        progress_message = "Generating Java symbol list used by the %s apex" % ctx.attr.name,
712        mnemonic = "ApexUsingJavaSymbolsForCoverage",
713    )
714    return java_symbols_used_by_apex
715
716def _validate_apex_deps(ctx):
717    transitive_deps = depset(
718        transitive = [
719            d[ApexDepsInfo].transitive_deps
720            for d in (
721                ctx.attr.native_shared_libs_32 +
722                ctx.attr.native_shared_libs_64 +
723                ctx.attr.binaries +
724                ctx.attr.prebuilts
725            )
726        ],
727    )
728    validation_files = []
729    if not ctx.attr._unsafe_disable_apex_allowed_deps_check[BuildSettingInfo].value:
730        validation_files.append(validate_apex_deps(ctx, transitive_deps, ctx.file.allowed_apex_deps_manifest))
731
732    transitive_unvalidated_targets = []
733    transitive_invalid_targets = []
734    for _, attr_deps in get_dep_targets(ctx.attr, predicate = lambda target: ApexAvailableInfo in target).items():
735        for dep in attr_deps:
736            transitive_unvalidated_targets.append(dep[ApexAvailableInfo].transitive_unvalidated_targets)
737            transitive_invalid_targets.append(dep[ApexAvailableInfo].transitive_invalid_targets)
738
739    invalid_targets = depset(transitive = transitive_invalid_targets).to_list()
740    if len(invalid_targets) > 0:
741        invalid_targets_msg = "\n    ".join([
742            "{label}; apex_available tags: {tags}".format(label = target.label, tags = list(apex_available_tags))
743            for target, apex_available_tags in invalid_targets
744        ])
745        msg = ("`{apex_name}` apex has transitive dependencies that do not include the apex in " +
746               "their apex_available tags:\n    {invalid_targets_msg}").format(
747            apex_name = ctx.label,
748            invalid_targets_msg = invalid_targets_msg,
749        )
750        fail(msg)
751
752    transitive_unvalidated_targets_output_file = ctx.actions.declare_file(ctx.attr.name + "_unvalidated_deps.txt")
753    ctx.actions.write(
754        transitive_unvalidated_targets_output_file,
755        "\n".join([
756            str(label) + ": " + str(reason)
757            for label, reason in depset(transitive = transitive_unvalidated_targets).to_list()
758        ]),
759    )
760    return transitive_deps, transitive_unvalidated_targets_output_file, validation_files
761
762def _verify_updatability(ctx):
763    # TODO(b/274732759): Add these checks as more APEXes are converted to Bazel.
764    #
765    # Keep this in sync with build/soong/apex/apex.go#checkUpdatable.
766    #
767    # - Cannot use platform APIs.
768    # - Cannot use external VNDK libs.
769    # - Does not set future_updatable.
770
771    if not ctx.attr.min_sdk_version:
772        fail("updatable APEXes must set min_sdk_version.")
773
774def _generate_sbom(ctx, file_mapping, metadata_file_mapping, apex_file):
775    apex_filename = paths.basename(apex_file.path)
776    sbom_metadata_csv = ctx.actions.declare_file(apex_filename + "-sbom-metadata.csv")
777    command = []
778    metadata_files = []
779    sbom_metadata_csv_columns = [
780        "installed_file",
781        "module_path",
782        "soong_module_type",
783        "is_prebuilt_make_module",
784        "product_copy_files",
785        "kernel_module_copy_files",
786        "is_platform_generated",
787        "build_output_path",
788        "static_libraries",
789        "whole_static_libraries",
790        "is_static_lib",
791    ]
792    command.append("echo " + ",".join(sbom_metadata_csv_columns))
793    command.append("echo %s,%s,,,,,,%s,,," % (apex_filename, ctx.label.package, apex_file.path))
794    for installed_file, bazel_output_file in file_mapping.items():
795        if metadata_file_mapping[installed_file]:
796            metadata_files.append(metadata_file_mapping[installed_file])
797        command.append("echo %s,%s,,,,,,%s,,," % (installed_file, paths.dirname(bazel_output_file.short_path), bazel_output_file.path))
798    ctx.actions.run_shell(
799        inputs = file_mapping.values(),
800        outputs = [sbom_metadata_csv],
801        mnemonic = "GenerateSBOMMetadata",
802        command = "(" + "; ".join(command) + ") > " + sbom_metadata_csv.path,
803    )
804
805    sbom_file = ctx.actions.declare_file(apex_filename + ".spdx.json")
806    sbom_fragment_file = ctx.actions.declare_file(apex_filename + "-fragment.spdx")
807    inputs = [
808        apex_file,
809        sbom_metadata_csv,
810        ctx.executable._generate_sbom,
811    ]
812    inputs += file_mapping.values()
813    inputs += metadata_files
814
815    build_fingerprint = ctx.attr._build_fingerprint[BuildFingerprintInfo].fingerprint_blank_build_number
816    ctx.actions.run(
817        inputs = inputs,
818        outputs = [sbom_file, sbom_fragment_file],
819        arguments = [
820            "--output_file",
821            sbom_file.path,
822            "--metadata",
823            sbom_metadata_csv.path,
824            "--build_version",
825            build_fingerprint,
826            "--product_mfr",
827            ctx.attr._product_manufacturer[BuildSettingInfo].value,
828            "--json",
829            "--unbundled_apex",
830        ],
831        mnemonic = "GenerateSBOM",
832        executable = ctx.executable._generate_sbom,
833    )
834    return [sbom_file, sbom_fragment_file]
835
836# See the APEX section in the README on how to use this rule.
837def _apex_rule_impl(ctx):
838    verify_toolchain_exists(ctx, "//build/bazel/rules/apex:apex_toolchain_type")
839    if ctx.attr.updatable:
840        _verify_updatability(ctx)
841
842    apex_toolchain = ctx.toolchains["//build/bazel/rules/apex:apex_toolchain_type"].toolchain_info
843
844    apexer_outputs = _run_apexer(ctx, apex_toolchain)
845    unsigned_apex = apexer_outputs.unsigned_apex
846
847    if AndroidAppCertificateInfo in ctx.attr.certificate_override[0]:
848        apex_cert_info = ctx.attr.certificate_override[0][AndroidAppCertificateInfo]
849    else:
850        apex_cert_info = ctx.attr.certificate[0][AndroidAppCertificateInfo]
851
852    private_key = apex_cert_info.pk8
853    public_key = apex_cert_info.pem
854
855    signed_apex = ctx.actions.declare_file(ctx.attr.name + ".apex")
856    signed_capex = None
857
858    _run_signapk(ctx, unsigned_apex, signed_apex, private_key, public_key, "BazelApexSigning")
859
860    if ctx.attr.compressible and _compression_enabled(ctx):
861        compressed_apex_output_file = _run_apex_compression_tool(ctx, apex_toolchain, signed_apex, ctx.attr.name + ".capex.unsigned")
862        signed_capex = ctx.actions.declare_file(ctx.attr.name + ".capex")
863        _run_signapk(ctx, compressed_apex_output_file, signed_capex, private_key, public_key, "BazelCompressedApexSigning")
864
865    apex_key_info = ctx.attr.key[ApexKeyInfo]
866
867    arch = platforms.get_target_arch(ctx.attr._platform_utils)
868    zip_files = apex_zip_files(
869        actions = ctx.actions,
870        name = ctx.label.name,
871        tools = struct(
872            aapt2 = apex_toolchain.aapt2,
873            zip2zip = ctx.executable._zip2zip,
874            merge_zips = ctx.executable._merge_zips,
875            soong_zip = apex_toolchain.soong_zip,
876        ),
877        apex_file = signed_apex,
878        arch = arch,
879        secondary_arch = platforms.get_target_secondary_arch(ctx.attr._platform_utils),
880    )
881
882    transitive_apex_deps, transitive_unvalidated_targets_output_file, apex_deps_validation_files = _validate_apex_deps(ctx)
883
884    optional_output_groups = {}
885    if signed_capex:
886        optional_output_groups["signed_compressed_output"] = [signed_capex]
887
888    return [
889        DefaultInfo(files = depset([signed_apex])),
890        ApexInfo(
891            signed_output = signed_apex,
892            signed_compressed_output = signed_capex,
893            unsigned_output = unsigned_apex,
894            requires_native_libs = apexer_outputs.requires_native_libs,
895            provides_native_libs = apexer_outputs.provides_native_libs,
896            bundle_key_info = apex_key_info,
897            container_key_info = apex_cert_info,
898            package_name = ctx.attr.package_name,
899            backing_libs = apexer_outputs.backing_libs,
900            symbols_used_by_apex = apexer_outputs.symbols_used_by_apex,
901            installed_files = apexer_outputs.installed_files,
902            java_symbols_used_by_apex = apexer_outputs.java_symbols_used_by_apex,
903            base_file = zip_files.apex_only,
904            base_with_config_zip = zip_files.apex_with_config,
905        ),
906        OutputGroupInfo(
907            coverage_files = [apexer_outputs.symbols_used_by_apex],
908            java_coverage_files = [apexer_outputs.java_symbols_used_by_apex],
909            backing_libs = depset([apexer_outputs.backing_libs]),
910            installed_files = depset([apexer_outputs.installed_files]),
911            transitive_unvalidated_targets = depset([transitive_unvalidated_targets_output_file]),
912            apex_sbom = depset(_generate_sbom(ctx, apexer_outputs.file_mapping, apexer_outputs.metadata_file_mapping, signed_apex)),
913            capex_sbom = depset(_generate_sbom(ctx, apexer_outputs.file_mapping, apexer_outputs.metadata_file_mapping, signed_capex) if signed_capex else []),
914            _validation = apex_deps_validation_files,
915            **optional_output_groups
916        ),
917        ApexDepsInfo(transitive_deps = transitive_apex_deps),
918        ApexMkInfo(
919            make_modules_to_install = apexer_outputs.make_modules_to_install,
920            files_info = apexer_outputs.make_files_info,
921        ),
922        collect_deps_clang_tidy_info(ctx),
923    ]
924
925# These are the standard aspects that should be applied on all edges that
926# contribute to an APEX's payload.
927STANDARD_PAYLOAD_ASPECTS = [
928    license_aspect,
929    apex_available_aspect,
930    apex_deps_validation_aspect,
931]
932
933_apex = rule(
934    implementation = _apex_rule_impl,
935    attrs = {
936        # Attributes that configure the APEX container.
937        "manifest": attr.label(allow_single_file = [".json"]),
938        "android_manifest": attr.label(allow_single_file = [".xml"]),
939        "package_name": attr.string(),
940        "logging_parent": attr.string(),
941        "file_contexts": attr.label(allow_single_file = True, mandatory = True),
942        "canned_fs_config": attr.label(
943            allow_single_file = True,
944            doc = """Path to the canned fs config file for customizing file's
945uid/gid/mod/capabilities. The content of this file is appended to the
946default config, so that the custom entries are preferred.
947
948The format is /<path_or_glob> <uid> <gid> <mode> [capabilities=0x<cap>], where
949path_or_glob is a path or glob pattern for a file or set of files, uid/gid
950are numerial values of user ID and group ID, mode is octal value for the
951file mode, and cap is hexadecimal value for the capability.""",
952        ),
953        "key": attr.label(providers = [ApexKeyInfo], mandatory = True),
954        "certificate": attr.label(
955            providers = [AndroidAppCertificateInfo],
956            mandatory = True,
957            cfg = apex_transition,
958        ),
959        "certificate_override": attr.label(
960            providers = [[AndroidAppCertificateInfo], [NoAndroidAppCertificateInfo]],
961            mandatory = True,
962            cfg = apex_transition,
963        ),
964        "min_sdk_version": attr.string(
965            default = "current",
966            doc = """The minimum SDK version that this APEX must support at minimum. This is usually set to
967the SDK version that the APEX was first introduced.
968
969When not set, defaults to "10000" (or "current").""",
970        ),
971        "updatable": attr.bool(default = True, doc = """Whether this APEX is considered updatable or not.
972
973When set to true, this will enforce additional rules for making sure that the
974APEX is truly updatable. To be updatable, min_sdk_version should be set as well."""),
975        "installable": attr.bool(default = True),
976        "compressible": attr.bool(default = False),
977        "base_apex_name": attr.string(
978            default = "",
979            doc = "The name of the base apex of this apex. For example, the AOSP variant of this apex.",
980        ),
981        "apex_available_name": attr.string(
982            default = "",
983            doc = "The name that dependencies can specify in their apex_available " +
984                  "properties to refer to this module. If not specified, this " +
985                  "defaults to Soong module name.",
986        ),
987        "variant_version": attr.string(
988            default = "",
989            doc = "Variant version of the mainline module. Must be an integer between 0-9",
990            values = [""] + [str(i) for i in range(10)],
991        ),
992
993        # Attributes that contribute to the payload.
994        "native_shared_libs_32": attr.label_list(
995            providers = [ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo],
996            aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
997            cfg = shared_lib_transition_32,
998            doc = "The libs compiled for 32-bit",
999        ),
1000        "native_shared_libs_64": attr.label_list(
1001            providers = [ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo],
1002            aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
1003            cfg = shared_lib_transition_64,
1004            doc = "The libs compiled for 64-bit",
1005        ),
1006        "binaries": attr.label_list(
1007            providers = [
1008                # The dependency must produce _all_ of the providers in _one_ of these lists.
1009                [ShBinaryInfo, RuleLicensedDependenciesInfo],  # sh_binary
1010                [StrippedCcBinaryInfo, CcInfo, ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo],  # cc_binary (stripped)
1011            ],
1012            cfg = apex_transition,
1013            aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
1014        ),
1015        "prebuilts": attr.label_list(
1016            providers = [PrebuiltFileInfo, RuleLicensedDependenciesInfo],
1017            cfg = apex_transition,
1018            aspects = STANDARD_PAYLOAD_ASPECTS,
1019        ),
1020
1021        # Required to use apex_transition. This is an acknowledgement to the risks of memory bloat when using transitions.
1022        "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
1023
1024        # Tools that are not part of the apex_toolchain.
1025        "_staging_dir_builder": attr.label(
1026            cfg = "exec",
1027            doc = "The staging dir builder to avoid the problem where symlinks are created inside apex image.",
1028            executable = True,
1029            default = "//build/bazel/rules:staging_dir_builder",
1030        ),
1031        "_signapk": attr.label(
1032            cfg = "exec",
1033            doc = "The signapk tool.",
1034            executable = True,
1035            default = "//build/make/tools/signapk",
1036        ),
1037        "_zip2zip": attr.label(
1038            cfg = "exec",
1039            allow_single_file = True,
1040            doc = "The tool zip2zip. Used to convert apex file to the expected directory structure.",
1041            default = "//build/soong/cmd/zip2zip:zip2zip",
1042            executable = True,
1043        ),
1044        "_merge_zips": attr.label(
1045            cfg = "exec",
1046            allow_single_file = True,
1047            doc = "The tool merge_zips. Used to combine base zip and config file into a single zip for mixed build aab creation.",
1048            default = "//build/soong/cmd/merge_zips",
1049            executable = True,
1050        ),
1051        "_platform_utils": attr.label(
1052            default = "//build/bazel/platforms:platform_utils",
1053        ),
1054        "_build_fingerprint": attr.label(
1055            default = "//build/bazel/rules:build_fingerprint",
1056        ),
1057        "_generate_sbom": attr.label(
1058            cfg = "exec",
1059            doc = "SBOM generation tool",
1060            executable = True,
1061            default = "//build/make/tools/sbom:generate-sbom",
1062        ),
1063
1064        # allowed deps check
1065        "_unsafe_disable_apex_allowed_deps_check": attr.label(
1066            default = "//build/bazel/rules/apex:unsafe_disable_apex_allowed_deps_check",
1067        ),
1068        "allowed_apex_deps_manifest": attr.label(
1069            allow_single_file = True,
1070            default = "//packages/modules/common/build:allowed_deps.txt",
1071        ),
1072
1073        # Build settings.
1074        "_always_use_prebuilt_sdks": attr.label(
1075            default = "//build/bazel/product_config:always_use_prebuilt_sdks",
1076        ),
1077        "_apex_global_min_sdk_version_override": attr.label(
1078            default = "//build/bazel/product_config:apex_global_min_sdk_version_override",
1079            doc = "If specified, override the min_sdk_version of this apex and in the transition and checks for dependencies.",
1080        ),
1081        "_compressed_apex": attr.label(
1082            default = "//build/bazel/product_config:compressed_apex",
1083        ),
1084        "_manifest_package_name_overrides": attr.label(
1085            default = "//build/bazel/product_config:manifest_package_name_overrides",
1086        ),
1087        "_override_apex_manifest_default_version": attr.label(
1088            default = "//build/bazel/rules/apex:override_apex_manifest_default_version",
1089            doc = "If specified, override 'version: 0' in apex_manifest.json with this value instead of the branch default. Non-zero versions will not be changed.",
1090        ),
1091        "_product_manufacturer": attr.label(
1092            default = "//build/bazel/product_config:product_manufacturer",
1093        ),
1094        "_unbundled_build_apps": attr.label(
1095            default = "//build/bazel/product_config:unbundled_build_apps",
1096        ),
1097        "_unbundled_build": attr.label(
1098            default = "//build/bazel/product_config:unbundled_build",
1099        ),
1100
1101        # Api_fingerprint
1102        "_unbundled_build_target_sdk_with_api_fingerprint": attr.label(
1103            default = "//build/bazel/rules/apex:unbundled_build_target_sdk_with_api_fingerprint",
1104        ),
1105        "_platform_sdk_codename": attr.label(
1106            default = "//build/bazel/rules/apex:platform_sdk_codename",
1107        ),
1108        "_api_fingerprint_txt": attr.label(
1109            default = "//frameworks/base/api:api_fingerprint",
1110            allow_single_file = True,
1111        ),
1112    },
1113    # The apex toolchain is not mandatory so that we don't get toolchain resolution errors even
1114    # when the apex is not compatible with the current target (via target_compatible_with).
1115    toolchains = [config_common.toolchain_type("//build/bazel/rules/apex:apex_toolchain_type", mandatory = False)],
1116    fragments = ["platform"],
1117)
1118
1119def apex(
1120        name,
1121        manifest = "apex_manifest.json",
1122        android_manifest = None,
1123        file_contexts = None,
1124        key = None,
1125        certificate = None,
1126        certificate_name = None,
1127        min_sdk_version = None,
1128        updatable = True,
1129        installable = True,
1130        compressible = False,
1131        native_shared_libs_32 = [],
1132        native_shared_libs_64 = [],
1133        binaries = [],
1134        prebuilts = [],
1135        package_name = None,
1136        logging_parent = None,
1137        canned_fs_config = None,
1138        testonly = False,
1139        # TODO(b/255400736): tests are not fully supported yet.
1140        tests = [],
1141        target_compatible_with = [],
1142        **kwargs):
1143    "Bazel macro to correspond with the APEX bundle Soong module."
1144
1145    # If file_contexts is not specified, then use the default from //system/sepolicy/apex.
1146    # https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/builder.go;l=259-263;drc=b02043b84d86fe1007afef1ff012a2155172215c
1147    if file_contexts == None:
1148        file_contexts = "//system/sepolicy/apex:" + name + "-file_contexts"
1149
1150    if testonly:
1151        compressible = False
1152    elif tests:
1153        fail("Apex with tests attribute needs to be testonly.")
1154
1155    if certificate and certificate_name:
1156        fail("Cannot use both certificate_name and certificate attributes together. Use only one of them.")
1157    app_cert_name = name + "_app_certificate"
1158    if certificate_name:
1159        # use the name key in the default cert dir
1160        android_app_certificate_with_default_cert(
1161            name = app_cert_name,
1162            cert_name = certificate_name,
1163        )
1164        certificate_label = ":" + app_cert_name
1165    elif certificate:
1166        certificate_label = certificate
1167    else:
1168        # use the default testkey
1169        android_app_certificate_with_default_cert(name = app_cert_name)
1170        certificate_label = ":" + app_cert_name
1171
1172    target_compatible_with = select({
1173        "//build/bazel_common_rules/platforms/os:android": [],
1174        "//conditions:default": ["@platforms//:incompatible"],
1175    }) + target_compatible_with
1176
1177    # This flag will be set based on the value of PRODUCT_CERTIFICATE_OVERRIDES
1178    native.label_flag(
1179        name = name + "_certificate_override",
1180        build_setting_default = "//build/bazel/rules/android:no_android_app_certificate",
1181    )
1182
1183    _apex(
1184        name = name,
1185        manifest = manifest,
1186        android_manifest = android_manifest,
1187        file_contexts = file_contexts,
1188        key = key,
1189        certificate = certificate_label,
1190        certificate_override = ":" + name + "_certificate_override",
1191        min_sdk_version = min_sdk_version,
1192        updatable = updatable,
1193        installable = installable,
1194        compressible = compressible,
1195        native_shared_libs_32 = native_shared_libs_32,
1196        native_shared_libs_64 = native_shared_libs_64,
1197        binaries = binaries,
1198        prebuilts = prebuilts,
1199        package_name = package_name,
1200        logging_parent = logging_parent,
1201        canned_fs_config = canned_fs_config,
1202        testonly = testonly,
1203        target_compatible_with = target_compatible_with,
1204        **kwargs
1205    )
1206