Understand Android Vendor SELinux Policy Build
Since the introduction of the Treble framework in Android 8, Android has divided the system into System and Vendor parts, allowing independent upgrades of system and vendor. Subsequently, Product, ODM, and other partitions were introduced. In this context, SELinux policies are also divided into several parts: platform (system), system_ext, product, vendor, and odm.
This article uses the Vendor partition as an example to analyze in detail how to include sepolicy files in the Android build system, how Android.mk variables are passed to the Soong build system, and finally how they correspond to modules and variables in Android.bp.
1. Including SELinux Policy Files in Android Makefile
First, the SELinux policy files for the Vendor are added to the Android build system through the BOARD_VENDOR_SEPOLICY_DIRS or BOARD_SEPOLICY_DIRS variables. For example:
BOARD_VENDOR_SEPOLICY_DIRS += vendor/xx/featureA/sepolicy
2. Passing Android Makefile Variables to Soong
These Makefile variables are converted to JSON format via soong_config.mk and then passed to the Soong build system, specifying the corresponding Go language variables (e.g., BoardVendorSepolicyDirs).
./build/make/core/soong_config.mk:203: $(call add_json_list, BoardVendorSepolicyDirs, $(BOARD_VENDOR_SEPOLICY_DIRS) $(BOARD_SEPOLICY_DIRS))
3. Declaration of Variables in Soong
In Soong’s variable.go file, the variable that stores the path of the Vendor SELinux policy files is defined.
./build/soong/android/variable.go:355: BoardVendorSepolicyDirs []string `json:",omitempty"`
4. Interface to Get Vendor Policy Paths
Soong provides an interface to get the list of paths to Vendor policy files.
func (c *deviceConfig) VendorSepolicyDirs() []string {
return c.config.productVariables.BoardVendorSepolicyDirs
}
5. Correspondence Between Variables in Android.bp and SELinux Variables in Makefile
In the build/soong/build_files.go file of the Android build system, the mapping between variables in Android.bp and SELinux variables in Makefile is implemented.
func (b *buildFiles) GenerateAndroidBuildActions(ctx android.ModuleContext) {
b.srcs = make(map[string]android.Paths)
b.srcs[".reqd_mask"] = b.findSrcsInDirs(ctx, filepath.Join(ctx.ModuleDir(), "reqd_mask"))
b.srcs[".plat_public"] = b.findSrcsInDirs(ctx, filepath.Join(ctx.ModuleDir(), "public"))
b.srcs[".plat_private"] = b.findSrcsInDirs(ctx, filepath.Join(ctx.ModuleDir(), "private"))
b.srcs[".plat_vendor"] = b.findSrcsInDirs(ctx, filepath.Join(ctx.ModuleDir(), "vendor"))
b.srcs[".system_ext_public"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().SystemExtPublicSepolicyDirs()...)
b.srcs[".system_ext_private"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().SystemExtPrivateSepolicyDirs()...)
b.srcs[".product_public"] = b.findSrcsInDirs(ctx, ctx.Config().ProductPublicSepolicyDirs()...)
b.srcs[".product_private"] = b.findSrcsInDirs(ctx, ctx.Config().ProductPrivateSepolicyDirs()...)
b.srcs[".vendor"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().VendorSepolicyDirs()...)
b.srcs[".odm"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().OdmSepolicyDirs()...)
if ctx.DeviceConfig().PlatformSepolicyVersion() == ctx.DeviceConfig().BoardSepolicyVers() {
// vendor uses the same source with plat policy
b.srcs[".reqd_mask_for_vendor"] = b.srcs[".reqd_mask"]
b.srcs[".plat_vendor_for_vendor"] = b.srcs[".plat_vendor"]
b.srcs[".plat_public_for_vendor"] = b.srcs[".plat_public"]
b.srcs[".plat_private_for_vendor"] = b.srcs[".plat_private"]
b.srcs[".system_ext_public_for_vendor"] = b.srcs[".system_ext_public"]
b.srcs[".system_ext_private_for_vendor"] = b.srcs[".system_ext_private"]
b.srcs[".product_public_for_vendor"] = b.srcs[".product_public"]
b.srcs[".product_private_for_vendor"] = b.srcs[".product_private"]
} else {
// use vendor-supplied plat prebuilts
b.srcs[".reqd_mask_for_vendor"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().BoardReqdMaskPolicy()...)
b.srcs[".plat_vendor_for_vendor"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().BoardPlatVendorPolicy()...)
b.srcs[".plat_public_for_vendor"] = b.findSrcsInDirs(ctx, filepath.Join(ctx.ModuleDir(), "prebuilts", "api", ctx.DeviceConfig().BoardSepolicyVers(), "public"))
b.srcs[".plat_private_for_vendor"] = b.findSrcsInDirs(ctx, filepath.Join(ctx.ModuleDir(), "prebuilts", "api", ctx.DeviceConfig().BoardSepolicyVers(), "private"))
b.srcs[".system_ext_public_for_vendor"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().BoardSystemExtPublicPrebuiltDirs()...)
b.srcs[".system_ext_private_for_vendor"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().BoardSystemExtPrivatePrebuiltDirs()...)
b.srcs[".product_public_for_vendor"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().BoardProductPublicPrebuiltDirs()...)
b.srcs[".product_private_for_vendor"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().BoardProductPrivatePrebuiltDirs()...)
}
}
In the code above, b.srcs[xxx] with xxx refers to variables in Android.bp, which can be used directly in the Android.bp file. The following table summarizes this:
| Android.bp | Corresponding Android.mk Variable or Directory Path |
|---|---|
| .reqd_mask | system/sepolicy/reqd_mask |
| .plat_public | system/sepolicy/public |
| .plat_private | system/sepolicy/private |
| .plat_vendor | system/sepolicy/vendor |
| .system_ext_public | $(SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS) $(BOARD_PLAT_PUBLIC_SEPOLICY_DIR) |
| .system_ext_private | $(SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS) $(BOARD_PLAT_PRIVATE_SEPOLICY_DIR) |
| .product_public | $(PRODUCT_PUBLIC_SEPOLICY_DIRS) |
| .product_private | $(PRODUCT_PRIVATE_SEPOLICY_DIRS) |
| .vendor | $(BOARD_VENDOR_SEPOLICY_DIRS) $(BOARD_SEPOLICY_DIRS) |
If BOARD_SEPOLICY_VERS and PLATFORM_SEPOLICY_VERSION are equal, it indicates that the Vendor uses the current platform’s SELinux rules; otherwise, it uses the SELinux rules specified by BOARD_SEPOLICY_VERS. When they are equal, the related SELinux policy variables in Android.bp can be simplified as follows:
| Android.bp | Value |
|---|---|
| .reqd_mask_for_vendor | .reqd_mask |
| .plat_public_for_vendor | .plat_public |
| .plat_private_for_vendor | .plat_private |
| .plat_vendor_for_vendor | .plat_vendor |
| .system_ext_public_for_vendor | .system_ext_public |
| .system_ext_private_for_vendor | .system_ext_private |
| .product_public_for_vendor | .product_public |
| .product_private_for_vendor | .product_private |
6. Compiling the plat_policy_for_vendor Module
The final compilation of plat_policy_for_vendor generates a plat_policy_for_vendor.cli file, which depends on the plat_policy_for_vendor.conf file. The compilation process of plat_policy_for_vendor.conf is as follows:
se_policy_conf {
name: "plat_policy_for_vendor.conf",
srcs: plat_policies_for_vendor,
installable: false,
}
plat_policies_for_vendor is as follows:
plat_policies_for_vendor = [
":se_build_files{.plat_public_for_vendor}",
":se_build_files{.plat_private_for_vendor}",
":se_build_files{.system_ext_public_for_vendor}",
":se_build_files{.system_ext_private_for_vendor}",
":se_build_files{.product_public_for_vendor}",
":se_build_files{.product_private_for_vendor}",
]
As seen, the compilation process for plat_policy_for_vendor does not use the sepolicy files defined in plat_vendor_for_vendor. Therefore, if a rule is added in system_ext_public_for_vendor that depends on a type defined in plat_vendor_for_vendor, a compilation error will inevitably occur.


good