IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    AOSP14移植记录

    FranzKafka95发表于 2024-01-26 08:58:56
    love 0
    Read Time:6 Minute, 36 Second

    Android14已经出来一段时间了,虽然一直都是Beta Version,但行业内已经有厂商开始了适配。近期我也开始了这方面的相关工作,该文章用于记录我在AOSP14移植过程中遇到的一些典型问题。由于移植过程中的涉及到的模块比较多,遇到的问题也比较繁杂,此处仅记录问题的解决思路与方法,涉及到一些更深层次的知识不做深究。

    前期准备

    基线选择:在我开始移植时,AOSP14已经发布了最新的r18版本,抱着要用就用最新的想法,我们也选用了r18作为我们移植的源码Base。其版本信息如下:

    • Security revision:2023-12-05
    • Build:UQ1A.231205.015.A1
    • Branch:android-14.0.0_r18

    Manifest链接参考这里。

    Kernel选择:AOSP14默认使用Kernel 6.1.23,版本信息:6.1.23-android14-4-00257-g7e35917775b8-ab9964412;但也支持5.10,5.15等早期版本。由于我是从AOSP12升级至AOSP14,Kernel部分仍选择5.10版本。

    硬件上使用的是我司自己设计生产的开发板,具体信息略过不表。

    移植步骤

    移植时将相关的配置部分迁移至AOSP14源码目录下的device/${VENDOR_NAME}内;将HAL实现相关迁移至AOSP14源码目录下的hardware/${VENDOR_NAME}内,其余我们自定义的部分如App等迁移至AOSP14源码目录下,新建vendor目录。其目录规划如下:

    模块目录备注
    APPpackages/apps/*:原生APP vendor/${OEM_NAME}/apps/*:特性APP,不区分board
    FrameWork/
    Native/
    HALhardware/interfaces/* vendor/${{VENDOR_NAME }/hardware/interfaces/* hardware/${VENDOR_NAME}/interfaces/*HAL分为原生HAL和我们自定义/实现的HAL 自定义实现的HAL统一放置于 hardware/${VENDOR_NAME}/interfaces/* vendor/bst/hardware/interfaces/*
    device/${OEM_NAME}${CHIP}/${BOARD} ……
    hardware/${OEM_NAME}${CHIP}/${BOARD}
    ……
    Audio
    Camera
    Bluetooth
    ${MODULE} …..
    文件夹目录主要分为Chip相关和Module相关;Module相关如audio,camera,bt等,用于存放代码实现,如果有部分代码实现跟平台相关,则建平台子目录区分;Chip相关则存放配置文件、makefile等;同一模块不同Chip之间的差异应通过Chip目录内的配置或者makefile实现区分
    vendor/${OEM_NAME}${CHIP}/${BOARD}
    ……
    Audio
    Camera
    Bluetooth
    ${MODULE}
    …..
    文件夹目录主要分为Chip相关和Module相关;Module相关如audio,camera,bt等,用于存放代码实现,如果有部分代码实现跟平台相关,则建平台子目录区分;Chip相关则存放配置文件、makefile等;同一模块不同Chip之间的差异应通过Chip目录内的配置或者makefile实现区分

    问题记录

    1.BOARD_BUILD_SYSTEM_ROOT_IMAGE is obsolete

    这是由于在AOSP14中,BOARD_BUILD_SYSTEM_ROOT_IMAGE 已经被废弃;同时使用BOARD_BUILD_GKI_BOOT_IMAGE_WITHOUT_RAMDISK进行替换。

    具体修复示例可参考该链接。

    2.ndk相关依赖报错:undefined module “android.hardware.xxxxx-V1-ndk_platform”

    由于AOSP14中ndk_platform后端已经被废弃,采用ndk后端可以同样满足开发者的需求,因此在相关库的支持中我们不再需要ndk_platform的后端,编译时出现这些报错,我们可以找到对应的Android.bp,替换相应的依赖库即可,示例如下:

             "android.hardware.audio@6.0",
             "android.hardware.audio.common@6.0",
             "android.hardware.audio.effect@6.0",
    -        "android.hardware.automotive.audiocontrol-V1-ndk_platform",
    -        "android.frameworks.automotive.powerpolicy-V1-ndk_platform",
    +        "android.hardware.automotive.audiocontrol-V1-ndk",
    +        "android.frameworks.automotive.powerpolicy-V1-ndk",
             "audiocontrol-caremu",
             "libbase",
             "libbinder",

    3.out/host/linux-x86/mkdtboimg.py不存在

    AOSP14中对mkdtboimg做了一些变更,修改了其名称,因此我们也需要同步这部分改动;

    MKDTBOIMG               := system/libufdt/utils/src/mkdtboimg.py
    或者
    MKDTBOIMG              := $(HOST_OUT_EXECUTABLES)/mkdtboimg

    至于mkdtboimg具体是干嘛的呢,看名字就能猜个大概:用于创建设备树二进制对象,我们烧录镜像的dtbo.img,就需要通过此工具进行打包。

    4.提示framework compatibility matrix are incompatible

    这是由于我们集成的HAL与framework_compatibility_matrix.xml不匹配导致,解决办法要么是重新检查集成打包的HAL,确保framework_compatibility_matrix.xml中记录的HAL都被集成,且版本信息保持一致;要么将未被集成的HAL组件从framework_compatibility_matrix.xml中删除。

    5.SEPolicy配置file.te sysfs_gpu 重定义错误

    解决该问题的办法很简单,直接屏蔽发生重定义的地方;需要说明的是,在SEPolicy中,type定义的类型是不允许重复的。

    6.内核速率限制导致init进程日志无法打印

    在移植过程中,我们需要尽可能详细的日志,以确保启动过程中的任何异常都能被及时观察到。但是我在移植过程中频繁见到这样的信息;

    printk: init: 141 output lines suppressed due to ratelimiting

    这表明某些时候init进程打印到Kernel内的日志可能会因为速率限制而导致无法打印,这会严重影响我们对问题的诊断。此时我们需要修改内核代码,放宽这些限制。内核的日志速率限制依靠宏定义,默认的内核日志速率限制为5s内10条,这里更改内容如下:

    iff --git a/include/linux/ratelimit_types.h b/include/linux/ratelimit_types.h
    index b676aa419..db7eb5be2 100644
    --- a/include/linux/ratelimit_types.h
    +++ b/include/linux/ratelimit_types.h
    @@ -7,7 +7,7 @@
     #include <linux/spinlock_types.h>
    
     #define DEFAULT_RATELIMIT_INTERVAL     (5 * HZ)
    -#define DEFAULT_RATELIMIT_BURST                10
    +#define DEFAULT_RATELIMIT_BURST                1000

    将其速率限制放宽为5s内1000条,需要说明的是过多的日志打印会影响系统的启动。

    7.init进程日志调整

    除了上述Kernel的日志打印限制,init自身也存在一些日志的限制。在移植调试阶段,我们可能会对init进程的日志做一些调整,主要包含两个方面:

    1)调整相关宏定义:init的源码中会根据编译宏来选择性地打印某些日志,如下所示:

     if (LOG_UEVENTS) {
            LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
                      << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent- 
                      >major<< ", " << uevent->minor << " }";
        }
    

    这里我们会看到,init会根据LOG_EVENTS的宏定义打印额外的日志,该宏定义通过Android.bp中的cflags进行控制:

    cflags: [
            "-DALLOW_FIRST_STAGE_CONSOLE=0",
            "-DALLOW_LOCAL_PROP_OVERRIDE=0",
            "-DALLOW_PERMISSIVE_SELINUX=0",
            "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
            "-DDUMP_ON_UMOUNT_FAILURE=0",
            "-DINIT_FULL_SOURCES",
            "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=0",
            "-DLOG_UEVENTS=0",
            "-DREBOOT_BOOTLOADER_ON_PANIC=0",
            "-DSHUTDOWN_ZERO_TIMEOUT=0",
            "-DWORLD_WRITABLE_KMSG=0",
            "-Wall",
            "-Werror",
            "-Wextra",
            "-Wno-unused-parameter",
            "-Wthread-safety",
        ],

    在必要时我们需要手动调整这些宏定义,以尽可能地获取日志。

    2)调整日志等级:init自身也有一套自己的日志等级,我们可以通过在init.rc中加入loglevel字段来控制其日志等级,其日志等级分为7-0,其中日志7属于最低等级,所有日志都会打印,0则属于最高等级,仅打印ERROR类型的日志。

    #Sets init's log level to the integer level, from 7 (all logging) to 0 (fatal logging #only). The numeric values correspond to the kernel log levels, but this command does #not affect the kernel log level. Use the write command to write to #/proc/sys/kernel/printk to change that.Properties are expanded within level.
    
    on early-init
       loglevel 7
    

    需要注意的是,设定loglevel的时机一般在early-init阶段。

    8.logd未启动时输出日志到内核日志

    在移植调试过程中遇到的一个情况是,logd未启动或者启动失败,使用Android日志系统打印的日志无法记录下来,这对诊断服务启动失败这类问题是比较麻烦的,不过我们可以在服务的rc配置中加入stdio_to_kmsg字段来使这部分日志打印到Kmsg中,如下所示:

    service prng_seeder /system/bin/prng_seeder
        user prng_seeder
        group prng_seeder
        stdio_to_kmsg
        socket prng_seeder stream+listen 0666 prng_seeder prng_seeder

    9.出现”Vold-failed“,”boringssl-self-check-failed“等错误

    出现这些错误时,许多守护进程如vold,logd,lmkd等未能正常启动,一些报错日志如下所示:

    1352: [    8.769387] init: cannot execv('/system/bin/prng_seeder'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    1769: [   13.781216] init: cannot execv('/system/bin/prng_seeder'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    1916: [   15.625308] init: cannot execv('/system/bin/logd'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    1930: [   15.783286] init: cannot execv('/system/bin/lmkd'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    1950: [   15.938412] init: cannot execv('/system/bin/sh'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    1970: [   16.152591] init: cannot execv('/system/bin/carwatchdogd'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    2147: [   18.800270] init: cannot execv('/system/bin/prng_seeder'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    2270: [   20.660487] init: cannot execv('/system/bin/logd'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    2282: [   20.805788] init: cannot execv('/system/bin/lmkd'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    2294: [   20.960923] init: cannot execv('/system/bin/sh'). See the 'Debugging init' section of init's README.md for tips: No such file or directory
    2300: [   21.012662] init: cannot execv('/system/bin/vold'). See the 'Debugging init' section of init's README.md for tips: No such file or directory

    更早一些的日志提示如下:

    [    6.107744] e2fsck: linker: Warning: failed to find generated linker configuration from "/linkerconfig/ld.config.txt"
    [    6.118354] e2fsck: WARNING: linker: Warning: failed to find generated linker configuration from "/linkerconfig/ld.config.txt"

    通过对比AOSP14 Cuttlefish的启动日志,没有发现上述错误;从报错信息来看,像是系统找不到对应执行的binary,直觉告诉我可能与/linkerconfig/ld.config.txt文件有关;我在正常运行的系统中查看该文件内容:

    dir.system = /system/bin/
    dir.system = /system/xbin/
    dir.system = /system_ext/bin/
    dir.product = /product/bin/
    dir.vendor = /odm/bin/
    dir.vendor = /vendor/bin/
    ...

    推测ld.config.txt用于指导安卓系统去执行、链接系统中的各种binary或者so库,我们在init.rc中可以看到如下说明:

    # Run apexd-bootstrap so that APEXes that provide critical libraries
    # become available. Note that this is executed as exec_start to ensure that
    # the libraries are available to the processes started after this statement.
    exec_start apexd-bootstrap

    进而追踪到init进程源码:

    int main(int /*argc*/, char** argv) {
      android::base::InitLogging(argv, &android::base::KernelLogger);
      // TODO(b/158468454): add a -v flag or an external setting to change severity.
      android::base::SetMinimumLogSeverity(android::base::INFO);
    
      // set umask to 022 so that files/dirs created are accessible to other
      // processes e.g.) /apex/apex-info-list.xml is supposed to be read by other
      // processes
      umask(022);
    
      InstallSigtermSignalHandler();
    
      android::apex::SetConfig(android::apex::kDefaultConfig);
    
      android::apex::ApexdLifecycle& lifecycle =
          android::apex::ApexdLifecycle::GetInstance();
      bool booting = lifecycle.IsBooting();
    
      const bool has_subcommand = argv[1] != nullptr;
      if (!android::sysprop::ApexProperties::updatable().value_or(false)) {
        if (!has_subcommand) {
      }
    
      if (has_subcommand) {
        return HandleSubcommand(argv);
       }
       ....
      }

    在其main函数内处理时,我们看到apexd首先判断是否存在启动参数,再通过android::sysprop::ApexProperties::updatable().value_or(false)区分进入不同的分支;通过观察出现问题时的日志和Trout中正常启动的日志,发现两者进入不同的分支,最终找到/build/make/target/product/updatable_apex.mk:

    ifneq ($(OVERRIDE_TARGET_FLATTEN_APEX),true)
      # com.android.apex.cts.shim.v1_prebuilt overrides CtsShimPrebuilt
      # and CtsShimPrivPrebuilt since they are packaged inside the APEX.
      PRODUCT_PACKAGES += com.android.apex.cts.shim.v1_prebuilt
      PRODUCT_VENDOR_PROPERTIES := ro.apex.updatable=true
      TARGET_FLATTEN_APEX := false
      # Use compressed apexes in pre-installed partitions.
      # Note: this doesn't mean that all pre-installed apexes will be compressed.
      #  Whether an apex is compressed or not is controlled at apex Soong module
      #  via compresible property.
      PRODUCT_COMPRESSED_APEX := true
    endif

    我们可以看到android::sysprop::ApexProperties::updatable()获取的值就是ro.apex.updatable的值,进而生成ld.config.txt,因此我们将该makefile在我们自己的device.mk中进行引用,即解决该问题。

    10.adb remount,提示Device must be bootloader unlocked

    该日志对应的源码位于fs_mgr_remount.cpp内,如下所示:

    //system/core/fs_mgr/fs_mgr_remount.cpp
    if (android::base::GetProperty("ro.boot.verifiedbootstate", "") != "orange") {
          LOG(ERROR) << "Device must be bootloader unlocked";
          return EXIT_FAILURE;
    }

    通过搜索Makefile,并没有发现直接设置ro.boot.verifiedbootstate的地方,但发现通过BOARD_KERNEL_CMDLINE 设定verifiedbootstate,同时对比Trout的makefile,最终确认修改BoardConfig.mk

    BOARD_KERNEL_CMDLINE += androidboot.verifiedbootstate=orange

    Bingo,最终编译之后烧录系统:

    Happy
    Happy
    0 0 %
    Sad
    Sad
    0 0 %
    Excited
    Excited
    0 0 %
    Sleepy
    Sleepy
    0 0 %
    Angry
    Angry
    0 0 %
    Surprise
    Surprise
    0 0 %

    The post AOSP14移植记录 first appeared on FranzKafka Blog.



沪ICP备19023445号-2号
友情链接