diff --git a/lib/roles/device.js b/lib/roles/device.js index 1ca06d86..ac92b223 100644 --- a/lib/roles/device.js +++ b/lib/roles/device.js @@ -31,6 +31,7 @@ module.exports = function(options) { .dependency(require('./device/plugins/forward')) .dependency(require('./device/plugins/group')) .dependency(require('./device/plugins/reboot')) + .dependency(require('./device/plugins/connect')) .define(function(options, solo) { if (process.send) { // Only if we have a parent process diff --git a/lib/roles/device/plugins/connect.js b/lib/roles/device/plugins/connect.js new file mode 100644 index 00000000..63d918d8 --- /dev/null +++ b/lib/roles/device/plugins/connect.js @@ -0,0 +1,108 @@ +var util = require('util') + +var syrup = require('syrup') +var Promise = require('bluebird') + +var logger = require('../../../util/logger') +var wire = require('../../../wire') +var wireutil = require('../../../wire/util') +var lifecycle = require('../../../util/lifecycle') + +module.exports = syrup.serial() + .dependency(require('../support/adb')) + .dependency(require('../support/router')) + .dependency(require('../support/push')) + .dependency(require('./group')) + .define(function(options, adb, router, push, group) { + var log = logger.createLogger('device:plugins:connect') + , plugin = Object.create(null) + , activeServer = null + + plugin.port = options.ports.pop() + plugin.url = util.format('%s:%s', options.publicIp, plugin.port) + + plugin.start = function() { + return new Promise(function(resolve, reject) { + if (plugin.isRunning()) { + return resolve(plugin.url) + } + + var server = adb.createTcpUsbBridge(options.serial) + + server.on('listening', function() { + resolve(plugin.url) + }) + + server.on('connection', function(conn) { + log.info('New remote ADB connection from %s', conn.remoteAddress) + }) + + server.on('error', reject) + + log.info(util.format('Listening on port %d', plugin.port)) + server.listen(plugin.port) + + activeServer = server + lifecycle.share('Remote ADB', activeServer) + }) + } + + plugin.stop = Promise.method(function() { + if (plugin.isRunning()) { + activeServer.close() + activeServer.end() + } + }) + + plugin.end = Promise.method(function() { + if (plugin.isRunning()) { + activeServer.end() + } + }) + + plugin.isRunning = function() { + return !!activeServer + } + + lifecycle.observe(plugin.stop) + group.on('leave', plugin.end) + + router + .on(wire.ConnectStartMessage, function(channel) { + var reply = wireutil.reply(options.serial) + plugin.start() + .then(function(url) { + push.send([ + channel + , reply.okay(url) + ]) + }) + .catch(function(err) { + log.error('Unable to start remote connect service', err.stack) + push.send([ + channel + , reply.fail(err.message) + ]) + }) + }) + .on(wire.ConnectStopMessage, function(channel) { + var reply = wireutil.reply(options.serial) + plugin.end() + .then(function() { + push.send([ + channel + , reply.okay() + ]) + }) + .catch(function(err) { + log.error('Failed to stop connect service', err.stack) + push.send([ + channel + , reply.fail(err.message) + ]) + }) + }) + + return plugin.start() + .return(plugin) + }) diff --git a/lib/roles/provider.js b/lib/roles/provider.js index ca8c349d..9cc98da3 100644 --- a/lib/roles/provider.js +++ b/lib/roles/provider.js @@ -31,7 +31,7 @@ module.exports = function(options) { // port, we must ensure that we allocate ports in fixed groups. var ports = options.ports.slice( 0 - , options.ports.length - options.ports.length % 3 + , options.ports.length - options.ports.length % 4 ) // Information about total devices @@ -276,7 +276,7 @@ module.exports = function(options) { // Spawn a device worker function spawn() { - var allocatedPorts = ports.splice(0, 3) + var allocatedPorts = ports.splice(0, 4) , proc = options.fork(device, allocatedPorts) , resolver = Promise.defer() diff --git a/lib/roles/websocket.js b/lib/roles/websocket.js index 6bcaf8ed..981efed8 100644 --- a/lib/roles/websocket.js +++ b/lib/roles/websocket.js @@ -484,6 +484,26 @@ module.exports = function(options) { ) ]) }) + .on('connect.start', function(channel, responseChannel) { + joinChannel(responseChannel) + push.send([ + channel + , wireutil.transaction( + responseChannel + , new wire.ConnectStartMessage() + ) + ]) + }) + .on('connect.stop', function(channel, responseChannel) { + joinChannel(responseChannel) + push.send([ + channel + , wireutil.transaction( + responseChannel + , new wire.ConnectStopMessage() + ) + ]) + }) .on('browser.open', function(channel, responseChannel, data) { joinChannel(responseChannel) push.send([ diff --git a/lib/wire/wire.proto b/lib/wire/wire.proto index 6f81b24e..f34701d1 100644 --- a/lib/wire/wire.proto +++ b/lib/wire/wire.proto @@ -52,6 +52,8 @@ enum MessageType { ScreenCaptureMessage = 50; ProviderHeartbeatMessage = 51; RebootMessage = 52; + ConnectStartMessage = 53; + ConnectStopMessage = 54; } message Envelope { @@ -374,6 +376,12 @@ message StoreOpenMessage { message ScreenCaptureMessage { } +message ConnectStartMessage { +} + +message ConnectStopMessage { +} + // Events, these must be kept in sync with STFService/wire.proto message AirplaneModeEvent { diff --git a/res/app/components/stf/control/control-service.js b/res/app/components/stf/control/control-service.js index 0fef4615..55f6c81a 100644 --- a/res/app/components/stf/control/control-service.js +++ b/res/app/components/stf/control/control-service.js @@ -189,6 +189,14 @@ module.exports = function ControlServiceFactory( return sendTwoWay('logcat.stop') } + this.startRemoteConnect = function() { + return sendTwoWay('connect.start') + } + + this.stopRemoteConnect = function() { + return sendTwoWay('connect.stop') + } + this.openBrowser = function(url, browser) { return sendTwoWay('browser.open', { url: url