Loading app/src/controller/controller.manager.js +56 −37 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ import { COMMAND } from "./enums/command.enum.js"; import { Step } from "./utils/step.class.js"; import { DebugManager } from "./debug.manager.js"; const LOGGER = DebugManager.getLogger("ControllerManager"); /* * Class to manage process * Check and display the steps, interact with deviceManager Loading Loading @@ -50,24 +52,26 @@ export class ControllerManager { * @returns {Promise<void>} Resolves when the next step is started. */ async next() { const fn = "next"; let current = this.steps[this.currentIndex]; let next = this.steps[this.currentIndex + 1]; DebugManager.log("Controller Manager Next", next); LOGGER.info(fn, "Advancing to", next.toString()); if (next) { if (next.mode) { const alreadyInMode = this.inInMode(next.mode); DebugManager.log( `next() step="${next.name}" requires mode="${next.mode}", ` + LOGGER.info( fn, `${next.name}" requires mode="${next.mode}", ` + `alreadyInMode=${alreadyInMode}, needUserGesture=${next.needUserGesture}`, ); //if next step require another mode [adb|fastboot|bootloader] if (!alreadyInMode) { //we need reboot DebugManager.log(`next() rebooting to ${next.mode}...`); LOGGER.info(fn, `Rebooting to ${next.mode}...`); await this.deviceManager.reboot(next.mode); DebugManager.log(`next() reboot to ${next.mode} completed`); LOGGER.info(fn, `Reboot to ${next.mode} completed`); } if (next.needUserGesture) { // Wait for the device to appear on the USB bus before showing the Loading @@ -75,23 +79,23 @@ export class ControllerManager { // devices after a mode switch. The actual connect happens via // executeStep when the user clicks (WebUSB requestDevice() requires // a user gesture). DebugManager.log( `next() waiting for device on USB bus (needUserGesture=true, deferring connect)...`, LOGGER.info( fn, `Waiting for device on USB bus (needUserGesture=true, deferring connect)...`, ); await this.deviceManager.waitForDeviceOnBus(); DebugManager.log(`next() device wait complete, showing step to user`); LOGGER.info(fn, "Device wait complete, showing step to user"); } else { DebugManager.log( `next() connecting to ${next.mode} automatically...`, ); LOGGER.info(fn, `Connecting to ${next.mode} automatically...`); await this.deviceManager.connect(next.mode); DebugManager.log(`next() auto-connect to ${next.mode} completed`); LOGGER.info(fn, `Auto-connect to ${next.mode} completed`); } } this.currentIndex++; current = this.steps[this.currentIndex]; DebugManager.log( `next() advancing to step="${current.name}", needUserGesture=${current.needUserGesture}`, LOGGER.info( fn, `Advancing to step="${current.name}", needUserGesture=${current.needUserGesture}`, ); this.view.onStepStarted(this.currentIndex, current); if (!current.needUserGesture) { Loading @@ -108,9 +112,10 @@ export class ControllerManager { * @returns {Promise<void>} Resolves when the step has been processed. */ async executeStep(stepName, loader) { const fn = "executeStep"; const current = this.steps[this.currentIndex]; let this_command; DebugManager.log("ControllerManager Execute step", current); LOGGER.info(fn, "Executing", current); document.getElementById("error-message-state").style.display = "none"; if (current.name === stepName) { let res = true; Loading @@ -119,7 +124,7 @@ export class ControllerManager { for (i = 0; i < current.commands.length && res; i++) { this_command = current.commands[i]; res = await this.runCommand(this_command, loader); DebugManager.log("run command > ", this_command, "returns ", res); LOGGER.info(fn, this_command, "returns ", res); } const next = this.steps[this.currentIndex + 1]; let previous = this.steps[this.currentIndex - 1]; Loading Loading @@ -168,7 +173,8 @@ export class ControllerManager { * @returns {Promise<boolean>} True when the command succeeds. */ async runCommand(cmd, loader) { DebugManager.log("ControllerManager run command:", cmd); const fn = "runCommand"; LOGGER.info(fn, "Running command", cmd); switch (cmd.type) { case COMMAND.download: return this.runDownloadCommand(); Loading Loading @@ -274,6 +280,7 @@ export class ControllerManager { * @returns {Promise<boolean>} True when flashing succeeds. */ async runFlashCommand(cmd) { const fn = "runFlashCommand"; const FLASH_COOLDOWN_MS = this.resources?.flash_cooldown_ms ?? 2500; const result = await this.deviceManager.flash( cmd.file, Loading @@ -282,7 +289,8 @@ export class ControllerManager { this.view.onInstalling(cmd.file, done, total); }, ); DebugManager.log( LOGGER.info( fn, `Flash cooldown: waiting ${FLASH_COOLDOWN_MS}ms before next operation`, ); await new Promise((resolve) => setTimeout(resolve, FLASH_COOLDOWN_MS)); Loading @@ -296,19 +304,21 @@ export class ControllerManager { * @returns {Promise<boolean>} True when unlock flow completes. */ async runUnlockCommand(cmd) { const fn = "runUnlockCommand"; let isUnlocked = false; let gotoStep = ""; if (cmd.partition) { if (cmd.partition.startsWith("goto_")) { gotoStep = cmd.partition.substring(5); DebugManager.log("goto step", gotoStep); LOGGER.info(fn, "goto step", gotoStep); isUnlocked = await this.deviceManager.getUnlocked("unlocked"); } else { isUnlocked = await this.deviceManager.getUnlocked(cmd.partition); } } DebugManager.log( "ControllerManager unlock: ", LOGGER.info( fn, "unlock:", this.deviceManager.adb.getProductName() + " isUnlocked = " + isUnlocked, ); if (!isUnlocked) { Loading @@ -316,22 +326,23 @@ export class ControllerManager { await this.deviceManager.unlock(cmd.command); } catch (e) { if (e.bootloaderMessage?.includes("already")) { DebugManager.log("device already unlocked"); LOGGER.info(fn, "device already unlocked"); } else if (e.bootloaderMessage?.includes("not allowed")) { DebugManager.log("device unlock is not allowed"); LOGGER.info(fn, "device unlock is not allowed"); throw new Error(`Unlock not allowed: ${e.message || e}`); } else { throw e; } } } else { DebugManager.log("The phone is not locked - bypass lock process"); LOGGER.warn(fn, "The phone is not locked - bypass lock process"); if (gotoStep == "") { this.currentIndex++; } else { do { this.currentIndex++; DebugManager.log( LOGGER.info( fn, "Bypass step", this.steps[this.currentIndex].name + " " + Loading @@ -351,6 +362,7 @@ export class ControllerManager { * @returns {Promise<boolean>} True when lock flow completes. */ async runLockCommand(cmd) { const fn = "runLockCommand"; let isLocked = false; if (cmd.partition) { isLocked = !(await this.deviceManager.getUnlocked(cmd.partition)); Loading @@ -361,7 +373,7 @@ export class ControllerManager { isLocked = true; } catch (e) { if (e.bootloaderMessage?.includes("already")) { DebugManager.log("device already locked"); LOGGER.info(fn, "device already locked"); isLocked = true; } else { throw new Error(`Lock failed: ${e.message || e}`); Loading Loading @@ -419,7 +431,8 @@ export class ControllerManager { * @returns {Promise<boolean>} True when forwarded command finishes. */ async runUnknownCommand(cmd) { DebugManager.log(`try unknown command ${cmd.command}`); const fn = "runUnknownCommand"; LOGGER.info(fn, `try unknown command ${cmd.command}`); await this.deviceManager.runCommand(cmd.command); return true; } Loading @@ -430,12 +443,13 @@ export class ControllerManager { * @returns {Promise<void>} Resolves when device data is loaded. */ async onDeviceConnected() { const fn = "onDeviceConnected"; const productName = this.deviceManager.getProductName(); if (this.deviceManager.isFirstConnection()) { this.deviceManager.markAsConnected(); this.view.updateData("product-name", productName); this.model = productName; DebugManager.log("ControllerManager Model:", this.model); LOGGER.info(fn, "Model", this.model); try { const resources = await this.getResources(); Loading @@ -459,8 +473,9 @@ export class ControllerManager { * @returns {Promise<void>} Resolves when version is valid. */ async checkAndroidVersion(versionRequired) { const fn = "checkAndroidVersion"; const android = await this.deviceManager.getAndroidVersion(); DebugManager.log("current android version:", android); LOGGER.info(fn, "current android version:", android); if (android) { this.view.updateData("android-version", android); if (android < versionRequired) { Loading @@ -475,6 +490,7 @@ export class ControllerManager { * @returns {Promise<object>} Resource object or throws device-model-not-supported. */ async getResources() { const fn = "getResources"; let resources = null; try { let current_security_path_level = null; Loading @@ -482,17 +498,18 @@ export class ControllerManager { const security_patch = await this.deviceManager.adb.getProp( "ro.build.version.security_patch", ); //DebugManager.log('security_patch', security_patch) //LOGGER.info('security_patch', security_patch) current_security_path_level = parseInt( security_patch.replace(/-/g, ""), 10, ); DebugManager.log( LOGGER.info( fn, "current_security_path_level", current_security_path_level, ); } catch { DebugManager.log("Security patch Error"); LOGGER.info(fn, "Security patch Error"); current_security_path_level = null; } let this_model = this.deviceManager.adb.banner.device; Loading @@ -501,7 +518,7 @@ export class ControllerManager { if (model.includes("Teracube") && model.includes("2e")) { try { const serial = await this.deviceManager.adb.getSerialNumber(); DebugManager.log("serial numer:", serial); LOGGER.info(fn, "serial numer:", serial); if (serial.startsWith("2021")) { this_model = "emerald"; } else if (serial.startsWith("2020")) { Loading Loading @@ -536,16 +553,18 @@ export class ControllerManager { current_security_path_level != null && typeof resources.security_patch_level != "undefined" ) { DebugManager.log( LOGGER.info( fn, `EOS Rom has security patch ${current_security_path_level}`, ); const new_security_path_level = parseInt( resources.security_patch_level.replace(/-/g, ""), 10, ); DebugManager.log(`New security patch ${new_security_path_level}`); LOGGER.info(fn, `New security patch ${new_security_path_level}`); if (current_security_path_level > new_security_path_level) { DebugManager.log( LOGGER.info( fn, "Bypass lock procedure", `resources/${this_model}-safe.json`, ); Loading @@ -556,7 +575,7 @@ export class ControllerManager { } } catch (e) { resources = null; DebugManager.log("getResources Error: " + e); LOGGER.info(fn, "getResources Error: " + e); throw Error("device-model-not-supported"); } Loading app/src/controller/debug.manager.js +90 −14 Original line number Diff line number Diff line import { Log } from "./utils/log.class.js"; /** * Centralized debug logger used by controller components. */ export class DebugManager { constructor() {} /** * Creates a scoped logger bound to one origin string. * * @param {string} origin Source label for all logs emitted by this logger. * @returns {{info: (functionName: string, ...args: any[]) => void, verbose: (functionName: string, ...args: any[]) => void, warn: (functionName: string, ...args: any[]) => void, error: (functionName: string, ...args: any[]) => void}} Logger methods bound to origin. */ static getLogger(origin) { if (!origin || typeof origin !== "string") { throw new Error( new Date(), "DebugManager.getLogger requires a non-empty origin", ); } /** * Writes a debug message to the console. * Emits a log line built from the Log class. * * @param {"INFO"|"VERBOSE"|"WARN"|"ERROR"} type Log level. * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ static log(...args) { console.log("[DEBUG]", ...args); const emit = (type, fn, ...args) => { const log = new Log( type, `${origin}.${fn}`, args.map((arg) => String(arg)).join(" "), ); if (type === "WARN") { console.warn(log.toString()); return; } if (type === "ERROR") { console.error(log.toString()); return; } console.log(log.toString()); }; /** * Logs an info message. * * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ const info = (fn, ...args) => { emit("INFO", fn, ...args); }; /** * Writes an error message to the console. * Logs a verbose message. * * @param {...any} args Values to log as an error. * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ const verbose = (fn, ...args) => { emit("VERBOSE", fn, ...args); }; /** * Logs a warning message. * * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ static error(...args) { console.error("[ERROR]", ...args); const warn = (fn, ...args) => { emit("WARN", fn, ...args); }; /** * Logs an error message. * * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ const error = (fn, ...args) => { emit("ERROR", fn, ...args); }; return { info, verbose, warn, error, }; } } app/src/controller/device.manager.js +17 −17 Original line number Diff line number Diff line Loading @@ -6,13 +6,12 @@ import { Device } from "./device/device.class.js"; import { DebugManager } from "./debug.manager.js"; import { MODE } from "./enums/mode.enum.js"; const LOGGER = DebugManager.getLogger("DeviceManager"); /** * Facade over ADB, bootloader, recovery and download operations. */ export class DeviceManager { /** * Creates a new DeviceManager and initializes all sub-managers. */ constructor() { this.model = ""; this.rom = undefined; Loading Loading @@ -334,31 +333,33 @@ export class DeviceManager { * @returns {Promise<void>} Resolves when device appears or timeout expires. */ waitForDeviceOnBus(timeoutMs = 30000) { const fn = "waitForDeviceOnBus"; const startTime = Date.now(); return new Promise((resolve) => { const devices = navigator.usb.getDevices(); devices.then((list) => { DebugManager.log( `waitForDeviceOnBus: getDevices() returned ${list.length} device(s)`, LOGGER.info( fn, `getDevices() returned ${list.length} device(s)`, list.map((d) => `${d.vendorId}:${d.productId} "${d.productName}"`), ); if (list.length > 0) { DebugManager.log( "waitForDeviceOnBus: device already visible, no wait needed", ); LOGGER.info(fn, "Device already visible, no wait needed"); resolve(); return; } DebugManager.log( `waitForDeviceOnBus: no devices found, listening for USB connect event (timeout=${timeoutMs}ms)...`, LOGGER.info( fn, `No devices found, listening for USB connect event (timeout=${timeoutMs}ms)...`, ); const timeout = setTimeout(() => { navigator.usb.removeEventListener("connect", onConnect); const elapsed = Date.now() - startTime; DebugManager.log( `waitForDeviceOnBus: timeout after ${elapsed}ms, no device appeared. Proceeding anyway.`, LOGGER.info( fn, `Timeout after ${elapsed}ms, no device appeared. Proceeding anyway.`, ); resolve(); }, timeoutMs); Loading @@ -366,17 +367,16 @@ export class DeviceManager { const onConnect = (event) => { const elapsed = Date.now() - startTime; const d = event.device; DebugManager.log( `waitForDeviceOnBus: USB connect event after ${elapsed}ms - ` + LOGGER.info( fn, `USB connect event after ${elapsed}ms - ` + `vendorId=${d.vendorId} productId=${d.productId} ` + `productName="${d.productName}" serialNumber="${d.serialNumber}"`, ); clearTimeout(timeout); navigator.usb.removeEventListener("connect", onConnect); // Small delay to let the device fully initialize after enumeration DebugManager.log( "waitForDeviceOnBus: waiting 1000ms for device to stabilize...", ); LOGGER.info(fn, "Waiting 1000ms for device to stabilize..."); setTimeout(resolve, 1000); }; Loading app/src/controller/device/adb.class.js +15 −9 Original line number Diff line number Diff line Loading @@ -2,6 +2,8 @@ import { Device } from "./device.class.js"; import { DebugManager } from "../debug.manager.js"; import { AdbDevice } from "../../lib/index.ts"; const LOGGER = DebugManager.getLogger("ADB"); /** * ADB mode implementation backed by `AdbDevice`. */ Loading Loading @@ -29,8 +31,9 @@ export class ADB extends Device { * @returns {Promise<void>} Resolves when the device is connected. */ async connect() { const fn = "connect"; try { console.log("debug adb connect"); LOGGER.info(fn); // Try to find a paired device first, then request if needed this._adbDevice = await AdbDevice.requestDevice(); Loading @@ -39,14 +42,14 @@ export class ADB extends Device { this.device = { name: this._adbDevice.usbDevice.productName }; const banner = this._adbDevice.banner; DebugManager.log("----------------------------------"); DebugManager.log("Model", banner.model); DebugManager.log("product", banner.product); DebugManager.log("Name", this._adbDevice.usbDevice.productName); DebugManager.log(">Device (codename)", banner.device); DebugManager.log("----------------------------------"); LOGGER.info(fn, "----------------------------------"); LOGGER.info(fn, "Model", banner.model); LOGGER.info(fn, "product", banner.product); LOGGER.info(fn, "Name", this._adbDevice.usbDevice.productName); LOGGER.info(fn, ">Device (codename)", banner.device); LOGGER.info(fn, "----------------------------------"); } catch (e) { console.error(e); LOGGER.error(fn, `Cannot connect ADB ${e.message || e}`); this.device = null; throw new Error(`Cannot connect ADB ${e.message || e}`); } Loading Loading @@ -105,7 +108,8 @@ export class ADB extends Device { * @returns {Promise<any>} Shell command result. */ async runCommand(cmd) { DebugManager.log("ADB Run command>", cmd); const fn = "runCommand"; LOGGER.info(fn, cmd); return await this._adbDevice.shell(cmd); } Loading @@ -116,6 +120,8 @@ export class ADB extends Device { * @returns {Promise<any>} Reboot command result. */ async reboot(mode) { const fn = "reboot"; LOGGER.info(fn, mode); return await this._adbDevice.reboot(mode); } } app/src/controller/device/bootloader.class.js +51 −48 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
app/src/controller/controller.manager.js +56 −37 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ import { COMMAND } from "./enums/command.enum.js"; import { Step } from "./utils/step.class.js"; import { DebugManager } from "./debug.manager.js"; const LOGGER = DebugManager.getLogger("ControllerManager"); /* * Class to manage process * Check and display the steps, interact with deviceManager Loading Loading @@ -50,24 +52,26 @@ export class ControllerManager { * @returns {Promise<void>} Resolves when the next step is started. */ async next() { const fn = "next"; let current = this.steps[this.currentIndex]; let next = this.steps[this.currentIndex + 1]; DebugManager.log("Controller Manager Next", next); LOGGER.info(fn, "Advancing to", next.toString()); if (next) { if (next.mode) { const alreadyInMode = this.inInMode(next.mode); DebugManager.log( `next() step="${next.name}" requires mode="${next.mode}", ` + LOGGER.info( fn, `${next.name}" requires mode="${next.mode}", ` + `alreadyInMode=${alreadyInMode}, needUserGesture=${next.needUserGesture}`, ); //if next step require another mode [adb|fastboot|bootloader] if (!alreadyInMode) { //we need reboot DebugManager.log(`next() rebooting to ${next.mode}...`); LOGGER.info(fn, `Rebooting to ${next.mode}...`); await this.deviceManager.reboot(next.mode); DebugManager.log(`next() reboot to ${next.mode} completed`); LOGGER.info(fn, `Reboot to ${next.mode} completed`); } if (next.needUserGesture) { // Wait for the device to appear on the USB bus before showing the Loading @@ -75,23 +79,23 @@ export class ControllerManager { // devices after a mode switch. The actual connect happens via // executeStep when the user clicks (WebUSB requestDevice() requires // a user gesture). DebugManager.log( `next() waiting for device on USB bus (needUserGesture=true, deferring connect)...`, LOGGER.info( fn, `Waiting for device on USB bus (needUserGesture=true, deferring connect)...`, ); await this.deviceManager.waitForDeviceOnBus(); DebugManager.log(`next() device wait complete, showing step to user`); LOGGER.info(fn, "Device wait complete, showing step to user"); } else { DebugManager.log( `next() connecting to ${next.mode} automatically...`, ); LOGGER.info(fn, `Connecting to ${next.mode} automatically...`); await this.deviceManager.connect(next.mode); DebugManager.log(`next() auto-connect to ${next.mode} completed`); LOGGER.info(fn, `Auto-connect to ${next.mode} completed`); } } this.currentIndex++; current = this.steps[this.currentIndex]; DebugManager.log( `next() advancing to step="${current.name}", needUserGesture=${current.needUserGesture}`, LOGGER.info( fn, `Advancing to step="${current.name}", needUserGesture=${current.needUserGesture}`, ); this.view.onStepStarted(this.currentIndex, current); if (!current.needUserGesture) { Loading @@ -108,9 +112,10 @@ export class ControllerManager { * @returns {Promise<void>} Resolves when the step has been processed. */ async executeStep(stepName, loader) { const fn = "executeStep"; const current = this.steps[this.currentIndex]; let this_command; DebugManager.log("ControllerManager Execute step", current); LOGGER.info(fn, "Executing", current); document.getElementById("error-message-state").style.display = "none"; if (current.name === stepName) { let res = true; Loading @@ -119,7 +124,7 @@ export class ControllerManager { for (i = 0; i < current.commands.length && res; i++) { this_command = current.commands[i]; res = await this.runCommand(this_command, loader); DebugManager.log("run command > ", this_command, "returns ", res); LOGGER.info(fn, this_command, "returns ", res); } const next = this.steps[this.currentIndex + 1]; let previous = this.steps[this.currentIndex - 1]; Loading Loading @@ -168,7 +173,8 @@ export class ControllerManager { * @returns {Promise<boolean>} True when the command succeeds. */ async runCommand(cmd, loader) { DebugManager.log("ControllerManager run command:", cmd); const fn = "runCommand"; LOGGER.info(fn, "Running command", cmd); switch (cmd.type) { case COMMAND.download: return this.runDownloadCommand(); Loading Loading @@ -274,6 +280,7 @@ export class ControllerManager { * @returns {Promise<boolean>} True when flashing succeeds. */ async runFlashCommand(cmd) { const fn = "runFlashCommand"; const FLASH_COOLDOWN_MS = this.resources?.flash_cooldown_ms ?? 2500; const result = await this.deviceManager.flash( cmd.file, Loading @@ -282,7 +289,8 @@ export class ControllerManager { this.view.onInstalling(cmd.file, done, total); }, ); DebugManager.log( LOGGER.info( fn, `Flash cooldown: waiting ${FLASH_COOLDOWN_MS}ms before next operation`, ); await new Promise((resolve) => setTimeout(resolve, FLASH_COOLDOWN_MS)); Loading @@ -296,19 +304,21 @@ export class ControllerManager { * @returns {Promise<boolean>} True when unlock flow completes. */ async runUnlockCommand(cmd) { const fn = "runUnlockCommand"; let isUnlocked = false; let gotoStep = ""; if (cmd.partition) { if (cmd.partition.startsWith("goto_")) { gotoStep = cmd.partition.substring(5); DebugManager.log("goto step", gotoStep); LOGGER.info(fn, "goto step", gotoStep); isUnlocked = await this.deviceManager.getUnlocked("unlocked"); } else { isUnlocked = await this.deviceManager.getUnlocked(cmd.partition); } } DebugManager.log( "ControllerManager unlock: ", LOGGER.info( fn, "unlock:", this.deviceManager.adb.getProductName() + " isUnlocked = " + isUnlocked, ); if (!isUnlocked) { Loading @@ -316,22 +326,23 @@ export class ControllerManager { await this.deviceManager.unlock(cmd.command); } catch (e) { if (e.bootloaderMessage?.includes("already")) { DebugManager.log("device already unlocked"); LOGGER.info(fn, "device already unlocked"); } else if (e.bootloaderMessage?.includes("not allowed")) { DebugManager.log("device unlock is not allowed"); LOGGER.info(fn, "device unlock is not allowed"); throw new Error(`Unlock not allowed: ${e.message || e}`); } else { throw e; } } } else { DebugManager.log("The phone is not locked - bypass lock process"); LOGGER.warn(fn, "The phone is not locked - bypass lock process"); if (gotoStep == "") { this.currentIndex++; } else { do { this.currentIndex++; DebugManager.log( LOGGER.info( fn, "Bypass step", this.steps[this.currentIndex].name + " " + Loading @@ -351,6 +362,7 @@ export class ControllerManager { * @returns {Promise<boolean>} True when lock flow completes. */ async runLockCommand(cmd) { const fn = "runLockCommand"; let isLocked = false; if (cmd.partition) { isLocked = !(await this.deviceManager.getUnlocked(cmd.partition)); Loading @@ -361,7 +373,7 @@ export class ControllerManager { isLocked = true; } catch (e) { if (e.bootloaderMessage?.includes("already")) { DebugManager.log("device already locked"); LOGGER.info(fn, "device already locked"); isLocked = true; } else { throw new Error(`Lock failed: ${e.message || e}`); Loading Loading @@ -419,7 +431,8 @@ export class ControllerManager { * @returns {Promise<boolean>} True when forwarded command finishes. */ async runUnknownCommand(cmd) { DebugManager.log(`try unknown command ${cmd.command}`); const fn = "runUnknownCommand"; LOGGER.info(fn, `try unknown command ${cmd.command}`); await this.deviceManager.runCommand(cmd.command); return true; } Loading @@ -430,12 +443,13 @@ export class ControllerManager { * @returns {Promise<void>} Resolves when device data is loaded. */ async onDeviceConnected() { const fn = "onDeviceConnected"; const productName = this.deviceManager.getProductName(); if (this.deviceManager.isFirstConnection()) { this.deviceManager.markAsConnected(); this.view.updateData("product-name", productName); this.model = productName; DebugManager.log("ControllerManager Model:", this.model); LOGGER.info(fn, "Model", this.model); try { const resources = await this.getResources(); Loading @@ -459,8 +473,9 @@ export class ControllerManager { * @returns {Promise<void>} Resolves when version is valid. */ async checkAndroidVersion(versionRequired) { const fn = "checkAndroidVersion"; const android = await this.deviceManager.getAndroidVersion(); DebugManager.log("current android version:", android); LOGGER.info(fn, "current android version:", android); if (android) { this.view.updateData("android-version", android); if (android < versionRequired) { Loading @@ -475,6 +490,7 @@ export class ControllerManager { * @returns {Promise<object>} Resource object or throws device-model-not-supported. */ async getResources() { const fn = "getResources"; let resources = null; try { let current_security_path_level = null; Loading @@ -482,17 +498,18 @@ export class ControllerManager { const security_patch = await this.deviceManager.adb.getProp( "ro.build.version.security_patch", ); //DebugManager.log('security_patch', security_patch) //LOGGER.info('security_patch', security_patch) current_security_path_level = parseInt( security_patch.replace(/-/g, ""), 10, ); DebugManager.log( LOGGER.info( fn, "current_security_path_level", current_security_path_level, ); } catch { DebugManager.log("Security patch Error"); LOGGER.info(fn, "Security patch Error"); current_security_path_level = null; } let this_model = this.deviceManager.adb.banner.device; Loading @@ -501,7 +518,7 @@ export class ControllerManager { if (model.includes("Teracube") && model.includes("2e")) { try { const serial = await this.deviceManager.adb.getSerialNumber(); DebugManager.log("serial numer:", serial); LOGGER.info(fn, "serial numer:", serial); if (serial.startsWith("2021")) { this_model = "emerald"; } else if (serial.startsWith("2020")) { Loading Loading @@ -536,16 +553,18 @@ export class ControllerManager { current_security_path_level != null && typeof resources.security_patch_level != "undefined" ) { DebugManager.log( LOGGER.info( fn, `EOS Rom has security patch ${current_security_path_level}`, ); const new_security_path_level = parseInt( resources.security_patch_level.replace(/-/g, ""), 10, ); DebugManager.log(`New security patch ${new_security_path_level}`); LOGGER.info(fn, `New security patch ${new_security_path_level}`); if (current_security_path_level > new_security_path_level) { DebugManager.log( LOGGER.info( fn, "Bypass lock procedure", `resources/${this_model}-safe.json`, ); Loading @@ -556,7 +575,7 @@ export class ControllerManager { } } catch (e) { resources = null; DebugManager.log("getResources Error: " + e); LOGGER.info(fn, "getResources Error: " + e); throw Error("device-model-not-supported"); } Loading
app/src/controller/debug.manager.js +90 −14 Original line number Diff line number Diff line import { Log } from "./utils/log.class.js"; /** * Centralized debug logger used by controller components. */ export class DebugManager { constructor() {} /** * Creates a scoped logger bound to one origin string. * * @param {string} origin Source label for all logs emitted by this logger. * @returns {{info: (functionName: string, ...args: any[]) => void, verbose: (functionName: string, ...args: any[]) => void, warn: (functionName: string, ...args: any[]) => void, error: (functionName: string, ...args: any[]) => void}} Logger methods bound to origin. */ static getLogger(origin) { if (!origin || typeof origin !== "string") { throw new Error( new Date(), "DebugManager.getLogger requires a non-empty origin", ); } /** * Writes a debug message to the console. * Emits a log line built from the Log class. * * @param {"INFO"|"VERBOSE"|"WARN"|"ERROR"} type Log level. * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ static log(...args) { console.log("[DEBUG]", ...args); const emit = (type, fn, ...args) => { const log = new Log( type, `${origin}.${fn}`, args.map((arg) => String(arg)).join(" "), ); if (type === "WARN") { console.warn(log.toString()); return; } if (type === "ERROR") { console.error(log.toString()); return; } console.log(log.toString()); }; /** * Logs an info message. * * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ const info = (fn, ...args) => { emit("INFO", fn, ...args); }; /** * Writes an error message to the console. * Logs a verbose message. * * @param {...any} args Values to log as an error. * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ const verbose = (fn, ...args) => { emit("VERBOSE", fn, ...args); }; /** * Logs a warning message. * * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ static error(...args) { console.error("[ERROR]", ...args); const warn = (fn, ...args) => { emit("WARN", fn, ...args); }; /** * Logs an error message. * * @param {string} fn Function name where the log is emitted. * @param {...any} args Values to log. * @returns {void} */ const error = (fn, ...args) => { emit("ERROR", fn, ...args); }; return { info, verbose, warn, error, }; } }
app/src/controller/device.manager.js +17 −17 Original line number Diff line number Diff line Loading @@ -6,13 +6,12 @@ import { Device } from "./device/device.class.js"; import { DebugManager } from "./debug.manager.js"; import { MODE } from "./enums/mode.enum.js"; const LOGGER = DebugManager.getLogger("DeviceManager"); /** * Facade over ADB, bootloader, recovery and download operations. */ export class DeviceManager { /** * Creates a new DeviceManager and initializes all sub-managers. */ constructor() { this.model = ""; this.rom = undefined; Loading Loading @@ -334,31 +333,33 @@ export class DeviceManager { * @returns {Promise<void>} Resolves when device appears or timeout expires. */ waitForDeviceOnBus(timeoutMs = 30000) { const fn = "waitForDeviceOnBus"; const startTime = Date.now(); return new Promise((resolve) => { const devices = navigator.usb.getDevices(); devices.then((list) => { DebugManager.log( `waitForDeviceOnBus: getDevices() returned ${list.length} device(s)`, LOGGER.info( fn, `getDevices() returned ${list.length} device(s)`, list.map((d) => `${d.vendorId}:${d.productId} "${d.productName}"`), ); if (list.length > 0) { DebugManager.log( "waitForDeviceOnBus: device already visible, no wait needed", ); LOGGER.info(fn, "Device already visible, no wait needed"); resolve(); return; } DebugManager.log( `waitForDeviceOnBus: no devices found, listening for USB connect event (timeout=${timeoutMs}ms)...`, LOGGER.info( fn, `No devices found, listening for USB connect event (timeout=${timeoutMs}ms)...`, ); const timeout = setTimeout(() => { navigator.usb.removeEventListener("connect", onConnect); const elapsed = Date.now() - startTime; DebugManager.log( `waitForDeviceOnBus: timeout after ${elapsed}ms, no device appeared. Proceeding anyway.`, LOGGER.info( fn, `Timeout after ${elapsed}ms, no device appeared. Proceeding anyway.`, ); resolve(); }, timeoutMs); Loading @@ -366,17 +367,16 @@ export class DeviceManager { const onConnect = (event) => { const elapsed = Date.now() - startTime; const d = event.device; DebugManager.log( `waitForDeviceOnBus: USB connect event after ${elapsed}ms - ` + LOGGER.info( fn, `USB connect event after ${elapsed}ms - ` + `vendorId=${d.vendorId} productId=${d.productId} ` + `productName="${d.productName}" serialNumber="${d.serialNumber}"`, ); clearTimeout(timeout); navigator.usb.removeEventListener("connect", onConnect); // Small delay to let the device fully initialize after enumeration DebugManager.log( "waitForDeviceOnBus: waiting 1000ms for device to stabilize...", ); LOGGER.info(fn, "Waiting 1000ms for device to stabilize..."); setTimeout(resolve, 1000); }; Loading
app/src/controller/device/adb.class.js +15 −9 Original line number Diff line number Diff line Loading @@ -2,6 +2,8 @@ import { Device } from "./device.class.js"; import { DebugManager } from "../debug.manager.js"; import { AdbDevice } from "../../lib/index.ts"; const LOGGER = DebugManager.getLogger("ADB"); /** * ADB mode implementation backed by `AdbDevice`. */ Loading Loading @@ -29,8 +31,9 @@ export class ADB extends Device { * @returns {Promise<void>} Resolves when the device is connected. */ async connect() { const fn = "connect"; try { console.log("debug adb connect"); LOGGER.info(fn); // Try to find a paired device first, then request if needed this._adbDevice = await AdbDevice.requestDevice(); Loading @@ -39,14 +42,14 @@ export class ADB extends Device { this.device = { name: this._adbDevice.usbDevice.productName }; const banner = this._adbDevice.banner; DebugManager.log("----------------------------------"); DebugManager.log("Model", banner.model); DebugManager.log("product", banner.product); DebugManager.log("Name", this._adbDevice.usbDevice.productName); DebugManager.log(">Device (codename)", banner.device); DebugManager.log("----------------------------------"); LOGGER.info(fn, "----------------------------------"); LOGGER.info(fn, "Model", banner.model); LOGGER.info(fn, "product", banner.product); LOGGER.info(fn, "Name", this._adbDevice.usbDevice.productName); LOGGER.info(fn, ">Device (codename)", banner.device); LOGGER.info(fn, "----------------------------------"); } catch (e) { console.error(e); LOGGER.error(fn, `Cannot connect ADB ${e.message || e}`); this.device = null; throw new Error(`Cannot connect ADB ${e.message || e}`); } Loading Loading @@ -105,7 +108,8 @@ export class ADB extends Device { * @returns {Promise<any>} Shell command result. */ async runCommand(cmd) { DebugManager.log("ADB Run command>", cmd); const fn = "runCommand"; LOGGER.info(fn, cmd); return await this._adbDevice.shell(cmd); } Loading @@ -116,6 +120,8 @@ export class ADB extends Device { * @returns {Promise<any>} Reboot command result. */ async reboot(mode) { const fn = "reboot"; LOGGER.info(fn, mode); return await this._adbDevice.reboot(mode); } }
app/src/controller/device/bootloader.class.js +51 −48 File changed.Preview size limit exceeded, changes collapsed. Show changes