(serial: string, timeoutMs = 60000)
| 493 | } |
| 494 | |
| 495 | export async function waitForAndroidBoot(serial: string, timeoutMs = 60000): Promise<void> { |
| 496 | const timeoutBudget = timeoutMs; |
| 497 | const deadline = Deadline.fromTimeoutMs(timeoutBudget); |
| 498 | const maxAttempts = Math.max(1, Math.ceil(timeoutBudget / ANDROID_BOOT_POLL_MS)); |
| 499 | let lastBootResult: ExecResult | undefined; |
| 500 | let timedOut = false; |
| 501 | try { |
| 502 | await retryWithPolicy( |
| 503 | async ({ deadline: attemptDeadline }) => { |
| 504 | if (attemptDeadline?.isExpired()) { |
| 505 | timedOut = true; |
| 506 | throw new AppError('COMMAND_FAILED', 'Android boot deadline exceeded', { |
| 507 | serial, |
| 508 | timeoutMs, |
| 509 | elapsedMs: deadline.elapsedMs(), |
| 510 | message: 'timeout', |
| 511 | }); |
| 512 | } |
| 513 | const remainingMs = Math.max(1_000, attemptDeadline?.remainingMs() ?? timeoutBudget); |
| 514 | const result = await readAndroidBootProp( |
| 515 | serial, |
| 516 | Math.min(remainingMs, ANDROID_BOOT_PROP_TIMEOUT_MS), |
| 517 | ); |
| 518 | lastBootResult = result; |
| 519 | if (result.stdout.trim() === '1') return; |
| 520 | throw new AppError('COMMAND_FAILED', 'Android device is still booting', { |
| 521 | serial, |
| 522 | stdout: result.stdout, |
| 523 | stderr: result.stderr, |
| 524 | exitCode: result.exitCode, |
| 525 | }); |
| 526 | }, |
| 527 | { |
| 528 | maxAttempts, |
| 529 | baseDelayMs: ANDROID_BOOT_POLL_MS, |
| 530 | maxDelayMs: ANDROID_BOOT_POLL_MS, |
| 531 | jitter: 0, |
| 532 | shouldRetry: (error) => { |
| 533 | const reason = classifyBootFailure({ |
| 534 | error, |
| 535 | stdout: lastBootResult?.stdout, |
| 536 | stderr: lastBootResult?.stderr, |
| 537 | context: { platform: 'android', phase: 'boot' }, |
| 538 | }); |
| 539 | return reason !== 'ADB_TRANSPORT_UNAVAILABLE' && reason !== 'ANDROID_BOOT_TIMEOUT'; |
| 540 | }, |
| 541 | }, |
| 542 | { |
| 543 | deadline, |
| 544 | phase: 'boot', |
| 545 | classifyReason: (error) => |
| 546 | classifyBootFailure({ |
| 547 | error, |
| 548 | stdout: lastBootResult?.stdout, |
| 549 | stderr: lastBootResult?.stderr, |
| 550 | context: { platform: 'android', phase: 'boot' }, |
| 551 | }), |
| 552 | }, |
no test coverage detected