openstf / adbkit

A pure Node.js client for the Android Debug Bridge.
Other
825 stars 240 forks source link

Stuck after track command #42

Open fdgonthier opened 8 years ago

fdgonthier commented 8 years ago

I'm trying to use ADBKit inside an Electron app. I call trackClient and the tracker seems to work, BUT, none of the next command called on the same ADBClient object fail to work.

The only way I managed to get it working was by adding a timeout() call on one of the methods I'm trying to call. The error is caught and then suddenly the other methods run.

Here is what (doesn't) happen without the timeout:

adb:command Send '0012host:track-devices' +0ms Tracker detected ADDED device: 094c9a99 Adding tracker item Tracker detected ONLINE device: 094c9a99

Here is the logging with a timeout inserted:

adb:command Send '0012host:track-devices' +0ms Tracker detected ADDED device: 094c9a99 Adding tracker item Tracker detected ONLINE device: 094c9a99 Error receiving informations failed to fetch info on 094c9a99 Not sure what to do here adb:command Send '0017host:transport:094c9a99' +147ms adb:command Send '0017host:transport:094c9a99' +3ms adb:command Send '0022shell:pm list features 2>/dev/null' +1ms adb:command Send '000Dshell:getprop' +1ms

I must say that this worked well with NW.js although I'm not sure it's related.

fdgonthier commented 8 years ago

Additional information. The problem seems to stem from Electron or at least Electron's version of Node.js.

I'm digging into ADBkit connection code and noticed the following:

  connect: ->
    console.log "test"
    @socket = new Net.Socket
    @socket.setNoDelay true
    @parser = new Parser @socket
    @socket.on 'connect', =>
      console.log "Connected"
      this.emit 'connect'
    @socket.on 'end', =>
      this.emit 'end'
    @socket.on 'drain', =>
      this.emit 'drain'
    @socket.on 'timeout', =>
      this.emit 'timeout'
    @socket.on 'error', (err) =>
      this._handleError err
    @socket.on 'close', (hadError) =>
      this.emit 'close', hadError
    @socket.connect @options
    return this

If I remove the first console.log statement, the connection goes through fine and I see the "Connected" console.log message. If I keep it there, the connection is never made. This code is very timing sensitive for some reasons... can't figure out why yet

fdgonthier commented 8 years ago

This sample only ever works reliably in Electron if there is NO console.log statement in ADBKit code, which means there is a race condition somewhere. I admit the bug is probably not in ADBKit...


var adb = require("adbkit");
var util = require("util");
var Promise = require("bluebird");

try {
    var Electron = require("electron");
} catch (e) {}

var main = function () {
    //console.log("Creating ADB client");

    adbClient = adb.createClient();

    adbClient.trackDevices()
        .then((tracker) => {
            tracker.on("add", (device) => {
                //console.log("Added: " + device.id);

                Promise.join(
                    adbClient.getFeatures(device.id),
                    adbClient.getProperties(device.id),
                    (features, props) => {
                        console.log("Features: " + util.inspect(features));
                        console.log("Properties: " + util.inspect(props));
                    });
            });
            tracker.on("remove", (device) => {
                console.log("Removed: " + device.id);
            });
            tracker.on("change", (device) => {
                console.log("Changed: " + device.id);
            });
            tracker.on("error", (err) => {
                console.log("Error: " + err);
            });
        });
};

if (Electron)
    Electron.app.on("ready", main);
else
    main();