diff --git a/lib/emulator-manager.js b/lib/emulator-manager.js index e878461a8..18a75fb0d 100644 --- a/lib/emulator-manager.js +++ b/lib/emulator-manager.js @@ -103,6 +103,8 @@ async function launchEmulator(avdName, disableAnimations, disableLinuxHardwareAc }); // wait for emulator to complete booting await waitForDevice(port, emulatorBootTimeout); + // boot_completed doesn't guarantee every service is registered yet (#432) + await waitForService(port, 'input'); await adb(port, `shell input keyevent 82`); if (disableAnimations) { console.log('Disabling animations.'); @@ -175,6 +177,33 @@ async function waitForDevice(port, emulatorBootTimeout) { attempts++; } } +/** + * Wait for a ServiceManager-registered service (e.g. 'input') to be available. + */ +async function waitForService(port, serviceName, maxAttempts = 15, retryIntervalSeconds = 1) { + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + let result = ''; + try { + await exec.exec(`adb -s emulator-${port} shell service check ${serviceName}`, [], { + listeners: { + stdout: (data) => { + result += data.toString(); + }, + }, + }); + } + catch (error) { + console.warn(error instanceof Error ? error.message : error); + } + if (result.includes('found') && !result.includes('not found')) { + return; + } + if (attempt === maxAttempts) { + throw new Error(`Timeout waiting for the '${serviceName}' service to become available.`); + } + await delay(retryIntervalSeconds * 1000); + } +} function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/src/emulator-manager.ts b/src/emulator-manager.ts index d10943d43..831d1a140 100644 --- a/src/emulator-manager.ts +++ b/src/emulator-manager.ts @@ -97,6 +97,9 @@ export async function launchEmulator( // wait for emulator to complete booting await waitForDevice(port, emulatorBootTimeout); + + // boot_completed doesn't guarantee every service is registered yet (#432) + await waitForService(port, 'input'); await adb(port, `shell input keyevent 82`); if (disableAnimations) { @@ -170,6 +173,35 @@ async function waitForDevice(port: number, emulatorBootTimeout: number): Promise } } +/** + * Wait for a ServiceManager-registered service (e.g. 'input') to be available. + */ +async function waitForService(port: number, serviceName: string, maxAttempts = 15, retryIntervalSeconds = 1): Promise { + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + let result = ''; + try { + await exec.exec(`adb -s emulator-${port} shell service check ${serviceName}`, [], { + listeners: { + stdout: (data: Buffer) => { + result += data.toString(); + }, + }, + }); + } catch (error) { + console.warn(error instanceof Error ? error.message : error); + } + + if (result.includes('found') && !result.includes('not found')) { + return; + } + + if (attempt === maxAttempts) { + throw new Error(`Timeout waiting for the '${serviceName}' service to become available.`); + } + await delay(retryIntervalSeconds * 1000); + } +} + function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); }