云设备: Redroid介绍

406 阅读7分钟

简介对比

Redroid (Remote Android) 是一个 GPU 加速的 AIC(云端 Android)解决方案。你可以在 Linux 主机上(如 Docker、Podman、K8s 等)启动多个实例。Redroid 支持 arm64 和 amd64 架构,适用于云游戏、虚拟手机、自动化测试等场景。

image.png

系统支持

目前支持:

  • 安卓 15 ( redroid/redroid:15.0.0-latest)
  • 仅限 Android 15 64 位(redroid/redroid:15.0.0_64only-latest
  • Android 14(redroid/redroid:14.0.0-latest
  • 仅限 Android 14 64 位(redroid/redroid:14.0.0_64only-latest
  • Android 13(redroid/redroid:13.0.0-latest
  • 仅限 Android 13 64 位(redroid/redroid:13.0.0_64only-latest
  • Android 12(redroid/redroid:12.0.0-latest
  • 仅限 Android 12 64 位(redroid/redroid:12.0.0_64only-latest
  • Android 11(redroid/redroid:11.0.0-latest
  • Android 10(redroid/redroid:10.0.0-latest
  • Android 9(redroid/redroid:9.0.0-latest
  • Android 8.1(redroid/redroid:8.1.0-latest

实际运行效果

image.png

工具安装

安装GMS提供谷歌app下载并实现设备认证

GMS服务简介

Google流动服务(GMS)(英文:Google Mobile Services)是Google的一项服务,让用户利用流动电话或其他流动装置使用Google搜寻、Google地图、Gmail、YouTube等Google产品。

目前,Google流动服务不再允许用户自行安装,自行安装可能无法通过Play兼容性测试,也无法下载需要DRM的软件。

安装GMS

创建文件 gapps_build.sh,  填写以下内容. 执行命令 ./gapps_build.sh <当前路径>

#!/bin/bash
 
# 下载链接和对应的 MD5 值
declare -A dl_links=(
    ["x86_64"]="https://cfhcable.dl.sourceforge.net/project/opengapps/x86_64/20220503/open_gapps-x86_64-10.0-pico-20220503.zip 5fb186bfb7bed8925290f79247bec4cf"
    ["x86"]="https://udomain.dl.sourceforge.net/project/opengapps/x86/20220122/open_gapps-x86-10.0-pico-20220122.zip 9e39e45584b7ade4529e6be654af7b81"
    ["arm64-v8a"]="https://liquidtelecom.dl.sourceforge.net/project/opengapps/arm64/20220122/open_gapps-arm64-10.0-pico-20220122.zip 8dfa6e76aeb2d1d5aed40b058e8a852c"
    ["armeabi-v7a"]="https://nav.dl.sourceforge.net/project/opengapps/arm/20220122/open_gapps-arm-10.0-pico-20220122.zip a48ccbd25eb0a3c5e30f5db5435f5536"
)
 
# 获取主机架构
arch=$(uname -m)
case $arch in
    x86_64)
        key="x86_64"
        ;;
    x86)
        key="x86"
        ;;
    aarch64)
        key="arm64-v8a"
        ;;
    arm*)
        key="armeabi-v7a"
        ;;
    *)
        echo "Unsupported architecture: $arch"
        exit 1
        ;;
esac
 
# 解析下载链接和 MD5 值
dl_link=$(echo ${dl_links[$key]} | awk '{print $1}')
act_md5=$(echo ${dl_links[$key]} | awk '{print $2}')
 
# 下载位置和文件名
download_loc=$1
dl_file_name="$download_loc/open_gapps.zip"
copy_dir="./gapps"
extract_to="/tmp/ogapps/extract"
non_apks=("defaultetc-common.tar.lz" "defaultframework-common.tar.lz" "googlepixelconfig-common.tar.lz")
skip=("setupwizarddefault-x86_64.tar.lz" "setupwizardtablet-x86_64.tar.lz")
 
# 创建下载目录
mkdir -p "$download_loc"
 
# 下载 OpenGapps
echo -e "\033[32mDownloading OpenGapps now .....\033[0m"
# wget -O "$dl_file_name" "$dl_link"
 
# 验证 MD5 值
if [[ "$(md5sum "$dl_file_name" | awk '{print $1}')" != "$act_md5" ]]; then
    echo "MD5 mismatch!"
    exit 1
fi
 
# 创建提取目录
rm -rf "$copy_dir"
mkdir -p "$copy_dir"
mkdir -p "$extract_to/appunpack"
 
# 解压下载的文件
echo "Extracting OpenGapps ..."
unzip -d "$extract_to" "$dl_file_name"
 
# 处理提取的文件
for lz_file in "$extract_to/Core"/*.tar.lz; do
    # 清空 appunpack 目录
    rm -rf "$extract_to/appunpack/*"
 
    # 检查是否跳过此文件
    if [[ ! " ${skip[@]} " =~ " $(basename "$lz_file") " ]]; then
        if [[ ! " ${non_apks[@]} " =~ " $(basename "$lz_file") " ]]; then
            echo "Processing app package : $lz_file"
            tar --lzip -xvf "$lz_file" -C "$extract_to/appunpack"
            app_name=$(ls "$extract_to/appunpack" | head -n 1)
            xx_dpi=$(ls "$extract_to/appunpack/$app_name" | head -n 1)
            app_priv=$(ls "$extract_to/appunpack/$app_name/nodpi" | head -n 1)
            app_src_dir="$extract_to/appunpack/$app_name/$xx_dpi/$app_priv"
            mkdir -p "$copy_dir/system/priv-app"
            for app in "$app_src_dir"/*; do
                cp -r "$app" "$copy_dir/system/priv-app/$(basename "$app")"
            done
        else
            echo "Processing extra package : $lz_file"
            tar --lzip -xvf "$lz_file" -C "$extract_to/appunpack"
            app_name=$(ls "$extract_to/appunpack" | head -n 1)
            common_content_dirs=$(ls "$extract_to/appunpack/$app_name/common")
            mkdir -p "$copy_dir/system"
            for ccdir in $common_content_dirs; do
                cp -r "$extract_to/appunpack/$app_name/common/$ccdir" "$copy_dir/system/$ccdir"
            done
        fi
    fi
done

打包Docker

创建Dockerfile文件

FROM redroid/redroid:11.0.0 -latest 
COPY gapps /

运行命令 docker build --build-arg userid=(idu)buildarggroupid=(id -u) --build-arg groupid=(id -g) --build-arg username=$(id -un) -t redroid-builder-gapps .

谷歌设备认证

在安装完GMS后可能会出现  PlayProtect "Device Not Certified" 的提示

通过adb连接设备并获取对应的android id进行手动注册
执行
adb root
adb shell 'sqlite3 /data/data/com.google.android.gsf/databases/gservices.db \
"select * from main where name = "android_id";"

访问https ://www.google.com/android/uncertified/  填入android id进行注册。重启设备后,就可以下载google store的app

安装面具Magisk实现动态修改设备信息

Magisk简介

Magisk通过挂载一个和系统文件完全隔离的空间,实现数据的动态修改和恢复。实际是通过启动时在 boot 中创建钩子,把 /data/magisk.img 挂载到 /magisk,构建出一个在 system 基础上能够自定义替换,增加以及删除的文件系统,所有操作都在启动的时候完成,实际上并没有对 /system 分区进行修改(即 systemless 接口,以不触动 /system 的方式修改 /system)。

image.png

安装Magisk

创建文件 magisk_build.sh,  填写以下内容. 执行命令 ./magisk_build.sh <当前路径>

image.png

安装Magisk

创建文件 magisk_build.sh,  填写以下内容. 执行命令 ./magisk_build.sh <当前路径>

#!/bin/bash
 
# Set download location and variables
download_loc=$1
echo "download_loc", $download_loc
dl_link="https://web.archive.org/web/20230718224206if_/https://objects.githubusercontent.com/github-production-release-asset-2e65be/514574759/50ec2f91-174b-4918-8587-04e847458bfd?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20230718%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230718T224206Z&X-Amz-Expires=300&X-Amz-Signature=ee54e872b4d3c1388601941e85b2fcf84d5e06968618271ea2f5e3ea5947d4e1&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=514574759&response-content-disposition=attachment%3B%20filename%3Dapp-debug.apk&response-content-type=application%2Fvnd.android.package-archive"
dl_file_name="$download_loc/magisk.apk"
act_md5="ec98dcee84a47785dc551eb7c465b25f"
extract_to="/tmp/magisk_unpack"
copy_dir="./magisk"
magisk_dir="$copy_dir/system/etc/init/magisk"
machine=$(uname -m)
 
# Create directories
rm -rf "$copy_dir"
mkdir -p "$magisk_dir"
mkdir -p "$copy_dir/sbin"
 
# Download Magisk
echo -e "\033[32mDownloading latest Magisk-Delta now .....\033[0m"
wget "$dl_link" -O "$dl_file_name"
 
# Verify MD5
if [ "$(md5sum "$dl_file_name" | awk '{ print $1 }')" != "$act_md5" ]; then
    echo "MD5 checksum does not match!"
    exit 1
fi
 
unzip -d $extract_to $dl_file_name
 
 
# Copy Magisk libraries
echo -e "\033[32mCopying magisk libs now ...\033[0m"
lib_dir="$extract_to/lib/$machine"
 
for filename in "$lib_dir"/*.so; do
    if [[ -f "$filename" ]]; then
        base_name=$(basename "$filename" .so)
        n_path="$magisk_dir/$base_name"
        cp "$filename" "$n_path"
        chmod +x "$n_path"
    fi
done
 
# Copy Magisk APK
cp "$dl_file_name" "$magisk_dir/magisk.apk"
 
# Prepare bootanim.rc
bootanim_path="$copy_dir/system/etc/init/bootanim.rc"
gz_filename="$bootanim_path.gz"
original_bootanim="
service bootanim /system/bin/bootanimation
    class core animation
    user graphics
    group graphics audio
    disabled
    oneshot
    ioprio rt 0
    task_profiles MaxPerformance
 
"
bootanim_component="
on post-fs-data
    start logd
    exec u:r:su:s0 root root -- /system/etc/init/magisk/magisk${machine} --auto-selinux --setup-sbin /system/etc/init/magisk
    exec u:r:su:s0 root root -- /system/etc/init/magisk/magiskpolicy --live --magisk \"allow * magisk_file lnk_file *\"
    mkdir /sbin/.magisk 700
    mkdir /sbin/.magisk/mirror 700
    mkdir /sbin/.magisk/block 700
    copy /system/etc/init/magisk/config /sbin/.magisk/config
    rm /dev/.magisk_unblock
    exec u:r:su:s0 root root -- /sbin/magisk --auto-selinux --post-fs-data
    wait /dev/.magisk_unblock 40
    rm /dev/.magisk_unblock
 
on zygote-start
    exec u:r:su:s0 root root -- /sbin/magisk --auto-selinux --service
 
on property:sys.boot_completed=1
    mkdir /data/adb/magisk 755
    exec u:r:su:s0 root root -- /sbin/magisk --auto-selinux --boot-complete
    exec -- /system/bin/sh -c \"if [ ! -e /data/data/io.github.huskydg.magisk ] ; then pm install /system/etc/init/magisk/magisk.apk ; fi\"
 
on property:init.svc.zygote=restarting
    exec u:r:su:s0 root root -- /sbin/magisk --auto-selinux --zygote-restart
 
on property:init.svc.zygote=stopped
    exec u:r:su:s0 root root -- /sbin/magisk --auto-selinux --zygote-restart
"
 
# Create gzipped bootanim.rc
echo -n "$original_bootanim" | gzip > "$gz_filename"
echo -e "$original_bootanim$bootanim_component" > "$bootanim_path"
 
# Set permissions
echo "0o644" $bootanim_path
chmod 644 "$bootanim_path"

打包Docker

创建Dockerfile文件

FROM redroid/redroid:11.0.0-latest
COPY magisk /

运行命令 docker build --build-arg userid=$(id -u) --build-arg groupid=$(id -g) --build-arg username=$(id -un) -t redroid-builder-magisk .

动态修改设备信息

在使用redroid可以通过以下两种方式进行设备信息修改。这两者实际是通过改写build.prop文件内容进行设备信息修改

Magisk模块

首选需要安装MagiskManager app,  安装方式参考本次文章。然后安装下列任意模块即可覆盖机型型号,设备参数等字段信息

magisk.suchenqaq.club/query.php?f…

github.com/Magisk-Modu…

你也可以自定义编写 Magisk 模块进行安装,主要是覆盖相应的系统属性。

Redroid命令行指定

可以不依赖MagiskManager和Magisk模块,只通过命令行参数指定覆盖build.prop对应的系统属性。可修改属性字段的参考

# begin common build properties
# autogenerated by build/make/tools/buildinfo_common.sh
ro.system.build.date=Mon May 27 14:38:53 UTC 2024
ro.system.build.date.utc=1716820733
ro.system.build.fingerprint=redroid/redroid_x86_64/redroid_x86_64:11/RD2A.211001.002/frank05271438:userdebug/test-keys
ro.system.build.id=RD2A.211001.002
ro.system.build.tags=test-keys
ro.system.build.type=userdebug
ro.system.build.version.incremental=eng.frank.20240527.144006
ro.system.build.version.release=11
ro.system.build.version.release_or_codename=11
ro.system.build.version.sdk=30
ro.product.system.brand=redroid
ro.product.system.device=redroid_x86_64
ro.product.system.manufacturer=redroid
ro.product.system.model=redroid11_x86_64
ro.product.system.name=redroid_x86_64
# end common build properties
# begin build properties
# autogenerated by buildinfo.sh
ro.build.id=RD2A.211001.002
ro.build.display.id=redroid_x86_64-userdebug 11 RD2A.211001.002 eng.frank.20240527.144006 test-keys
ro.build.version.incremental=eng.frank.20240527.144006
ro.build.version.sdk=30
ro.build.version.preview_sdk=0
ro.build.version.preview_sdk_fingerprint=REL
ro.build.version.codename=REL
ro.build.version.all_codenames=REL
ro.build.version.release=11
ro.build.version.release_or_codename=11
ro.build.version.security_patch=2021-10-01
ro.build.version.base_os=
ro.build.version.min_supported_target_sdk=23
ro.build.date=Mon May 27 14:38:53 UTC 2024
ro.build.date.utc=1716820733
ro.build.type=userdebug
ro.build.user=frank
ro.build.host=build0
ro.build.tags=test-keys
ro.build.flavor=redroid_x86_64-userdebug
# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,
# use ro.product.cpu.abilist instead.
ro.product.cpu.abi=x86_64
ro.product.cpu.abilist=x86_64,x86,arm64-v8a,armeabi-v7a,armeabi
ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi
ro.product.cpu.abilist64=x86_64,arm64-v8a
ro.product.locale=en-US
ro.wifi.channels=
# ro.build.product is obsolete; use ro.product.device
ro.build.product=redroid_x86_64
# Do not try to parse description or thumbprint
ro.build.description=redroid_x86_64-userdebug 11 RD2A.211001.002 eng.frank.20240527.144006 test-keys
# end build properties
 
#
# ADDITIONAL_BUILD_PROPERTIES
#
ro.treble.enabled=true
persist.debug.dalvik.vm.core_platform_api_policy=just-warn
dalvik.vm.lockprof.threshold=500
net.bt.name=Android

启动命令

docker run -itd --rm --privileged \
    -v ~/android/data/data5655:/data \
    -p 5655:5555 \
    redroid/redroid:11.0.0-latest \
    ro.product.cpu.abilist=x86_64,arm64-v8a,x86,armeabi-v7a,armeabi \
    ro.product.cpu.abilist64=x86_64,arm64-v8a \
    ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi \
    ro.dalvik.vm.isa.arm=x86 \
    ro.dalvik.vm.isa.arm64=x86_64

安装ndk实现arm64指令翻译

libndk_translation简介

libndk_translation是用于在x86机器运行arm架构应用的指令翻译模块,可以将app中的一些arm指令翻译为对应x86指令。但存在部分模块翻译失败,或者冲突的情况。

安装NDK

创建文件 ndk_build.sh,  填写以下内容. 执行命令 ./ndk_build.sh <当前路径>

# 设置下载位置、提取位置和文件名
download_loc=$1
copy_dir="./ndk"
dl_link="https://github.com/supremegamers/vendor_google_proprietary_ndk_translation-prebuilt/archive/181d9290a69309511185c4417ba3d890b3caaaa8.zip"
dl_file_name="${download_loc}/libndktranslation.zip"
extract_to="/tmp/libndkunpack"
act_md5="0beff55f312492f24d539569d84f5bfb"
 
# 下载 libndk
echo -e "\033[32mDownloading libndk now .....\033[0m"
wget -O "${dl_file_name}" "${dl_link}"
 
# 创建提取目录
mkdir -p "${extract_to}"
 
# 解压下载的文件
unzip "${dl_file_name}" -d "${extract_to}"
 
# 复制 libndk 库文件
if [ -d "${copy_dir}" ]; then
    rm -rf "${copy_dir}"
fi
chmod +x "${extract_to}"/* -R
 
echo -e "\033[32mCopying libndk library files ...\033[0m"
mkdir -p "${copy_dir}/system"
cp -r "${extract_to}/vendor_google_proprietary_ndk_translation-prebuilt-181d9290a69309511185c4417ba3d890b3caaaa8/prebuilts" "${copy_dir}/system"
 
# 设置 init 文件的权限
init_path="${copy_dir}/system/etc/init/ndk_translation.rc"
chmod 644 "${init_path}"

打包Docker

创建Dockerfile文件

COPY ndk /

运行命令 docker build --build-arg userid=$(id -u) --build-arg groupid=$(id -g) --build-arg username=$(id -un) -t redroid-builder-ndk .

常见问题

  1. libndk_translation 存在部分指令翻译失败,例如flutter应用可能在x86架构上的机器运行失败. 类似 Abort message: 'vendor/unbundled_google/libs/ndk_translation/runtime/host_call_frame_arm.cc:41: CHECK failed: 2927458984 == 2927460320'。 这种情况换aarch64的机器重新部署 可以参考章节 qemu安装aarch64系统运行arm
  2. 在翻译指令过程中,内存会使用增大,因此后续在运行redroid应用时预留多一些内存

编译redroid并打包docker镜像

环境准备

以下环境安装二选一执行即可

环境镜像

Dockerfile如下,已经包含依赖项:Android SDK、JDK 8、Python 3.8 和 Ubuntu 20.04。

以及所需的依赖包:

  • repo
  • 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
FROM ubuntu:20.04
  
ARG userid=1000
ARG groupid=1000
ARG username=crawler
 
# 使用这些参数
RUN echo "User ID: ${userid}, Group ID: ${groupid}, Username: ${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 \
    && echo "install wget" \
    && apt install wget
 RUN yes|apt-get install libwxgtk3.0-gtk3-0v5
  
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
RUN echo "创建下载目录/home/$username/download" \
    && mkdir -p /home/$username/download \
    && wget https://dl.google.com/android/repository/platform-tools-latest-linux.zip -O  /home/$username/download/platform-tools-latest-linux.zip \
    && unzip /home/$username/download/platform-tools-latest-linux.zip -d /home/$username \
    && echo "create project dir" \
    && mkdir -p /home/$username/android/lineage
  
ENV HOME=/home/$username \
    USER=$username \
    USE_CCACHE=1 \
    CCACHE_EXEC=/usr/bin/ccache \
    PATH=/src/.repo/repo:/src/prebuilts/jdk/jdk8/linux-x86/bin/:/home/$username/platform-tools:$PATH
 
 # jACK IS THE CURRENTLY USED jAVA TOOLCHAIN FOR BUILDING lINEAGEos 14.1 AND 15.1. iT IS KNOWN TO RUN OUT OF MEMORY OFTEN IF NOT CONFIGURED CORRRT Jack_vm_args="-dFILE.ENCODING=utf-8 -xx:+tIEREDcOMPILATION -xMX4g"
 # ENV ANDROID_JACK_VM_ARGS="-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4G"
  
ENTRYPOINT chroot --userspec=$(cat /root/username):$(cat /root/username) / /bin/bash -i

执行docker build -t redroid-build-base . 打包编译环境。或者直接拉取内部环境镜像到本地执行redroid编译

本地环境

依赖环境: 内存>16G, CPU > 8 core,  系统Ubuntu20.04

其他依赖参考上述Dockerfile 执行apt-get install安装相应的依赖包即可

Redroid自编译

编译步骤参考下述链接,直接跳到第5步开始执行

github.com/remote-andr…

qemu安装aarch64系统运行arm镜像

qemu简介

hosomikai317.blogspot.com/2018/07/qem…

zh.wikipedia.org/zh-tw/QEMU

安装qemu

cloud.tencent.com/developer/a…

参考启动命令

& "F:\Program Files\qemu\qemu-system-aarch64.exe" -m 8192 -cpu cortex-a72 -smp 8,sockets=4,cores=2 -M virt -bios "F:\VM\ARM\ubuntu\QEMU_EFI.fd" -device VGA -device nec-usb-xhci -device usb-mouse -device usb-kbd -drive if=none,file="F:\VM\ARM\ubuntu\ubuntu.qcow2",id=hd0 -device virtio-blk-device,drive=hd0 -drive if=none,file=,id=cdrom,media=cdrom -device virtio-scsi-device -device scsi-cd,drive=cdrom -net nic -net user,hostfwd=tcp:127.0.0.1:2222-:22,hostfwd=tcp:127.0.0.1:5555-:5555

常见问题

由于qemu是通过动态二进制模拟CPU执行,从而模拟出相应的IO硬件设备(如网卡,磁盘等). 在模拟执行过程中需要时间翻译指令,因此当遇到需要图像渲染的情况就会异常缓慢

若需要稳定运行redroid的arm镜像,可以购买arm服务器或者裸机开发板,树莓派,香橙派(www.cnblogs.com/kanadebliss…)等

将第三方应用预设为系统应用

系统应用和普通应用的区别

安装位置

  • 系统应用:存放在设备的 /system 分区下的 app 或 priv-app 目录中。这些应用在出厂时由设备制造商或操作系统开发者预装在系统中。同时系统应用可能获取更低级API权限,更多权限操作,例如实现静默安装等操作。但无法通过命令行进行卸载,用户只能通过“禁用”功能禁用它们,或通过具有 root 权限的工具手动删除它们。
  • 普通应用:安装在设备的 /data/app 分区。这些应用通常是用户从应用商店(如 Google Play)下载或手动安装的。应用受到用户严格管控,但是需要权限时需要用户手动赋予权限。

应用权限

  • 系统应用:可以拥有更多的权限,甚至是普通应用无法获得的高级权限。因为系统应用在系统层面上运行,能够访问某些受保护的 API 和资源(如读写系统设置、后台静默安装应用、访问低层硬件功能等)。例如,系统应用可以调用 READ_PRIVILEGED_PHONE_STATE 这样的受保护权限。
  • 普通应用:权限受到用户的严格控制,通常需要用户在安装或使用时授权。普通应用不能使用一些系统级权限,即使申请了也会被拒绝。

安装第三方应用作为系统应用

参考GMS服务安装,安装后的应用不可卸载

创建own文件夹,安装以下目录层级创建

mkdir -p own/system/priv-app

image.png

打包Docker

创建Dockerfile文件

FROM redroid/redroid:11.0.0-latest
COPY own /

运行命令 docker build --build-arg userid=$(id -u) --build-arg groupid=$(id -g) --build-arg username=$(id -un) -t redroid-builder-own .