Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 178 additions & 14 deletions packages/hap-debugger/src/adb/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ class ADBModule {
this._lastPromise = null

this.emulators = new Map()
// reverse 自愈巡检定时器:周期性校验每台设备的 adb reverse 规则是否仍然存在,缺失则补建
this._reverseWatchdogTimer = null
// 防止一次巡检尚未结束时又触发下一次巡检,避免并发堆积
this._reverseWatchdogRunning = false
// 按设备 sn 维度串行化 reverse 相关操作,避免同一台设备上 adb 命令交错导致状态不一致
this._reverseSerialQueue = new Map()

this.init()
}
Expand All @@ -56,6 +62,7 @@ class ADBModule {
this._listen(event, this.onDeviceRemoved.bind(this))
})
this.devicesEmitter.start()
this.startReverseWatchdog()
}

/**
Expand Down Expand Up @@ -178,7 +185,7 @@ class ADBModule {
async getForwardPort(client, remotePort) {
let remote2local = client.remote2local
if (!remote2local) return client.port

let port = remote2local[remotePort]
if (!port) {
// addForwardPort
Expand All @@ -189,7 +196,9 @@ class ADBModule {

async addForwardPort(client, remotePort) {
const port = this._getNextLocalForwardPort()
debuglog(`addForwardPort():(${client.sn}) (local port: ${port}, remote port: ${remotePort}) start`)
debuglog(
`addForwardPort():(${client.sn}) (local port: ${port}, remote port: ${remotePort}) start`
)
const upForwardResult = await this.establishADBProxyLink(
'forward',
[client.sn].concat([port, remotePort])
Expand All @@ -202,7 +211,9 @@ class ADBModule {
}
client.remote2local[remotePort] = port
await this._writeClientLogFile(client)
debuglog(`addForwardPort():(${client.sn}) (local port: ${port}, remote port: ${remotePort}) end`)
debuglog(
`addForwardPort():(${client.sn}) (local port: ${port}, remote port: ${remotePort}) end`
)
return port
}

Expand All @@ -213,29 +224,180 @@ class ADBModule {
const { sn } = event
try {
setTimeout(async () => {
const { result } = await this.commander._commandFactory(`adb -s ${sn} reverse --list`)
const reverseBoolean = result && result.indexOf(this.option.localReversePort) !== -1
if (reverseBoolean) {
colorconsole.info(
`### App Server ### onCheckDeviceReverse(): (${sn})建立adb reverse成功 )`
)
} else {
colorconsole.error(
`### App Server ### onCheckDeviceReverse(): (${sn})建立adb reverse失败,请重新运行命令调试 )`
)
}
// 统一走“检查 + 修复”的逻辑:如果 reverse 已经丢失(例如设备快速拔插导致规则被清空),这里会自动补建
await this._ensureReverseForDevice(sn, { log: true })
}, 6000)
} catch (err) {
colorconsole.error(`### App Server ### onCheckDeviceReverse(): adb reverse连接检测失败`)
}
}

startReverseWatchdog() {
// 避免重复启动
if (this._reverseWatchdogTimer) return
// 2s 进行一次 reverse 的自检
const intervalMs = Number(this.option.reverseWatchdogIntervalMs || 2000)
this._reverseWatchdogTimer = setInterval(() => {
this._runReverseWatchdogTick()
}, intervalMs)
if (this._reverseWatchdogTimer && typeof this._reverseWatchdogTimer.unref === 'function') {
this._reverseWatchdogTimer.unref()
}
}

stopReverseWatchdog() {
if (this._reverseWatchdogTimer) {
clearInterval(this._reverseWatchdogTimer)
this._reverseWatchdogTimer = null
}
this._reverseWatchdogRunning = false
this._reverseSerialQueue.clear()
}

_runReverseWatchdogTick() {
// 上一轮还在跑就跳过,避免并发叠加导致 adb 压力过大
if (this._reverseWatchdogRunning) return
this._reverseWatchdogRunning = true
;(async () => {
// 这里用的是 currentDeviceMap,依赖 adb-commander 库
// 后续如果有问题,这里可以改成实时 adb devices 去查设备列表
const serials = Array.from(this.currentDeviceMap.keys())
if (serials.length === 0) return
// 多设备允许并发,但同一 sn 内部仍然会串行(见 _enqueueReverseTask)
const concurrency = Number(this.option.reverseWatchdogConcurrency || 3)
await this._mapWithConcurrency(serials, concurrency, (sn) => {
return this._enqueueReverseTask(sn, () => this._ensureReverseForDevice(sn))
})
})()
.catch((err) => {
debuglog(`reverse watchdog tick failed: ${err && err.message ? err.message : String(err)}`)
})
.finally(() => {
this._reverseWatchdogRunning = false
})
}

_enqueueReverseTask(sn, task) {
// 对同一个 sn 的任务按 promise 链串起来:保证同一设备上的 adb reverse --list / reverse 操作不会交错执行
const previous = this._reverseSerialQueue.get(sn) || Promise.resolve()
const next = previous
.catch(() => undefined)
.then(() => task())
.finally(() => {
if (this._reverseSerialQueue.get(sn) === next) {
this._reverseSerialQueue.delete(sn)
}
})
this._reverseSerialQueue.set(sn, next)
return next
}

async _mapWithConcurrency(items, concurrency, iterator) {
// 简单并发池:最多同时执行 limit 个 iterator
const limit = Number.isFinite(concurrency) && concurrency > 0 ? concurrency : 1
const executing = new Set()
const results = []

for (const item of items) {
const p = Promise.resolve().then(() => iterator(item))
results.push(p)
executing.add(p)
const cleanup = () => executing.delete(p)
p.then(cleanup, cleanup)
if (executing.size >= limit) {
await Promise.race(executing)
}
}

return Promise.allSettled(results)
}

_reverseListMatchesExpected(reverseListOutput, localReversePort, remoteReversePort) {
// adb reverse --list 的典型输出每行类似:
// tcp:12345 tcp:12306
if (!reverseListOutput) return false
const expectedLocal = Number(localReversePort)
const expectedRemote = Number(remoteReversePort)
const lines = String(reverseListOutput)
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)

for (const line of lines) {
const match = line.match(/^tcp:(\d+)\s+tcp:(\d+)$/)
if (!match) continue
const a = Number(match[1])
const b = Number(match[2])
if (
(a === expectedLocal && b === expectedRemote) ||
(a === expectedRemote && b === expectedLocal)
) {
return true
}
}

const localStr = String(expectedLocal)
const remoteStr = String(expectedRemote)
return lines.some((line) => line.includes(localStr) && line.includes(remoteStr))
}

async _ensureReverseForDevice(sn, { log = false } = {}) {
const localReversePort = this.option.localReversePort
let reverseListOutput = ''

try {
// 使用 -s sn 做到按设备维度校验,支持多设备同时连接
const { result } = await this.commander._commandFactory(`adb -s ${sn} reverse --list`)
reverseListOutput = result || ''
} catch (err) {
reverseListOutput = ''
}

const ok = this._reverseListMatchesExpected(
reverseListOutput,
localReversePort,
REMOTE_REVERSE_PORT
)
if (ok) {
if (log) {
colorconsole.info(`### App Server ### (${sn}) adb reverse已就绪`)
}
return { ok: true }
}

// reverse 缺失:执行一次补建
const reverseResult = await this.establishADBProxyLink('reverse', [
sn,
localReversePort,
REMOTE_REVERSE_PORT
])

if (reverseResult.err) {
if (log) {
colorconsole.error(
`### App Server ### (${sn}) 建立adb reverse失败(local port: ${localReversePort}, remote port: ${REMOTE_REVERSE_PORT})`
)
}
return { ok: false, err: reverseResult.err }
}

if (log) {
colorconsole.info(
`### App Server ### (${sn}) 已修复adb reverse(local port: ${localReversePort}, remote port: ${REMOTE_REVERSE_PORT})`
)
}

return { ok: true, repaired: true }
}
/**
* 移除设备事件
*/
async onDeviceRemoved(event) {
const { sn } = event
colorconsole.info(`### App Server ### 手机设备(${sn})被拔出`)
this.currentDeviceMap.delete(sn)
// 避免该设备后续队列中残留任务引用
this._reverseSerialQueue.delete(sn)
if (this.DEBUG) {
debuglog(
`deviceRemoved():(${sn}) cachedDeviceList: ${JSON.stringify(
Expand Down Expand Up @@ -342,6 +504,8 @@ class ADBModule {

_stop() {
colorconsole.log(`### ADB stop`)
// 停止定时巡检,避免进程无法退出或持续刷 adb
this.stopReverseWatchdog()
this.devicesEmitter.stop()
}
}
Expand Down
Loading