#! /bin/bash
set -e
PHONE_ADB_SERIAL_NUMBER=${ADB_DEVICE} # The adb serial number can be found by running `adb devices`.
if [$PHONE_ADB_SERIAL_NUMBER = '']; then
PHONE_ADB_SERIAL_NUMBER=$(echo $(adb devices) | sed -rn 's/(^|(.* ))([^ ]*) device(( .*)|$)/\3/; T; p; q')
if [[ -n $PHONE_ADB_SERIAL_NUMBER ]]; then
print_message "ERROR: no suitable phone connected"
exit 1
fi
fi
print_message() {
printf "$1\n"
}
check_phone_is_connected() {
if ! adb devices | grep --silent "$PHONE_ADB_SERIAL_NUMBER"; then
print_message "ERROR: phone '$PHONE_ADB_SERIAL_NUMBER' is not connected"
adb devices -l
exit 1
fi
print_message "Operations will be performed on:"
adb devices -l | grep $PHONE_ADB_SERIAL_NUMBER
}
check_phone_is_in_fastboot_mode() {
fastboot devices | grep --silent "$PHONE_ADB_SERIAL_NUMBER"
}
get_lineage_version() {
adb -s "$PHONE_ADB_SERIAL_NUMBER" shell getprop ro.lineage.version | tr -d '\n'
}
get_device_name() {
adb -s "$PHONE_ADB_SERIAL_NUMBER" shell getprop ro.lineage.device | tr -d '\n'
}
get_device_downloads_page() {
echo "https://download.lineageos.org/$(get_device_name)"
}
get_build_url() {
curl --no-progress-meter "$(get_device_downloads_page)" |
grep --only-matching --ignore-case \
"https://mirrorbits.lineageos.org/[^\"]*"$(
)"$(get_lineage_version)-signed.zip" |
head -1
}
check_exists_latest_lineageos_build() {
if [[ -f /tmp/lineageos.zip ]]; then
# The --location lets curl follow the redirection.
print_message "$(get_build_url)?sha256"
curl --location "$(get_build_url)?sha256" --output /tmp/lineageos.sha256
hash1=$( awk '{print $1}' /tmp/lineageos.sha256 )
hash2=$( sha256sum /tmp/lineageos.zip | awk '{print $1}' )
[[ -n $hash1 && -n $hash2 && $hash1 == $hash2 ]]
fi
return
}
download_latest_lineageos_build() {
# The --location lets curl follow the redirection.
curl --location "$(get_build_url)" --output /tmp/lineageos.zip
}
extract_boot_image() {
# See https://wiki.lineageos.org/extracting_blobs_from_zips to understand the different ways to extract the boot.img file.
if is_ota_block_based; then
extract_boot_image_from_block_based_ota
elif is_ota_payload_based; then
extract_boot_image_from_payload_based_ota
else
extract_boot_image_from_file_based_ota
fi
}
extract_boot_image_from_file_based_ota() {
echo 'ERROR: the function "extract_boot_image_from_file_based_ota" is not implemented'
exit 1
}
extract_payload_from_payload_based_ota() {
unzip -od /tmp /tmp/lineageos.zip payload.bin
}
generate_random_alnum_string_of_length_6() {
tr -dc A-Za-z0-9 </dev/urandom | head -c 6
}
extract_boot_image_from_payload_file() {
# For cases in which the repo has already been cloned, I prefer to create a random directory name to clone into, instead of deleting any file or directory on the user's machine.
scripts_directory_name="scripts_$(generate_random_alnum_string_of_length_6)"
git clone https://github.com/LineageOS/scripts /tmp/$scripts_directory_name
python3 /tmp/$scripts_directory_name/update-payload-extractor/extract.py --partitions boot --output_dir /tmp/ /tmp/payload.bin
}
extract_boot_image_from_payload_based_ota() {
extract_payload_from_payload_based_ota
extract_boot_image_from_payload_file
}
is_ota_payload_based() {
unzip -l /tmp/lineageos.zip | grep -wq payload.bin
}
is_ota_block_based() {
unzip -l /tmp/lineageos.zip | grep -wq boot.img
}
extract_boot_image_from_block_based_ota() {
unzip -od /tmp /tmp/lineageos.zip boot.img
}
transfer_unpatched_boot_image_to_phone() {
adb -s "$PHONE_ADB_SERIAL_NUMBER" push /tmp/boot.img /sdcard/Download/boot.img
}
patch_boot_image_on_phone() {
adb -s "$PHONE_ADB_SERIAL_NUMBER" shell /data/adb/magisk/boot_patch.sh /sdcard/Download/boot.img
adb -s "$PHONE_ADB_SERIAL_NUMBER" shell mv /data/adb/magisk/new-boot.img /sdcard/Download/patched-boot.img
}
transfer_patched_boot_image_to_pc() {
adb -s "$PHONE_ADB_SERIAL_NUMBER" pull /sdcard/Download/patched-boot.img /tmp
}
reboot_to_bootloader() {
adb -s "$PHONE_ADB_SERIAL_NUMBER" reboot bootloader
}
wait_for_phone_to_be_in_fastboot() {
SECONDS=0
until check_phone_is_in_fastboot_mode; do
if ((SECONDS > 60)); then
echo "Giving up..."
exit 2
fi
echo "Phone is not in fastboot mode yet. Waiting..."
sleep 5
done
}
flash_patched_boot_image() {
fastboot -s "$PHONE_ADB_SERIAL_NUMBER" flash boot /tmp/patched-boot.img
}
reboot_phone() {
fastboot -s "$PHONE_ADB_SERIAL_NUMBER" reboot
}
check_magisk_app() {
magisk_package_name="com.topjohnwu.magisk"
magisk_package_name_count=$(adb -s "$PHONE_ADB_SERIAL_NUMBER" shell pm list packages "$magisk_package_name" | wc -l)
if [ "${magisk_package_name_count}" -eq 1 ]
then
print_message "Magisk app seems to be installed on selected phone"
elif [ "${magisk_package_name_count}" -eq 0 ]
then
print_message "ERROR: Magisk app seems not installed on selected phone."
exit -1
else
print_message "ERROR: More than one Magisk app entry is present:"
adb -s "$PHONE_ADB_SERIAL_NUMBER" shell pm list packages "$magisk_package_name"
exit -1
fi
}
is_rooted_debugging_enabled() {
adb -s "$PHONE_ADB_SERIAL_NUMBER" root
}
main() {
print_message "Looking for phone '$PHONE_ADB_SERIAL_NUMBER'"
check_phone_is_connected
print_message "Checking on phone if rooted debugging is enabled"
is_rooted_debugging_enabled
print_message "Checking on phone if Magisk is installed"
check_magisk_app
print_message "Checking if build archive already exists"
if check_exists_latest_lineageos_build; then
print_message "Build archive found, skipping download"
else
print_message "Build archive not found or hash mismatch, downloading from $(get_build_url)"
download_latest_lineageos_build
fi
print_message "Extracting 'boot.img' from build archive in /tmp/boot.img"
extract_boot_image
print_message "Copying from PC to phone the boot image in /sdcard/Download/boot.img"
transfer_unpatched_boot_image_to_phone
print_message "Patching boot image with Magisk script and moving it in /sdcard/Download/patched-boot.img"
patch_boot_image_on_phone
print_message "Copying patched boot image from phone to PC in /tmp/patched-boot.img"
transfer_patched_boot_image_to_pc
print_message "Rebooting phone in fastboot mode"
reboot_to_bootloader
wait_for_phone_to_be_in_fastboot
print_message "Flashing patched boot image on phone"
flash_patched_boot_image
print_message "Rebooting phone"
reboot_phone
}
# Run the main only when this file is executed as script, to help with testing.
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi