一、背景分析

之所以能够把Android打包成docker,主要还是由于Android建立在Linux kernel之上。我们知道,容器技术实现的基础是chroot+namespace+cgroups,这些功能都是由内核直接提供的。那么从理论上而言,将Android的init的进程以容器的形式跑在一个独立的名称空间中是完全可行的。
此时Android的最底层的kernel将共享宿主机的kernel:
- 当宿主机kernel运行在ARM处理器时,容器在CPU及内存层面几乎没有损耗。
- 当宿主机kernel运行在X86处理器时,需转义成ARM指令集,存在损耗。
Android的kernel与桌面的kernel相比,做了一定的定制和优化,比如:
- 增加了移动设备所需的特定驱动程序和功能的支持。
- 针对移动设备特定功能的补丁和改进,以提高功耗效率、性能和与移动硬件的兼容性。
同时对于内核进行了扩展,比如:
- 增加了Binder,Android系统中的进程间通信(IPC)机制,用于不同应用程序组件之间的通信。它允许应用程序在不同的进程之间传递数据,执行远程调用等。这种机制对于Android的组件化架构和应用间通信非常关键。
- 增加了Ashmem,Android Shared Memory,用于共享匿名内存的机制。它允许不同的应用程序共享内存,这对于共享数据和内存映射文件对Android系统的性能和资源管理非常重要。
从实际运行的角度而言,我们只需要保证我们的X86 server系统,如Ubuntu的运行中的内核,包含了Android内核所需的特定模块,就能确保Android系统能在主机内核的基础上跑起来。
二、remote-android项目分析
这个项目的文档支撑不是很好,对于Android framework部分几乎没有谈及,只是对于使用部分进行了简单的介绍。好在repo的整体架构比较清晰。
说明部分,含部分操作代码:
Framework部分:
- https://github.com/remote-android/device_redroid
- https://github.com/remote-android/device_redroid-prebuilts
- https://github.com/remote-android/redroid-c2
- https://github.com/remote-android/redroid-omx
- https://github.com/remote-android/vendor_redroid
Android patch脚本:
X86 to ARM指令转义:
docker build部分:
- https://github.com/remote-android/redroid-patcheshttps://github.com/remote-android/redroid-doc/blob/master/android-builder-docker
三、Android系统源码整合
Android系统源码极其庞大,使用传统的git进行组织及管理已经不现实。因此google开发了repo工具进行代码的辅助管理。
建议直接下载google官方最新的repo工具并放置到PATH中。通过apt安装的版本可能不支持最新的功能。
link:https://storage.googleapis.com/git-repo-downloads/repo
源码的下载,Android的源码大小已经达到了上百G,原始存储在https://android.googlesource.com这个网站,已经被墙。因此想通过VPN直接下载下来不是很现实。同时,我们在进行repo同步前,还有可能对于相关的依赖进行二次的裁剪及增加,如通过VPN控制,会使得整个过程变得比较复杂。
因此建议将最原始的google/LineageOS的源替换成清华源,其余的部分不会非常大,可以继续使用VPN拉取,确保稳定。
cat >> ~/.gitconfig <<EOF
[url "https://mirrors.tuna.tsinghua.edu.cn/git/git-repo"]
insteadof = https://gerrit.googlesource.com/git-repo
[url "https://mirrors.tuna.tsinghua.edu.cn/git/lineageOS/"]
insteadof = https://review.lineageos.org/
[url "https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/"]
insteadof = https://android.googlesource.com/
[url "https://mirrors.tuna.tsinghua.edu.cn/git/lineageOS/LineageOS/"]
insteadof = https://github.com/LineageOS/
EOF
源码下载完成之后就可以进行正常的framework的开发、优化、配置,最终编译。
这个部分是整个项目最核心的部分,需要Android framework工程师参与,一般的业务开发可能还不行。
redroid的源码准备过程:
# fetch code
#####################
mkdir ~/redroid && cd ~/redroid
# check supported branch in https://github.com/remote-android/redroid-patches.git
repo init -u https://android.googlesource.com/platform/manifest --git-lfs --depth=1 -b android-11.0.0_r48
# add local manifests
git clone https://github.com/remote-android/local_manifests.git ~/redroid/.repo/local_manifests -b 11.0.0
# sync code
repo sync -c
# apply redroid patches
git clone https://github.com/remote-android/redroid-patches.git ~/redroid-patches
~/redroid-patches/apply-patch.sh ~/redroid
四、Android系统的编译过程
redroid基于docker容器进行Anroid系统的打包,打包时会临时启动一个交互式接口的容器。
创建build容器:
git clone <https://github.com/remote-android/redroid-doc>
cd android-builder-docker/
sudo docker build --build-arg userid=$(id -u) --build-arg groupid=$(id -g) --build-arg username=$(id -un) -t redroid-builder .
整个容器的重点创建了与宿主机完全一样的uid/gid的用户,并完成Android系统编译所需依赖包的安装,最终使用与宿主机一致的用户chroot到该容器内,开展进一步的编译。
特别注意,这个容器仅仅用作编译使用,与后期android容器的制作无关。同时,理论上直接使用宿主机编译也是可以的。
raw Dockerfile:
FROM ubuntu:20.04
ARG userid
ARG groupid
ARG username
# COPY apt.conf /etc/apt/apt.conf
# COPY sources.list etc/apt/sources.list
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update \\
&& echo "install package for building AOSP" \\
&& apt-get install -y git-core gnupg flex bison build-essential zip curl zlib1g-dev \\
gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev \\
libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig \\
&& echo "install utils" \\
&& apt-get install -y sudo rsync \\
&& echo "install packages for build mesa3d or meson related" \\
&& apt-get install -y python3-pip pkg-config python3-dev ninja-build \\
&& pip3 install mako meson \\
&& echo "packages for legacy mesa3d (< 22.0.0)" \\
&& apt-get install -y python2 python-mako python-is-python2 python-enum34 gettext
RUN groupadd -g $groupid $username \\
&& useradd -m -u $userid -g $groupid $username \\
&& echo "$username ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers \\
&& echo $username >/root/username \\
&& echo "$username:$username" | chpasswd && adduser $username sudo
ENV HOME=/home/$username \\
USER=$username \\
PATH=/src/.repo/repo:/src/prebuilts/jdk/jdk8/linux-x86/bin/:$PATH
ENTRYPOINT chroot --userspec=$(cat /root/username):$(cat /root/username) / /bin/bash -i
Android系统编译:
# start builder
#####################
docker run -it --rm --hostname redroid-builder --name redroid-builder -v ~/redroid:/src redroid-builder
####################
# build redroid
#####################
cd /src
. build/envsetup.sh
lunch redroid_x86_64-userdebug
# redroid_arm64-userdebug
# redroid_x86_64_only-userdebug (64 bit only, redroid 12+)
# redroid_arm64_only-userdebug (64 bit only, redroid 12+)
# start to build
m
五、容器镜像制作
在讲容器镜像制作之前,简单的对于linux的系统启动流程进行一下回顾,不然其实比较难以理解OS镜像制作的本质是什么。
一个传统的linux操作系统的启动主要由以下部分组成:
POST → (legacy/UEFI) BIOS → 按照顺序找到启动介质 → 找到bootloader (MBR/GPT) → kernel(ramdisk/ramfs) → rootfs(只读) → switchroot(chroot) → init
当我们使用容器的方式启动一个系统时,kernel是已经启动的状态,并且已经加载所需的模块。启动容器时,会调用kernel中namespace相关的系统调用,创建一个隔离的名称空间,挂载文件目录,chroot后init即可。
因此,创建一个OS镜像的本质就是制定一个根文件系统并指定该文件系统的init程序,也就是Android系统的init。
理解之后,就可以进行镜像的制作。
# create redroid image in *HOST*
#####################
cd ~/redroid/out/target/product/redroid_x86_64
sudo mount system.img system -o ro
sudo mount vendor.img vendor -o ro
sudo tar --xattrs -c vendor -C system --exclude="vendor" . |sudo docker import -c 'ENTRYPOINT ["/init", "androidboot.hardware=redroid"]' - redroid
sudo umount system vendor
# create rootfs only image for develop purpose
tar --xattrs -c -C root . | docker import -c 'ENTRYPOINT ["/init", "androidboot.hardware=redroid"]' - redroid-dev
此处使用tar及docker import指令相结合的方式,将Android系统的文件目录打包成基础镜像,将Androidinit指定为ENTRYPOINT,这样就可以在启动时方便的接收参数。
cd C:\\Users\\Administrator\\Desktop\\scrcpy-win64-v2.0
.\\adb.exe connect 192.168.100.199:5555
.\\scrcpy.exe -s 192.168.100.199:5555
Param | Description | Default |
---|---|---|
androidboot.redroid_width | display width | 720 |
androidboot.redroid_height | display height | 1280 |
androidboot.redroid_fps | display FPS | 30(GPU enabled)15 (GPU not enabled) |
androidboot.redroid_dpi | display DPI | 320 |
androidboot.use_memfd | use memfd to replace deprecated ashmemplan to enable by default | false |
androidboot.use_redroid_overlayfs | use overlayfs to share data partition/data-base: shared data partition/data-diff: private data | 0 |
androidboot.redroid_net_ndns | number of DNS server, 8.8.8.8 will be used if no DNS server specified | 0 |
androidboot.redroid_net_dns<1..N> | DNS | |
androidboot.redroid_net_proxy_type | Proxy type; choose from: static, pac, none, unassigned | |
androidboot.redroid_net_proxy_host | ||
androidboot.redroid_net_proxy_port | 3128 | |
androidboot.redroid_net_proxy_exclude_list | comma seperated list | |
androidboot.redroid_net_proxy_pac | ||
androidboot.redroid_gpu_mode | choose from: auto, host, guest;guest: use software rendering;host: use GPU accelerated rendering;auto: auto detect | auto |
androidboot.redroid_gpu_node | auto-detect | |
ro.xxx | DEBUG purpose, allow override ro.xxx prop; For example, set ro.secure=0, then root adb shell provided by default |
留言