Android 新增一个自定义分区

时间:2025-01-26 18:00:25

 在某个项目中,有一个需求,需要新增一个xxx分区,这个分区类似于vendor/oem分区,名字为指定的。此处有点好奇,为什么不直接使用oem分区,而是另外弄一个分区名出来。

功能实现点
在root目录下新增分区的挂载目录,将自定义分区的内容生成一个img。
配置方案,将需要拷进自动以分区的模块、文件等配置好。
在dts中加入xxx分区的支持。
增加init解析rc的路径,支持xxx/etc/init下的rc文件。
修改PackageManagerService,启动时包扫描增加xxx/app下的apk支持。
修改PATH环境变量,使xxx/bin在PATH环境路径下。
修改Android library路径,使JNI能找到xxx/lib中的so。
修改烧写分区,使刷入到flash中。
Android custom images
        AndroidP提供了build_custom_images的task,Makefile的路径如下:

android/build/make/core/tasks/build_custom_images.mk
android/build/make/core/tasks/tools/build_custom_image.mk
        第一个Makefile定义了custom_images这个目标,已经声明了一些需要设置的参数:

custom_image_parameter_variables := \
  CUSTOM_IMAGE_MOUNT_POINT \
  CUSTOM_IMAGE_PARTITION_SIZE \
  CUSTOM_IMAGE_FILE_SYSTEM_TYPE \
  CUSTOM_IMAGE_DICT_FILE \
  CUSTOM_IMAGE_MODULES \
  CUSTOM_IMAGE_COPY_FILES \
  CUSTOM_IMAGE_SELINUX \
  CUSTOM_IMAGE_SUPPORT_VERITY \
  CUSTOM_IMAGE_SUPPORT_VERITY_FEC \
  CUSTOM_IMAGE_VERITY_BLOCK_DEVICE \
  CUSTOM_IMAGE_AVB_HASH_ENABLE \
  CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS \
  CUSTOM_IMAGE_AVB_HASHTREE_ENABLE \
  CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS \
  CUSTOM_IMAGE_AVB_KEY_PATH \
  CUSTOM_IMAGE_AVB_ALGORITHM \
        这些变量的含义在代码的上方有注释,其中PRODUCT_CUSTOM_IMAGE_MAKEFILES这个变量是自定义的分区image的mk文件,image(分区)的名字就是mk的名字。然后调用第二个Makefile文件去编译生成img。

        在这里,原生的Makefile中没找到自动添加custom_images这个目标的方式,只能通过`make custom_images`的方式去生成。为了在make的时候自动生成custom_images,可做以下修改:

diff --git a/core/tasks/build_custom_images.mk b/core/tasks/build_custom_images.mk
index c9b07da57..93c06ab1d 100644
--- a/core/tasks/build_custom_images.mk
+++ b/core/tasks/build_custom_images.mk
@@ -50,7 +50,10 @@
 #
 # To build all those images, run "make custom_images".
 
-ifneq ($(filter $(MAKECMDGOALS),custom_images),)
+# ifneq ($(filter $(MAKECMDGOALS),custom_images),)
+ifneq ($(PRODUCT_CUSTOM_IMAGE_MAKEFILES),)
+
+$(DEFAULT_GOAL): custom_images
        在$(DEFAULT_GOAL)中添加custom_images这个目标,在core/tasks/build_custom_images.mk中修改判断条件,当PRODUCT_CUSTOM_IMAGE_MAKEFILES变量非空时即生成custom_images这个目标。

        core/tasks/tools/build_custom_image.mk中的my_staging_dir是指定生成custom_images中间文件目录的地方,默认是方案out目录下obj/PACKAGING/xxxx_intermediates/xxx下,我改到方案out目录下的xxx目录下。

custom_image mk配置
        在中增加PRODUCT_CUSTOM_IMAGE_MAKEFILES的配置,如下:

PRODUCT_CUSTOM_IMAGE_MAKEFILES += device/xxx/xxx/
BOARD_ROOT_EXTRA_FOLDERS += xxx
        BOARD_ROOT_EXTRA_FOLDERS变量的值是指在root目录下创建一个目录,这个主要是为xxx分区提供好挂载点。

        然后就是我们需要根据自己的需求写,这里xxx就是我们的分区名:

CUSTOM_IMAGE_MOUNT_POINT := xxx
CUSTOM_IMAGE_PARTITION_SIZE := 11111111111
CUSTOM_IMAGE_FILE_SYSTEM_TYPE := ext4
CUSTOM_IMAGE_SELINUX := true         # 支持编译时指定好selinux权限
 
 
CUSTOM_IMAGE_MODULES += \
    aaaaa \
    bbbbb
 
CUSTOM_IMAGE_COPY_FILES += \
    aaaaaaaa/:etc/init/ 
         这些配置变量可参考注释。注意,如果我们的分区是有一些服务的,那么此时最好配置好selinux,CUSTOM_IMAGE_SELINUX设置为true,然后在中BOARD_SEPOLICY_DIRS加入自己的selinux配置,在file_contexts中将整个分区的所有内容默认设置为oemfs(方便使用,oemfs是已定义的selinux规则):

/xxx(/.*)?                     u:object_r:oemfs:s0
分区的挂载
        AndroidP比较特殊,使用了system as root,因此如果自定义分区中有一些rc文件,那么此时就需要在first state挂载上,如果没有rc文件, 无需在init解析rc前挂载,则只需在fstab上挂载即可。

        first state挂载是需要将分区信息写入到dts中,如下:

    firmware {
        android {
            fstab {
                compatible = "android,fstab";
                name = "fstab";
                vendor {
                    compatible = "android,vendor";
                    dev = "/dev/block/by-name/vendor";
                    fsmgr_flags = "wait,recoveryonly";
                    mnt_flags = "ro,barrier=1";
                    name = "vendor";
                    status = "ok";
                    type = "ext4";
                };
                xxx {
                    compatible = "android,xxx";
                    dev = "/dev/block/by-name/XXX";
                    fsmgr_flags = "wait,recoveryonly";
                    mnt_flags = "ro,barrier=1";
                    name = "xxx";
                    status = "ok";
                    type = "ext4";
                };
            };
        };
    };
增加rc文件扫描路径
        如果自定义分区中有需要增加的rc文件,可修改init的代码,如下:

diff --git a/init/ b/init/
index e51a09301..69eb5c28c 100644
--- a/init/
+++ b/init/
@@ -125,6 +125,9 @@ static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_
         if (!("/vendor/etc/init")) {
             late_import_paths.emplace_back("/vendor/etc/init");
         }
+        if (!("/xxx/etc/init")) {
+            late_import_paths.emplace_back("/xxx/etc/init");
+        }
     } else {
         (bootscript);
     }
增加包扫描路径
        如果自定义分区中有放入预装的app,则可修改PackageManagerService的源码,增加包扫描的路径:

diff --git a/services/core/java/com/android/server/pm/ b/services/core/java/com/android/server/pm/
index cf35d0a6d3c..f20873576ee 100644
--- a/services/core/java/com/android/server/pm/
+++ b/services/core/java/com/android/server/pm/
@@ -2660,6 +2660,15 @@ public class PackageManagerService extends
                     | SCAN_AS_SYSTEM,
                     0);
 
+            // Collect ordinary ctc packages.
+            final File ctcAppDir = new File("/xxx", "app");
+            scanDirTracedLI(ctcAppDir,
+                    mDefParseFlags
+                    | PackageParser.PARSE_IS_SYSTEM_DIR,
+                    scanFlags
+                    | SCAN_AS_SYSTEM,
+                    0);
+
             // Collect privileged vendor packages.
             File privilegedVendorAppDir = new File((), "priv-app");
             try {
        在这里,由于我自定义分区的app需要具有与system同等的权限,因此参数与扫描system下的APP一样。

新增PATH路径
        如果自定义分区中有一些可执行文件可被其他人执行,可将该路径添加到PATH变量下:

diff --git a/libc/include/ b/libc/include/
index 922d1ceeb..e5fbcc99c 100644
--- a/libc/include/
+++ b/libc/include/
@@ -38,7 +38,7 @@
 #define        _PATH_BSHELL    "/system/bin/sh"
 #endif
 #define        _PATH_CONSOLE   "/dev/console"
-#define        _PATH_DEFPATH   "/sbin:/system/sbin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"
+#define        _PATH_DEFPATH   "/sbin:/system/sbin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin:/xxx/bin"
 #define        _PATH_DEV       "/dev/"
 #define        _PATH_DEVNULL   "/dev/null"
 #define        _PATH_KLOG      "/proc/kmsg"
       这个宏在init启动的时候使用到了。

       或者在rc文件中使用export的方式修改:

on init
    export PATH /sbin:/system/sbin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin:/xxx/bin
        ext4的分区,在打包成img时,部分文件目录的权限会被修改,如bin这种需要可执行的权限,还需修改打包时权限的设置:

diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 5b79b1d7d..8f3fe41dc 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -203,6 +203,7 @@ static const struct fs_path_config android_files[] = {
     { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib64/valgrind/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
+    { 00755, AID_ROOT,      AID_SHELL,     0, "ctc/bin/*" },
     { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
     // clang-format on
新增Android libraries路径
        如果自定义分区支持App,则还需考虑apk加载jni库的路径。Android的jni库加载的路径,是在android/bionic/linker/中加载的,同时还会去读取中的配置,具体的过程可以去分析中的源码。

        因此我们需要做以下的修改:

        android/bionic仓库下:

diff --git a/linker/ b/linker/
index c78b9aba6..750ab39a3 100644
--- a/linker/
+++ b/linker/
@@ -96,6 +96,7 @@ static const char* const kLdConfigVndkLiteFilePath = "/system/etc/
 static const char* const kSystemLibDir     = "/system/lib64";
 static const char* const kOdmLibDir        = "/odm/lib64";
 static const char* const kVendorLibDir     = "/vendor/lib64";
+static const char* const kCtcLibDir        = "/ctc/lib64";
 static const char* const kAsanSystemLibDir = "/data/asan/system/lib64";
 static const char* const kAsanOdmLibDir    = "/data/asan/odm/lib64";
 static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib64";
@@ -103,6 +104,7 @@ static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib64";
 static const char* const kSystemLibDir     = "/system/lib";
 static const char* const kOdmLibDir        = "/odm/lib";
 static const char* const kVendorLibDir     = "/vendor/lib";
+static const char* const kCtcLibDir        = "/ctc/lib";
 static const char* const kAsanSystemLibDir = "/data/asan/system/lib";
 static const char* const kAsanOdmLibDir    = "/data/asan/odm/lib";
 static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib";
@@ -114,6 +116,7 @@ static const char* const kDefaultLdPaths[] = {
   kSystemLibDir,
   kOdmLibDir,
   kVendorLibDir,
+  kCtcLibDir,
   nullptr
 };
        android/system/core仓库下:

diff --git a/rootdir/etc/ b/rootdir/etc/
index 42dc7abe7..936757b08 100644
--- a/rootdir/etc/
+++ b/rootdir/etc/
@@ -39,6 +39,7 @@ = sphal,vndk,rs
  = true
 
   = /system/${LIB}
+ += /ctc/${LIB}
  += /%PRODUCT%/${LIB}
        库的路径已经增加上,还有prebuilt的jni库需要拷贝到自定义分区下的模块目录下,修改build下的Makefile:

diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index ce917590b..6638b1d5b 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -369,7 +369,7 @@ ifneq (true,$(my_generate_dm))
   $(my_all_targets): $(installed_odex) $(installed_vdex) $(installed_art)
 else
   ALL_MODULES.$(my_register_name).INSTALLED += $(my_installed_dm)
-  ALL_MODULES.$(my_register_name).BUILT_INSTALLED += $(my_built_dm) $(my_installed_dm)
+  ALL_MODULES.$(my_register_name).BUILT_INSTALLED += $(my_built_dm):$(my_installed_dm)
 
   # Make sure to install the .dm when you run "make <module_name>"
   $(my_all_targets): $(installed_dm)
diff --git a/core/install_jni_libs_internal.mk b/core/install_jni_libs_internal.mk
index a99d88ad7..5b1bbaf6b 100644
--- a/core/install_jni_libs_internal.mk
+++ b/core/install_jni_libs_internal.mk
@@ -95,7 +95,8 @@ my_jni_shared_libraries += $(my_prebuilt_jni_libs)
 else # not my_embed_jni
 # Install my_prebuilt_jni_libs as separate files.
 $(foreach lib, $(my_prebuilt_jni_libs), \
-    $(eval $(call copy-one-file, $(lib), $(my_app_lib_path)/$(notdir $(lib)))))
+    $(eval $(call copy-one-file, $(lib), $(my_app_lib_path)/$(notdir $(lib))))\
+       $(eval ALL_MODULES.$(my_register_name).PREBUILT_INSTALLED += $(lib):$(my_app_lib_path)/$(notdir $(lib))))
 
 $(LOCAL_INSTALLED_MODULE) : $(addprefix $(my_app_lib_path)/, $(notdir $(my_prebuilt_jni_libs)))
 endif  # my_embed_jni
diff --git a/core/tasks/tools/build_custom_image.mk b/core/tasks/tools/build_custom_image.mk
index a1151e908..49033b74f 100644
--- a/core/tasks/tools/build_custom_image.mk
+++ b/core/tasks/tools/build_custom_image.mk
@@ -26,7 +26,7 @@ my_custom_image_name := $(basename $(notdir $(my_custom_imag_makefile)))
 
 intermediates := $(call intermediates-dir-for,PACKAGING,$(my_custom_image_name))
 my_built_custom_image := $(intermediates)/$(my_custom_image_name).img
-my_staging_dir := $(intermediates)/$(CUSTOM_IMAGE_MOUNT_POINT)
+my_staging_dir := $(PRODUCT_OUT)/$(CUSTOM_IMAGE_MOUNT_POINT)
 
 # Collect CUSTOM_IMAGE_MODULES's installd files and their PICKUP_FILES.
 my_built_modules :=
@@ -38,6 +38,8 @@ $(foreach m,$(CUSTOM_IMAGE_MODULES),\
     $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).PICKUP_FILES)))\
   $(eval _built_files := $(strip $(ALL_MODULES.$(m).BUILT_INSTALLED)\
     $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT_INSTALLED)))\
+  $(eval _prebuilt_files := $(strip $(ALL_MODULES.$(m).PREBUILT_INSTALLED)\
+    $(ALL_MODULES.$(m)$(TARGET_2ND_ARCH_MODULE_SUFFIX).PREBUILT_INSTALLED)))\
   $(if $(_pickup_files)$(_built_files),,\
     $(warning Unknown installed file for module '$(m)'))\
   $(eval my_pickup_files += $(_pickup_files))\
@@ -52,6 +54,17 @@ $(foreach m,$(CUSTOM_IMAGE_MODULES),\
       $(eval my_copy_dest := $(wordlist 2,999,$(my_copy_dest)))\
       $(eval my_copy_dest := $(subst $(space),/,$(my_copy_dest)))\
       $(eval my_copy_pairs += $(bui):$(my_staging_dir)/$(my_copy_dest)))\
+  )\
+  $(foreach i, $(_prebuilt_files),\
+    $(eval prebui_ins := $(subst :,$(space),$(i)))\
+    $(eval ins := $(word 2,$(prebui_ins)))\
+    $(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\
+      $(eval prebui := $(word 1,$(prebui_ins)))\
+      $(eval my_copy_dest := $(patsubst $(PRODUCT_OUT)/%,%,$(ins)))\
+      $(eval my_copy_dest := $(subst /,$(space),$(my_copy_dest)))\
+      $(eval my_copy_dest := $(wordlist 2,999,$(my_copy_dest)))\
+      $(eval my_copy_dest := $(subst $(space),/,$(my_copy_dest)))\
+      $(eval my_copy_pairs += $(prebui):$(my_staging_dir)/$(my_copy_dest)))\
   ))
刷写分区
        各个厂商的实现不一样,这里不展开。

总结
        经过上面的修改后,一个自定义的分区基本可以完成我们需要的功能,后续有遇到问题再进行修正。
————————————————
版权声明:本文为****博主「chongyuzhao」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:/zcyxiaxi/article/details/119113593