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 incustom.mk
, you can modify the code indevice.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 generatedbuild.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 incustom.mk
and set this property todevice.mk
Forced assignment in (240). For example:
- 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
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 indevice.mk
and set this property tocustom.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!