Files
android-dev-ext/services.js
2017-01-22 15:40:33 +00:00

322 lines
11 KiB
JavaScript

const chrome = require('./chrome-polyfill').chrome;
const { new_socketfd } = require('./sockets');
const { create_chrome_socket, accept_chrome_socket, destroy_chrome_socket } = chrome;
var start_request = function(fd) {
if (fd.closeState) return;
// read service passed from client
D('waiting for adb request...');
readx_with_data(fd, function(err, data) {
if (err) {
D('SS: error %o', err);
return;
}
handle_request(fd, data.asString());
start_request(fd);
});
}
var handle_request = exports.handle_request = function(fd, service) {
if (!service){
D('SS: no service');
sendfailmsg(fd, 'No service received');
return false;
}
D('adb request: %s', service);
if (service.slice(0,4) === 'host') {
// trim 'host:'
return handle_host_request(service.slice(5), 'kTransportAny', null, fd);
}
if (!fd.transport) {
D('No transport configured - using any found');
var t = acquire_one_transport('CS_DEVICE', 'kTransportAny', null);
t = check_one_transport(t, '', fd);
if (!t) return false;
fd.transport = t;
}
// once we call open_device_service, the fd belongs to the transport
open_device_service(fd.transport, fd, service, function(err, serviceinfo) {
if (err) {
sendfailmsg(fd, 'Device connection failed');
return;
}
D('device service opened: %o', serviceinfo);
send_okay(fd);
});
return true;
}
var sendfailmsg = function(fd, reason) {
reason = reason.slice(0, 0xffff);
var msg = 'FAIL' + intToHex(reason.length,4) + reason;
writex(fd, msg);
}
var handle_host_request = function(service, ttype, serial, replyfd) {
var transport;
if (service === 'kill') {
cl('service kill request');
send_okay(replyfd);
killall_devices();
//window.close();
return false;
}
if (service.slice(0,9) === 'transport') {
var t,serialmatch;
switch(service.slice(9)) {
case '-any':
t = acquire_one_transport('CS_ANY','kTransportAny',null);
break;
case '-local':
t = acquire_one_transport('CS_ANY','kTransportLocal',null);
break;
case '-usb':
t = acquire_one_transport('CS_ANY','kTransportUsb',null);
break;
default:
if (serialmatch = service.slice(9).match(/^:(.+)/))
t = acquire_one_transport('CS_ANY','kTransportAny',serialmatch[1]);
break;
}
t = check_one_transport(t, serialmatch&&serialmatch[1], replyfd);
if (!t) return false;
// set the transport in the fd - the client can use it
// to send raw data directly to the device
D('transport configured: %o', t);
replyfd.transport = t;
adb_writebytes(replyfd, "OKAY");
return false;
}
if (service.slice(0,7) === 'devices') {
var use_long = service.slice(7)==='-l';
D('Getting device list');
var transports = list_transports(use_long);
D('Wrote device list');
send_msg_with_okay(replyfd, transports);
return false;
}
if (service === 'version') {
var version = intToHex(ADB_SERVER_VERSION, 4);
send_msg_with_okay(replyfd, version);
return false;
}
if (service.slice(0,9) === 'emulator:') {
var port = service.slice(9);
port = port&&parseInt(port, 10)||0;
if (!port || port <= 0 || port >= 65536) {
D('Invalid emulator port: %s', service);
return false;
}
local_connect(port, function(err) {
});
// no reply needed
return false;
}
if (service.slice(0,9) === 'get-state') {
transport = acquire_one_transport('CS_ANY', ttype, serial, null);
transport = check_one_transport(transport, serial, replyfd);
if (!transport) return false;
var state = connection_state_name(transport);
send_msg_with_okay(replyfd, state);
return false;
}
if (service === 'killforward-all') {
remove_all_forward_listeners();
writex(replyfd, 'OKAY');
return false;
}
var fwdmatch = service.match(/^forward:(tcp:\d+);(jdwp:\d+)/);
if (fwdmatch) {
transport = acquire_one_transport('CS_ANY', ttype, serial, null);
transport = check_one_transport(transport, serial, replyfd);
if (!transport) return false;
install_forward_listener(fwdmatch[1], fwdmatch[2], transport, function(err) {
if (err) return sendfailmsg(replyfd, err.msg);
// on the host, 1st OKAY is connect, 2nd OKAY is status
writex(replyfd, 'OKAY');
writex(replyfd, 'OKAY');
});
return false;
}
if (service === 'track-devices') {
writex(replyfd, 'OKAY');
add_device_tracker(replyfd);
// fd now belongs to the tracker
return true;
}
if (service === 'track-devices-extended') {
writex(replyfd, 'OKAY');
add_device_tracker(replyfd, true);
// fd now belongs to the tracker
return true;
}
cl('Ignoring host service request: %s', service);
return false;
}
var check_one_transport = function(t, serial, replyfd) {
var which = serial||'(null)';
switch((t||[]).length) {
case 0:
sendfailmsg(replyfd, "device '"+which+"' not found");
return null;
case 1: t = t[0];
break;
default:
sendfailmsg(replyfd, 'more than one device/emulator');
return null;
}
switch(t.connection_state) {
case 'CS_DEVICE': break;
case 'CS_UNAUTHORIZED':
sendfailmsg(replyfd, 'device unauthorized.\r\nCheck for a confirmation dialog on your device or reconnect the device.');
return null;
default:
sendfailmsg(replyfd, 'Device not ready');
return null;
}
return t;
}
var forward_listeners = {};
var install_forward_listener = function(local, remote, t, cb) {
var localport = parseInt(local.split(':').pop(), 10);
var socket = chrome.socket;
create_chrome_socket('forward listener:'+localport, function(socketInfo) {
if (chrome.runtime.lastError) {
return cb({msg:chrome.runtime.lastError.message||'socket creation failed'});
}
socket.listen(socketInfo.socketId, '127.0.0.1', localport, 5,
function(result) {
if (chrome.runtime.lastError) {
var err = {msg:chrome.runtime.lastError.message||'socket listen failed'};
destroy_setup(socketInfo);
return cb(err);
}
if (result < 0) {
destroy_setup(socketInfo);
return cb({msg:'Cannot bind to socket'});
}
forward_listeners[localport] = {
port:localport,
socketId: socketInfo.socketId,
connectors_fd: null,
connect_cb:function(){},
};
accept_chrome_socket('forward server:'+localport, socketInfo.socketId, function(acceptInfo) {
accept_forward_connection(socketInfo.socketId, acceptInfo, localport, local, remote, t);
});
// listener is ready
D('started forward listener on port %d: %d', localport, socketInfo.socketId);
cb();
}
);
});
function destroy_setup(socketInfo) {
destroy_chrome_socket(socketInfo.socketId);
}
}
var connect_forward_listener = exports.connect_forward_listener = function(port, opts, cb) {
// if we're implementing the adb service, this will already be created
// if we're connecting via the adb executable, we need to create a dummy entry
if (!forward_listeners[port]) {
if (opts && opts.create) {
forward_listeners[port] = {
is_external_adb: true,
port:port,
socketId: null,
connectors_fd: null,
connect_cb:function(){},
}
} else {
D('Refusing forward connection request - forwarder for port %d does not exist', port);
return cb();
}
}
create_chrome_socket('forward client:'+port, function(createInfo) {
// save the receiver info
forward_listeners[port].connectors_fd = new_socketfd(createInfo.socketId);
forward_listeners[port].connect_cb = cb;
// do the connect - everything from here on is handled in the accept routine
chrome.socket.connect(createInfo.socketId, '127.0.0.1', port, function(result) {
chrome.socket.setNoDelay(createInfo.socketId, true, function(result) {
var x = forward_listeners[port];
if (x.is_external_adb) {
delete forward_listeners[port];
x.connect_cb(x.connectors_fd);
}
});
});
});
}
var accept_forward_connection = exports.accept_forward_connection = function(listenerSocketId, acceptInfo, port, local, remote, t) {
if (chrome.runtime.lastError) {
D('Forward port socket accept failed: '+port);
var listener = remove_forward_listener(listenerSocketId);
return listener.connect_cb();
}
// on accept - create the remote connection to the device
D('Binding forward port connection to remote port %s', remote);
var sfd = new_socketfd(acceptInfo.socketId);
// remove the listener
var listener = remove_forward_listener(listenerSocketId);
chrome.socket.setNoDelay(acceptInfo.socketId, true, function(result) {
// start the connection as a service
open_device_service(t, sfd, remote, function(err) {
listener.connect_cb(listener.connectors_fd);
});
});
}
var remove_forward_listener = exports.remove_forward_listener = function(socketId) {
for (var port in forward_listeners) {
if (forward_listeners[port].socketId === socketId) {
var x = forward_listeners[port];
delete forward_listeners[port];
destroy_chrome_socket(x.socketId);
D('removed forward listener: %d', x.socketId);
return x;
}
}
}
var remove_all_forward_listeners = exports.remove_all_forward_listeners = function() {
var ports = Object.keys(forward_listeners);
while (ports.length) {
remove_forward_listener(forward_listeners[ports.pop()].socketId);
}
}