blob: 6ba68be43b0fec34c46b841b4e8e1636769e74b3 [file] [log] [blame] [edit]
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0
#
# Device Interaction Library
# Wraps ADB and Fastboot operations with context management.
#
# Usage:
# source path/to/device_util.sh
# device_util::init "$serial_number"
# device_util::unlock_screen
#
# --- Include Guard ---
if [[ -n "${__DEVICE_UTIL_SOURCED__:-}" ]]; then
return 0
fi
readonly __DEVICE_UTIL_SOURCED__=1
# --- Dependencies ---
if [[ -z "${__COMMON_LIB_SOURCED__:-}" ]]; then
_DEVICE_UTIL_SCRIPT_PATH="$(realpath "${BASH_SOURCE[0]}")"
_DEVICE_UTIL_SCRIPT_DIR="$(dirname "${_DEVICE_UTIL_SCRIPT_PATH}")"
_COMMON_LIB_PATH="${_DEVICE_UTIL_SCRIPT_DIR}/../common_lib.sh"
if [[ ! -f "$_COMMON_LIB_PATH" ]]; then
echo "FATAL ERROR (device_util): Cannot find common_lib.sh" >&2
return 1
fi
if ! source "$_COMMON_LIB_PATH"; then
echo "FATAL ERROR (device_util): Failed to source library '$_COMMON_LIB_PATH'" >&2
return 1
fi
fi
# --- Internal State ---
_DEVICE_UTIL_INPUT_SERIAL=""
_DEVICE_UTIL_ADB_SERIAL=""
_DEVICE_UTIL_FASTBOOT_SERIAL=""
_DEVICE_UTIL_MODE="" # "ADB" or "FASTBOOT"
_DEVICE_UTIL_TYPE="" # "PHYSICAL" or "VIRTUAL"
# --- Helper Functions (Internal) ---
function _device_util::find_fastboot_serial() {
local target_serial="$1"
local device_ids
device_ids=$(fastboot devices | awk '{print $1}')
while IFS= read -r device_id; do
# Skip empty lines
[[ -z "$device_id" ]] && continue
local _output
_output=$(fastboot -s "$device_id" getvar serialno 2>&1)
local detected_serial
detected_serial=$(echo "$_output" | grep -Po "serialno: \K[A-Z0-9]+")
if [[ "$detected_serial" == "$target_serial" ]]; then
_DEVICE_UTIL_FASTBOOT_SERIAL="$device_id"
log_info "Device $target_serial matches fastboot id $_DEVICE_UTIL_FASTBOOT_SERIAL"
return 0
fi
done <<< "$device_ids"
log_error "Cannot find device in fastboot with serial: $target_serial"
return 1
}
function _device_util::find_adb_serial() {
local target_serial="$1"
log_info "Searching for device $target_serial in adb devices..."
local _device_ids
_device_ids=$(adb devices | awk '$2 == "device" {print $1}')
while IFS= read -r device_id; do
# Skip empty lines
[[ -z "$device_id" ]] && continue
local detected_serial
detected_serial=$(adb -s "$device_id" shell getprop ro.serialno 2>&1)
# Trim whitespace
detected_serial="${detected_serial%"${detected_serial##*[![:space:]]}"}"
if [[ "$detected_serial" == "$target_serial" ]]; then
_DEVICE_UTIL_ADB_SERIAL="$device_id"
log_info "Device $target_serial matches adb id $_DEVICE_UTIL_ADB_SERIAL"
return 0
fi
done <<< "$_device_ids"
log_error "Cannot find device in adb with serial: $target_serial. Check USB/Auth."
return 1
}
# --- Public Functions ---
function device_util::init() {
local serial="$1"
if [[ -z "$serial" ]]; then
log_error "Serial number is required."
return 1
fi
_DEVICE_UTIL_INPUT_SERIAL="$serial"
_DEVICE_UTIL_ADB_SERIAL=""
_DEVICE_UTIL_FASTBOOT_SERIAL=""
_DEVICE_UTIL_MODE=""
_DEVICE_UTIL_TYPE=""
local found_device=false
# Check ADB
if adb devices | grep -q "$serial"; then
_DEVICE_UTIL_MODE="ADB"
_DEVICE_UTIL_ADB_SERIAL="$serial"
found_device=true
fi
# Check Fastboot
if ! $found_device && fastboot devices | grep -q "$serial"; then
_DEVICE_UTIL_MODE="FASTBOOT"
_DEVICE_UTIL_FASTBOOT_SERIAL="$serial"
found_device=true
fi
# Check Pontis (Google internal tool)
if ! $found_device && command -v pontis &> /dev/null; then
local pontis_info
pontis_info=$(pontis devices 2>/dev/null | grep "$serial")
if [[ "$pontis_info" == *Fastboot* ]]; then
_DEVICE_UTIL_MODE="FASTBOOT"
log_info "Device $serial found via Pontis (Fastboot)"
if ! _device_util::find_fastboot_serial "$serial"; then
return 1
fi
found_device=true
elif [[ "$pontis_info" == *ADB* ]]; then
_DEVICE_UTIL_MODE="ADB"
log_info "Device $serial found via Pontis (ADB)"
if ! _device_util::find_adb_serial "$serial"; then
return 1
fi
found_device=true
fi
fi
if ! $found_device; then
log_error "Device '$serial' not found in ADB, Fastboot, or Pontis."
return 1
fi
# Determine Type (Physical vs Virtual)
if [[ "$_DEVICE_UTIL_MODE" == "ADB" ]]; then
local product
product=$(adb -s "$_DEVICE_UTIL_ADB_SERIAL" shell getprop ro.product.board)
if [[ "$product" == "cutf" || "$product" == "vsoc_x86"* ]]; then
_DEVICE_UTIL_TYPE="VIRTUAL"
else
_DEVICE_UTIL_TYPE="PHYSICAL"
fi
else
# Default to Physical for Fastboot unless we have better heuristics
_DEVICE_UTIL_TYPE="PHYSICAL"
fi
log_info "Context set. Serial: $_DEVICE_UTIL_INPUT_SERIAL, Mode: $_DEVICE_UTIL_MODE, Type: $_DEVICE_UTIL_TYPE"
return 0
}
function device_util::run_adb() {
if [[ -z "$_DEVICE_UTIL_ADB_SERIAL" ]]; then
log_error "No ADB serial available. Is the device in ADB mode?"
return 1
fi
adb -s "$_DEVICE_UTIL_ADB_SERIAL" "$@"
}
function device_util::run_fastboot() {
if [[ -z "$_DEVICE_UTIL_FASTBOOT_SERIAL" ]]; then
log_error "No Fastboot serial available. Is the device in Fastboot mode?"
return 1
fi
fastboot -s "$_DEVICE_UTIL_FASTBOOT_SERIAL" "$@"
}
function device_util::get_adb_serial() {
echo "$_DEVICE_UTIL_ADB_SERIAL"
}
function device_util::get_fastboot_serial() {
echo "$_DEVICE_UTIL_FASTBOOT_SERIAL"
}
function device_util::skip_setup_wizard() {
if [[ "$_DEVICE_UTIL_MODE" != "ADB" ]]; then
log_warn "Device not in ADB mode. Skipping."
return 1
fi
local serial="$_DEVICE_UTIL_ADB_SERIAL"
log_info "Checking package manager status..."
# Wait for PM to be ready
local retries=30
while ! device_util::run_adb shell pm path com.android.settings > /dev/null 2>&1; do
sleep 2
((retries--))
if ((retries <= 0)); then
log_warn "Timeout waiting for package manager."
return 1
fi
done
log_info "Disabling Setup Wizard..."
device_util::run_adb shell settings put global device_provisioned 1
device_util::run_adb shell settings put secure user_setup_complete 1
# Attempt to disable the wizard app directly
local wizard_pkg
wizard_pkg=$(device_util::run_adb shell pm list packages | grep -i 'setupwizard' | head -n 1 | cut -d':' -f2)
if [[ -n "$wizard_pkg" ]]; then
log_info "Disabling package $wizard_pkg"
device_util::run_adb shell pm disable-user --user 0 "$wizard_pkg"
fi
}
function device_util::unlock_screen() {
if [[ "$_DEVICE_UTIL_MODE" != "ADB" ]]; then
log_warn "Device not in ADB mode. Skipping."
return 1
fi
log_info "Waiting for boot complete..."
device_util::wait_for_boot_complete || return 1
log_info "Checking screen state..."
local dumpsys_out
dumpsys_out=$(device_util::run_adb shell dumpsys deviceidle)
local is_screen_on
is_screen_on=$(echo "$dumpsys_out" | grep "mScreenOn" | cut -d'=' -f2)
if [[ "$is_screen_on" == "false" ]]; then
log_info "Turning screen ON..."
device_util::run_adb shell input keyevent 26 # POWER
sleep 1
dumpsys_out=$(device_util::run_adb shell dumpsys deviceidle)
is_screen_on=$(echo "$dumpsys_out" | grep "mScreenOn" | cut -d'=' -f2)
fi
if [[ "$is_screen_on" == "true" ]]; then
local is_locked
is_locked=$(echo "$dumpsys_out" | grep "mScreenLocked" | cut -d'=' -f2)
if [[ "$is_locked" == "true" ]]; then
log_info "Sending MENU key to unlock..."
device_util::run_adb shell input keyevent 82 # MENU
else
log_info "Screen is already unlocked."
fi
else
log_error "Failed to turn on screen."
return 1
fi
}
function device_util::wait_for_boot_complete() {
local timeout_sec=120
local start_time=$(date +%s)
device_util::run_adb wait-for-device
log_info "Waiting for sys.boot_completed..."
while true; do
local boot_complete
boot_complete=$(device_util::run_adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')
if [[ "$boot_complete" == "1" ]]; then
return 0
fi
local current_time=$(date +%s)
if (( current_time - start_time > timeout_sec )); then
log_error "Timeout waiting for boot complete."
return 1
fi
sleep 3
done
}
function device_util::ensure_root() {
if [[ "$_DEVICE_UTIL_MODE" != "ADB" ]]; then return 1; fi
local id_out
id_out=$(device_util::run_adb shell id)
if [[ "$id_out" != *"uid=0(root)"* ]]; then
log_info "Restarting ADB as root..."
device_util::run_adb root
device_util::run_adb wait-for-device
else
log_info "Already root."
fi
}