搭建Android AOSP源码编译工作环境

  • 简单介绍系统架构、编译环境的搭建
  • 简单介绍利用 AndroidStudio 调试 system_process 进程的方法及编译更新部分系统模块的方式

Android系统架构

Android 系统架构如下两图所示:
在这里插入图片描述

  • 应用框架。应用框架最常被应用开发者使用。很多此类 API 都可以直接映射到底层 HAL 接口,并可提供与实现驱动程序相关的实用信息。
  • Binder IPC。Binder 进程间通信 (IPC) 机制允许应用框架跨越进程边界并调用 Android 系统服务代码,这使得高级框架 API 能与 Android 系统服务进行交互。在应用框架级别,开发者无法看到此类通信的过程,但一切似乎都在“按部就班地运行”。
  • 系统服务。系统服务是专注于特定功能的模块化组件,例如窗口管理器、搜索服务或通知管理器。应用框架 API 所提供的功能可与系统服务通信,以访问底层硬件。Android 包含两组服务:“系统”(诸如窗口管理器和通知管理器之类的服务)和“媒体”(与播放和录制媒体相关的服务)。
  • 硬件抽象层 (HAL)。HAL 可定义一个标准接口以供硬件供应商实现,这可让 Android 忽略较低级别的驱动程序实现。借助 HAL,硬件开发者可以顺利实现相关功能,而不会影响或更改更高级别的系统。HAL 实现会被封装成模块,并会由 Android 系统适时地加载。如需了解详情,请参阅硬件抽象层 (HAL)
  • Linux 内核。开发设备驱动程序与开发典型的 Linux 设备驱动程序类似。Android 使用的 Linux 内核版本包含一些特殊的补充功能,例如低内存终止守护进程(一个内存管理系统,可更主动地保留内存)、唤醒锁定(一种 PowerManager 系统服务)、Binder IPC 驱动程序,以及对移动嵌入式平台来说非常重要的其他功能。这些补充功能主要用于增强系统功能,不会影响驱动程序开发。可以使用任意版本的内核,只要它支持所需功能(如 Binder 驱动程序)即可。不过建议使用 Android 内核的最新版本。如需了解详情,请参阅构建内核一文。

以上部分内容来源: https://source.android.google.cn/devices/architecture?hl=zh-cn

搭建开发环境并编译源码

本节将讨论 Android 11 源码下载和编译的方法、AndroidStudio开发环境的搭建方法,及 system_process 进程的调试方法等相关知识。

选择分支

可参考 source.android.google.cn 的文档 代号、标记和 Build 号 ,可以看到当前最新的版本及标记如下:

Build标记版本支持的设备安全补丁程序级别
RQ2A.210305.007android-11.0.0_r33Android11
RQ1D.210105.003android-11.0.0_r28Android11Pixel 3、Pixel 3 XL、Pixel 4a (5G)、Pixel 52021-01-05
RQ1A.210105.003android-11.0.0_r27Android11Pixel 3、Pixel 3 XL、Pixel 4、Pixel 4a (5G)、Pixel 4 XL、Pixel 52021-01-05
RQ1A.210105.002android-11.0.0_r26Android11Pixel 3a、Pixel 3a XL、Pixel 4a2021-01-05

参考代码仓库中的最新Tag,我们选择 android-11.0.0_r33

搭建Android构建环境

构建环境的搭建可参考官方的 搭建构建环境 文档,以下结合实际搭建过程中遇到的问题简要进行介绍。

构建环境要求

首先需要机器满足一些要求,一般来说,需要:

  • 操作系统: Linux 或者 macOS
  • 内存: 16GB的可用RAM/交换空间
  • 硬盘: 检出代码至少250GB可用磁盘空间,如果需要构建,则还需要150GB

从Android8.0 开始,可以使用源码中带的Dockerfile来构建一个镜像用于编译aosp源码;下面我们将分别介绍如何构建编译用的Docker镜像及直接在macOS11.2上搭建构建环境,构建时根据情况任选一种即可。

提示:

最新的构建要求可以参考官方文档 aosp源码构建要求

搭建构建环境-Docker

docker镜像基于ubuntu:18.04,所以ubuntu上也可以按照dockerfile中的命令配置环境

这里假设安装了Docker,如果没有安装,可以参考Docker官方安装文档进行安装。

mkdir docker
cd docker

将如下内容保存为Dockerfile,并存储到之前建立的docker目录

FROM ubuntu:18.04

ARG userid
ARG groupid
ARG username

RUN apt-get update \
    && apt-get install -y git-core gnupg flex bison build-essential zip \
    curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev \
    x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig \
    python3 \
    && rm -rf /var/lib/apt/lists/* 
    
# 最新的AOSP源码中已经带了JDK,所以无需安装
# RUN curl -o jdk8.tgz https://android.googlesource.com/platform/prebuilts/jdk/jdk8/+archive/master.tar.gz \
#  && tar -zxf jdk8.tgz linux-x86 \
#  && mv linux-x86 /usr/lib/jvm/java-8-openjdk-amd64 \
#  && rm -rf jdk8.tgz

RUN curl -o /usr/local/bin/repo https://storage.googleapis.com/git-repo-downloads/repo \
 && chmod a+x /usr/local/bin/repo \
 && ln -s /usr/bin/python3 /usr/bin/python

RUN groupadd -f -g $groupid $username || true \
 && useradd -m -u $userid -g $groupid $username \
 && echo $username >/root/username \
 && echo "export USER="$username >>/home/$username/.gitconfig
COPY gitconfig /home/$username/.gitconfig
RUN chown $userid:$groupid /home/$username/.gitconfig
ENV HOME=/home/$username
ENV USER=$username
ENTRYPOINT chroot --userspec=$(cat /root/username):$(cat /root/username) / /bin/bash -i

以上dockerfile基于源码中 build/make/tools/docker/Dockerfile 修改

构建镜像:

# Copy your host gitconfig, or create a stripped down version
$ cp ~/.gitconfig gitconfig
$ docker build --build-arg userid=$(id -u) --build-arg groupid=$(id -g) --build-arg username=$(id -un) -t android-build-bionic .

使用镜像:

可以直接使用我已经构建好的镜像: hanlyjiang/android-build-bionic,可以使用如下命令拉取并修改tag:

docker pull hanlyjiang/android-build-bionic

docker tag hanlyjiang/android-build-bionic android-build-bionic:latest

假设aosp的源码目录位于 ~/aosp

# 设置变量指向源码顶层目录
ANDROID_BUILD_TOP=~/aosp
# 如自行构建镜像,此处
BUILD_IMAGE=android-build-bionic
cd $ANDROID_BUILD_TOP
# 运行容器,并将aosp源码目录挂载到容器内部的/src目录
docker run -it --rm --name aosp-builder -v $PWD:/src $BUILD_IMAGE

启动成功后就会进入容器的shell中,之后在容器中进行执行对应的编译命令即可。

docker使用提示:

  1. 上面的docker run 命令中添加了 --rm 选项,在启动的交互式bash结束后就会自动移除容器。之后需要再次输入docker run命令启动容器;为了避免每次都输入如上命令启动容器,可以移除--rm 选项
  2. 我们执行构建一般需要较长时间,期间如果我们退出容器的bash,则构建任务会终止,这显然不是我们想要的,我们可以通过 ctrl+P ctrl+Q 命令告诉docker我们需要关闭容器到本机的输入输出重定向,但是并不暂停任何执行的任务。之后再使用 docker attach aosp-builder(aosp-builder是我们启动时指定的容器名称) 即可重新将容器输入输出重定向到本机终端,继续与容器内的shell交互。
搭建构建环境-macOS11.2

这里介绍下macOS(x86)编译的环境配置

  1. 准备源码目录

    由于macOS默认的分区是不区分大小写的,但是aosp编译需要区分大小写,所以需要创建一个区分大小写的分区,这里可以有两种选择:

    1)使用另外一个磁盘(如外置的移动硬盘),然后使用磁盘工具将其抹掉为区分大小写的分区;

    2)创建一个区分大小写的磁盘映像,可通过如下命令执行

    hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 250g ~/android.dmg.sparseimage
    
    # 定义装载卸载函数
    mountAndroid() { hdiutil attach ~/android.dmg.sparseimage -mountpoint /Volumes/android; }
    umountAndroid() { hdiutil detach /Volumes/android; }
    # 装载刚刚创建的磁盘映像到 /Volumes/android 目录,之后cd 到此目录执行即可
    mountAndroid
    
  2. 安装 xcode 命令行工具

    xcode-select --install
    
  3. 设置bash

    # 可加入到~/.bash_profile 文件中
    ulimit -S -n 2048
    
  4. 下载 10.15 SDK

    从此处下载: https://github.com/phracker/MacOSX-SDKs/releases

    下载完成后放入到此目录:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/

  5. 修复代码(在下载完成源码之后执行)

    • 编辑文件 system/core/base/cmsg.cpp

    • 添加一行:size_t psize = getpagesize();

      namespace base {
          
      size_t psize = getpagesize();
          
      ssize_t SendFileDescriptorVector(borrowed_fd sockfd, const void* data, size_t len,
      
    • 将文件中所有 PAGE_SIZE 替换为 psize

参考:

下载源码

安装Repo

提示: 先前构建的docker镜像中已经安装了repo,如使用镜像,可跳过此步骤

mkdir ~/.bin
PATH=~/.bin:$PATH

# 这里可能需要开启代理
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

# 验证是否安装成功
repo help

输出如下则表示安装成功:

usage: repo COMMAND [ARGS]

repo is not yet installed.  Use "repo init" to install it here.

The most commonly used repo commands are:

  init      Install repo in the current working directory
  help      Display detailed help on a command

For access to the full online help, install repo ("repo init").
获取aosp源码底包
wget https://mirrors.tuna.tsinghua.edu.cn/aosp-monthly/aosp-latest.tar
# 解压
tar xf aosp-latest.tar

或者直接下载:

mkdir aosp ; cd aosp 
repo init -u https://android.googlesource.com/platform/manifest -b android-11.0.0_r33

然后执行代码同步命令:

repo sync -c -j16

repo sync 命令说明:

  • 通过 -c 指定只获取当前分支
  • 通过 -j16 指定并行获取的线程数量,这里我电脑为8核,所以指定了16个线程

提示:

代码下载完成后,我们复制一份到另外一个目录只用于浏览代码 rsync -a -H --progress aosp aosp-ro

编译源码

首先, 我们使用 repo 启动一个工作分支

repo start dev_android11_r33 --all

初始化环境:

source build/envsetup.sh

envsetup.sh 脚本会导入若干命令,可使用 hmm 查看完整的可用命令列表。

选择目标:

lunch aosp_x86_64-eng

输出如下:

$ lunch aosp_x86_64-eng
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=11
TARGET_PRODUCT=aosp_x86_64
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_ARCH=x86_64
TARGET_ARCH_VARIANT=x86_64
TARGET_2ND_ARCH=x86
TARGET_2ND_ARCH_VARIANT=x86_64
HOST_ARCH=x86_64
HOST_OS=darwin
HOST_OS_EXTRA=Darwin-20.3.0-x86_64-11.2.3
HOST_BUILD_TYPE=release
BUILD_ID=RQ2A.210305.007
OUT_DIR=out
PRODUCT_SOONG_NAMESPACES=device/generic/goldfish device/generic/goldfish-opengl hardware/google/camera hardware/google/camera/devices/EmulatedCamera device/generic/goldfish device/generic/goldfish-opengl

构建:

make -j16

提示:构建命令可参考 官方文档-编译 Android

构建成功后输出如下:

[100% 119047/119047] Create system-qemu.img now
removing out/target/product/generic_x86_64/system-qemu.img.qcow2
out/host/darwin-x86/bin/sgdisk --clear out/target/product/generic_x86_64/system-qemu.img

#### build completed successfully (14:34:15 (hh:mm:ss)) ####

我们这里使用模拟器进行运行

emulator

运行起来之后,查看其中的系统版本号如下:

20210403135316

使用AndroidStudio调试 system_process

本小节介绍如何使用AS来调试Android Java Framework的核心进程 system_process。

下载安装AndroidStudio

AndroidStudio官方页面下载安装运行即可

生成AS项目配置

总的流程如下:

  • 全量编译一次aosp源码
  • 编译development/tools/idegen 生成 idegen.jar 文件;
  • 使用development/tools/idegen/idegen.sh 生成AS项目配置
  • 使用AndroidStudio打开生成的配置,导入源码项目

其中 idegen.sh 仅是简单的使用java执行idegen.jar,idegen.jar 会在源码根目录生成androidstudio使用的两个文件,即android.imlandroid.ipr,生成过程中会读取 excluded-paths 文件,该文件中可以使用正则表达式定义过滤规则,该文件会从三个路径去读取:

  • development/tools/idegen/excluded-paths
  • vendor/google/excluded-paths
  • 源码根目录的 excluded-paths

所以我们只需要将自己的过滤规则定义到源码根目录的 excluded-paths文件中即可。

android.iml 中有三种xml配置:

1)sourceFolder;

  1. excludeFolder;

  2. orderEntry;

idegen.jar 在执行时会根据配置的规则自动写入这三种规则,其中sourceFolder用于指定源码目录-即as会将其作为源码导入,excludeFolder用于指定排除目录-即as压根不会去搜索任何文件,也不会对其中的文件建立索引,orderEntry 则一般用于指定依赖的jar文件;

在idegen.jar的逻辑中:

  • 没有被过滤规则匹配到的目录都会尝试去其中搜寻java及jar文件,只有在其中找到了源码文件,才会被加入到sourceFolder中。
  • 过滤规则中配置的目录,只有其中有java及jar文件时,才会被加入到excludeFolder配置中。
  • 如果目录中找到了java文件,会去读取java文件,自动确认sourceFolder的开始路径,以保证路径能正确匹配包名,如:有个目录de vice/test/src/java/android/Test.java,其中Test.java的包为android,那么sourceFolder的路径会自动设置为de vice/test/src/java/
修改idegen代码并编译idegen.jar

修改 development/tools/idegen 模块代码及配置

  1. src/Log.java 为了方便我们查看生成的过程,打开debug日志开关

    class Log {
    
        static final boolean DEBUG = true;
        
        // ...
    }
    
  2. src/IntelliJ.java , 这里由于代码中书写错误,prebuilt 实际上在源码中并不存在,我们手动将其修改为 prebuilts

            sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/out/target/product\"/>\n");
    // 修改这一行 prebuilt --> prebuilts
            sourceRootsXml.append("<excludeFolder url=\"file://$MODULE_DIR$/prebuilts\"/>\n");
    
    
  3. 编译生成 idegen.jar

    # 在源码根目录执行
    source build/envsetup.sh
    cd development/tools/idegen
    mm -j16
    

    成功生成后输出如下:

    [100% 228/228] Install: out/host/darwin-x86/framework/idegen.jar
    #### build completed successfully (05:30 (mm:ss)) ####
    
手动获取java文件
手动获取AIDL生成的java文件

我们通过如下命令将aidl生成的java文件拷贝到 源码根目录的idea/aidl/src目录下

croot
mkdir -p idea/aidl/src
find out/soong/.intermediates/frameworks/base/api-stubs-docs-non-updatable/android_common/gen/aidl -name "*.srcjar" | xargs -I {} unzip {} -d idea/aidl/src
获取生成的资源
cp -R out/soong/.intermediates/frameworks/base/core/res/framework-res/android_common/gen/aapt2/R idea/framework-res_R
编辑排除规则并生成配置
  1. 在项目根目录中添加一个 excluded-paths 文件,输入如下内容(一行作为一个匹配规则,规则的格式按Java中Pattern类的规则编写)

    # 几个根目录的规则
    ^art/.*
    ^packages/.*
    ^bootable/.*
    ^build/.*
    ^cts/.*
    ^dalvik/.*
    ^developers/.*
    ^external/.*
    ^platform_testing/.*
    ^pdk/.*
    ^sdk/.*
    ^system/.*
    ^test/.*
    
    # platform-compat中有注解的类
    ^tools/(?!(platform-compat))
    ^development/.*
    ^device/.*
    ^prebuilts/*
    
    # 这里我们查看这两个模块,所以注释掉
    #^libcore/.*
    #^frameworks/.*
    # 关于out其他的一些规则
    ^out/soong/.intermediates/(?!((frameworks)|(libcore)))
    # ./out/soong/.intermediates/frameworks/native/libs/binder/libbinder/android_x86_64_shared/gen/aidl/android/os/BnServiceManager.h
    # ^out/soong/.intermediates/.*
    ^out/target/.*
    
    # 根据实际运行情况补充的规则
    # 移除可能的jar
    # 如 ./frameworks/base/tools/aapt2/integration-tests/CommandTests/android-28.jar
    ^frameworks/base/tools/aapt2/.*\.jar
    ^frameworks/base/tests
    ^libcore/support/src/test
    ^libcore/luni/src/test
    gradle-wrapper.jar
    
    # 对于sdk源码的隐藏,我们exclude掉,以使可以找到真正的源码
    ^libcore/ojluni/annotations
    
  2. 生成iml文件,执行下面命令生成

    development/tools/idegen/idegen.sh
    

    完成后根目录下即生成了android.iml,android.ipr

导入到AndroidStudio

androidstudio 配置
  1. 配置jvm选项,根据机器实际情况设置vm分配的内存策略
    在这里插入图片描述

    -Xmx16g
    -Xms2g
    
  2. 配置 idea 属性
    在这里插入图片描述

    # 大小写敏感(macOS)
    idea.case.sensitive.fs=true
    
    idea.max.intellisense.filesize=100000
    
导入项目到AS并配置

配置sdk主要是为了避免查看或调试代码时,AS仍然去使用SDK中配置的class,而不是我们的aosp中的java源码。

  1. 使用AndroidStudio 打开项目根目录下 android.ipr 文件即可导入项目;

  2. 配置项目SDK,进入项目设置,添加一个新的JDK,并删除所有依赖jar库,我们这里取名为:1.8 (No Libraries)

    20210402095209

  3. 配置项目SDK,进入项目设置,添加一个新的SDK,并删除所有jar库,对应的JDK选择之前创建的1.8 (No Libraries),这里我们将SDK命名为 Android API 30 Platform-NoLib

    20210402095255

  4. 在Project设置中,选择项目SDK为之前设置的 Android API 30 Platform-NoLib

    20210402095343

  5. 进入项目设置-模块设置,将模块的SDK设置为之前设置的 Android API 30 Platform-NoLib
    在这里插入图片描述

附加调试器到 system_process
  1. 附加调试器到 system_process 进程

20210402093915
在这里插入图片描述

  1. 成功后的Debugger界面如下所示

    20210402100639

  2. 打断点测试,这里我们选择在 Binder.java 文件中添加断点,然后在模拟器中打开相机APP以触发断点逻辑。下图就表示我们到达了断点,说明我们可以通过AS来调试该进程了。

在这里插入图片描述

提示:附加调试器时,如找不到设备,可参考第5小节中的 错误解决:Debug中不显示设备

修改并更新系统模块

在调试分析过程中,可能我们需要修改一下其中内容以协助分析,可以按如下方式进行。

Framework

构建 framework 分两块:

  1. JAR 部分使用命令 mmm frameworks/base/
  2. 资源部分: mmm frameworks/base/core/res/

安装:

# 获取root权限
adb root
adb disable-verity
adb reboot
adb remount
# 移除旧的framework
adb shell
cd /system/framework/
rm framework-res.apk
exit

# 推入新的framework
adb push out/target/product/<device_targeted>/system/framework/framework-res.apk /system/framework/

# 重启服务
adb reboot

packages中app

  • 构建
cd ~/aosp
source build/envsetup.sh 
lunch aosp_x86_64-eng
# 重新构建单个模块 (如 Dialer)
mmm packages/apps/dialer/
# 生成的文件会在控制台中输出来
  • 更新到系统
adb install -r out/target/product/<device_targeted>/system/priv-app/<app_name>/<app_name>.apk
adb reboot

SystemUI

  • 编译
cd WORKING_DIRECTORY
source build/envsetup.sh
# 选择设备
lunch 31
# 重新构建SystemUI
mmm frameworks/base/packages/SystemUI/
  • 安装到系统
adb install -r out/target/product/<device_targeted>/system/priv-app/SystemUI/SystemUI.apk
adb reboot

使用提示及常见错误

使用提示:项目源码浏览配置

筛选Project代码范围
  1. 点击project窗口的设置按钮,选择 “Edit Scopes”

    20210402104239

  2. 新建一个规则,输入名称,并在Pattern中输入过滤规则: file[android]:frameworks//*||file[android]:libcore//*
    在这里插入图片描述

过滤VCS配置中不必要的模块

打开VCS配置,移除frameworks和libcore之外的所有其他模块

20210402105446

使用提示:导入其他模块的源码到AS

不建议直接修改 android.iml 文件,应当通过修改源码根目录自定义的 excluded-paths 文件中定义的规则来包含其他模块的源码。

修改此文件后,重新执行 development/tools/idegen/idegen.sh 来生成最新的as项目配置。生成完成后一般as如果是打开状态,会自动更新索引,如果索引更新有问题,可关闭项目,然后重新打开。

错误解决:macOS .DS_Store 导致打包镜像失败

编译基本完成,但是最后打包system.img文件时报错,报错内容如下:

set_selinux_xattr: No such file or directory searching for label "/.DS_Store"
e2fsdroid: No such file or directory while configuring the file system

10:00:38 ninja failed with: exit status 1

解决方式:删除源码目录中的所有 .DS_Store,可通过如下命令进行统一清理

find . -name ".DS_Store"  | xargs -I {} rm {} 

说明: .DS_Store 文件为macOS中存储文件夹界面展示相关的一些信息,在Finder中查看对应目录后可能会生成

错误解决:附加调试器时不显示设备

在附加调试器时,可能会如下图一样,模拟器已经启动了,但是附加调试器的窗口中设备列表中没有设备

在这里插入图片描述

此时检查自己的项目配置中的SDK是否选择了AndroidSDK,设置成 Android SDK 即可

20210402121537

错误解决:模拟器无法启动-报找不到内核版本信息

当使用 android-11.0.0_r28 版本时,编译通过后,使用emulator启动时,报出如下错误

emulator: ERROR: Can't find 'Linux version ' string in kernel image file: /Volumes/HIKVISION/google-aosp/out/target/product/generic_x86_64/kernel-ranchu

原因是emulator版本过旧,需要使用新的emulator,执行如下命令获取新的版本:

cd prebuilts/android-emulator
# 查询可用分支,https://android.googlesource.com/platform/prebuilts/android-emulator/+refs
git fetch aosp android-11.0.0_r33:refs/remotes/m/android-11.0.0_r33
# 检出分支
git co -b android-11.0.0_r33 refs/remotes/m/
android-11.0.0_r33

获取完成后再次执行 emulator 命令

emulator -version
# Android emulator version 30.3.0.0 (build_id 6946397) (CL:N/A)
emulator 
# 启动模拟器

参考文章

参考:

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值