diff --git a/lib/devices/android/android.js b/lib/devices/android/android.js index 80e292a64..21e6e8d82 100644 --- a/lib/devices/android/android.js +++ b/lib/devices/android/android.js @@ -40,7 +40,6 @@ Android.prototype.initialize = function(opts) { this.verbose = opts.verbose; this.queue = []; this.progress = 0; - this.onStop = function() {}; this.implicitWaitMs = 0; this.commandTimeoutMs = 60 * 1000; this.origCommandTimeoutMs = this.commandTimeoutMs; @@ -52,6 +51,9 @@ Android.prototype.initialize = function(opts) { this.asyncWaitMs = 0; this.remote = null; this.curWindowHandle = null; + this.didLaunch = false; + this.launchCb = function() {}; + this.uiautomatorExitCb = function() {}; this.capabilities = { platform: 'LINUX' , browserName: 'Android' @@ -64,113 +66,109 @@ Android.prototype.initialize = function(opts) { }; Android.prototype.start = function(cb, onDie) { - if (typeof onDie === "function") { - this.onStop = onDie; - } - var didLaunch = false; - - var onLaunch = function(err, launchCb) { - if (typeof launchCb === "undefined" || launchCb === null) { - launchCb = cb; - } - // These messages are from adb.js. Must update when adb.js changes. - var relaunchOn = [ - 'Could not find a connected Android device' - , 'Device did not become ready' - ]; - var checkShouldRelaunch = function(msg) { - var relaunch = false; - _.each(relaunchOn, function(relaunchMsg) { - relaunch = relaunch || msg.indexOf(relaunchMsg) !== -1; - }); - return relaunch; - }; - - if (err) { - if (err.message === null || - typeof err.message === 'undefined' || - checkShouldRelaunch(err.message.toString())) { - logger.error(err); - logger.error("Above error isn't fatal, maybe relaunching adb will help...."); - this.adb.waitForDevice(function(err) { - if (err) return launchCb(err); - didLaunch = true; - launchCb(); - }); - } else { - // error is already printed by ADB.prototype.waitForActivity - this.shutdown(); - this.adb = null; - this.onStop = null; - launchCb(err); - } - } else { - logger.info("ADB launched! Ready for commands (will time out in " + - (this.commandTimeoutMs / 1000) + "secs)"); - this.resetTimeout(); - didLaunch = true; - launchCb(null); - } - }.bind(this); - - var onExit = function(code) { - if (!didLaunch) { - var msg = "UiAutomator quit before it successfully launched"; - logger.error(msg); - cb(new Error(msg)); - code = code || 1; - } else if (typeof this.cbForCurrentCmd === "function") { - var error = new UnknownError("UiAutomator died while responding to " + - "command, please check appium logs!"); - this.cbForCurrentCmd(error, null); - code = code || 1; - } - - if (this.adb) { - this.uninstallApp(function() { - this.adb = null; - this.uiautomator = null; - this.shuttingDown = false; - this.onStop(code); - this.onStop = null; - }.bind(this)); - } else { - logger.info("We're in uiautomator's exit callback but adb is gone already"); - } - }.bind(this); + this.launchCb = cb; + this.uiautomatorExitCb = onDie; if (this.adb === null) { // Pass Android opts and Android ref to adb. + logger.info("Starting android appium"); this.adb = new ADB(this.opts); this.uiautomator = new UiAutomator(this.adb, this.opts); - this.startAppium(onLaunch, onExit); + this.uiautomator.onExit = this.uiautomatorExitCb; + + logger.debug("Using fast reset? " + this.opts.fastReset); + + async.series([ + this.prepareDevice.bind(this), + this.pushStrings.bind(this), + this.requestXmlCompression.bind(this), + this.uninstallApp.bind(this), + this.installApp.bind(this), + this.forwardPort.bind(this), + this.pushAppium.bind(this), + this.pushUnlock.bind(this), + this.uiautomator.start.bind(this.uiautomator), + this.wakeUp.bind(this), + this.unlockScreen.bind(this), + this.startApp.bind(this) + ], function(err) { + this.launchCb(err); + }.bind(this)); } else { logger.error("Tried to start ADB when we already have one running!"); } }; -Android.prototype.startAppium = function(onLaunch, onExit) { - logger.info("Starting android appium"); - this.uiautomator.onExit = onExit; +Android.prototype.onLaunch = function(err) { + var readyToGo = function() { + logger.info("ADB launched! Ready for commands (will time out in " + + (this.commandTimeoutMs / 1000) + "secs)"); + this.resetTimeout(); + this.didLaunch = true; + this.launchCb(); + }.bind(this); - logger.debug("Using fast reset? " + this.opts.fastReset); + var giveUp = function(err) { + this.shutdown(function() { + this.launchCb(err); + }.bind(this)); + }.bind(this); - async.series([ - this.prepareDevice.bind(this), - this.pushStrings.bind(this), - this.requestXmlCompression.bind(this), - this.uninstallApp.bind(this), - this.installApp.bind(this), - this.forwardPort.bind(this), - this.pushAppium.bind(this), - this.pushUnlock.bind(this), - this.uiautomator.start.bind(this.uiautomator), - this.wakeUp.bind(this), - this.unlockScreen.bind(this), - this.startApp.bind(this) - ], function(err) { - onLaunch(err); + if (err) { + if (this.checkShouldRelaunch(err)) { + logger.error(err); + logger.error("Above error isn't fatal, maybe relaunching adb will help...."); + this.adb.waitForDevice(function(err) { + if (err) return giveUp(err); + readyToGo(); + }); + } else { + giveUp(err); + } + } else { + readyToGo(); + } +}; + +Android.prototype.onUiautomatorExit = function(code) { + if (!this.didLaunch) { + var msg = "UiAutomator quit before it successfully launched"; + logger.error(msg); + this.launchCb(new Error(msg)); + } else if (typeof this.cbForCurrentCmd === "function") { + var error = new UnknownError("UiAutomator died while responding to " + + "command, please check appium logs!"); + this.cbForCurrentCmd(error, null); + code = code || 1; + } + + if (this.adb) { + this.uninstallApp(function() { + this.adb = null; + this.uiautomator = null; + this.shuttingDown = false; + }.bind(this)); + } else { + logger.info("We're in uiautomator's exit callback but adb is gone already"); + } +}; + +Android.prototype.checkShouldRelaunch = function(launchErr) { + if (launchErr.message === null || typeof launchErr.message === 'undefined') { + logger.error("We're checking if we should relaunch based on something " + + "which isn't an error object. Check the codez!"); + return false; + } + var msg = launchErr.message.toString(); + var relaunchOn = [ + 'Could not find a connected Android device' + , 'Device did not become ready' + ]; + var relaunch = false; + _.each(relaunchOn, function(relaunchMsg) { + relaunch = relaunch || msg.indexOf(relaunchMsg) !== -1; }); + return relaunch; }; Android.prototype.pushStrings = function(cb) { @@ -255,25 +253,21 @@ Android.prototype.uninstallApp = function(cb) { }; Android.prototype.stop = function(cb) { + this.shuttingDown = true; + this.adb.goToHome(function() { + this.shutdown(cb); + }.bind(this)); +}; + +Android.prototype.cleanup = function() { + // should be called after all shutdowns if (this.commandTimeout) { clearTimeout(this.commandTimeout); } - if (this.adb === null) { - logger.info("Trying to stop adb but it already exited"); - if (cb) { - cb(); - } - } else { - if (cb) { - this.onStop = cb; - } - this.shuttingDown = true; - this.adb.goToHome(function() { - this.shutdown(); - }.bind(this)); - this.queue = []; - this.progress = 0; - } + this.queue = []; + this.progress = 0; + this.adb = null; + this.uiautomator = null; }; Android.prototype.shutdown = function() {