Android13 automotive移植

需求背景

目前Google已经释放Android 13的基线代码,其中包括了automotive的product,但芯片厂商针对automotive的Android 13基线并未释放,在释放前,下面的方案商无法得知芯片厂商的将会以哪个product来释放automotive基线,因此,需要提前预研在其他product上移植automotive功能。

相关概念

  • automotive

    翻译过来就是汽车的意思,它是基于Android平台的车载信息娱乐系统,简称IVI(In-Vehicle Infotainment),在Android 13中,automotive长这样

  • product

    AOSP针对不同的设备,设置的不同的产品。我们在编译源码时,执行lunch命令后展示的列表就是当前基线提供的所有product

    shell 复制代码
    q@jason-OptiPlex-7080:/projects/AOSP_Android13$ source build/envsetup.sh 
    q@jason-OptiPlex-7080:/projects/AOSP_Android13$ lunch
    
    You're building on Linux
    
    Lunch menu .. Here are the common combinations:
         1. aosp_arm-eng
         2. aosp_arm64-eng
         3. aosp_barbet-userdebug
         4. aosp_bluejay-userdebug
         5. aosp_bluejay_car-userdebug
         6. aosp_bramble-userdebug
         7. aosp_bramble_car-userdebug
         8. aosp_car_arm-userdebug
         9. aosp_car_arm64-userdebug
         10. aosp_car_x86-userdebug
         11. aosp_car_x86_64-userdebug
         12. aosp_cf_arm64_auto-userdebug
         13. aosp_cf_arm64_phone-userdebug
         14. aosp_cf_x86_64_foldable-userdebug
         15. aosp_cf_x86_64_pc-userdebug
         16. aosp_cf_x86_64_phone-userdebug
         17. aosp_cf_x86_64_tv-userdebug
         18. aosp_cf_x86_auto-userdebug
         19. aosp_cf_x86_phone-userdebug
         20. aosp_cf_x86_tv-userdebug
         21. aosp_cheetah-userdebug
         22. aosp_cloudripper-userdebug
         23. aosp_coral-userdebug
         24. aosp_coral_car-userdebug
         25. aosp_flame-userdebug
         26. aosp_flame_car-userdebug
         27. aosp_oriole-userdebug
         28. aosp_oriole_car-userdebug
         29. aosp_panther-userdebug
         30. aosp_raven-userdebug
         31. aosp_raven_car-userdebug
         32. aosp_ravenclaw-userdebug
         33. aosp_redfin-userdebug
         34. aosp_redfin_car-userdebug
         35. aosp_redfin_vf-userdebug
         36. aosp_rice14-userdebug
         37. aosp_slider-userdebug
         38. aosp_sunfish-userdebug
         39. aosp_sunfish_car-userdebug
         40. aosp_trout_arm64-userdebug
         41. aosp_trout_x86-userdebug
         42. aosp_whitefin-userdebug
         43. aosp_x86-eng
         44. aosp_x86_64-eng
         45. arm_krait-eng
         46. arm_v7_v8-eng
         47. armv8-eng
         48. armv8_cortex_a55-eng
         49. armv8_kryo385-eng
         50. beagle_x15-userdebug
         51. beagle_x15_auto-userdebug
         52. car_ui_portrait-userdebug
         53. car_x86_64-userdebug
         54. db845c-userdebug
         55. gsi_car_arm64-userdebug
         56. gsi_car_x86_64-userdebug
         57. hikey-userdebug
         58. hikey64_only-userdebug
         59. hikey960-userdebug
         60. hikey960_tv-userdebug
         61. hikey_tv-userdebug
         62. poplar-eng
         63. poplar-user
         64. poplar-userdebug
         65. qemu_trusty_arm64-userdebug
         66. rb5-userdebug
         67. sdk_car_arm-userdebug
         68. sdk_car_arm64-userdebug
         69. sdk_car_portrait_x86_64-userdebug
         70. sdk_car_x86-userdebug
         71. sdk_car_x86_64-userdebug
         72. sdk_pc_x86_64-userdebug
         73. silvermont-eng
         74. uml-userdebug
         75. yukawa-userdebug
         76. yukawa_sei510-userdebug
    
    Which would you like? [aosp_arm-eng]
    Pick from common choices above (e.g. 13) or specify your own (e.g. aosp_barbet-eng):

    不同的product打包出来的镜像略有不同,最后在设备上展示的效果也不一样,比如,手机刷的镜像跟车载刷的镜像,Launcher,Settings等不尽相同。在上面lunch的product列表中,带有car标识的都是automotive product,其他的大部分是phone product。

  • automotive移植

    就是将automotive的功能移植到非automotive的product上,即将非automotive product改造成automotive product,本篇我们将72. sdk_pc_x86_64-userdebug改造成71. sdk_car_x86_64-userdebug72. sdk_pc_x86_64-userdebug长这样

​ 现在我们要将它改造成上面automotive的样子

product编译配置

每个product的编译离不开四个文件:

  • AndroidProducts.mk:定义product的名字,以及对应的Makefile文件
  • product.mk:对应product的Makefile文件,比如sdk_pc_x86_64.mk,里面包含了product所有的软件配置
  • BoardConfig.mk:对应product的硬件配置信息
  • device.mk:芯片厂商自定义的硬件配置信息

​ 看一下我们要改造的72. sdk_pc_x86_64-userdebug这四个文件的内容

makefile 复制代码
#AndroidProducts.mk
PRODUCT_MAKEFILES := \
    $(LOCAL_DIR)/sdk_pc_x86_64.mk \

COMMON_LUNCH_CHOICES := \
    sdk_pc_x86_64-userdebug \

COMMON_LUNCH_CHOICES定义了lunch combo名称为sdk_pc_x86_64-userdebug,该名称包括产品名+构建模式,userdebug表示调试模式,此外还有user模式和eng模式,关于这三个模式的区别,这里不做过多介绍,可自行查阅相关资料。PRODUCT_MAKEFILES指定了product的makefile文件,$(LOCAL_DIR)表示当前目录。

makefile 复制代码
#sdk_pc_x86_64.mk
PRODUCT_COPY_FILES += device/generic/goldfish/pc/config.ini.pc:config.ini

$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_x86_64.mk)

PRODUCT_NAME := sdk_pc_x86_64
PRODUCT_DEVICE := emulator64_x86_64
PRODUCT_BRAND := Android
PRODUCT_MODEL := PC on x86_64 emulator
PRODUCT_PACKAGE_OVERLAYS := device/generic/goldfish/pc/overlay
PRODUCT_COPY_FILES += \
    device/generic/goldfish/pc/data/etc/pc.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/pc.xml
PRODUCT_SDK_ADDON_SYS_IMG_SOURCE_PROP := device/generic/goldfish/pc/images_source.prop_template

sdk_pc_x86_64.mk文件里定义了产品的name、device硬件配置、brand、model等信息,其中PRODUCT_DEVICE指向BoardConfig.mkdevice.mk的父目录

$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_x86_64.mk)表示该文件继承自sdk_x86_64.mk$(SRC_TARGET_DIR)指向build/target目录。

PRODUCT_COPY_FILES表示将源码中的文件复制到镜像分区中

makefile 复制代码
#BoardConfig.mk
# x86_64 emulator specific definitions
TARGET_CPU_ABI := x86_64
TARGET_ARCH := x86_64
TARGET_ARCH_VARIANT := x86_64
TARGET_2ND_ARCH_VARIANT := x86_64

BOARD_DO_NOT_STRIP_VENDOR_MODULES := true

TARGET_PRELINK_MODULE := false
include build/make/target/board/BoardConfigGsiCommon.mk
include build/make/target/board/BoardConfigEmuCommon.mk

BOARD_USERDATAIMAGE_PARTITION_SIZE := 576716800

BOARD_SEPOLICY_DIRS += device/generic/goldfish/sepolicy/x86

# Wifi.
BOARD_WLAN_DEVICE           := emulator
BOARD_HOSTAPD_DRIVER        := NL80211
BOARD_WPA_SUPPLICANT_DRIVER := NL80211
BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_simulated
BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_simulated
WPA_SUPPLICANT_VERSION      := VER_0_8_X
WIFI_DRIVER_FW_PATH_PARAM   := "/dev/null"
WIFI_DRIVER_FW_PATH_STA     := "/dev/null"
WIFI_DRIVER_FW_PATH_AP      := "/dev/null"

BoardConfig.mk中定义了cpu的参数,wifi配置,并且通过include引用了BoardConfigGsiCommon.mkBoardConfigEmuCommon.mk文件里的配置,前者用于通用系统映像的配置,后者用于模拟器的配置。

device.mk是芯片厂商自定义的配置,这里我们看的是原生代码,分析此文件没啥意义。

上述是72. sdk_pc_x86_64-userdebug的编译配置文件,同理可析71. sdk_car_x86_64-userdebug

product编译流程

当我们执行source build/envsetup.sh命令时,会将envsetup.sh文件中定义的函数加载到当前shell环境,建立shell命令,比如lunchmakecroot等。

当执行lunch命令时,会执行envsetup.sh中的lunch()函数

shell 复制代码
function lunch()
{
	# 定义局部变量`answer`来存放用户通过`lunch`命令传进来的参数(即`lunch-combo`,可以是字符串或数字)
    local answer

    if [[ $# -gt 1 ]]; then
        echo "usage: lunch [target]" >&2
        return 1
    fi

    local used_lunch_menu=0

    if [ "$1" ]; then
        answer=$1
    else
    	# 如果用户没有没有传进来任何值,则打印出`lunch-combo`列表,提示用户输入
        print_lunch_menu
        echo "Which would you like? [aosp_arm-eng]"
        echo -n "Pick from common choices above (e.g. 13) or specify your own (e.g. aosp_barbet-eng): "
        read answer
        used_lunch_menu=1
    fi

    local selection=

    if [ -z "$answer" ]
    then
    	#如果用户没有选择,则默认选择为aosp_arm-eng
        selection=aosp_arm-eng
    elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
    then
        local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES))
        if [ $answer -le ${#choices[@]} ]
        then
            # array in zsh starts from 1 instead of 0.
            if [ -n "$ZSH_VERSION" ]
            then
                selection=${choices[$(($answer))]}
            else
                selection=${choices[$(($answer-1))]}
            fi
        fi
    else
        selection=$answer
    fi

    #省略部分代码

    TARGET_PRODUCT=$product \
    TARGET_BUILD_VARIANT=$variant \
    TARGET_PLATFORM_VERSION=$version \
    #build_build_var_cache()最终会执行到soong_ui.bash
    build_build_var_cache
    if [ $? -ne 0 ]
    then
        if [[ "$product" =~ .*_(eng|user|userdebug) ]]
        then
            echo "Did you mean -${product/*_/}? (dash instead of underscore)"
        fi
        return 1
    fi
    #get_build_var()最终会执行到soong_ui.bash
    export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
    export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
    if [ -n "$version" ]; then
      export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
    else
      unset TARGET_PLATFORM_VERSION
    fi
    
    #省略部分代码
}

# Get the exact value of a build variable.
function get_build_var()
{
    if [ "$BUILD_VAR_CACHE_READY" = "true" ]
    then
        eval "echo \"\${var_cache_$1}\""
        return 0
    fi

    local T=$(gettop)
    if [ ! "$T" ]; then
        echo "Couldn't locate the top of the tree.  Try setting TOP." >&2
        return 1
    fi
    #执行soong_ui.bash
    (\cd $T; build/soong/soong_ui.bash --dumpvar-mode $1)
}

lunch()函数最终会执行build/soong/soong_ui.bash脚本

bash 复制代码
#记录了开始构建时的精确时间戳,以毫秒为单位
export TRACE_BEGIN_SOONG=$(date +%s%N)

# Function to find top of the source tree (if $TOP isn't set) by walking up the
# tree.
#定义gettop函数
function gettop
{
	#找到源代码树的根目录,如果环境变量 TOP 已经设置并且对应的路径存在 build/soong/root.bp 文件,那么函数将返回该路径;否则,它将从当前目录向上查找,直到找到名为 build/soong/root.bp 的文件。
    local TOPFILE=build/soong/root.bp
    if [ -n "${TOP-}" -a -f "${TOP-}/${TOPFILE}" ] ; then
        # The following circumlocution ensures we remove symlinks from TOP.
        (cd $TOP; PWD= /bin/pwd)
    else
        if [ -f $TOPFILE ] ; then
            # The following circumlocution (repeated below as well) ensures
            # that we record the true directory name and not one that is
            # faked up with symlink names.
            PWD= /bin/pwd
        else
            local HERE=$PWD
            T=
            while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
                \cd ..
                T=`PWD= /bin/pwd -P`
            done
            \cd $HERE
            if [ -f "$T/$TOPFILE" ]; then
                echo $T
            fi
        fi
    fi
}

#将当前工作目录(PWD)保存到环境变量 ORIGINAL_PWD 中
# Save the current PWD for use in soong_ui
export ORIGINAL_PWD=${PWD}
#调用 gettop 函数,并将返回的结果保存到环境变量 TOP 中
export TOP=$(gettop)
#加载 microfactory.bash 脚本,该脚本应该位于 TOP 环境变量所指示的路径中
source ${TOP}/build/soong/scripts/microfactory.bash

#调用 Android 构建系统的soong_ui,具体构建和运行某些组件
soong_build_go soong_ui android/soong/cmd/soong_ui
soong_build_go mk2rbc android/soong/mk2rbc/cmd
soong_build_go rbcrun rbcrun/cmd

#将当前目录切换到 TOP 环境变量所指示的路径
cd ${TOP}
#执行 soong_ui 程序,其路径为 getoutdir 环境变量的值,参数为 $@(表示所有传入的命令行参数)
exec "$(getoutdir)/soong_ui" "$@"

上面在执行soong_build_go soong_ui android/soong/cmd/soong_ui时,会执行/build/soong/cmd/soong_ui/main.go中的main()函数

go 复制代码
func main() {
    //省略部分代码,省略的代码为构造build.Findsources()函数所需要的参数
	// Create a source finder.
	f := build.NewSourceFinder(buildCtx, config)
	defer f.Shutdown()
    //寻找资源文件
	build.FindSources(buildCtx, config, f)
	
	c.run(buildCtx, config, args, logsDir)
}

/build/soong/cmd/soong_ui/main.go中的main()函数中,核心主要是通过build.FindSources()去寻找所有的makefile文件和bp文件

go 复制代码
// FindSources searches for source files known to <f> and writes them to the filesystem for
// use later.
func FindSources(ctx Context, config Config, f *finder.Finder) {
	// note that dumpDir in FindSources may be different than dumpDir in NewSourceFinder
	// if a caller such as multiproduct_kati wants to share one Finder among several builds
	dumpDir := config.FileListDir()
	os.MkdirAll(dumpDir, 0777)

	// Stop searching a subdirectory recursively after finding an Android.mk.
    //寻找所有的Android.mk文件,并添加到Android.mk.list中
	androidMks := f.FindFirstNamedAt(".", "Android.mk")
	err := dumpListToFile(ctx, config, androidMks, filepath.Join(dumpDir, "Android.mk.list"))
	if err != nil {
		ctx.Fatalf("Could not export module list: %v", err)
	}

	// Gate collecting/reporting mk metrics on builds that specifically request
	// it, as identifying the total number of mk files adds 4-5ms onto null
	// builds.
	if config.reportMkMetrics {
		androidMksTotal := f.FindNamedAt(".", "Android.mk")

		ctx.Metrics.SetToplevelMakefiles(len(androidMks))
		ctx.Metrics.SetTotalMakefiles(len(androidMksTotal))
		ctx.Metrics.DumpMkMetrics(config.MkMetrics())
	}

	// Stop searching a subdirectory recursively after finding a CleanSpec.mk.
    //寻找所有的CleanSpec.mk文件,并添加到CleanSpec.mk.list中
	cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
	err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
	if err != nil {
		ctx.Fatalf("Could not export module list: %v", err)
	}

	// Only consider AndroidProducts.mk in device/, vendor/ and product/, recursively in these directories.
    //寻找所有的AndroidProducts.mk文件,并添加到AndroidProducts.mk.list中
	androidProductsMks := f.FindNamedAt("device", "AndroidProducts.mk")
	androidProductsMks = append(androidProductsMks, f.FindNamedAt("vendor", "AndroidProducts.mk")...)
	androidProductsMks = append(androidProductsMks, f.FindNamedAt("product", "AndroidProducts.mk")...)
	err = dumpListToFile(ctx, config, androidProductsMks, filepath.Join(dumpDir, "AndroidProducts.mk.list"))
	if err != nil {
		ctx.Fatalf("Could not export product list: %v", err)
	}

	// Recursively look for all Bazel related files.
	bazelFiles := f.FindMatching(".", findBazelFiles)
	err = dumpListToFile(ctx, config, bazelFiles, filepath.Join(dumpDir, "bazel.list"))
	if err != nil {
		ctx.Fatalf("Could not export bazel BUILD list: %v", err)
	}

	// Recursively look for all OWNERS files.
	owners := f.FindNamedAt(".", "OWNERS")
	err = dumpListToFile(ctx, config, owners, filepath.Join(dumpDir, "OWNERS.list"))
	if err != nil {
		ctx.Fatalf("Could not find OWNERS: %v", err)
	}

	// Recursively look for all TEST_MAPPING files.
	testMappings := f.FindNamedAt(".", "TEST_MAPPING")
	err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
	if err != nil {
		ctx.Fatalf("Could not find TEST_MAPPING: %v", err)
	}

	// Recursively look for all Android.bp files
    //寻找所有的Android.bp文件,并添加到Android.bp.list中
	androidBps := f.FindNamedAt(".", "Android.bp")
	if len(androidBps) == 0 {
		ctx.Fatalf("No Android.bp found")
	}
	err = dumpListToFile(ctx, config, androidBps, filepath.Join(dumpDir, "Android.bp.list"))
	if err != nil {
		ctx.Fatalf("Could not find modules: %v", err)
	}

	// Recursively look for all product/board config files.
	configurationFiles := f.FindMatching(".", findProductAndBoardConfigFiles)
	err = dumpListToFile(ctx, config, configurationFiles, filepath.Join(dumpDir, "configuration.list"))
	if err != nil {
		ctx.Fatalf("Could not export product/board configuration list: %v", err)
	}

	if config.Dist() {
		f.WaitForDbDump()
		// Dist the files.db plain text database.
		distFile(ctx, config, f.DbPath, "module_paths")
	}
}

执行完build.FindSources()后,所有的AndroidProduct.mk文件都将被发现,这时就能展示所有的lunch combo项,然后根据用户选择的combo,选择对应的AndroidProduct.mk

最后执行make -j16命令后,调用build/envsetup.sh里面的make()函数,最终会根据对应AndroidProduct.mk的规则,选择对应的product.mk进行编译。

移植思路

了解了product编译流程后,自然而然我们能想到的移植思路是,在72. sdk_pc_x86_64-userdebugAndroidProduct.mk文件里配置71. sdk_car_x86_64-userdebug的product.mk,也就是将72. sdk_pc_x86_64-userdebug的编译流程改向71. sdk_car_x86_64-userdebug的编译流程,这样我们的目标product就会按照车载product的配置进行编译了。查看车载product的编译配置,即打开sdk_car_x86_64.mk文件

makefile 复制代码
PRODUCT_PACKAGE_OVERLAYS := device/generic/car/common/overlay

$(call inherit-product, device/generic/car/emulator/aosp_car_emulator.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/sdk_x86_64.mk)

EMULATOR_VENDOR_NO_SOUND := true
PRODUCT_NAME := sdk_car_x86_64
PRODUCT_DEVICE := emulator_car_x86_64
PRODUCT_BRAND := Android
PRODUCT_MODEL := Car on x86_64 emulator

可以看到,这里只是继承了device/generic/car/emulator/aosp_car_emulator.mk$(SRC_TARGET_DIR)/product/sdk_x86_64.mk这两个makefile文件,以及添加了PRODUCT_PACKAGE_OVERLAYSEMULATOR_VENDOR_NO_SOUND两项配置,我们将这些配置全部复制到sdk_pc_x86_64.mk

当然,PRODUCT_NAME是不能变的,并且PRODUCT_DEVICE选择车载的硬件配置。

最后执行编译

shell 复制代码
source build/envsetup.sh
lunch sdk_pc_x86_64-userdebug
make -j16

等待编译完成,启动模拟器

shell 复制代码
emulator

完美进入automotive,并且Model显示我们自定义的名称

参考资料

blog.csdn.net/daoshuti/ar...

写给应用开发的 Android Framework 教程------玩转 AOSP 篇之添加 Product

相关推荐
烬奇小云3 小时前
认识一下Unicorn
android·python·安全·系统安全
顾北川_野15 小时前
Android 进入浏览器下载应用,下载的是bin文件无法安装,应为apk文件
android
CYRUS STUDIO15 小时前
Android 下内联汇编,Android Studio 汇编开发
android·汇编·arm开发·android studio·arm
右手吉他15 小时前
Android ANR分析总结
android
PenguinLetsGo17 小时前
关于 Android15 GKI2407R40 导致梆梆加固软件崩溃
android·linux
杨武博19 小时前
音频格式转换
android·音视频
音视频牛哥21 小时前
Android音视频直播低延迟探究之:WLAN低延迟模式
android·音视频·实时音视频·大牛直播sdk·rtsp播放器·rtmp播放器·android rtmp
ChangYan.21 小时前
CondaError: Run ‘conda init‘ before ‘conda activate‘解决办法
android·conda
二流小码农21 小时前
鸿蒙开发:ForEach中为什么键值生成函数很重要
android·ios·harmonyos
夏非夏1 天前
Android 生成并加载PDF文件
android