Android source code customization: MK file execution sequence | attribute override

Foreword

In the Android build system, there are some mechanisms and tools that can help us customize and optimize source code, such as:

  • mk file: This is a Makefile used to describe build rules and dependencies. Some variables and instructions can be defined in the mk file to control the compilation and packaging process of the source code.
  • Property (sysprop): This is a system property used to control system behavior and configuration. We can define and assign properties in different places to affect the runtime state of the system.

In this article, I will introduce the basic concepts and usage of these mechanisms and tools, and use some examples to show how to use them to customize and optimize Android source code. We’ll also cover how to debug whether our modifications are taking effect, and how to view the details of our modifications.

1. How to determine the execution order of mk files

In the Android build system, the xxx.mk file is a Makefile used to describe build rules and dependencies. mk files can include other mk files through the include or -include directives to form a complex build graph. The execution order of mk files is determined by dependencies in the build graph, not by file names or directory structures.

The build system will start execution from build/make/core/main.mk in the root directory, and then select the corresponding mk file according to different target platforms and products. For example, if I select rk3568_s as the target project, then the build system will execute the following mk file:

  • build/make/core/main.mk
  • build/make/target/product/core.mk
  • device/rockchip/rk356x/rk3568_s/rk3568_s.mk
  • device/rockchip/rk356x/BoardConfig.mk
  • device/rockchip/common/BoardConfig.mk
  • vendor/customize/customize.mk

Each mk file may contain other mk files, forming a nested structure. You can print some information by adding the $(warning) or $(info) directive in the mk file to observe their execution order. During debugging, the following code was added to rk3568_s.mk and customize.mk:

$(warning Enter rk3568_s > device.mk is included $(TARGET_PRODUCT))
 
ifeq ($(TARGET_PRODUCT),rk3568_s)
$(warning Enter rk3568_s)
endif
$(warning Enter customize > customize.mk is included $(TARGET_PRODUCT))
 
ifeq ($(filter $(TARGET_PRODUCT),rk3568_s_beta rk3568_s),)
#Here is the code executed when TARGET_PRODUCT is not rk3568_s_beta or rk3568_s
else
#Here is the code executed when TARGET_PRODUCT is rk3568_s_beta or rk3568_s
$(warning Enter $(MY_CUSTOM_PATH)/rk3568_s_beta)
endif

So when I execute lunch rk3568_s-userdebug, I will see the following output:

device/rockchip/rk356x/rk3568_s/rk3568_s.mk:17: warning: Enter rk3568_s > device.mk is included rk3568_s
device/rockchip/rk356x/rk3568_s/rk3568_s.mk:20: warning: Enter rk3568_s
vendor/customize/customize.mk:3: warning: Enter customize > customize.mk is included rk3568_s
vendor/customize/customize.mk:12: warning: Enter ./vendor/customize/rk3568_s_beta

This shows that rk3568_s.mk is executed before customize.mk, and they both correctly identify the target products. In addition to determining the order, they can also be passed in the mk file Add $(warning) or $(info) instructions to print to confirm whether some modules are compiled and whether they have entered the compilation conditions we expected. You understand what I mean, not After making the changes, just flash the machine to verify. The results of many contents can be known during compilation.

2. How to override the PRODUCT_PROPERTY_OVERRIDES attribute

In Android, there are some system properties (sysprop) used to control system behavior and configuration. For example, the ro.sf.lcd_density property specifies screen density (dpi). These properties can be defined and assigned values in different places, for example:

  • In the device-related mk file, use the PRODUCT_PROPERTY_OVERRIDES + = = directive.
  • In device-related Java code, use the SystemProperties.set(, ) method.
  • In device-specific C/C++ code, use the property_set(, ) function.
  • On the command line, use the setprop command.

When a property is defined and assigned in multiple places, they may conflict and overwrite. To avoid this situation, Android’s build system provides some rules and tools to check and handle property conflicts and overrides.

Attribute assignments can be divided into two types: mandatory and optional. Forced assignment refers to the assignment using the PRODUCT_PROPERTY_OVERRIDES + = = directive directly in the mk file, which indicates that this property must be set to this value. Optional assignment refers to the assignment using the PRODUCT_PROPERTY_OVERRIDES + = ?= directive in the mk file. It means that this property can be set to this value, but it can also be forced by other assignments. cover.

When a property has multiple forced assignments, the build system checks to see if they have the same value. If there is the same value, the build system ignores the optional assignment and sets the property to this value. If there are no identical values, the build system will report an error and indicate that a duplicate assignment was found.

In fact, I didn’t know about this problem at first, because when I was debugging the Android source code in the early days, I probably remembered that PRODUCT_PROPERTY_OVERRIDES could be defined in different MKs to form override attributes. However, according to my debugging, I found that

Suppose I have a file such as device.mk defined in the Android source code device directory.
PRODUCT_PROPERTY_OVERRIDES + = ro.sf.lcd_density=240
Then there is a customized custom.mk in the vendor directory and it is defined again.
PRODUCT_PROPERTY_OVERRIDES + = ro.sf.lcd_density=320
Will compiling Android source code ultimately use 240 or 320? I began to understand that according to the coverage principle, only 240 should exist in vendor/build.prop for direct compilation. .
Compilation error:
error: found duplicate sysprop assignments:
ro.sf.lcd_density=240
ro.sf.lcd_density=320

The data shows: Since it has been determined that custom.mk under the vendor is loaded last, its definition will overwrite the previous definition. But starting with Android 9, the build system no longer allows the same property to be defined in multiple places, so you will see this error.

So this is why forced assignment is used in both device.mk and custom.mk:

PRODUCT_PROPERTY_OVERRIDES + = ro.sf.lcd_density=240
PRODUCT_PROPERTY_OVERRIDES + = ro.sf.lcd_density=320

When executing make, you will see the following error:

error: found duplicate sysprop assignments:
ro.sf.lcd_density=240
ro.sf.lcd_density=320

This is because the build system does not allow multiple different forced assignments to the same property.

In order to solve this problem, there are several methods:

  • Modify one of the forced assignments to an optional assignment: If you want a forced assignment to be overridden by another forced assignment, you can add a question mark (?) before the attribute name to indicate that it is an optional assignment. . If you want the assignment in device.mk to be overwritten by the assignment in custom.mk, you can modify the code in device.mk as:
PRODUCT_PROPERTY_OVERRIDES + = ?ro.sf.lcd_density=240

When executing make, the build system ignores the optional assignment in device.mk and sets this property to the value in custom.mk Force assignment (320).

  • Modify the build tools to remove duplicate forced assignments: If you do not want to modify the code in the mk file, you can also modify the tools used in the build system to handle property conflicts and overrides. This tool is a py script located at build/make/tools/post_process_props.py. It reads all defined properties and decides whether to delete or keep them based on their type and value. This script can be modified to implement the desired logic. Tried the following modifications:

    • Removed the restriction on duplicate attributes: Commented out the error code so that the build system no longer checks for duplicate attributes. In this way, when you execute make, the build system will retain all defined properties and write them to the final generated build.prop file. For example:
    rk_android12.0_sdk/out/target/product/rk3568_s/vendor$ grep -rn "ro.sf.lcd_density"
    build.prop:91:ro.sf.lcd_density=240
    build.prop:156:ro.sf.lcd_density=320
    

    The problem with this is that when the system starts, it reads the last defined property as the final value, ignoring previous definitions. This can lead to some inconsistencies and confusion.

    • Removed the last one of duplicate attributes: Added some code so that when the build system encounters a duplicate attribute, it deletes the last defined attribute and retains the first defined attribute. In this way, when you execute make, the build system will remove the forced assignment in custom.mk and set this property to device.mk Forced assignment in (240). For example:
rk_android12.0_sdk/out/target/product/rk3568_s/vendor$ grep -rn "ro.sf.lcd_density"

  #build.prop:92:ro.sf.lcd_density=240
  # Removed by post_process_props.py because Duplicate property, keeping the first one
  build.prop:161:#ro.sf.lcd_density=320

The problem with this is that you don’t want the assignment in device.mk to overwrite the assignment in custom.mk, but the other way around, but the result is that custom is commented out. mk’s 240.
According to normal understanding, custom.mk(240) should be loaded later than device.mk(320), so 320 should be deleted? But the py script loads and writes in reverse order? This doesn’t meet my expectations.

  • Removed the first one of duplicate attributes: Continue to modify so that when the build system encounters a duplicate attribute, it deletes the first defined attribute and retains the last defined attribute. In this way, when you execute make, the build system will remove the forced assignment in device.mk and set this property to custom.mk Forced assignment in (320). For example:
# Removed by post_process_props.py because Duplicate property, keeping the last one
build.prop:92:#ro.sf.lcd_density=240
# Removed by post_process_props.py because Duplicate property, keeping the first one
#build.prop:161:ro.sf.lcd_density=320

Doing this allows the assignment in custom.mk to overwrite the assignment in device.mk without having to modify the code in the native device.mk file regardless of how the system attributes were written before. Yes, we only need to overwrite it again in our customized mk file, and there is no need to find the entire source code. However, the disadvantage of this is that it may affect the assignment order and results of other properties because it changes the default behavior of the build tool. But according to my tests, it has no effect for the time being.

Summary

In this article, some mechanisms and tools for Android source code customization are introduced, including:

  • mk file: Introduces the structure and syntax of mk files, and how to determine the execution order of mk files.
  • Properties (sysprop): Introduces the types and assignment methods of properties, and how to override the values of properties.

I hope this article can be helpful to you. If you have any questions or suggestions, please leave a message in the comment area. Thanks!