AOSP编译实战:从整编到模块编译的进阶指南

AOSP编译实战:从整编到模块编译的进阶指南 1. AOSP项目整编实战指南第一次接触AOSP整编时我花了整整三天才成功编译出第一个系统镜像。当时最大的误区就是以为和普通应用开发一样简单结果在环境配置阶段就踩了不少坑。现在回想起来那些错误其实都很基础但确实让新手很头疼。1.1 标准编译系统全流程标准的AOSP整编流程其实就像做菜需要先准备好食材环境配置然后按照菜谱编译命令一步步操作。这里我整理了一个完整的操作清单# 1. 初始化环境相当于打开厨房的灯 source build/envsetup.sh # 2. 选择编译目标决定做什么菜 lunch aosp_arm-eng # 以ARM架构工程版为例 # 3. 开始编译开火做饭 make -j$(nproc) # 使用所有CPU核心加速编译这个过程中最容易出问题的是lunch阶段。我第一次编译时傻乎乎地直接运行了make结果报了一堆奇怪的错误。后来才知道必须先用lunch选择目标设备类型。常见的编译目标有aosp_arm-engARM架构工程版aosp_x86_64-userdebugx86_64架构调试版sdk_phone_x86_64模拟器专用版本编译完成后产物会统一存放在out/target/product/[设备名]目录下。我习惯用tree命令快速查看生成的文件结构tree -L 3 out/target/product/generic_x86_64/system1.2 厂商定制编译系统解析在实际项目中我们很少直接使用原生AOSP的编译系统。各大厂商都会基于AOSP开发自己的编译框架比如# 某厂商的典型编译命令 source vendor_setup.sh # 加载厂商定制环境 mk -u newk logo # 编译user版本包含内核和logo分区这种定制系统通常会提供很多快捷参数-u指定user版本-d指定userdebug版本newk只编译内核logo只编译开机logo我参与过的一个项目其编译系统做了深度定制甚至支持模块化编译参数./build.sh --modulevendor --variantuserdebug --logfull这种设计虽然提高了灵活性但也增加了学习成本。建议新手先用标准AOSP熟悉基础流程再接触厂商定制系统。2. 模块编译深度解析模块编译是AOSP开发中最实用的技能之一。记得有次我只需要修改Settings应用的一个字符串如果整编至少要等2小时而模块编译只用了3分钟就搞定了。2.1 模块编译原理剖析AOSP的模块系统就像乐高积木每个模块都是独立的构建单元。关键是要理解几个核心命令的区别mm编译当前目录下的模块Building in current directorymmm编译指定路径的模块Building specified directorymake [模块名]全局编译指定模块实际操作示例# 编译Settings应用 mmm packages/apps/Settings # 编译SystemUI make SystemUI # 编译framework-res资源包 mm frameworks/base/core/res模块编译的核心在于定位Android.mk或Android.bp文件。这两个文件就像模块的身份证记录了模块的所有关键信息。我常用的定位技巧是# 快速查找模块定义文件 find . -name Android.bp | grep Settings2.2 典型模块编译实战以修改SystemUI状态栏为例完整的工作流应该是修改代码vim frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java增量编译mmm frameworks/base/packages/SystemUI部署到设备adb root adb remount adb push out/target/product/generic_x86_64/system/priv-app/SystemUI/SystemUI.apk /system/priv-app/SystemUI/重启生效adb shell killall systemui # 优雅重启SystemUI进程这里有个坑要注意直接adb install系统应用通常会失败必须用adb push到对应系统目录。我遇到过最棘手的情况是推送后界面黑屏后来发现是SELinux权限问题需要用adb shell restorecon修复。3. 编译产物部署技巧编译只是第一步如何把生成的产物部署到设备上才是真正的挑战。不同位置的APK需要不同的处理方式这里我总结了一张部署方式对照表模块类型典型路径部署方式生效方式普通系统应用/system/app/Settingsadb push重启或kill进程特权应用/system/priv-app/SystemUIadb push chmodkill进程Framework资源/system/framework/framework-res.apkadb push强制重启系统服务/system/framework/services.jaradb push强制重启VNDK库/vendor/lib/hw/bluetooth.default.soadb push reboot强制重启3.1 adb部署的进阶技巧常规的adb push/pull大家都会用但有几个实用技巧可能很多人不知道批量推送adb remount adb shell mkdir -p /system/app/NewApp adb push out/target/product/xxx/system/app/NewApp/* /system/app/NewApp/保留文件权限adb push --preserve-permissions framework.jar /system/framework/快速验证文件差异adb pull /system/framework/framework.jar old.jar diff -u (unzip -l old.jar) (unzip -l framework.jar)我强烈建议每次push前先备份原文件特别是修改系统核心组件时。有次我直接覆盖了services.jar导致设备变砖最后只能重新刷机。4. Android构建系统解析理解Android.mk和Android.bp文件的区别就像掌握C和Rust的差异一样重要。这两种构建描述文件代表了Android构建系统的演进历程。4.1 Android.mk深度解读Android.mk使用Makefile语法典型的模块定义如下LOCAL_PATH : $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE : MyLibrary LOCAL_SRC_FILES : $(call all-subdir-java-files) LOCAL_JAVA_LIBRARIES : framework LOCAL_MODULE_TAGS : optional include $(BUILD_JAVA_LIBRARY)关键参数解析LOCAL_MODULE_TAGS控制模块编译条件eng只在工程版编译user在用户版编译tests测试模块LOCAL_MODULE_PATH指定安装路径$(TARGET_OUT_SYSTEM_APPS)system/app$(TARGET_OUT_VENDOR_SHARED_LIBRARIES)vendor/lib4.2 Android.bp新特性Android.bp采用更简洁的BluePrint语法同一个模块可以这样定义java_library { name: MyLibrary, srcs: [src/**/*.java], libs: [framework], installable: true, product_specific: true, # 编译到product分区 }新引入的关键特性visibility控制模块可见性visibility: [//frameworks/base]apex_available支持APEX更新apex_available: [com.android.runtime]cc_object支持模块化C编译我在移植一个旧项目时需要将Android.mk转换为Android.bp发现最麻烦的是处理条件编译。旧系统常用ifeq判断变量而新系统推荐用override_系列属性cc_defaults { name: flags_defaults, cflags: [-Wall], } cc_library { name: mylib, defaults: [flags_defaults], override_cflags: [-O3], # 覆盖默认配置 }5. 设备解锁与分区管理第一次尝试修改系统分区时我遇到了经典的Read-only file system错误。这个经历让我深刻认识到Android分区安全机制的重要性。5.1 解锁完整流程安全解锁需要以下步骤启用开发者选项连续点击版本号7次开启OEM解锁选项进入fastboot模式adb reboot bootloader执行解锁fastboot flashing unlock禁用验证adb root adb disable-verity adb reboot重新挂载adb root adb remount5.2 分区挂载技巧解锁后还需要了解各个分区的挂载特性# 查看所有分区挂载状态 adb shell mount | grep -E system|vendor|product # 临时重新挂载为可写 adb shell mount -o rw,remount /system # 查看分区属性 adb shell getprop | grep partition特别提醒某些厂商设备的分区布局可能很特殊比如动态分区system、vendor等合并为super分区只读分区odm等分区即使解锁也无法修改逻辑分区A/B系统有slot_a/slot_b6. 厂商定制项目实战在MTK平台项目中我发现模块编译流程与AOSP标准有很大差异。以Settings应用为例典型的工作流是# 1. 进入模块目录 cd packages/apps/Settings # 2. 使用gradle编译 ./gradlew assembleMtk8765Release # 3. 查找产出物 find build/outputs -name *.apk # 4. 部署到设备 adb install build/outputs/apk/release/Settings-mtk8765-release.apk这种编译方式的特点是使用Gradle而非Make构建需要配置厂商特定的签名密钥产出物路径与AOSP标准不同在某个高通平台项目中我发现更复杂的场景同一个APK需要针对不同运营商生成不同变体。解决方案是在Android.bp中使用soong_configssoong_config_module_type { name: carrier_settings, module_type: android_app, config_namespace: carriers, variables: [tmo, att, verizon], } carrier_settings { name: Settings_carrier, tmo: { srcs: [src/tmo/**/*.java], package_name: com.android.settings.tmo, }, att: { srcs: [src/att/**/*.java], manifest: AndroidManifest-att.xml, }, }这种设计允许通过一个构建命令生成多个变体make Settings_carrier_tmo Settings_carrier_att