|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +# SPDX-FileCopyrightText: (c) 2022 ale5000 |
| 4 | +# SPDX-License-Identifier: GPL-3.0-or-later |
| 5 | +# SPDX-FileType: SOURCE |
| 6 | + |
| 7 | +# NOTE: This script simulate a real recovery but it relies on the flashable zip to use the suggested paths. |
| 8 | +# REALLY IMPORTANT: A misbehaving flashable zip can damage your real system. |
| 9 | + |
| 10 | +set -e |
| 11 | +set -o pipefail |
| 12 | + |
| 13 | +fail_with_msg() |
| 14 | +{ |
| 15 | + echo "${1}" |
| 16 | + exit 1 |
| 17 | +} |
| 18 | + |
| 19 | +link_folder() |
| 20 | +{ |
| 21 | + ln -sf "${2}" "${1}" || mkdir -p "${1}" || fail_with_msg "Failed to link dir '${1}'" |
| 22 | +} |
| 23 | + |
| 24 | +if test -z "${1}"; then fail_with_msg 'You must pass the filename of the flashable ZIP as parameter'; fi |
| 25 | + |
| 26 | +# Reset environment |
| 27 | +if ! "${ENV_RESETTED:-false}"; then |
| 28 | + THIS_SCRIPT="$(realpath "${0}" 2>&-)" || fail_with_msg 'Failed to get script filename' |
| 29 | + # Create the temp dir (must be done before resetting environment) |
| 30 | + OUR_TEMP_DIR="$(mktemp -d -t ANDR-RECOV-XXXXXX)" || fail_with_msg 'Failed to create our temp dir' |
| 31 | + exec env -i ENV_RESETTED=true THIS_SCRIPT="${THIS_SCRIPT}" OUR_TEMP_DIR="${OUR_TEMP_DIR}" PATH="${PATH}" bash "${THIS_SCRIPT}" "$@" || fail_with_msg 'failed: exec' |
| 32 | + exit 127 |
| 33 | +fi |
| 34 | +unset ENV_RESETTED |
| 35 | +unset LC_TIME |
| 36 | +uname_o_saved="$(uname -o)" || fail_with_msg 'Failed to get uname -o' |
| 37 | + |
| 38 | +# Check dependencies |
| 39 | +CUSTOM_BUSYBOX="$(which busybox)" || fail_with_msg 'BusyBox is missing' |
| 40 | + |
| 41 | +# Get dir of this script |
| 42 | +THIS_SCRIPT_DIR="$(dirname "${THIS_SCRIPT}")" || fail_with_msg 'Failed to get script dir' |
| 43 | +unset THIS_SCRIPT |
| 44 | + |
| 45 | +newline=' |
| 46 | +' |
| 47 | +for _current_file in "$@" |
| 48 | +do |
| 49 | + FILES="${FILES}$(realpath "${_current_file}")${newline}" || fail_with_msg "Invalid filename: ${_current_file}" |
| 50 | +done |
| 51 | + |
| 52 | +# Ensure we have a path the the temp dir and empty it (should be already empty, but we must be sure) |
| 53 | +if test -z "${OUR_TEMP_DIR}"; then fail_with_msg 'Failed to create our temp dir'; fi |
| 54 | +rm -rf "${OUR_TEMP_DIR:?}"/* || fail_with_msg 'Failed to empty our temp dir' |
| 55 | + |
| 56 | +# Setup the needed variables |
| 57 | +FLASHABLE_ZIP_PATH="$(realpath "${1}" 2>/dev/null)" || fail_with_msg 'Failed to get the flashable ZIP' |
| 58 | +OVERRIDE_DIR="${THIS_SCRIPT_DIR}/override" |
| 59 | +BASE_SIMULATION_PATH="${OUR_TEMP_DIR}/root"; mkdir -p "${BASE_SIMULATION_PATH}" # Internal var |
| 60 | +INIT_DIR="$(pwd)" |
| 61 | + |
| 62 | +# Simulate the environment variables (part 1) |
| 63 | +EXTERNAL_STORAGE="${BASE_SIMULATION_PATH}/sdcard0" |
| 64 | +SECONDARY_STORAGE="${BASE_SIMULATION_PATH}/sdcard1" |
| 65 | +LD_LIBRARY_PATH=".:${BASE_SIMULATION_PATH}/sbin" |
| 66 | +ANDROID_DATA="${BASE_SIMULATION_PATH}/data" |
| 67 | +ANDROID_ROOT="${BASE_SIMULATION_PATH}/system" |
| 68 | +ANDROID_PROPERTY_WORKSPACE='21,32768' |
| 69 | +TZ='CET-1CEST,M3.5.0,M10.5.0' |
| 70 | +TMPDIR="${BASE_SIMULATION_PATH}/tmp" |
| 71 | + |
| 72 | +# Simulate the Android environment inside the temp folder |
| 73 | +cd "${BASE_SIMULATION_PATH}" || fail_with_msg 'Failed to change dir to the base simulation path' |
| 74 | +mkdir -p "${ANDROID_ROOT}" |
| 75 | +mkdir -p "${BASE_SIMULATION_PATH}/system/priv-app" |
| 76 | +mkdir -p "${BASE_SIMULATION_PATH}/system/app" |
| 77 | +mkdir -p "${ANDROID_ROOT}/bin" |
| 78 | +mkdir -p "${ANDROID_DATA}" |
| 79 | +mkdir -p "${EXTERNAL_STORAGE}" |
| 80 | +mkdir -p "${SECONDARY_STORAGE}" |
| 81 | +link_folder "${BASE_SIMULATION_PATH}/sbin" "${BASE_SIMULATION_PATH}/system/bin" |
| 82 | +link_folder "${BASE_SIMULATION_PATH}/sdcard" "${EXTERNAL_STORAGE}" |
| 83 | + |
| 84 | +{ |
| 85 | + echo 'ro.build.version.sdk=25' |
| 86 | + echo 'ro.product.cpu.abi=x86_64' |
| 87 | + echo 'ro.product.cpu.abi2=armeabi-v7a' |
| 88 | + echo 'ro.product.cpu.abilist=x86_64,x86,arm64-v8a,armeabi-v7a,armeabi' |
| 89 | + echo 'ro.product.cpu.abilist32=x86,armeabi-v7a,armeabi' |
| 90 | + echo 'ro.product.cpu.abilist64=x86_64,arm64-v8a' |
| 91 | +} > "${ANDROID_ROOT}/build.prop" |
| 92 | + |
| 93 | +touch "${BASE_SIMULATION_PATH}/AndroidManifest.xml" |
| 94 | +printf 'a\0n\0d\0r\0o\0i\0d\0.\0p\0e\0r\0m\0i\0s\0s\0i\0o\0n\0.\0F\0A\0K\0E\0_\0P\0A\0C\0K\0A\0G\0E\0_\0S\0I\0G\0N\0A\0T\0U\0R\0E\0' > "${BASE_SIMULATION_PATH}/AndroidManifest.xml" |
| 95 | +mkdir -p "${ANDROID_ROOT}/framework" |
| 96 | +zip -D -9 -X -UN=n -nw -q "${ANDROID_ROOT}/framework/framework-res.apk" 'AndroidManifest.xml' || fail_with_msg 'Failed compressing framework-res.apk' |
| 97 | +rm -f "${BASE_SIMULATION_PATH}/AndroidManifest.xml" |
| 98 | + |
| 99 | +mkdir -p "${TMPDIR}" |
| 100 | +cp -rf "${THIS_SCRIPT_DIR}/updater.sh" "${TMPDIR}/updater" || fail_with_msg 'Failed to copy the updater script' |
| 101 | +chmod +x "${TMPDIR}/updater" || fail_with_msg "chmod failed on '${TMPDIR}/updater'" |
| 102 | + |
| 103 | +# Setup recovery output |
| 104 | +recovery_fd=99 |
| 105 | +if test -e "/proc/self/fd/${recovery_fd}"; then fail_with_msg 'Recovery FD already exist'; fi |
| 106 | +mkdir -p "${THIS_SCRIPT_DIR}/output" |
| 107 | +touch "${THIS_SCRIPT_DIR}/output/recovery-output.log" |
| 108 | +if test "${uname_o_saved}" != 'MS/Windows'; then |
| 109 | + sudo chattr +aAd "${THIS_SCRIPT_DIR}/output/recovery-output.log" || fail_with_msg "chattr failed on 'recovery-output.log'" |
| 110 | +fi |
| 111 | +# shellcheck disable=SC3023 |
| 112 | +exec 99>> "${THIS_SCRIPT_DIR}/output/recovery-output.log" |
| 113 | + |
| 114 | +# Simulate the environment variables (part 2) |
| 115 | +PATH="${OVERRIDE_DIR}:${BASE_SIMULATION_PATH}/sbin:${ANDROID_ROOT}/bin:${PATH}" # We have to keep the original folders inside PATH otherwise everything stop working |
| 116 | +export EXTERNAL_STORAGE |
| 117 | +export LD_LIBRARY_PATH |
| 118 | +export ANDROID_DATA |
| 119 | +export PATH |
| 120 | +export ANDROID_ROOT |
| 121 | +export ANDROID_PROPERTY_WORKSPACE |
| 122 | +export TZ |
| 123 | +export TMPDIR |
| 124 | +export CUSTOM_BUSYBOX |
| 125 | + |
| 126 | +# Prepare before execution |
| 127 | +export OVERRIDE_DIR |
| 128 | +FLASHABLE_ZIP_NAME="$("${CUSTOM_BUSYBOX}" basename "${FLASHABLE_ZIP_PATH}")" || fail_with_msg 'Failed to get the filename of the flashable ZIP' |
| 129 | +"${CUSTOM_BUSYBOX}" cp -rf "${FLASHABLE_ZIP_PATH}" "${SECONDARY_STORAGE}/${FLASHABLE_ZIP_NAME}" || fail_with_msg 'Failed to copy the flashable ZIP' |
| 130 | +"${CUSTOM_BUSYBOX}" unzip -opq "${SECONDARY_STORAGE}/${FLASHABLE_ZIP_NAME}" 'META-INF/com/google/android/update-binary' > "${TMPDIR}/update-binary" || fail_with_msg 'Failed to extract the update-binary' |
| 131 | +chmod +x "${TMPDIR}/update-binary" || fail_with_msg "chmod failed on '${TMPDIR}/update-binary'" |
| 132 | + |
| 133 | +# Execute the script that will run the flashable zip |
| 134 | +set +e |
| 135 | +"${CUSTOM_BUSYBOX}" ash "${TMPDIR}/updater" 3 "${recovery_fd}" "${SECONDARY_STORAGE}/${FLASHABLE_ZIP_NAME}" | TZ=UTC ts '[%H:%M:%S]'; STATUS="$?" |
| 136 | +set -e |
| 137 | + |
| 138 | +# Close recovery output |
| 139 | +# shellcheck disable=SC3023 |
| 140 | +exec 99>&- |
| 141 | +if test "${uname_o_saved}" != 'MS/Windows'; then |
| 142 | + sudo chattr -a "${THIS_SCRIPT_DIR}/output/recovery-output.log" || fail_with_msg "chattr failed on 'recovery-output.log'" |
| 143 | +fi |
| 144 | + |
| 145 | +# Parse recovery output |
| 146 | +last_msg_printed=false |
| 147 | +while IFS=' ' read -r ui_command text; do |
| 148 | + if test "${ui_command}" = 'ui_print'; then |
| 149 | + if test "${last_msg_printed}" = true && test "${text}" = ''; then |
| 150 | + last_msg_printed=false |
| 151 | + else |
| 152 | + echo "${text}" |
| 153 | + last_msg_printed=true |
| 154 | + fi |
| 155 | + else |
| 156 | + echo "> COMMAND: ${ui_command} ${text}" |
| 157 | + last_msg_printed=false |
| 158 | + fi |
| 159 | +done < "${THIS_SCRIPT_DIR}/output/recovery-output.log" > "${THIS_SCRIPT_DIR}/output/recovery-output-parsed.log" |
| 160 | + |
| 161 | +# Final cleanup |
| 162 | +cd "${INIT_DIR}" || fail_with_msg 'Failed to change back the folder' |
| 163 | +unset TMPDIR |
| 164 | +rm -rf "${OUR_TEMP_DIR:?}" & |
| 165 | +if test "${STATUS}" -ne 0; then fail_with_msg "Installation failed with error ${STATUS}"; fi |
0 commit comments