本系列开发环境为:
- Host PC:Windows 11(Intel i5 12600k,32G RAM)
- Virtual Machine:VMware Workstation
- Client PC:Ubuntu 18 Desktop(50G ROM、2*6 Core、16G RAM)
注意:
1、请不要使用
Virtual Box,因为它嵌套虚拟化会有问题,导致Android模拟器无法启动。
2、Host PC 需要在BIOS中开启虚拟化。
3、Client PC 需要开启 虚拟化 Intel VT-x/EPT 或 AMD-V/RVI(V)。
Client PC只有50G的磁盘空间,用于安装系统镜像以及编译时的依赖,保存源码肯定是不够的,那我们Android源码保存在哪呢?这就涉及到接下来要讲的 挂载虚拟磁盘 。
一、挂载虚拟磁盘
1、创建虚拟磁盘
VMware Workstation创建虚拟磁盘的方式很简单。进入菜单栏虚拟机->设置, 点击 添加 按钮,然后选择硬盘,然后磁盘类型选择SCSI,并选择创建新虚拟磁盘,进入到下图页面。
这里将设置最大磁盘大小,建议Android 5分配 80G;Android 8分配 200G;Android 12分配 300G。
同时选择将虚拟磁盘存储为单个文件,这样做的好处在于,可很方便的将源码进行转移。
最后设置磁盘文件名即可。
2、创建分区
- 1、查找虚拟磁盘设备文件
ls /dev/sd*
该命令能够获得所有的虚拟磁盘及其分区。其中文件名不是以数字结尾的,表示其为一个虚拟磁盘,否则为对应磁盘的一个分区。我们需要找到没有分区的虚拟设备,这里以 /dev/sdb 为例,即此时还不存在 /dev/sdb1 这个设备文件。
- 2、划分分区
sudo fdisk /dev/sdb
通过输入m,可以查看具体的帮助信息。
分区划分完毕,就可以发现生成了/dev/sdb1 这个设备文件。
- 3、格式化分区
sudo mkfs.ext4 /dev/sdb1
将分区格式化为 ext4 格式。
3、挂载分区
- 1、创建挂载点
sudo mkdir /mnt/android-8
- 2、挂载分区
sudo mount /dev/sdb1 /mnt/android-8/
该命令是将 /dev/sdb1 分区挂载到 /mnt/android-8/路径上,挂载完成后, /mnt/android-8/路径下会出现 lost+found 文件夹。
注意:同一个挂载点只能挂载一个文件系统
- 3、修改分区拥有者
新创建的分区,默认拥有者都为root,这意味着我们没有权限对其进行读写的,所以需要将其拥有者变为自身。
sudo chown [所有者]:[组] /mnt/android-8 -R
自此,我们已经完成了分区挂载,并可以通过路径对虚拟磁盘进行读写操作。
4、开机自动挂载分区
由于mount 指令挂载磁盘是临时的,这意味系统重启后,我们需要再次 mount。如何在让系统在开机时,自动将 /dev/sdb1 分区挂载到 /mnt/android-8/路径上。方法如下:
- 1、找到分区的UUID:
ll /dev/disk/by-uuid/
- 2、将下面内容添加到
/etc/fstab文件中:
UUID=分区的UUID /mnt/android-8/ ext4 errors=remount-ro 0 1
二、Android源码编译
Android源码分为2种:
- 1、
AOSP(Android Open Source Project):Google官方开源的系统源码,该源码没有针对非官方的硬件设备进行适配,所以它只能运行在模拟器或Pixel设备上。 - 2、
BSP(板级支持包:Broad Support Package)包:它硬件厂商基于AOSP,针对具体的硬件设备进行适配后的源码,增加了一些特定硬件驱动和预置应用程序。
使用AOSP的好处是,不需要硬件支持,可以通过模拟器体验最新的Android系统。
而BSP的好处在于,有硬件的加持,运行速度更快,且可以操作实际的硬件,如一些开发板会提供UART、IIC、IIS、LCD等硬件接口。
1、环境搭建
具体的环境配置可以参考Google官方提供的搭建构建环境。
2、源码下载
2.1、下载 repo 工具
mkdir ~/bin
PATH=~/bin:$PATH
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > ~/bin/repo
chmod a+x ~/bin/repo
2.2、修改repo镜像源
vim ~/.bashrc
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/'
source ~/.bashrc
2.3、初始化仓库并同步源码
- 1、建立工作目录:
mkdir Aosp
cd Aosp
- 2、初始化仓库
repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest
该命令默认初始化仓库到最新的代码分支,如果需要某个特定分支,如android-6.0.1_r81,命令如下:
repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-6.0.1_r81
- 3、同步源码
repo syn
如需加快同步速度,请传递-c(当前分支)和-jN(N表示线程数)标志:
repo sync -c -j48
具体操作可以参考 中科大 或 清华大学 的开源软件镜像站下载AOSP源码。
3、源码编译
source ./build/envsetup.sh
lanch
//然后输入你需要编译的镜像序号
make update-api -j48
make -j48
如果想手动指定编译版本号,可在make之前先输入
export BUILD_NUMBER=XXX如:
export BUILD_NUMBER=${USER}-'date +%Y%m%d-%H%M%S'
由于我使用的是Firefly的ROC-RK3399-PC开发板,系统可以直接在 Firefly官网 进行下载,并按照官方也给出的编译教程 进行编译即可。
4、编译常见问题:
错误信息1:
Lex: aidl <= frameworks/base/tools/aidl/aidl_language_l.l
flex-2.5.39: loadlocale.c:130:_nl_intern_locale_data: ?? 'cnt < (sizeof (_nl_value_type_LC_TIME) / sizeof (_nl_value_type_LC_TIME[0]))' ???
Export includes file: frameworks/base/tools/aapt/Android.mk -- out/host/linux-x86/obj/STATIC_LIBRARIES/libaapt_intermediates/export_includes
Export includes file: frameworks/base/libs/androidfw/Android.mk -- out/host/linux-x86/obj/STATIC_LIBRARIES/libandroidfw_intermediates/export_includes
Export includes file: external/libpng/Android.mk -- out/host/linux-x86/obj/STATIC_LIBRARIES/libpng_intermediates/export_includes
build/core/binary.mk:646: recipe for target 'out/host/linux-x86/obj/EXECUTABLES/aidl_intermediates/aidl_language_l.cpp' failed
make: *** [out/host/linux-x86/obj/EXECUTABLES/aidl_intermediates/aidl_language_l.cpp] 已放弃 (core dumped)
解决方法:
export LC_ALL=C
错误信息2:
prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6//x86_64-linux/include/c++/4.6/bits/basic_string.h:270: error: unsupported reloc 42
prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6//x86_64-linux/include/c++/4.6/bits/basic_string.h:270: error: unsupported reloc 42
prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6//x86_64-linux/include/c++/4.6/bits/basic_string.h:235: error: unsupported reloc 42
prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6//x86_64-linux/include/c++/4.6/bits/basic_string.h:235: error: unsupported reloc 42
libnativehelper/JNIHelp.cpp:310: error: unsupported reloc 42
libnativehelper/JNIHelp.cpp:311: error: unsupported reloc 42
libnativehelper/JNIHelp.cpp:332: error: unsupported reloc 42
libnativehelper/JNIHelp.cpp:322: error: unsupported reloc 42
libnativehelper/JNIHelp.cpp:338: error: unsupported reloc 42
libnativehelper/toStringArray.cpp:21: error: unsupported reloc 42
libnativehelper/toStringArray.cpp:21: error: unsupported reloc 42
clang: error: linker command failed with exit code 1 (use -v to see invocation)
build/core/host_shared_library_internal.mk:44: recipe for target 'out/host/linux-x86/obj/lib/libnativehelper.so' failed
make: *** [out/host/linux-x86/obj/lib/libnativehelper.so] Error 1
host StaticLib: libc++abi (out/host/linux-x86/obj/STATIC_LIBRARIES/libc++abi_intermediates/libc++abi.a)
解决方法:
WITHOUT_HOST_CLANG=true
错误信息2:
external/libcxx/src/thread.cpp:133: error: unsupported reloc 43
external/libcxx/include/thread:146: error: unsupported reloc 43
external/libcxx/include/thread:149: error: unsupported reloc 43
external/libcxx/include/thread:149: error: unsupported reloc 43
clang: error: linker command failed with exit code 1 (use -v to see invocation)
build/core/host_shared_library_internal.mk:44: recipe for target 'out/host/linux-x86/obj32/lib/libc++.so' failed
make: *** [out/host/linux-x86/obj32/lib/libc++.so] Error 1
解决方法:
cp /usr/bin/ld.gold prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6/x86_64-linux/bin/ld
5、运行模拟器
emulator -verbose -cores 4 -show-kernel -writable-system
6、生成Android Studio源码工程
make idegen
development/tools/idegen/idegen.sh
如果你编译的是Android 5,可能会遇到以下错误:
Exception in thread "main" java.io.FileNotFoundException: ./out/target/product/tiny4412/obj/GYP/shared_intermediates/res.java (Is a directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:146)
at java.io.FileReader.<init>(FileReader.java:72)
at Configuration.parsePackageName(Configuration.java:204)
at Configuration.rootOf(Configuration.java:180)
at Configuration.traverse(Configuration.java:140)
at Configuration.traverse(Configuration.java:167)
at Configuration.traverse(Configuration.java:167)
at Configuration.traverse(Configuration.java:167)
at Configuration.traverse(Configuration.java:167)
at Configuration.traverse(Configuration.java:167)
at Configuration.traverse(Configuration.java:167)
at Configuration.traverse(Configuration.java:167)
at Configuration.<init>(Configuration.java:72)
at Main.main(Main.java:37)
解决办法是将
./out/target/product/hammerhead/obj/GYP/shared_intermediates/res.java
修改为:
./out/target/product/hammerhead/obj/GYP/shared_intermediates/res.j
6、单模块编译
AOSP源码虽然十分庞大,但它也是由一个个模块组成。在此之前,我们必须知道什么是一个模块:
- 1、目录下有
Android.mk文件。 - 2、
Android.mk文件中指定了LOCAL_PACKAGE_NAME变量。 - 3、
LOCAL_PACKAGE_NAME就表示模块名。
当我们修改了系统某个模块的源码后,难免会涉及到对它进行编译和验证。这时,我们通常不会再次编译整个系统镜像再烧写,而是只单独编译修改过的模块,并使其生效。
单模块编译的前提是:已成功编译过完整的系统镜像。
1、常用编译指令
先初始化环境:source build/envsetup.sh,然后使用hmm命令查看所有支持的编译指令。
| 编译指令 | 解释 |
|---|---|
| make [module_name] | 无参数,则表示编译整个Android代码 |
| m | 在源码树的根目录执行编译 |
| mm | 编译当前路径下所有模块,但不包含依赖 |
| mmm [module_path] | 编译指定路径下所有模块,但不包含依赖 |
| mma | 编译当前路径下所有模块,且包含依赖 |
| mmma [module_path] | 编译指定路径下所有模块,且包含依赖 |
注意:使用m命令编译framework只有在系统初次编译后第一次使用有效,之后编译会失败,需使用make命令。
下面列举一些常用模块的编译指令:
| 模块 | make命令 | mmm命令 |
|---|---|---|
| init | make init | mmm system/core/init |
| zygote | make app_process | mmm frameworks/base/cmds/app_process |
| system_server | make services | mmm frameworks/base/services |
| java framework | make framework | mmm frameworks/base |
| framework资源 | make framework-res | mmm frameworks/base/core/res |
| jni framework | make libandroid_runtime | mmm frameworks/base/core/jni |
| binder | make libbinder | mmm frameworks/native/libs/binder |
2、模块生效
1、如果是采用make(全编译)或make snod(打包镜像),需要重新烧写镜像
2、如果编译的是app,直接adb install -r xxx.apk即可。
3、如果是系统库文件:如jar、so等,可通过push到相应目录(一般来说,与输出目录相对应),并重启使其生效。