IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> AndroidP新增一个自定义分区 -> 正文阅读

[移动开发]AndroidP新增一个自定义分区

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

功能实现点

  1. 在root目录下新增分区的挂载目录,将自定义分区的内容生成一个img。
  2. 配置方案,将需要拷进自动以分区的模块、文件等配置好。
  3. 在dts中加入xxx分区的支持。
  4. 增加init解析rc的路径,支持xxx/etc/init下的rc文件。
  5. 修改PackageManagerService,启动时包扫描增加xxx/app下的apk支持。
  6. 修改PATH环境变量,使xxx/bin在PATH环境路径下。
  7. 修改Android library路径,使JNI能找到xxx/lib中的so。
  8. 修改烧写分区,使xxx.img刷入到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/main.mk b/core/main.mk
index d8a2e8ec8..fd6907091 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -39,7 +39,7 @@ BUILD_SYSTEM := $(TOPDIR)build/make/core
 # This is the default target.  It must be the first declared target.
 .PHONY: droid
 DEFAULT_GOAL := droid
-$(DEFAULT_GOAL): droid_targets
+$(DEFAULT_GOAL): droid_targets custom_images

 .PHONY: droid_targets
 droid_targets:
diff --git a/core/tasks/build_custom_images.mk b/core/tasks/build_custom_images.mk
index c9b07da57..8b914ba3c 100644
--- a/core/tasks/build_custom_images.mk
+++ b/core/tasks/build_custom_images.mk
@@ -50,7 +50,8 @@
 #
 # To build all those images, run "make custom_images".

-ifneq ($(filter $(MAKECMDGOALS),custom_images),)
+# ifneq ($(filter $(MAKECMDGOALS),custom_images),)
+ifneq ($(PRODUCT_CUSTOM_IMAGE_MAKEFILES),)

 .PHONY: custom_images

diff --git a/core/tasks/tools/build_custom_image.mk b/core/tasks/tools/build_custom_image.mk
index a1151e908..cf6b1c6d1 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)

? ? ? ? 在$(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配置

? ? ? ? 在BoardConfig.mk中增加PRODUCT_CUSTOM_IMAGE_MAKEFILES的配置,如下:

PRODUCT_CUSTOM_IMAGE_MAKEFILES += device/xxx/xxx/xxx.mk
BOARD_ROOT_EXTRA_FOLDERS += xxx

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

? ? ? ? 然后就是我们需要根据自己的需求写xxx.mk,这里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/aaaaaaa.rc:etc/init/init.iptv.rc 

? ? ? ? ?这些配置变量可参考注释。注意,如果我们的分区是有一些服务的,那么此时最好配置好selinux,CUSTOM_IMAGE_SELINUX设置为true,然后在BoardConfig.mk中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/init.cpp b/init/init.cpp
index e51a09301..69eb5c28c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -125,6 +125,9 @@ static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_
         if (!parser.ParseConfig("/vendor/etc/init")) {
             late_import_paths.emplace_back("/vendor/etc/init");
         }
+        if (!parser.ParseConfig("/xxx/etc/init")) {
+            late_import_paths.emplace_back("/xxx/etc/init");
+        }
     } else {
         parser.ParseConfig(bootscript);
     }

增加包扫描路径

? ? ? ? 如果自定义分区中有放入预装的app,则可修改PackageManagerService的源码,增加包扫描的路径:

diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cf35d0a6d3c..f20873576ee 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2660,6 +2660,15 @@ public class PackageManagerService extends IPackageManager.Stub
                     | 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(Environment.getVendorDirectory(), "priv-app");
             try {

? ? ? ? 在这里,由于我自定义分区的app需要具有与system同等的权限,因此参数与扫描system下的APP一样。

新增PATH路径

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

diff --git a/libc/include/paths.h b/libc/include/paths.h
index 922d1ceeb..e5fbcc99c 100644
--- a/libc/include/paths.h
+++ b/libc/include/paths.h
@@ -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/linker.cpp中加载的,同时还会去读取ld.config.txt中的配置,具体的过程可以去分析linker.cpp中的源码。

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

? ? ? ? android/bionic仓库下:

diff --git a/linker/linker.cpp b/linker/linker.cpp
index c78b9aba6..750ab39a3 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -96,6 +96,7 @@ static const char* const kLdConfigVndkLiteFilePath = "/system/etc/ld.config.vndk
 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/ld.config.txt b/rootdir/etc/ld.config.txt
index 42dc7abe7..936757b08 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -39,6 +39,7 @@ additional.namespaces = sphal,vndk,rs
 namespace.default.isolated = true

 namespace.default.search.paths  = /system/${LIB}
+namespace.default.search.paths += /ctc/${LIB}
 namespace.default.search.paths += /%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)))\
   ))

刷写分区

? ? ? ? 各个厂商的实现不一样,这里不展开。

总结

? ? ? ? 经过上面的修改后,一个自定义的分区基本可以完成我们需要的功能,后续有遇到问题再进行修正。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 16:33:51  更:2021-07-28 16:33:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 3:22:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码