mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:07:04 +01:00
Split scripts/lib/mtk_common.sh with the same entry-point pattern
503 -> 132 across three parts. The seven mtk_root consumers are untouched because the path they source is unchanged. Identical 28-function set before and after, checked after each of the two carves. The `<<<` in this file are here-strings, not heredocs - worth confirming rather than assuming, after enforce_vbox_hosts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
This commit is contained in:
parent
51e3945507
commit
ef0a26abc7
@ -0,0 +1,17 @@
|
||||
{
|
||||
"title": "Split scripts/lib/mtk_common.sh under the 250-line cap",
|
||||
"objective": "Bring the shared MediaTek library that seven mtk_root scripts source under the cap, using the same entry-point pattern proven on common.sh, without changing any name it exports.",
|
||||
"acceptance_criteria": [
|
||||
"mtk_common.sh and all three new parts are at most 250 lines.",
|
||||
"mtk_common.sh remains the single path every consumer sources; it sources its own parts.",
|
||||
"Sourcing it yields exactly the same 28 function names as before, checked after EACH carve rather than only at the end.",
|
||||
"`<<<` occurrences are confirmed to be here-strings rather than heredocs before choosing a seam.",
|
||||
"The pre-commit shellcheck hook passes on every file, with no new suppression."
|
||||
],
|
||||
"out_of_scope": [
|
||||
"Executing any mtk_root script; they talk to a phone over USB.",
|
||||
"Changing what any helper does.",
|
||||
"The other over-cap linux_configuration scripts."
|
||||
],
|
||||
"verifier": "A subshell that sources mtk_common.sh and prints `declare -F`, diffed before and after; plus shellcheck -x, meta/scripts/verify_shell_split.sh on two consumers, and check_file_length.sh."
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
{
|
||||
"intent": "Split scripts/lib/mtk_common.sh with the same entry-point pattern used for common.sh.",
|
||||
"scope": [
|
||||
"linux_configuration/scripts/lib/mtk_common.sh (503 -> 132)",
|
||||
"lib/mtk_device.sh, mtk_classify.sh, mtk_partitions.sh (new)",
|
||||
"Non-goal: no behaviour change, no change to the public surface"
|
||||
],
|
||||
"changes": [
|
||||
"mtk_common.sh keeps its logging, path and sanitising helpers and sources three new parts: adb/device selection, device classification, and partition discovery.",
|
||||
"The seven mtk_root scripts that source it are untouched, because the path they source is unchanged.",
|
||||
"The `<<<` occurrences in this file are here-strings, not heredocs, so the heredoc hazard did not apply - worth checking rather than assuming, given the enforce_vbox_hosts failure."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "source mtk_common.sh in a subshell and diff `declare -F` before and after",
|
||||
"result": "pass",
|
||||
"evidence": "Identical 28-function set, checked after each of the two carves rather than only at the end."
|
||||
},
|
||||
{
|
||||
"command": "shellcheck -x mtk_common.sh and shellcheck on each part",
|
||||
"result": "pass",
|
||||
"evidence": "Zero findings, no suppression."
|
||||
},
|
||||
{
|
||||
"command": "meta/scripts/verify_shell_split.sh on 20-dump-stock.sh and install.sh",
|
||||
"result": "pass",
|
||||
"evidence": "Both consumers report 'libs sourced, functions defined' - they source mtk_common.sh from a sibling directory and still resolve everything through the new chain."
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"None of the seven mtk_root consumers was executed: they talk to a phone over USB. The function-set diff plus the stubbed runs are the available evidence.",
|
||||
"_MTK_LIB_DIR uses ${BASH_SOURCE[0]} because this file is only ever sourced; $0 would point at the calling script."
|
||||
],
|
||||
"rollback": [
|
||||
"git revert the commit; mtk_common.sh returns to 503 lines and the three parts disappear.",
|
||||
"After rollback, re-run the declare -F diff and expect the same 28 functions."
|
||||
]
|
||||
}
|
||||
88
linux_configuration/scripts/lib/mtk_classify.sh
Executable file
88
linux_configuration/scripts/lib/mtk_classify.sh
Executable file
@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
# Device classification, partition discovery and layout checks.
|
||||
#
|
||||
# Sourced by mtk_common.sh, which stays the single entry point the
|
||||
# mtk_root scripts source, so their public surface is unchanged.
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Classification
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# classify_device
|
||||
# Sets MTK_DEVICE_CLASS to ULEFONE, PIXEL or UNKNOWN, and MTK_CLASS_REASON to
|
||||
# a human-readable explanation of how it got there.
|
||||
#
|
||||
# ULEFONE requires a positive MediaTek signal AND a vendor/model match - both,
|
||||
# never either, and never "not a Pixel, so presumably the Ulefone". A third
|
||||
# phone attached by accident must land in UNKNOWN.
|
||||
classify_device() {
|
||||
local manufacturer="" model="" platform="" hardware="" device=""
|
||||
local mtk_match=0 ulefone_match=0 pixel_match=0
|
||||
|
||||
manufacturer="$(dev_getprop ro.product.manufacturer)"
|
||||
model="$(dev_getprop ro.product.model)"
|
||||
platform="$(dev_getprop ro.board.platform)"
|
||||
hardware="$(dev_getprop ro.hardware)"
|
||||
device="$(dev_getprop ro.product.device)"
|
||||
|
||||
MTK_PROP_MANUFACTURER="$manufacturer"
|
||||
MTK_PROP_MODEL="$model"
|
||||
MTK_PROP_PLATFORM="$platform"
|
||||
MTK_PROP_HARDWARE="$hardware"
|
||||
MTK_PROP_DEVICE="$device"
|
||||
|
||||
if [[ $platform =~ $MTK_PLATFORM_PATTERN ]] || [[ $hardware =~ $MTK_PLATFORM_PATTERN ]]; then
|
||||
mtk_match=1
|
||||
fi
|
||||
if [[ $manufacturer =~ $ULEFONE_VENDOR_PATTERN ]] || [[ $model =~ $ULEFONE_MODEL_PATTERN ]]; then
|
||||
ulefone_match=1
|
||||
fi
|
||||
if [[ $manufacturer =~ $PIXEL_VENDOR_PATTERN ]] && [[ $model =~ $PIXEL_MODEL_PATTERN ]]; then
|
||||
pixel_match=1
|
||||
fi
|
||||
|
||||
if [[ $mtk_match -eq 1 && $ulefone_match -eq 1 ]]; then
|
||||
MTK_DEVICE_CLASS="ULEFONE"
|
||||
MTK_CLASS_REASON="MediaTek platform [${platform:-unset}/${hardware:-unset}] and Ulefone identity [${manufacturer} ${model}]"
|
||||
elif [[ $pixel_match -eq 1 ]]; then
|
||||
MTK_DEVICE_CLASS="PIXEL"
|
||||
MTK_CLASS_REASON="Google Pixel identity [${manufacturer} ${model}]"
|
||||
else
|
||||
MTK_DEVICE_CLASS="UNKNOWN"
|
||||
MTK_CLASS_REASON="no rule matched: manufacturer=[${manufacturer}] model=[${model}] platform=[${platform}] hardware=[${hardware}]"
|
||||
fi
|
||||
|
||||
export MTK_DEVICE_CLASS MTK_CLASS_REASON
|
||||
export MTK_PROP_MANUFACTURER MTK_PROP_MODEL MTK_PROP_PLATFORM MTK_PROP_HARDWARE MTK_PROP_DEVICE
|
||||
}
|
||||
|
||||
# mtk_explain_classification - why classify_device decided what it did.
|
||||
mtk_explain_classification() {
|
||||
printf 'ro.product.manufacturer = %s\n' "${MTK_PROP_MANUFACTURER:-}"
|
||||
printf 'ro.product.model = %s\n' "${MTK_PROP_MODEL:-}"
|
||||
printf 'ro.product.device = %s\n' "${MTK_PROP_DEVICE:-}"
|
||||
printf 'ro.board.platform = %s\n' "${MTK_PROP_PLATFORM:-}"
|
||||
printf 'ro.hardware = %s\n' "${MTK_PROP_HARDWARE:-}"
|
||||
printf '\n'
|
||||
printf 'MediaTek pattern : %s\n' "$MTK_PLATFORM_PATTERN"
|
||||
printf 'Ulefone vendor : %s\n' "$ULEFONE_VENDOR_PATTERN"
|
||||
printf 'Ulefone model : %s\n' "$ULEFONE_MODEL_PATTERN"
|
||||
printf 'Pixel vendor : %s\n' "$PIXEL_VENDOR_PATTERN"
|
||||
printf 'Pixel model : %s\n' "$PIXEL_MODEL_PATTERN"
|
||||
printf '\n'
|
||||
printf 'Verdict: %s\n' "${MTK_DEVICE_CLASS:-unset}"
|
||||
printf 'Reason : %s\n' "${MTK_CLASS_REASON:-unset}"
|
||||
}
|
||||
|
||||
# mtk_assert_mediatek
|
||||
# A positive assertion, deliberately not "is not a Pixel". Used to gate the
|
||||
# only script that touches partitions.
|
||||
mtk_assert_mediatek() {
|
||||
if [[ ${MTK_PROP_PLATFORM:-} =~ $MTK_PLATFORM_PATTERN ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ ${MTK_PROP_HARDWARE:-} =~ $MTK_PLATFORM_PATTERN ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
@ -123,381 +123,10 @@ dev_list_by_name() {
|
||||
mtk_adb shell 'ls -1 /dev/block/by-name/ 2>/dev/null' 2>/dev/null | tr -d '\r' || true
|
||||
}
|
||||
|
||||
# mtk_adb <args...> - serial-pinned adb, so a second phone plugged in midway
|
||||
# cannot silently become the target.
|
||||
mtk_adb() {
|
||||
adb -s "${MTK_SERIAL:?mtk_select_device must run first}" "$@"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Device presence and authorization
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# mtk_list_devices - prints "<serial> <state>" per attached device.
|
||||
mtk_list_devices() {
|
||||
adb devices 2>/dev/null |
|
||||
awk 'NR > 1 && $2 ~ /^(device|offline|unauthorized|recovery|sideload)$/ { print $1, $2 }'
|
||||
}
|
||||
|
||||
# mtk_device_state <serial> - device|unauthorized|offline|absent
|
||||
mtk_device_state() {
|
||||
local serial="$1"
|
||||
local state=""
|
||||
|
||||
state="$(mtk_list_devices | awk -v s="$serial" '$1 == s { print $2; exit }')"
|
||||
printf '%s' "${state:-absent}"
|
||||
}
|
||||
|
||||
# mtk_select_device [requested-serial]
|
||||
# Sets MTK_SERIAL. Refuses when the choice is ambiguous rather than picking
|
||||
# one - a wrong guess here targets the wrong phone.
|
||||
mtk_select_device() {
|
||||
local requested="${1:-${MTK_SERIAL:-}}"
|
||||
local -a device_rows=()
|
||||
local -a serials=()
|
||||
local row=""
|
||||
|
||||
if [[ -n ${MTK_ROOT_FIXTURE:-} ]]; then
|
||||
MTK_SERIAL="${MTK_SERIAL:-fixture}"
|
||||
export MTK_SERIAL
|
||||
return 0
|
||||
fi
|
||||
|
||||
mapfile -t device_rows < <(mtk_list_devices)
|
||||
|
||||
if [[ ${#device_rows[@]} -eq 0 ]]; then
|
||||
mtk_error "No device visible to adb."
|
||||
mtk_error "Check: cable seated, USB debugging enabled, and try 'adb devices'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
for row in "${device_rows[@]}"; do
|
||||
serials+=("${row%% *}")
|
||||
done
|
||||
|
||||
if [[ -n $requested ]]; then
|
||||
for row in "${serials[@]}"; do
|
||||
if [[ $row == "$requested" ]]; then
|
||||
MTK_SERIAL="$requested"
|
||||
export MTK_SERIAL
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
mtk_error "Requested serial '${requested}' is not attached. Attached: ${serials[*]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ${#serials[@]} -gt 1 ]]; then
|
||||
mtk_error "Multiple devices attached (${serials[*]}); refusing to guess."
|
||||
mtk_error "Re-run with --serial <serial>."
|
||||
return 1
|
||||
fi
|
||||
|
||||
MTK_SERIAL="${serials[0]}"
|
||||
export MTK_SERIAL
|
||||
return 0
|
||||
}
|
||||
|
||||
# mtk_require_authorized
|
||||
# An unauthorized device answers every getprop with an empty string, which
|
||||
# reads exactly like "this device has no properties". Catch it up front so the
|
||||
# report never blames the device for a prompt sitting unanswered on its screen.
|
||||
mtk_require_authorized() {
|
||||
local state=""
|
||||
|
||||
[[ -n ${MTK_ROOT_FIXTURE:-} ]] && return 0
|
||||
|
||||
state="$(mtk_device_state "${MTK_SERIAL}")"
|
||||
case "$state" in
|
||||
device)
|
||||
return 0
|
||||
;;
|
||||
unauthorized)
|
||||
mtk_error "Device ${MTK_SERIAL} is UNAUTHORIZED."
|
||||
mtk_error "The RSA authorization prompt is on the phone's screen - unlock it and tap Allow."
|
||||
mtk_error "If no prompt appears: revoke USB debugging authorizations on the phone, then replug."
|
||||
return 1
|
||||
;;
|
||||
offline)
|
||||
mtk_error "Device ${MTK_SERIAL} is OFFLINE. Replug the cable, or run 'adb kill-server'."
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
mtk_error "Device ${MTK_SERIAL} is in state '${state}', which this toolkit does not read."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Classification
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# classify_device
|
||||
# Sets MTK_DEVICE_CLASS to ULEFONE, PIXEL or UNKNOWN, and MTK_CLASS_REASON to
|
||||
# a human-readable explanation of how it got there.
|
||||
#
|
||||
# ULEFONE requires a positive MediaTek signal AND a vendor/model match - both,
|
||||
# never either, and never "not a Pixel, so presumably the Ulefone". A third
|
||||
# phone attached by accident must land in UNKNOWN.
|
||||
classify_device() {
|
||||
local manufacturer="" model="" platform="" hardware="" device=""
|
||||
local mtk_match=0 ulefone_match=0 pixel_match=0
|
||||
|
||||
manufacturer="$(dev_getprop ro.product.manufacturer)"
|
||||
model="$(dev_getprop ro.product.model)"
|
||||
platform="$(dev_getprop ro.board.platform)"
|
||||
hardware="$(dev_getprop ro.hardware)"
|
||||
device="$(dev_getprop ro.product.device)"
|
||||
|
||||
MTK_PROP_MANUFACTURER="$manufacturer"
|
||||
MTK_PROP_MODEL="$model"
|
||||
MTK_PROP_PLATFORM="$platform"
|
||||
MTK_PROP_HARDWARE="$hardware"
|
||||
MTK_PROP_DEVICE="$device"
|
||||
|
||||
if [[ $platform =~ $MTK_PLATFORM_PATTERN ]] || [[ $hardware =~ $MTK_PLATFORM_PATTERN ]]; then
|
||||
mtk_match=1
|
||||
fi
|
||||
if [[ $manufacturer =~ $ULEFONE_VENDOR_PATTERN ]] || [[ $model =~ $ULEFONE_MODEL_PATTERN ]]; then
|
||||
ulefone_match=1
|
||||
fi
|
||||
if [[ $manufacturer =~ $PIXEL_VENDOR_PATTERN ]] && [[ $model =~ $PIXEL_MODEL_PATTERN ]]; then
|
||||
pixel_match=1
|
||||
fi
|
||||
|
||||
if [[ $mtk_match -eq 1 && $ulefone_match -eq 1 ]]; then
|
||||
MTK_DEVICE_CLASS="ULEFONE"
|
||||
MTK_CLASS_REASON="MediaTek platform [${platform:-unset}/${hardware:-unset}] and Ulefone identity [${manufacturer} ${model}]"
|
||||
elif [[ $pixel_match -eq 1 ]]; then
|
||||
MTK_DEVICE_CLASS="PIXEL"
|
||||
MTK_CLASS_REASON="Google Pixel identity [${manufacturer} ${model}]"
|
||||
else
|
||||
MTK_DEVICE_CLASS="UNKNOWN"
|
||||
MTK_CLASS_REASON="no rule matched: manufacturer=[${manufacturer}] model=[${model}] platform=[${platform}] hardware=[${hardware}]"
|
||||
fi
|
||||
|
||||
export MTK_DEVICE_CLASS MTK_CLASS_REASON
|
||||
export MTK_PROP_MANUFACTURER MTK_PROP_MODEL MTK_PROP_PLATFORM MTK_PROP_HARDWARE MTK_PROP_DEVICE
|
||||
}
|
||||
|
||||
# mtk_explain_classification - why classify_device decided what it did.
|
||||
mtk_explain_classification() {
|
||||
printf 'ro.product.manufacturer = %s\n' "${MTK_PROP_MANUFACTURER:-}"
|
||||
printf 'ro.product.model = %s\n' "${MTK_PROP_MODEL:-}"
|
||||
printf 'ro.product.device = %s\n' "${MTK_PROP_DEVICE:-}"
|
||||
printf 'ro.board.platform = %s\n' "${MTK_PROP_PLATFORM:-}"
|
||||
printf 'ro.hardware = %s\n' "${MTK_PROP_HARDWARE:-}"
|
||||
printf '\n'
|
||||
printf 'MediaTek pattern : %s\n' "$MTK_PLATFORM_PATTERN"
|
||||
printf 'Ulefone vendor : %s\n' "$ULEFONE_VENDOR_PATTERN"
|
||||
printf 'Ulefone model : %s\n' "$ULEFONE_MODEL_PATTERN"
|
||||
printf 'Pixel vendor : %s\n' "$PIXEL_VENDOR_PATTERN"
|
||||
printf 'Pixel model : %s\n' "$PIXEL_MODEL_PATTERN"
|
||||
printf '\n'
|
||||
printf 'Verdict: %s\n' "${MTK_DEVICE_CLASS:-unset}"
|
||||
printf 'Reason : %s\n' "${MTK_CLASS_REASON:-unset}"
|
||||
}
|
||||
|
||||
# mtk_assert_mediatek
|
||||
# A positive assertion, deliberately not "is not a Pixel". Used to gate the
|
||||
# only script that touches partitions.
|
||||
mtk_assert_mediatek() {
|
||||
if [[ ${MTK_PROP_PLATFORM:-} =~ $MTK_PLATFORM_PATTERN ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ ${MTK_PROP_HARDWARE:-} =~ $MTK_PLATFORM_PATTERN ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Partition discovery
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# discover_partitions
|
||||
# Sets MTK_AB_SCHEME, MTK_SLOT_SUFFIX, MTK_RAMDISK_PART, MTK_VBMETA_PART,
|
||||
# MTK_BY_NAME (newline-separated listing).
|
||||
#
|
||||
# The governing rule: never name a partition in a conditional that was not
|
||||
# first observed in the listing. The AOSP rule (devices launching on Android
|
||||
# 13+ carry the ramdisk in init_boot; earlier ones keep it in boot) explains
|
||||
# WHY the preference order below is correct, but the code never consults the
|
||||
# API level to decide - it reads the filesystem. That is what lets one code
|
||||
# path serve both a Pixel (no init_boot -> falls through to boot) and a
|
||||
# 13-launch MediaTek device (init_boot present -> taken) without branching on
|
||||
# device identity.
|
||||
discover_partitions() {
|
||||
local entries="" suffix="" candidate="" name=""
|
||||
|
||||
entries="$(dev_list_by_name)"
|
||||
MTK_BY_NAME="$entries"
|
||||
export MTK_BY_NAME
|
||||
|
||||
if [[ -z $entries ]]; then
|
||||
MTK_AB_SCHEME="unknown"
|
||||
MTK_SLOT_SUFFIX=""
|
||||
MTK_RAMDISK_PART=""
|
||||
MTK_VBMETA_PART=""
|
||||
export MTK_AB_SCHEME MTK_SLOT_SUFFIX MTK_RAMDISK_PART MTK_VBMETA_PART
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Slot scheme comes from observed suffixes, not from a product table.
|
||||
if grep -qE '_a$' <<<"$entries"; then
|
||||
MTK_AB_SCHEME="A/B"
|
||||
else
|
||||
MTK_AB_SCHEME="A-only"
|
||||
fi
|
||||
|
||||
# Prefer the device's own view of the active slot; fall back to _a only
|
||||
# because a dump has to start somewhere, and say so in the report.
|
||||
suffix="$(dev_getprop ro.boot.slot_suffix)"
|
||||
if [[ -z $suffix && $MTK_AB_SCHEME == "A/B" ]]; then
|
||||
suffix="_a"
|
||||
MTK_SLOT_ASSUMED=1
|
||||
else
|
||||
MTK_SLOT_ASSUMED=0
|
||||
fi
|
||||
MTK_SLOT_SUFFIX="$suffix"
|
||||
|
||||
# init_boot first: its presence is decisive. A device that has it keeps the
|
||||
# ramdisk there even though boot also exists, so checking boot first would
|
||||
# pick the wrong partition on exactly the hardware this toolkit is for.
|
||||
MTK_RAMDISK_PART=""
|
||||
for candidate in init_boot boot; do
|
||||
for name in "${candidate}${suffix}" "${candidate}"; do
|
||||
if grep -qxF -- "$name" <<<"$entries"; then
|
||||
MTK_RAMDISK_PART="$name"
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
MTK_VBMETA_PART=""
|
||||
for name in "vbmeta${suffix}" "vbmeta"; do
|
||||
if grep -qxF -- "$name" <<<"$entries"; then
|
||||
MTK_VBMETA_PART="$name"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
export MTK_AB_SCHEME MTK_SLOT_SUFFIX MTK_RAMDISK_PART MTK_VBMETA_PART MTK_SLOT_ASSUMED
|
||||
|
||||
[[ -n $MTK_RAMDISK_PART ]]
|
||||
}
|
||||
|
||||
# mtk_ramdisk_carrier - "init_boot" or "boot", suffix stripped.
|
||||
mtk_ramdisk_carrier() {
|
||||
local part="${MTK_RAMDISK_PART:-}"
|
||||
printf '%s' "${part%_[ab]}"
|
||||
}
|
||||
|
||||
# mtk_check_layout_expectation
|
||||
# Cross-check only. first_api_level >= 33 predicts init_boot; below predicts
|
||||
# boot. A mismatch means either a vendor deviation or a bad read - the human
|
||||
# should look, but the filesystem still wins.
|
||||
mtk_check_layout_expectation() {
|
||||
local api_level="" carrier="" expected=""
|
||||
|
||||
api_level="$(dev_getprop ro.product.first_api_level)"
|
||||
carrier="$(mtk_ramdisk_carrier)"
|
||||
|
||||
[[ -z $api_level || -z $carrier ]] && return 0
|
||||
[[ $api_level =~ ^[0-9]+$ ]] || return 0
|
||||
|
||||
if [[ $api_level -ge 33 ]]; then
|
||||
expected="init_boot"
|
||||
else
|
||||
expected="boot"
|
||||
fi
|
||||
|
||||
if [[ $carrier != "$expected" ]]; then
|
||||
mtk_warn "Layout disagrees with expectation: first_api_level=${api_level} predicts '${expected}', device shows '${carrier}'."
|
||||
mtk_warn "Trusting the device. Verify before flashing anything."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Host environment checks (read-only; install.sh owns the mutating half)
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# mtk_udev_catchall_files
|
||||
# Prints any rules file containing an ACTIVE wildcard-vendor rule granting
|
||||
# world read/write. Such a rule applies to every USB device on the system -
|
||||
# storage, security keys, everything - not just phones.
|
||||
#
|
||||
# Commented-out lines are ignored: --fix-udev disables a rule by commenting it
|
||||
# rather than deleting it, so matching those would leave the check permanently
|
||||
# red after it had already been dealt with. Only *.rules files are considered,
|
||||
# since udev itself skips any other suffix (including our .bak- backups).
|
||||
mtk_udev_catchall_files() {
|
||||
local file=""
|
||||
for file in /etc/udev/rules.d/*.rules; do
|
||||
[[ -f $file ]] || continue
|
||||
if grep -qE '^[^#]*idVendor\}=="\*"[^#]*MODE="0666"|^[^#]*MODE="0666"[^#]*idVendor\}=="\*"' \
|
||||
"$file" 2>/dev/null; then
|
||||
printf '%s\n' "$file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# mtk_check_venv - is the mtkclient venv actually usable?
|
||||
# Checks importability, not directory existence: the venv found on this host
|
||||
# in Aug 2026 existed, looked complete, and failed at `import usb` because a
|
||||
# Python upgrade had orphaned it.
|
||||
mtk_check_venv() {
|
||||
local python_bin=""
|
||||
python_bin="$(mtk_venv_python)"
|
||||
|
||||
if [[ ! -x $python_bin ]]; then
|
||||
printf 'missing: no interpreter at %s' "$python_bin"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! "$python_bin" -c 'import usb, usb.core, serial' 2>/dev/null; then
|
||||
local err=""
|
||||
err="$("$python_bin" -c 'import usb, usb.core, serial' 2>&1 | tail -1)"
|
||||
printf 'broken: %s' "$err"
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf 'ok: %s' "$("$python_bin" --version 2>&1)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# mtk_sha256 <file>
|
||||
mtk_sha256() {
|
||||
sha256sum "$1" | awk '{ print $1 }'
|
||||
}
|
||||
|
||||
# mtk_file_size <file>
|
||||
mtk_file_size() {
|
||||
stat -c '%s' "$1"
|
||||
}
|
||||
|
||||
# mtk_sane_ramdisk_size <bytes>
|
||||
# A dump outside this band is not necessarily wrong, but it is worth stopping
|
||||
# for: an empty or truncated read and an accidental whole-disk grab both look
|
||||
# like success to mtkclient's exit code.
|
||||
mtk_sane_ramdisk_size() {
|
||||
local size="$1"
|
||||
|
||||
if [[ ! $size =~ ^[0-9]+$ ]]; then
|
||||
printf 'size is not a number: %s' "$size"
|
||||
return 1
|
||||
fi
|
||||
if [[ $size -lt $MTK_RAMDISK_MIN_BYTES ]]; then
|
||||
printf 'implausibly small: %s bytes (< %s)' "$size" "$MTK_RAMDISK_MIN_BYTES"
|
||||
return 1
|
||||
fi
|
||||
if [[ $size -gt $MTK_RAMDISK_MAX_BYTES ]]; then
|
||||
printf 'implausibly large: %s bytes (> %s)' "$size" "$MTK_RAMDISK_MAX_BYTES"
|
||||
return 1
|
||||
fi
|
||||
printf '%s bytes' "$size"
|
||||
return 0
|
||||
}
|
||||
_MTK_LIB_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
|
||||
# shellcheck source=mtk_device.sh
|
||||
source "$_MTK_LIB_DIR/mtk_device.sh"
|
||||
# shellcheck source=mtk_classify.sh
|
||||
source "$_MTK_LIB_DIR/mtk_classify.sh"
|
||||
# shellcheck source=mtk_partitions.sh
|
||||
source "$_MTK_LIB_DIR/mtk_partitions.sh"
|
||||
|
||||
111
linux_configuration/scripts/lib/mtk_device.sh
Executable file
111
linux_configuration/scripts/lib/mtk_device.sh
Executable file
@ -0,0 +1,111 @@
|
||||
#!/bin/bash
|
||||
# adb wrappers, device listing, selection and authorisation.
|
||||
#
|
||||
# Sourced by mtk_common.sh, which stays the single entry point the
|
||||
# mtk_root scripts source, so their public surface is unchanged.
|
||||
|
||||
# mtk_adb <args...> - serial-pinned adb, so a second phone plugged in midway
|
||||
# cannot silently become the target.
|
||||
mtk_adb() {
|
||||
adb -s "${MTK_SERIAL:?mtk_select_device must run first}" "$@"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Device presence and authorization
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# mtk_list_devices - prints "<serial> <state>" per attached device.
|
||||
mtk_list_devices() {
|
||||
adb devices 2>/dev/null |
|
||||
awk 'NR > 1 && $2 ~ /^(device|offline|unauthorized|recovery|sideload)$/ { print $1, $2 }'
|
||||
}
|
||||
|
||||
# mtk_device_state <serial> - device|unauthorized|offline|absent
|
||||
mtk_device_state() {
|
||||
local serial="$1"
|
||||
local state=""
|
||||
|
||||
state="$(mtk_list_devices | awk -v s="$serial" '$1 == s { print $2; exit }')"
|
||||
printf '%s' "${state:-absent}"
|
||||
}
|
||||
|
||||
# mtk_select_device [requested-serial]
|
||||
# Sets MTK_SERIAL. Refuses when the choice is ambiguous rather than picking
|
||||
# one - a wrong guess here targets the wrong phone.
|
||||
mtk_select_device() {
|
||||
local requested="${1:-${MTK_SERIAL:-}}"
|
||||
local -a device_rows=()
|
||||
local -a serials=()
|
||||
local row=""
|
||||
|
||||
if [[ -n ${MTK_ROOT_FIXTURE:-} ]]; then
|
||||
MTK_SERIAL="${MTK_SERIAL:-fixture}"
|
||||
export MTK_SERIAL
|
||||
return 0
|
||||
fi
|
||||
|
||||
mapfile -t device_rows < <(mtk_list_devices)
|
||||
|
||||
if [[ ${#device_rows[@]} -eq 0 ]]; then
|
||||
mtk_error "No device visible to adb."
|
||||
mtk_error "Check: cable seated, USB debugging enabled, and try 'adb devices'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
for row in "${device_rows[@]}"; do
|
||||
serials+=("${row%% *}")
|
||||
done
|
||||
|
||||
if [[ -n $requested ]]; then
|
||||
for row in "${serials[@]}"; do
|
||||
if [[ $row == "$requested" ]]; then
|
||||
MTK_SERIAL="$requested"
|
||||
export MTK_SERIAL
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
mtk_error "Requested serial '${requested}' is not attached. Attached: ${serials[*]}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ${#serials[@]} -gt 1 ]]; then
|
||||
mtk_error "Multiple devices attached (${serials[*]}); refusing to guess."
|
||||
mtk_error "Re-run with --serial <serial>."
|
||||
return 1
|
||||
fi
|
||||
|
||||
MTK_SERIAL="${serials[0]}"
|
||||
export MTK_SERIAL
|
||||
return 0
|
||||
}
|
||||
|
||||
# mtk_require_authorized
|
||||
# An unauthorized device answers every getprop with an empty string, which
|
||||
# reads exactly like "this device has no properties". Catch it up front so the
|
||||
# report never blames the device for a prompt sitting unanswered on its screen.
|
||||
mtk_require_authorized() {
|
||||
local state=""
|
||||
|
||||
[[ -n ${MTK_ROOT_FIXTURE:-} ]] && return 0
|
||||
|
||||
state="$(mtk_device_state "${MTK_SERIAL}")"
|
||||
case "$state" in
|
||||
device)
|
||||
return 0
|
||||
;;
|
||||
unauthorized)
|
||||
mtk_error "Device ${MTK_SERIAL} is UNAUTHORIZED."
|
||||
mtk_error "The RSA authorization prompt is on the phone's screen - unlock it and tap Allow."
|
||||
mtk_error "If no prompt appears: revoke USB debugging authorizations on the phone, then replug."
|
||||
return 1
|
||||
;;
|
||||
offline)
|
||||
mtk_error "Device ${MTK_SERIAL} is OFFLINE. Replug the cable, or run 'adb kill-server'."
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
mtk_error "Device ${MTK_SERIAL} is in state '${state}', which this toolkit does not read."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
195
linux_configuration/scripts/lib/mtk_partitions.sh
Executable file
195
linux_configuration/scripts/lib/mtk_partitions.sh
Executable file
@ -0,0 +1,195 @@
|
||||
#!/bin/bash
|
||||
# Partition discovery, ramdisk carrier and layout expectations.
|
||||
#
|
||||
# Sourced by mtk_common.sh, which stays the single entry point the
|
||||
# mtk_root scripts source, so their public surface is unchanged.
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Partition discovery
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# discover_partitions
|
||||
# Sets MTK_AB_SCHEME, MTK_SLOT_SUFFIX, MTK_RAMDISK_PART, MTK_VBMETA_PART,
|
||||
# MTK_BY_NAME (newline-separated listing).
|
||||
#
|
||||
# The governing rule: never name a partition in a conditional that was not
|
||||
# first observed in the listing. The AOSP rule (devices launching on Android
|
||||
# 13+ carry the ramdisk in init_boot; earlier ones keep it in boot) explains
|
||||
# WHY the preference order below is correct, but the code never consults the
|
||||
# API level to decide - it reads the filesystem. That is what lets one code
|
||||
# path serve both a Pixel (no init_boot -> falls through to boot) and a
|
||||
# 13-launch MediaTek device (init_boot present -> taken) without branching on
|
||||
# device identity.
|
||||
discover_partitions() {
|
||||
local entries="" suffix="" candidate="" name=""
|
||||
|
||||
entries="$(dev_list_by_name)"
|
||||
MTK_BY_NAME="$entries"
|
||||
export MTK_BY_NAME
|
||||
|
||||
if [[ -z $entries ]]; then
|
||||
MTK_AB_SCHEME="unknown"
|
||||
MTK_SLOT_SUFFIX=""
|
||||
MTK_RAMDISK_PART=""
|
||||
MTK_VBMETA_PART=""
|
||||
export MTK_AB_SCHEME MTK_SLOT_SUFFIX MTK_RAMDISK_PART MTK_VBMETA_PART
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Slot scheme comes from observed suffixes, not from a product table.
|
||||
if grep -qE '_a$' <<<"$entries"; then
|
||||
MTK_AB_SCHEME="A/B"
|
||||
else
|
||||
MTK_AB_SCHEME="A-only"
|
||||
fi
|
||||
|
||||
# Prefer the device's own view of the active slot; fall back to _a only
|
||||
# because a dump has to start somewhere, and say so in the report.
|
||||
suffix="$(dev_getprop ro.boot.slot_suffix)"
|
||||
if [[ -z $suffix && $MTK_AB_SCHEME == "A/B" ]]; then
|
||||
suffix="_a"
|
||||
MTK_SLOT_ASSUMED=1
|
||||
else
|
||||
MTK_SLOT_ASSUMED=0
|
||||
fi
|
||||
MTK_SLOT_SUFFIX="$suffix"
|
||||
|
||||
# init_boot first: its presence is decisive. A device that has it keeps the
|
||||
# ramdisk there even though boot also exists, so checking boot first would
|
||||
# pick the wrong partition on exactly the hardware this toolkit is for.
|
||||
MTK_RAMDISK_PART=""
|
||||
for candidate in init_boot boot; do
|
||||
for name in "${candidate}${suffix}" "${candidate}"; do
|
||||
if grep -qxF -- "$name" <<<"$entries"; then
|
||||
MTK_RAMDISK_PART="$name"
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
MTK_VBMETA_PART=""
|
||||
for name in "vbmeta${suffix}" "vbmeta"; do
|
||||
if grep -qxF -- "$name" <<<"$entries"; then
|
||||
MTK_VBMETA_PART="$name"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
export MTK_AB_SCHEME MTK_SLOT_SUFFIX MTK_RAMDISK_PART MTK_VBMETA_PART MTK_SLOT_ASSUMED
|
||||
|
||||
[[ -n $MTK_RAMDISK_PART ]]
|
||||
}
|
||||
|
||||
# mtk_ramdisk_carrier - "init_boot" or "boot", suffix stripped.
|
||||
mtk_ramdisk_carrier() {
|
||||
local part="${MTK_RAMDISK_PART:-}"
|
||||
printf '%s' "${part%_[ab]}"
|
||||
}
|
||||
|
||||
# mtk_check_layout_expectation
|
||||
# Cross-check only. first_api_level >= 33 predicts init_boot; below predicts
|
||||
# boot. A mismatch means either a vendor deviation or a bad read - the human
|
||||
# should look, but the filesystem still wins.
|
||||
mtk_check_layout_expectation() {
|
||||
local api_level="" carrier="" expected=""
|
||||
|
||||
api_level="$(dev_getprop ro.product.first_api_level)"
|
||||
carrier="$(mtk_ramdisk_carrier)"
|
||||
|
||||
[[ -z $api_level || -z $carrier ]] && return 0
|
||||
[[ $api_level =~ ^[0-9]+$ ]] || return 0
|
||||
|
||||
if [[ $api_level -ge 33 ]]; then
|
||||
expected="init_boot"
|
||||
else
|
||||
expected="boot"
|
||||
fi
|
||||
|
||||
if [[ $carrier != "$expected" ]]; then
|
||||
mtk_warn "Layout disagrees with expectation: first_api_level=${api_level} predicts '${expected}', device shows '${carrier}'."
|
||||
mtk_warn "Trusting the device. Verify before flashing anything."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Host environment checks (read-only; install.sh owns the mutating half)
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# mtk_udev_catchall_files
|
||||
# Prints any rules file containing an ACTIVE wildcard-vendor rule granting
|
||||
# world read/write. Such a rule applies to every USB device on the system -
|
||||
# storage, security keys, everything - not just phones.
|
||||
#
|
||||
# Commented-out lines are ignored: --fix-udev disables a rule by commenting it
|
||||
# rather than deleting it, so matching those would leave the check permanently
|
||||
# red after it had already been dealt with. Only *.rules files are considered,
|
||||
# since udev itself skips any other suffix (including our .bak- backups).
|
||||
mtk_udev_catchall_files() {
|
||||
local file=""
|
||||
for file in /etc/udev/rules.d/*.rules; do
|
||||
[[ -f $file ]] || continue
|
||||
if grep -qE '^[^#]*idVendor\}=="\*"[^#]*MODE="0666"|^[^#]*MODE="0666"[^#]*idVendor\}=="\*"' \
|
||||
"$file" 2>/dev/null; then
|
||||
printf '%s\n' "$file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# mtk_check_venv - is the mtkclient venv actually usable?
|
||||
# Checks importability, not directory existence: the venv found on this host
|
||||
# in Aug 2026 existed, looked complete, and failed at `import usb` because a
|
||||
# Python upgrade had orphaned it.
|
||||
mtk_check_venv() {
|
||||
local python_bin=""
|
||||
python_bin="$(mtk_venv_python)"
|
||||
|
||||
if [[ ! -x $python_bin ]]; then
|
||||
printf 'missing: no interpreter at %s' "$python_bin"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! "$python_bin" -c 'import usb, usb.core, serial' 2>/dev/null; then
|
||||
local err=""
|
||||
err="$("$python_bin" -c 'import usb, usb.core, serial' 2>&1 | tail -1)"
|
||||
printf 'broken: %s' "$err"
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf 'ok: %s' "$("$python_bin" --version 2>&1)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# mtk_sha256 <file>
|
||||
mtk_sha256() {
|
||||
sha256sum "$1" | awk '{ print $1 }'
|
||||
}
|
||||
|
||||
# mtk_file_size <file>
|
||||
mtk_file_size() {
|
||||
stat -c '%s' "$1"
|
||||
}
|
||||
|
||||
# mtk_sane_ramdisk_size <bytes>
|
||||
# A dump outside this band is not necessarily wrong, but it is worth stopping
|
||||
# for: an empty or truncated read and an accidental whole-disk grab both look
|
||||
# like success to mtkclient's exit code.
|
||||
mtk_sane_ramdisk_size() {
|
||||
local size="$1"
|
||||
|
||||
if [[ ! $size =~ ^[0-9]+$ ]]; then
|
||||
printf 'size is not a number: %s' "$size"
|
||||
return 1
|
||||
fi
|
||||
if [[ $size -lt $MTK_RAMDISK_MIN_BYTES ]]; then
|
||||
printf 'implausibly small: %s bytes (< %s)' "$size" "$MTK_RAMDISK_MIN_BYTES"
|
||||
return 1
|
||||
fi
|
||||
if [[ $size -gt $MTK_RAMDISK_MAX_BYTES ]]; then
|
||||
printf 'implausibly large: %s bytes (> %s)' "$size" "$MTK_RAMDISK_MAX_BYTES"
|
||||
return 1
|
||||
fi
|
||||
printf '%s bytes' "$size"
|
||||
return 0
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user