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
This commit is contained in:
John Knox
2018-11-19 04:45:51 -08:00
committed by Facebook Github Bot
parent ddbb3c7f89
commit a6765deec6

View File

@@ -54,7 +54,15 @@ function getRunningEmulatorName(id: string): Promise<?string> {
}
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<string> = 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();