diff --git a/src/dispatcher/androidDevice.js b/src/dispatcher/androidDevice.js index 847757958..166d14925 100644 --- a/src/dispatcher/androidDevice.js +++ b/src/dispatcher/androidDevice.js @@ -8,6 +8,7 @@ import AndroidDevice from '../devices/AndroidDevice'; import child_process from 'child_process'; import promiseRetry from 'promise-retry'; +import {promisify} from 'util'; import type {Store} from '../reducers/index.js'; import type BaseDevice from '../devices/BaseDevice'; import type Logger from '../fb-stubs/Logger.js'; @@ -62,31 +63,56 @@ function getRunningEmulatorName(id: string): Promise { } export default (store: Store, logger: Logger) => { - // Using this client before adb server is started up will cause failures. - // this gets around this by waiting for listDevices first, which ensures - // the server is up and running before allowing any other operations. - + /* Adbkit will attempt to start the adb server if it's not already running, + however, it sometimes fails with ENOENT errors. So instead, we start it + manually before requesting a client. */ function createClient() { - const unsafeClient = adb.createClient(); - return promiseRetry( - (retry, number) => { - return unsafeClient - .listDevices() - .then(() => { - return unsafeClient; - }) - .catch(e => { - console.warn(`Failed to start adb client. Retrying. ${e.message}`); - retry(e); - }); - }, - { - minTimeout: 200, - retries: 5, - }, - ); + const adbPath = process.env.ANDROID_HOME + ? `${process.env.ANDROID_HOME}/platform-tools/adb` + : 'adb'; + return recordSuccessMetric( + promisify(child_process.exec)(`${adbPath} start-server`) + .then(result => { + if (result.error) { + throw new Error( + `Failed to start adb server: ${result.stderr.toString()}`, + ); + } + }) + .then(adb.createClient), + 'createADBClient.shell', + ).catch(err => { + console.error( + 'Failed to create adb client using shell adb command. Trying with adbkit', + ); + + /* In the event that starting adb with the above method fails, fallback + to using adbkit, though its known to be unreliable. */ + const unsafeClient = adb.createClient(); + return recordSuccessMetric( + promiseRetry( + (retry, number) => { + return unsafeClient + .listDevices() + .then(() => { + return unsafeClient; + }) + .catch(e => { + console.warn( + `Failed to start adb client. Retrying. ${e.message}`, + ); + retry(e); + }); + }, + { + minTimeout: 200, + retries: 5, + }, + ), + 'createADBClient.adbkit', + ); + }); } - const clientPromise = recordSuccessMetric(createClient(), 'createADBClient'); const watchAndroidDevices = () => { // get emulators @@ -105,7 +131,7 @@ export default (store: Store, logger: Logger) => { }, ); - clientPromise + recordSuccessMetric(createClient(), 'createADBClient') .then(client => { client .trackDevices()