#!/bin/bash -e
usage () {
    set +x
cat <<EOF
Usage: $0 device_serialno kernel_dist_dir output_dir

e.g. $0 123456789ABCDE out/mixed/dist output_dir

1) device_serialno is device serial-number can be found from "adb devices".

2) kernel_dist_dir is the path to where the kernel binaries can be found
    Image.lz4
    initramfs.img
    *.dtb
    dtbo.img
    vendor_dlkm.img

3) out_dir is the path to where the new build will be placed.

************************************************************************
This script is intended to avoid the dependency issue between device base-rom
and kernel prebuilt ramdisks under prebuilt/boot-artifacts/ramdisk/. To
achieve this, this script will dump/extract ramdisks from device via ADB
and recreate boot.img and vendor_boot.img based on it.

This script does not re-create any of the vbmeta partition images which
dm-verity has to be disabled prior to flashing these images.

Please refer to b/203499549 for the details.
************************************************************************

EOF
}

# Print error message and exit.
# Usage: exit_badparam message
#
# message is a string to be displayed before exit.
exit_badparam () {
    echo "ERROR: $1" >&2
    echo
    usage
    exit 1
}

cleanup_and_exit () {
    readonly result="$?"
    rm -rf "${TEMP_DIR}"
    exit "${result}"
}

# $1 header file to parse
# $2 key to get
get_bootimg_header_field() {
    local hfile="$1"
    local key="$2"
    if [ ! -f "${hfile}" ]; then
        echo "Boot image header file does not exist." >&2
        exit 1
    fi
    sed -ne "/^${key}: /s/^${key}: //p" "${hfile}"
}

# adb root
function adb_wait_root() {
    if ! adb -s $SERIALNO get-state >& /dev/null ; then
        echo
        echo "!!! Device $SERIALNO is not reachable via ADB !!!"
        echo "Please make sure device boot into Android and tap \"Allow\" to the \"Allow USB Debugging\" dialog."
        echo
        exit 1
    fi
    if ! adb -s $SERIALNO root | grep 'adbd is already running as root' ; then
        sleep 2
        adb -s $SERIALNO wait-for-device >& /dev/null
    fi
}

# Dump partitions from device
function dump_boot_images_from_device() {
	adb_wait_root
	for img in dtbo boot vendor_boot vendor_dlkm; do
		if [ "${img}" == "vendor_dlkm" ]; then
			adb -s $SERIALNO pull /dev/block/$(adb -s $SERIALNO shell getprop dev.mnt.dev.${img}) "${DEVICE_DIR}"/${img}.img
		else
			adb -s $SERIALNO pull /dev/block/bootdevice/by-name/${img}$(adb -s $SERIALNO shell getprop ro.boot.slot_suffix) "${DEVICE_DIR}"/${img}.img
		fi
	done
}

trap cleanup_and_exit EXIT

ARGS=()
KCOMPRESS=.lz4

while [[ $# -gt 0 ]]; do
    case "$1" in
        --*)
            exit_badparam "unrecognized option '$1'"
            shift
            ;;
        *)
            ARGS+=("$1")
            shift
            ;;
    esac
done

if [[ ${#ARGS[*]} -gt 4 ]]; then
	echo
    exit_badparam "Unexpected number of arguments"
fi

readonly SERIALNO="${ARGS[0]}"
readonly KERNEL_DIST_DIR="${ARGS[1]}"
readonly OUTPUT_DIR="${ARGS[2]}"
readonly DEVICE_DIR="${ARGS[2]}"/DUMP_FROM_DEVICE
readonly TEMP_DIR="$(mktemp -d --tmpdir "$(basename $0)"_XXXXXXXX)"
readonly LZ4="prebuilts/kernel-build-tools/linux-x86/bin/lz4"
readonly LZ4_COMPRESS="${LZ4} -c -12 --favor-decSpeed"
readonly LZ4_DECOMP="${LZ4} -c -f -d"

# Create destination directory if need be
if [ -n "${OUTPUT_DIR}" ] && [ ! -d "${OUTPUT_DIR}" ]; then
    mkdir -p "${OUTPUT_DIR}" || true
fi

RESP=${ARGS[3]:-""}
# Create device directory and dump paritions from device if need be
if [ -n "${DEVICE_DIR}" ] && [ ! -d "${DEVICE_DIR}" ]; then
    mkdir -p "${DEVICE_DIR}" || true
    dump_boot_images_from_device
else
	if [ "${RESP}" == "" ]; then
		echo
		read -p "Previous dumped files already exist in ${DEVICE_DIR}/ !!! Do you want to dump from device and replace it? (y/N): " RESP
		echo
	fi
	case $RESP in
		[Yy]* ) dump_boot_images_from_device;	;;
	esac
fi

KERNEL_IMAGE="${KERNEL_DIST_DIR}/Image${KCOMPRESS}"

# Extract boot images
echo "Extracting boot.img from ${DEVICE_DIR}/ ..."
cp -rf "${DEVICE_DIR}"/boot.img "${TEMP_DIR}"/boot.img

echo "Unpacking boot.img ..."
tools/mkbootimg/unpack_bootimg.py --boot_img "${TEMP_DIR}"/boot.img \
    --out "${TEMP_DIR}"/bootimg > "${TEMP_DIR}"/bootimg.header

get_bhf() {
    get_bootimg_header_field "${TEMP_DIR}"/bootimg.header "$1"
}

header_version="$(get_bhf 'boot image header version')"
if ! [ "${header_version}" -ge 0 ]; then
    echo "Invalid boot image header version: ${header_version}" >&2 && exit 1
fi

if [ "${header_version}" -ge 4 ]; then
    echo "Extracting vendor_boot.img from ${DEVICE_DIR}/ ..."
    cp -rf "${DEVICE_DIR}"/vendor_boot.img "${TEMP_DIR}"/vendor_boot.img
fi

get_vendor_bhf() {
    get_bootimg_header_field "${TEMP_DIR}"/vendor_bootimg.header "$1"
}

# Concatenate provided .dtb files into dtb.img
cat "${KERNEL_DIST_DIR}"/*.dtb > "${TEMP_DIR}"/dtb.img

MKBOOTIMGARGS=()
MKBOOTIMG_VENDORARGS=()

boot_ramdisk="${TEMP_DIR}"/bootimg/ramdisk

if [ "${header_version}" -eq 4 ]; then
    echo "Unpacking vendor_boot.img ..."
    tools/mkbootimg/unpack_bootimg.py --boot_img "${TEMP_DIR}"/vendor_boot.img \
        --out "${TEMP_DIR}"/vendor_bootimg --format=mkbootimg -0 > "${TEMP_DIR}"/vendor_bootimg.args

    while IFS= read -r -d '' ARG; do
        MKBOOTIMG_VENDORARGS+=("${ARG}")
    done <"${TEMP_DIR}"/vendor_bootimg.args
    MKBOOTIMG_VENDORARGS+=("--vendor_boot" "${TEMP_DIR}"/vendor_boot.img)

    if [ ! -f "${TEMP_DIR}"/vendor_bootimg/vendor-ramdisk-by-name/ramdisk_dlkm ]; then
        echo "dlkm ramdisk fragment missing from vendor_boot.img" >&2
        exit 1
    fi
    cp "${KERNEL_DIST_DIR}"/initramfs.img \
        "${TEMP_DIR}"/vendor_bootimg/vendor-ramdisk-by-name/ramdisk_dlkm

    if [ ! -f "${TEMP_DIR}"/vendor_bootimg/dtb ]; then
        echo "dtb missing from vendor_boot.img" >&2
        exit 1
    fi
    cp "${TEMP_DIR}"/dtb.img \
        "${TEMP_DIR}"/vendor_bootimg/dtb

    MKBOOTIMGARGS+=("--ramdisk" "${boot_ramdisk}")
else
    echo "ERROR: Unsupported boot image header version: ${header_version}" >&2
    exit 1
fi

# For header_version == 4, "unpack_bootimg --format=mkbootimg" will provide the
# --dtb argument.
if [ "${header_version}" -lt 4 ]; then
    MKBOOTIMGARGS+=("--dtb" "${TEMP_DIR}"/dtb.img)
fi

MKBOOTIMGARGS+=("--header_version" "${header_version}")
MKBOOTIMGARGS+=("--cmdline" "$(get_bhf 'command line args')")

# Create boot.img and vendor_boot.img. (vendor_boot.img is only created for
# header_version is 4 or later)
echo "Creating repacked boot.img and vendor_boot.img..."
tools/mkbootimg/mkbootimg.py \
    --kernel "$KERNEL_IMAGE" \
    "${MKBOOTIMGARGS[@]}" \
    "${MKBOOTIMG_VENDORARGS[@]}" \
    --output "${TEMP_DIR}"/boot.img

BOOT_SIZE=$(stat -c%s "$DEVICE_DIR"/boot.img)
prebuilts/kernel-build-tools/linux-x86/bin/avbtool add_hash_footer \
    --image "${TEMP_DIR}"/boot.img \
    --partition_size ${BOOT_SIZE} \
    --partition_name boot

VENDOR_BOOT_SIZE=$(stat -c%s "$DEVICE_DIR"/vendor_boot.img)
if [ "${header_version}" -ge 4 ]; then
    prebuilts/kernel-build-tools/linux-x86/bin/avbtool add_hash_footer \
        --image "${TEMP_DIR}"/vendor_boot.img \
        --partition_size ${VENDOR_BOOT_SIZE} \
        --partition_name vendor_boot
fi

# Update boot.img in device archive
cp -rf "${TEMP_DIR}/boot.img" "${OUTPUT_DIR}"
if [ "${header_version}" -ge 4 ]; then
    # and vendor_boot.img (if header_version >= 4)
    echo "Updating boot.img and vendor_boot.img in ${OUTPUT_DIR}/ ..."
    cp -rf "${TEMP_DIR}/vendor_boot.img" "${OUTPUT_DIR}"
fi

# Update {dtbo.img,vendor_dlkm.img} if they are products of the kernel build
for img in dtbo.img vendor_dlkm.img; do
	if [ -f "${KERNEL_DIST_DIR}/${img}" ]; then
		echo "Copying ${img} into ${OUTPUT_DIR}/ ..."
		cp -rf "${KERNEL_DIST_DIR}/${img}" "${OUTPUT_DIR}"
	fi
done

echo
echo "Images have been repacked at: ${OUTPUT_DIR}"/
echo
echo "You may use below command to flash repacked kernel images:"
echo "-------------------------------------------------------------"
echo "cd ${OUTPUT_DIR}/"
echo "fastboot oem disable-verity"
echo "fastboot flash boot boot.img"
echo "fastboot flash dtbo dtbo.img"
echo "fastboot flash vendor_boot vendor_boot.img"
echo "fastboot reboot fastboot"
echo "fastboot flash vendor_dlkm vendor_dlkm.img"
echo "fastboot -w"
echo "fastboot reboot"
echo "-------------------------------------------------------------"
echo
