From a6765deec66aec272710cd9301902df92b1d0313 Mon Sep 17 00:00:00 2001 From: John Knox Date: Mon, 19 Nov 2018 04:45:51 -0800 Subject: [PATCH] Fix missing android devices bug Summary: Finally tracked down this bug that's been annoying me. If you start flipper while the adb daemon is not running, then flipper will start the daemon, but it won't wait for it to have finished starting before telling the client to watch for devices. This is a race condition, and in practice never seems to work. To avoid this, I'm calling `adb devices` on the client and waiting for it to complete before doing anything else. This forces it to wait for the daemon to start up if it hasn't already, and if it has then it instantly returns. Reviewed By: passy Differential Revision: D13101963 fbshipit-source-id: b85bed24751ce5c8efdfc6e841400e4db2580ab0 --- src/dispatcher/androidDevice.js | 116 ++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/src/dispatcher/androidDevice.js b/src/dispatcher/androidDevice.js index 96cd22d06..3853cbf7c 100644 --- a/src/dispatcher/androidDevice.js +++ b/src/dispatcher/androidDevice.js @@ -54,7 +54,15 @@ function getRunningEmulatorName(id: string): Promise { } export default (store: Store, logger: Logger) => { - const client = adb.createClient(); + // Using this client before adb server is started up will cause failures. + // safeClient gets around this by waiting for listDevices first, which ensures + // the server is up and running. + const unsafeClient = adb.createClient(); + + const safeClient = () => + new Promise((resolve, reject) => { + resolve(unsafeClient.listDevices().then(() => unsafeClient)); + }); const watchAndroidDevices = () => { // get emulators @@ -73,67 +81,71 @@ export default (store: Store, logger: Logger) => { }, ); - client - .trackDevices() - .then(tracker => { - tracker.on('error', err => { - if (err.message === 'Connection closed') { - // adb server has shutdown, remove all android devices - const {connections} = store.getState(); - const deviceIDsToRemove: Array = connections.devices - .filter((device: BaseDevice) => device instanceof AndroidDevice) - .map((device: BaseDevice) => device.serial); + safeClient().then(client => { + client + .trackDevices() + .then(tracker => { + tracker.on('error', err => { + if (err.message === 'Connection closed') { + // adb server has shutdown, remove all android devices + const {connections} = store.getState(); + const deviceIDsToRemove: Array< + string, + > = connections.devices + .filter((device: BaseDevice) => device instanceof AndroidDevice) + .map((device: BaseDevice) => device.serial); - store.dispatch({ - type: 'UNREGISTER_DEVICES', - payload: new Set(deviceIDsToRemove), - }); - console.error('adb server was shutdown'); - setTimeout(watchAndroidDevices, 500); - } else { - throw err; - } - }); + store.dispatch({ + type: 'UNREGISTER_DEVICES', + payload: new Set(deviceIDsToRemove), + }); + console.error('adb server was shutdown'); + setTimeout(watchAndroidDevices, 500); + } else { + throw err; + } + }); - tracker.on('add', async device => { - if (device.type !== 'offline') { - const androidDevice = await createDevice(client, device); - store.dispatch({ - type: 'REGISTER_DEVICE', - payload: androidDevice, - }); - } - }); + tracker.on('add', async device => { + if (device.type !== 'offline') { + const androidDevice = await createDevice(client, device); + store.dispatch({ + type: 'REGISTER_DEVICE', + payload: androidDevice, + }); + } + }); - tracker.on('change', async device => { - if (device.type === 'offline') { + tracker.on('change', async device => { + if (device.type === 'offline') { + store.dispatch({ + type: 'UNREGISTER_DEVICES', + payload: new Set([device.id]), + }); + } else { + const androidDevice = await createDevice(client, device); + store.dispatch({ + type: 'REGISTER_DEVICE', + payload: androidDevice, + }); + } + }); + + tracker.on('remove', device => { store.dispatch({ type: 'UNREGISTER_DEVICES', payload: new Set([device.id]), }); + }); + }) + .catch(err => { + if (err.code === 'ECONNREFUSED') { + // adb server isn't running } else { - const androidDevice = await createDevice(client, device); - store.dispatch({ - type: 'REGISTER_DEVICE', - payload: androidDevice, - }); + throw err; } }); - - tracker.on('remove', device => { - store.dispatch({ - type: 'UNREGISTER_DEVICES', - payload: new Set([device.id]), - }); - }); - }) - .catch(err => { - if (err.code === 'ECONNREFUSED') { - // adb server isn't running - } else { - throw err; - } - }); + }); }; watchAndroidDevices();