Add new build command avd_patch

This commit is contained in:
topjohnwu 2022-01-19 05:12:11 -08:00
parent f443cbaa2b
commit 20ef724fad
7 changed files with 158 additions and 31 deletions

View File

@ -383,24 +383,6 @@ def build_stub(args):
build_apk(args, 'stub')
def build_snet(args):
if not op.exists(op.join('stub', 'src', 'main', 'java', 'com', 'topjohnwu', 'snet')):
error('snet sources have to be bind mounted on top of the stub folder')
header('* Building snet extension')
proc = execv([gradlew, 'stub:assembleRelease'])
if proc.returncode != 0:
error('Build snet extention failed!')
source = op.join('stub', 'build', 'outputs', 'apk',
'release', 'stub-release.apk')
target = op.join(config['outdir'], 'snet.jar')
# Extract classes.dex
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=False) as zout:
with zipfile.ZipFile(source) as zin:
zout.writestr('classes.dex', zin.read('classes.dex'))
rm(source)
header('Output: ' + target)
def cleanup(args):
support_targets = {'native', 'java'}
if args.target:
@ -470,13 +452,54 @@ def setup_avd(args):
abi = cmd_out([adb_path, 'shell', 'getprop', 'ro.product.cpu.abi'])
proc = execv([adb_path, 'push', f'native/out/{abi}/busybox', 'out/app-debug.apk',
'scripts/emulator.sh', '/data/local/tmp'])
'scripts/avd_magisk.sh', '/data/local/tmp'])
if proc.returncode != 0:
error('adb push failed!')
proc = execv([adb_path, 'shell', 'sh', '/data/local/tmp/emulator.sh'])
proc = execv([adb_path, 'shell', 'sh', '/data/local/tmp/avd_magisk.sh'])
if proc.returncode != 0:
error('emulator.sh failed!')
error('avd_magisk.sh failed!')
def patch_avd_ramdisk(args):
build_binary(args)
build_app(args)
header('* Patching emulator ramdisk.img')
# Create a backup to prevent accidental overwrites
backup = args.ramdisk + '.bak'
if not op.exists(backup):
cp(args.ramdisk, backup)
ini = op.join(op.dirname(args.ramdisk), 'advancedFeatures.ini')
with open(ini, 'r') as f:
adv_ft = f.read()
# Need to turn off system as root
if 'SystemAsRoot = on' in adv_ft:
# Create a backup
cp(ini, ini + '.bak')
adv_ft = adv_ft.replace('SystemAsRoot = on', 'SystemAsRoot = off')
with open(ini, 'w') as f:
f.write(adv_ft)
abi = cmd_out([adb_path, 'shell', 'getprop', 'ro.product.cpu.abi'])
proc = execv([adb_path, 'push', f'native/out/{abi}/busybox', 'out/app-debug.apk',
'scripts/avd_patch.sh', '/data/local/tmp'])
if proc.returncode != 0:
error('adb push failed!')
proc = execv([adb_path, 'push', backup, '/data/local/tmp/ramdisk.cpio.gz'])
if proc.returncode != 0:
error('adb push failed!')
proc = execv([adb_path, 'shell', 'sh', '/data/local/tmp/avd_patch.sh'])
if proc.returncode != 0:
error('avd_patch.sh failed!')
proc = execv([adb_path, 'pull', '/data/local/tmp/ramdisk.cpio.gz', args.ramdisk])
if proc.returncode != 0:
error('adb pull failed!')
def build_all(args):
@ -512,13 +535,13 @@ stub_parser = subparsers.add_parser('stub', help='build the stub app')
stub_parser.set_defaults(func=build_stub)
avd_parser = subparsers.add_parser(
'emulator', help='build and setup AVD for development')
'emulator', help='setup AVD for development')
avd_parser.set_defaults(func=setup_avd)
# Need to bind mount snet sources on top of stub folder
# Note: source code for the snet extension is *NOT* public
snet_parser = subparsers.add_parser('snet', help='build snet extension')
snet_parser.set_defaults(func=build_snet)
avd_patch_parser = subparsers.add_parser(
'avd_patch', help='patch AVD ramdisk.img')
avd_patch_parser.add_argument('ramdisk', help='path to ramdisk.img')
avd_patch_parser.set_defaults(func=patch_avd_ramdisk)
clean_parser = subparsers.add_parser('clean', help='cleanup')
clean_parser.add_argument(

View File

@ -180,6 +180,8 @@ void BootConfig::set(const kv_pairs &kv) {
strlcpy(hardware_plat, value.data(), sizeof(hardware_plat));
} else if (key == "androidboot.fstab_suffix") {
strlcpy(fstab_suffix, value.data(), sizeof(fstab_suffix));
} else if (key == "qemu") {
emulator = true;
}
}
}
@ -193,6 +195,7 @@ void BootConfig::print() {
LOGD("fstab_suffix=[%s]\n", fstab_suffix);
LOGD("hardware=[%s]\n", hardware);
LOGD("hardware.platform=[%s]\n", hardware_plat);
LOGD("emulator=[%d]\n", emulator);
}
#define read_dt(name, key) \
@ -230,7 +233,7 @@ void load_kernel_info(BootConfig *config) {
parse_prop_file("/.backup/.magisk", [=](auto key, auto value) -> bool {
if (key == "RECOVERYMODE" && value == "true") {
LOGD("Running in recovery mode, waiting for key...\n");
config->skip_initramfs = !check_key_combo();
config->skip_initramfs = config->emulator || !check_key_combo();
return false;
}
return true;

View File

@ -6,6 +6,7 @@ struct BootConfig {
bool skip_initramfs;
bool force_normal_boot;
bool rootwait;
bool emulator;
char slot[3];
char dt_dir[64];
char fstab_suffix[32];
@ -66,6 +67,10 @@ protected:
mmap_data magisk_config;
std::string custom_rules_dir;
// When this boolean is set, this means we are currently
// running magiskinit on legacy SAR AVD emulator
bool avd_hack = false;
void mount_with_dt();
bool patch_sepolicy(const char *file);
void setup_tmp(const char *path);

View File

@ -158,12 +158,19 @@ void MagiskInit::mount_with_dt() {
for (const auto &entry : fstab) {
if (is_lnk(entry.mnt_point.data()))
continue;
// When we force AVD to disable SystemAsRoot, it will always add system
// to dt fstab, which we actually have already mounted as root
if (avd_hack && entry.mnt_point == "/system")
continue;
// Derive partname from dev
sprintf(blk_info.partname, "%s%s", basename(entry.dev.data()), config->slot);
setup_block(true);
xmkdir(entry.mnt_point.data(), 0755);
xmount(blk_info.block_dev, entry.mnt_point.data(), entry.type.data(), MS_RDONLY, nullptr);
mount_list.push_back(entry.mnt_point);
// When avd_hack is true, do not add any early mount partitions to mount_list
// as we will actually forcefully disable original init's early mount
if (!avd_hack)
mount_list.push_back(entry.mnt_point);
}
}
@ -372,6 +379,7 @@ void SARInit::early_mount() {
xmkdir("/dev", 0755);
xmount("tmpfs", "/dev", "tmpfs", 0, "mode=755");
mount_list.emplace_back("/dev");
avd_hack = config->emulator;
mount_with_dt();
}
}
@ -399,7 +407,7 @@ bool SecondStageInit::prepare() {
void BaseInit::exec_init() {
// Unmount in reverse order
for (auto &p : reversed(mount_list)) {
if (xumount(p.data()) == 0)
if (xumount2(p.data(), MNT_DETACH) == 0)
LOGD("Unmount [%s]\n", p.data());
}
execv("/init", argv);

View File

@ -234,6 +234,10 @@ void SARBase::patch_rootdir() {
make_pair(SPLIT_PLAT_CIL, "xxx"), /* Force loading monolithic sepolicy */
make_pair(MONOPOLICY, sepol) /* Redirect /sepolicy to custom path */
});
if (avd_hack) {
// Force disable early mount on original init
init.patch({ make_pair("android,fstab", "xxx") });
}
xmkdir(ROOTOVL, 0);
int dest = xopen(ROOTOVL "/init", O_CREAT | O_WRONLY | O_CLOEXEC, 0);
xwrite(dest, init.buf, init.sz);

View File

@ -6,14 +6,17 @@
# Support emulator ABI: x86_64 and arm64
# Support API level: 23 - 31 (21 and 22 images do not have SELinux)
#
# With an emulator booted and accessible via ADB, usage:
# ./build.py emulator
#
# This script will stop zygote, simulate the Magisk start up process
# that would've happened before zygote was started, and finally
# restart zygote. This is useful for setting up the emulator for
# developing Magisk, testing modules, and developing root apps using
# the official Android emulator (AVD) instead of a real device.
#
# This only covers the "core" features of Magisk. Testing magiskinit
# and magiskboot require additional setups that are not covered here.
# This only covers the "core" features of Magisk. For testing
# magiskinit, please checkout avd_init.sh.
#
#####################################################################

81
scripts/avd_patch.sh Normal file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env bash
#####################################################################
# AVD MagiskInit Setup
#####################################################################
#
# Support emulator ABI: x86_64 and arm64
# Support API level: 23 - 31 (21 and 22 images do not have SELinux)
#
# With an emulator booted and accessible via ADB, usage:
# ./build.py avd_patch path/to/booted/avd-image/ramdisk.img
#
# The purpose of this script is to patch AVD ramdisk.img and do a
# full integration test of magiskinit under several circumstances.
# After patching ramdisk.img, close the emulator, then select
# "Cold Boot Now" in AVD Manager to force a full reboot.
#
#####################################################################
# AVD Init Configurations:
#
# rootfs w/o early mount: API 23 - 25
# rootfs with early mount: API 26 - 27
# Legacy system-as-root: API 28
# 2 stage init: API 29 - 31
#####################################################################
if [ ! -f /system/build.prop ]; then
# Running on PC
echo 'Please run `./build.py avd_patch` instead of directly executing the script!'
exit 1
fi
cd /data/local/tmp
chmod 755 busybox
if [ -z "$FIRST_STAGE" ]; then
export FIRST_STAGE=1
export ASH_STANDALONE=1
# Re-exec script with busybox
exec ./busybox sh $0
fi
# Extract files from APK
unzip -oj app-debug.apk 'assets/util_functions.sh'
. ./util_functions.sh
api_level_arch_detect
unzip -oj app-debug.apk "lib/$ABI/*" "lib/$ABI32/libmagisk32.so" -x "lib/$ABI/busybox.so"
for file in lib*.so; do
chmod 755 $file
mv "$file" "${file:3:${#file}-6}"
done
gzip -d ramdisk.cpio.gz
cp ramdisk.cpio ramdisk.cpio.orig
touch config
# For API 28, we also patch advancedFeatures.ini to disable SAR
# Manually override skip_initramfs by setting RECOVERYMODE=true
[ $API = "28" ] && echo 'RECOVERYMODE=true' >> config
./magiskboot compress=xz magisk32 magisk32.xz
./magiskboot compress=xz magisk64 magisk64.xz
export KEEPVERITY=false
export KEEPFORCEENCRYPT=true
./magiskboot cpio ramdisk.cpio \
"add 0750 init magiskinit" \
"mkdir 0750 overlay.d" \
"mkdir 0750 overlay.d/sbin" \
"add 0644 overlay.d/sbin/magisk32.xz magisk32.xz" \
"add 0644 overlay.d/sbin/magisk64.xz magisk64.xz" \
"patch" \
"backup ramdisk.cpio.orig" \
"mkdir 000 .backup" \
"add 000 .backup/.magisk config"
rm -f ramdisk.cpio.orig config magisk*.xz
gzip -9 ramdisk.cpio