mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Bug 1492700 - Split adb.js in several files;r=daisuke
Differential Revision: https://phabricator.services.mozilla.com/D13473 --HG-- rename : devtools/shared/adb/adb.js => devtools/shared/adb/commands/list-devices.js rename : devtools/shared/adb/adb.js => devtools/shared/adb/commands/prepare-tcp-connection.js rename : devtools/shared/adb/adb.js => devtools/shared/adb/commands/run-command.js rename : devtools/shared/adb/adb.js => devtools/shared/adb/commands/shell.js rename : devtools/shared/adb/adb.js => devtools/shared/adb/commands/track-devices.js extra : moz-landing-system : lando
This commit is contained in:
parent
ec7b320b38
commit
b77ab873fa
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { ADB } = require("devtools/shared/adb/adb");
|
const { prepareTCPConnection } = require("devtools/shared/adb/commands/index");
|
||||||
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||||
const { DebuggerServer } = require("devtools/server/main");
|
const { DebuggerServer } = require("devtools/server/main");
|
||||||
const { ClientWrapper } = require("./client-wrapper");
|
const { ClientWrapper } = require("./client-wrapper");
|
||||||
@ -29,7 +29,7 @@ async function createNetworkClient(host, port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function createUSBClient(socketPath) {
|
async function createUSBClient(socketPath) {
|
||||||
const port = await ADB.prepareTCPConnection(socketPath);
|
const port = await prepareTCPConnection(socketPath);
|
||||||
return createNetworkClient("localhost", port);
|
return createNetworkClient("localhost", port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { ADB } = require("devtools/shared/adb/adb");
|
const { shell } = require("devtools/shared/adb/commands/index");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Device instance is created and registered with the Devices module whenever
|
* A Device instance is created and registered with the Devices module whenever
|
||||||
@ -19,7 +19,7 @@ class AdbDevice {
|
|||||||
if (this._model) {
|
if (this._model) {
|
||||||
return this._model;
|
return this._model;
|
||||||
}
|
}
|
||||||
const model = await ADB.shell("getprop ro.product.model");
|
const model = await shell("getprop ro.product.model");
|
||||||
this._model = model.trim();
|
this._model = model.trim();
|
||||||
return this._model;
|
return this._model;
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ class AdbDevice {
|
|||||||
// 00000000: 00000002 00000000 00010000 0001 01 6551588
|
// 00000000: 00000002 00000000 00010000 0001 01 6551588
|
||||||
// /data/data/org.mozilla.fennec/firefox-debugger-socket
|
// /data/data/org.mozilla.fennec/firefox-debugger-socket
|
||||||
const query = "cat /proc/net/unix";
|
const query = "cat /proc/net/unix";
|
||||||
const rawSocketInfo = await ADB.shell(query);
|
const rawSocketInfo = await shell(query);
|
||||||
|
|
||||||
// Filter to lines with "firefox-debugger-socket"
|
// Filter to lines with "firefox-debugger-socket"
|
||||||
let socketInfos = rawSocketInfo.split(/\r?\n/);
|
let socketInfos = rawSocketInfo.split(/\r?\n/);
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { RuntimeTypes } = require("devtools/client/webide/modules/runtime-types");
|
const { RuntimeTypes } = require("devtools/client/webide/modules/runtime-types");
|
||||||
const { ADB } = require("devtools/shared/adb/adb");
|
const { prepareTCPConnection } = require("devtools/shared/adb/commands/index");
|
||||||
|
|
||||||
class AdbRuntime {
|
class AdbRuntime {
|
||||||
constructor(adbDevice, model, socketPath) {
|
constructor(adbDevice, model, socketPath) {
|
||||||
@ -33,7 +33,7 @@ class AdbRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connect(connection) {
|
connect(connection) {
|
||||||
return ADB.prepareTCPConnection(this._socketPath).then(port => {
|
return prepareTCPConnection(this._socketPath).then(port => {
|
||||||
connection.host = "localhost";
|
connection.host = "localhost";
|
||||||
connection.port = port;
|
connection.port = port;
|
||||||
connection.connect();
|
connection.connect();
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
const EventEmitter = require("devtools/shared/event-emitter");
|
const EventEmitter = require("devtools/shared/event-emitter");
|
||||||
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
||||||
const { ADB } = require("devtools/shared/adb/adb");
|
const { ADB } = require("devtools/shared/adb/adb");
|
||||||
|
const { trackDevices } = require("devtools/shared/adb/commands/index");
|
||||||
const { adbDevicesRegistry } = require("devtools/shared/adb/adb-devices-registry");
|
const { adbDevicesRegistry } = require("devtools/shared/adb/adb-devices-registry");
|
||||||
const { AdbRuntime } = require("devtools/shared/adb/adb-runtime");
|
const { AdbRuntime } = require("devtools/shared/adb/adb-runtime");
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ class ADBScanner extends EventEmitter {
|
|||||||
adbDevicesRegistry.on("unregister", this._updateRuntimes);
|
adbDevicesRegistry.on("unregister", this._updateRuntimes);
|
||||||
|
|
||||||
ADB.start().then(() => {
|
ADB.start().then(() => {
|
||||||
ADB.trackDevices();
|
trackDevices();
|
||||||
});
|
});
|
||||||
this._updateRuntimes();
|
this._updateRuntimes();
|
||||||
}
|
}
|
||||||
|
@ -7,21 +7,17 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { Cc, Ci } = require("chrome");
|
const { Cc, Ci } = require("chrome");
|
||||||
const EventEmitter = require("devtools/shared/event-emitter");
|
|
||||||
const client = require("./adb-client");
|
|
||||||
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
||||||
const { getFileForBinary } = require("./adb-binary");
|
const { getFileForBinary } = require("./adb-binary");
|
||||||
const { setTimeout } = require("resource://gre/modules/Timer.jsm");
|
const { setTimeout } = require("resource://gre/modules/Timer.jsm");
|
||||||
const { Services } = require("resource://gre/modules/Services.jsm");
|
const { Services } = require("resource://gre/modules/Services.jsm");
|
||||||
const { ConnectionManager } = require("devtools/shared/client/connection-manager");
|
const { runCommand } = require("./commands/index");
|
||||||
loader.lazyRequireGetter(this, "check",
|
loader.lazyRequireGetter(this, "check",
|
||||||
"devtools/shared/adb/adb-running-checker", true);
|
"devtools/shared/adb/adb-running-checker", true);
|
||||||
|
|
||||||
let ready = false;
|
let ready = false;
|
||||||
let didRunInitially = false;
|
let didRunInitially = false;
|
||||||
|
|
||||||
const OKAY = 0x59414b4f;
|
|
||||||
|
|
||||||
const ADB = {
|
const ADB = {
|
||||||
get didRunInitially() {
|
get didRunInitially() {
|
||||||
return didRunInitially;
|
return didRunInitially;
|
||||||
@ -137,7 +133,7 @@ const ADB = {
|
|||||||
*/
|
*/
|
||||||
async kill() {
|
async kill() {
|
||||||
try {
|
try {
|
||||||
await this.runCommand("host:kill");
|
await runCommand("host:kill");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dumpn("Failed to send host:kill command");
|
dumpn("Failed to send host:kill command");
|
||||||
}
|
}
|
||||||
@ -145,280 +141,6 @@ const ADB = {
|
|||||||
this.ready = false;
|
this.ready = false;
|
||||||
this.didRunInitially = false;
|
this.didRunInitially = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Start tracking devices connecting and disconnecting from the host.
|
|
||||||
// We can't reuse runCommand here because we keep the socket alive.
|
|
||||||
// @return The socket used.
|
|
||||||
trackDevices() {
|
|
||||||
dumpn("trackDevices");
|
|
||||||
const socket = client.connect();
|
|
||||||
let waitForFirst = true;
|
|
||||||
const devices = {};
|
|
||||||
|
|
||||||
socket.s.onopen = function() {
|
|
||||||
dumpn("trackDevices onopen");
|
|
||||||
Services.obs.notifyObservers(null, "adb-track-devices-start");
|
|
||||||
const req = client.createRequest("host:track-devices");
|
|
||||||
socket.send(req);
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.onerror = function(event) {
|
|
||||||
dumpn("trackDevices onerror: " + event);
|
|
||||||
Services.obs.notifyObservers(null, "adb-track-devices-stop");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.onclose = function() {
|
|
||||||
dumpn("trackDevices onclose");
|
|
||||||
|
|
||||||
// Report all devices as disconnected
|
|
||||||
for (const dev in devices) {
|
|
||||||
devices[dev] = false;
|
|
||||||
EventEmitter.emit(ADB, "device-disconnected", dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.obs.notifyObservers(null, "adb-track-devices-stop");
|
|
||||||
|
|
||||||
// When we lose connection to the server,
|
|
||||||
// and the adb is still on, we most likely got our server killed
|
|
||||||
// by local adb. So we do try to reconnect to it.
|
|
||||||
setTimeout(function() { // Give some time to the new adb to start
|
|
||||||
if (ADB.ready) { // Only try to reconnect/restart if the add-on is still enabled
|
|
||||||
ADB.start().then(function() { // try to connect to the new local adb server
|
|
||||||
// or, spawn a new one
|
|
||||||
ADB.trackDevices(); // Re-track devices
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.ondata = function(event) {
|
|
||||||
dumpn("trackDevices ondata");
|
|
||||||
const data = event.data;
|
|
||||||
dumpn("length=" + data.byteLength);
|
|
||||||
const dec = new TextDecoder();
|
|
||||||
dumpn(dec.decode(new Uint8Array(data)).trim());
|
|
||||||
|
|
||||||
// check the OKAY or FAIL on first packet.
|
|
||||||
if (waitForFirst) {
|
|
||||||
if (!client.checkResponse(data, OKAY)) {
|
|
||||||
socket.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const packet = client.unpackPacket(data, !waitForFirst);
|
|
||||||
waitForFirst = false;
|
|
||||||
|
|
||||||
if (packet.data == "") {
|
|
||||||
// All devices got disconnected.
|
|
||||||
for (const dev in devices) {
|
|
||||||
devices[dev] = false;
|
|
||||||
EventEmitter.emit(ADB, "device-disconnected", dev);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// One line per device, each line being $DEVICE\t(offline|device)
|
|
||||||
const lines = packet.data.split("\n");
|
|
||||||
const newDev = {};
|
|
||||||
lines.forEach(function(line) {
|
|
||||||
if (line.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [dev, status] = line.split("\t");
|
|
||||||
newDev[dev] = status !== "offline";
|
|
||||||
});
|
|
||||||
// Check which device changed state.
|
|
||||||
for (const dev in newDev) {
|
|
||||||
if (devices[dev] != newDev[dev]) {
|
|
||||||
if (dev in devices || newDev[dev]) {
|
|
||||||
const topic = newDev[dev] ? "device-connected"
|
|
||||||
: "device-disconnected";
|
|
||||||
EventEmitter.emit(ADB, topic, dev);
|
|
||||||
}
|
|
||||||
devices[dev] = newDev[dev];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// Sends back an array of device names.
|
|
||||||
listDevices() {
|
|
||||||
dumpn("listDevices");
|
|
||||||
|
|
||||||
return this.runCommand("host:devices").then(
|
|
||||||
function onSuccess(data) {
|
|
||||||
const lines = data.split("\n");
|
|
||||||
const res = [];
|
|
||||||
lines.forEach(function(line) {
|
|
||||||
if (line.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const [ device ] = line.split("\t");
|
|
||||||
res.push(device);
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// sends adb forward localPort devicePort
|
|
||||||
forwardPort(localPort, devicePort) {
|
|
||||||
dumpn("forwardPort " + localPort + " -- " + devicePort);
|
|
||||||
// <host-prefix>:forward:<local>;<remote>
|
|
||||||
|
|
||||||
return this.runCommand("host:forward:" + localPort + ";" + devicePort)
|
|
||||||
.then(function onSuccess(data) {
|
|
||||||
return data;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Prepare TCP connection for provided socket path.
|
|
||||||
// The returned value is a port number of localhost for the connection.
|
|
||||||
async prepareTCPConnection(socketPath) {
|
|
||||||
const port = ConnectionManager.getFreeTCPPort();
|
|
||||||
const local = `tcp:${ port }`;
|
|
||||||
const remote = socketPath.startsWith("@")
|
|
||||||
? `localabstract:${ socketPath.substring(1) }`
|
|
||||||
: `localfilesystem:${ socketPath }`;
|
|
||||||
await this.forwardPort(local, remote);
|
|
||||||
return port;
|
|
||||||
},
|
|
||||||
|
|
||||||
// Run a shell command
|
|
||||||
async shell(command) {
|
|
||||||
let state;
|
|
||||||
let stdout = "";
|
|
||||||
|
|
||||||
dumpn("shell " + command);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const shutdown = function() {
|
|
||||||
dumpn("shell shutdown");
|
|
||||||
socket.close();
|
|
||||||
reject("BAD_RESPONSE");
|
|
||||||
};
|
|
||||||
|
|
||||||
const runFSM = function runFSM(data) {
|
|
||||||
dumpn("runFSM " + state);
|
|
||||||
let req;
|
|
||||||
let ignoreResponseCode = false;
|
|
||||||
switch (state) {
|
|
||||||
case "start":
|
|
||||||
state = "send-transport";
|
|
||||||
runFSM();
|
|
||||||
break;
|
|
||||||
case "send-transport":
|
|
||||||
req = client.createRequest("host:transport-any");
|
|
||||||
socket.send(req);
|
|
||||||
state = "wait-transport";
|
|
||||||
break;
|
|
||||||
case "wait-transport":
|
|
||||||
if (!client.checkResponse(data, OKAY)) {
|
|
||||||
shutdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state = "send-shell";
|
|
||||||
runFSM();
|
|
||||||
break;
|
|
||||||
case "send-shell":
|
|
||||||
req = client.createRequest("shell:" + command);
|
|
||||||
socket.send(req);
|
|
||||||
state = "rec-shell";
|
|
||||||
break;
|
|
||||||
case "rec-shell":
|
|
||||||
if (!client.checkResponse(data, OKAY)) {
|
|
||||||
shutdown();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
state = "decode-shell";
|
|
||||||
if (client.getBuffer(data).byteLength == 4) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ignoreResponseCode = true;
|
|
||||||
// eslint-disable-next-lined no-fallthrough
|
|
||||||
case "decode-shell":
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
const text = new Uint8Array(client.getBuffer(data),
|
|
||||||
ignoreResponseCode ? 4 : 0);
|
|
||||||
stdout += decoder.decode(text);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dumpn("shell Unexpected State: " + state);
|
|
||||||
reject("UNEXPECTED_STATE");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const socket = client.connect();
|
|
||||||
socket.s.onerror = function(event) {
|
|
||||||
dumpn("shell onerror");
|
|
||||||
reject("SOCKET_ERROR");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.onopen = function(event) {
|
|
||||||
dumpn("shell onopen");
|
|
||||||
state = "start";
|
|
||||||
runFSM();
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.onclose = function(event) {
|
|
||||||
resolve(stdout);
|
|
||||||
dumpn("shell onclose");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.ondata = function(event) {
|
|
||||||
dumpn("shell ondata");
|
|
||||||
runFSM(event.data);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Asynchronously runs an adb command.
|
|
||||||
// @param command The command as documented in
|
|
||||||
// http://androidxref.com/4.0.4/xref/system/core/adb/SERVICES.TXT
|
|
||||||
runCommand(command) {
|
|
||||||
dumpn("runCommand " + command);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!this.ready) {
|
|
||||||
setTimeout(function() {
|
|
||||||
reject("ADB_NOT_READY");
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const socket = client.connect();
|
|
||||||
|
|
||||||
socket.s.onopen = function() {
|
|
||||||
dumpn("runCommand onopen");
|
|
||||||
const req = client.createRequest(command);
|
|
||||||
socket.send(req);
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.onerror = function() {
|
|
||||||
dumpn("runCommand onerror");
|
|
||||||
reject("NETWORK_ERROR");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.onclose = function() {
|
|
||||||
dumpn("runCommand onclose");
|
|
||||||
};
|
|
||||||
|
|
||||||
socket.s.ondata = function(event) {
|
|
||||||
dumpn("runCommand ondata");
|
|
||||||
const data = event.data;
|
|
||||||
|
|
||||||
const packet = client.unpackPacket(data, false);
|
|
||||||
if (!client.checkResponse(data, OKAY)) {
|
|
||||||
socket.close();
|
|
||||||
dumpn("Error: " + packet.data);
|
|
||||||
reject("PROTOCOL_ERROR");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(packet.data);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.ADB = ADB;
|
exports.ADB = ADB;
|
||||||
|
19
devtools/shared/adb/commands/index.js
Normal file
19
devtools/shared/adb/commands/index.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { listDevices } = require("./list-devices");
|
||||||
|
const { prepareTCPConnection } = require("./prepare-tcp-connection");
|
||||||
|
const { runCommand } = require("./run-command");
|
||||||
|
const { shell } = require("./shell");
|
||||||
|
const { trackDevices } = require("./track-devices");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
listDevices,
|
||||||
|
prepareTCPConnection,
|
||||||
|
runCommand,
|
||||||
|
shell,
|
||||||
|
trackDevices,
|
||||||
|
};
|
31
devtools/shared/adb/commands/list-devices.js
Normal file
31
devtools/shared/adb/commands/list-devices.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
||||||
|
/**
|
||||||
|
* The listDevices command is currently unused in DevTools. We are keeping it while
|
||||||
|
* working on RemoteDebugging NG, in case it becomes needed later. We will remove it from
|
||||||
|
* the codebase if unused at the end of the project. See Bug 1511779.
|
||||||
|
*/
|
||||||
|
const listDevices = function() {
|
||||||
|
dumpn("listDevices");
|
||||||
|
|
||||||
|
return this.runCommand("host:devices").then(
|
||||||
|
function onSuccess(data) {
|
||||||
|
const lines = data.split("\n");
|
||||||
|
const res = [];
|
||||||
|
lines.forEach(function(line) {
|
||||||
|
if (line.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [ device ] = line.split("\t");
|
||||||
|
res.push(device);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
exports.listDevices = listDevices;
|
12
devtools/shared/adb/commands/moz.build
Normal file
12
devtools/shared/adb/commands/moz.build
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
DevToolsModules(
|
||||||
|
'index.js',
|
||||||
|
'list-devices.js',
|
||||||
|
'prepare-tcp-connection.js',
|
||||||
|
'run-command.js',
|
||||||
|
'shell.js',
|
||||||
|
'track-devices.js',
|
||||||
|
)
|
33
devtools/shared/adb/commands/prepare-tcp-connection.js
Normal file
33
devtools/shared/adb/commands/prepare-tcp-connection.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
||||||
|
const { ConnectionManager } = require("devtools/shared/client/connection-manager");
|
||||||
|
const { runCommand } = require("./run-command");
|
||||||
|
|
||||||
|
// sends adb forward localPort devicePort
|
||||||
|
const forwardPort = function(localPort, devicePort) {
|
||||||
|
dumpn("forwardPort " + localPort + " -- " + devicePort);
|
||||||
|
// <host-prefix>:forward:<local>;<remote>
|
||||||
|
|
||||||
|
return runCommand("host:forward:" + localPort + ";" + devicePort)
|
||||||
|
.then(function onSuccess(data) {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Prepare TCP connection for provided socket path.
|
||||||
|
// The returned value is a port number of localhost for the connection.
|
||||||
|
const prepareTCPConnection = async function(socketPath) {
|
||||||
|
const port = ConnectionManager.getFreeTCPPort();
|
||||||
|
const local = `tcp:${ port }`;
|
||||||
|
const remote = socketPath.startsWith("@")
|
||||||
|
? `localabstract:${ socketPath.substring(1) }`
|
||||||
|
: `localfilesystem:${ socketPath }`;
|
||||||
|
await forwardPort(local, remote);
|
||||||
|
return port;
|
||||||
|
};
|
||||||
|
exports.prepareTCPConnection = prepareTCPConnection;
|
62
devtools/shared/adb/commands/run-command.js
Normal file
62
devtools/shared/adb/commands/run-command.js
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// Wrapper around the ADB utility.
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
||||||
|
const { setTimeout } = require("resource://gre/modules/Timer.jsm");
|
||||||
|
const { ADB } = require("../adb");
|
||||||
|
const client = require("../adb-client");
|
||||||
|
|
||||||
|
const OKAY = 0x59414b4f;
|
||||||
|
|
||||||
|
// Asynchronously runs an adb command.
|
||||||
|
// @param command The command as documented in
|
||||||
|
// http://androidxref.com/4.0.4/xref/system/core/adb/SERVICES.TXT
|
||||||
|
const runCommand = function(command) {
|
||||||
|
dumpn("runCommand " + command);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!ADB.ready) {
|
||||||
|
setTimeout(function() {
|
||||||
|
reject("ADB_NOT_READY");
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const socket = client.connect();
|
||||||
|
|
||||||
|
socket.s.onopen = function() {
|
||||||
|
dumpn("runCommand onopen");
|
||||||
|
const req = client.createRequest(command);
|
||||||
|
socket.send(req);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.onerror = function() {
|
||||||
|
dumpn("runCommand onerror");
|
||||||
|
reject("NETWORK_ERROR");
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.onclose = function() {
|
||||||
|
dumpn("runCommand onclose");
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.ondata = function(event) {
|
||||||
|
dumpn("runCommand ondata");
|
||||||
|
const data = event.data;
|
||||||
|
|
||||||
|
const packet = client.unpackPacket(data, false);
|
||||||
|
if (!client.checkResponse(data, OKAY)) {
|
||||||
|
socket.close();
|
||||||
|
dumpn("Error: " + packet.data);
|
||||||
|
reject("PROTOCOL_ERROR");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(packet.data);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.runCommand = runCommand;
|
101
devtools/shared/adb/commands/shell.js
Normal file
101
devtools/shared/adb/commands/shell.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// Wrapper around the ADB utility.
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
||||||
|
const client = require("../adb-client");
|
||||||
|
|
||||||
|
const OKAY = 0x59414b4f;
|
||||||
|
|
||||||
|
const shell = async function(command) {
|
||||||
|
let state;
|
||||||
|
let stdout = "";
|
||||||
|
|
||||||
|
dumpn("shell " + command);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const shutdown = function() {
|
||||||
|
dumpn("shell shutdown");
|
||||||
|
socket.close();
|
||||||
|
reject("BAD_RESPONSE");
|
||||||
|
};
|
||||||
|
|
||||||
|
const runFSM = function runFSM(data) {
|
||||||
|
dumpn("runFSM " + state);
|
||||||
|
let req;
|
||||||
|
let ignoreResponseCode = false;
|
||||||
|
switch (state) {
|
||||||
|
case "start":
|
||||||
|
state = "send-transport";
|
||||||
|
runFSM();
|
||||||
|
break;
|
||||||
|
case "send-transport":
|
||||||
|
req = client.createRequest("host:transport-any");
|
||||||
|
socket.send(req);
|
||||||
|
state = "wait-transport";
|
||||||
|
break;
|
||||||
|
case "wait-transport":
|
||||||
|
if (!client.checkResponse(data, OKAY)) {
|
||||||
|
shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = "send-shell";
|
||||||
|
runFSM();
|
||||||
|
break;
|
||||||
|
case "send-shell":
|
||||||
|
req = client.createRequest("shell:" + command);
|
||||||
|
socket.send(req);
|
||||||
|
state = "rec-shell";
|
||||||
|
break;
|
||||||
|
case "rec-shell":
|
||||||
|
if (!client.checkResponse(data, OKAY)) {
|
||||||
|
shutdown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = "decode-shell";
|
||||||
|
if (client.getBuffer(data).byteLength == 4) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ignoreResponseCode = true;
|
||||||
|
// eslint-disable-next-lined no-fallthrough
|
||||||
|
case "decode-shell":
|
||||||
|
const decoder = new TextDecoder();
|
||||||
|
const text = new Uint8Array(client.getBuffer(data),
|
||||||
|
ignoreResponseCode ? 4 : 0);
|
||||||
|
stdout += decoder.decode(text);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dumpn("shell Unexpected State: " + state);
|
||||||
|
reject("UNEXPECTED_STATE");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const socket = client.connect();
|
||||||
|
socket.s.onerror = function(event) {
|
||||||
|
dumpn("shell onerror");
|
||||||
|
reject("SOCKET_ERROR");
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.onopen = function(event) {
|
||||||
|
dumpn("shell onopen");
|
||||||
|
state = "start";
|
||||||
|
runFSM();
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.onclose = function(event) {
|
||||||
|
resolve(stdout);
|
||||||
|
dumpn("shell onclose");
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.ondata = function(event) {
|
||||||
|
dumpn("shell ondata");
|
||||||
|
runFSM(event.data);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.shell = shell;
|
114
devtools/shared/adb/commands/track-devices.js
Normal file
114
devtools/shared/adb/commands/track-devices.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// Wrapper around the ADB utility.
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const EventEmitter = require("devtools/shared/event-emitter");
|
||||||
|
const { dumpn } = require("devtools/shared/DevToolsUtils");
|
||||||
|
const { setTimeout } = require("resource://gre/modules/Timer.jsm");
|
||||||
|
const { Services } = require("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
const { ADB } = require("../adb");
|
||||||
|
const client = require("../adb-client");
|
||||||
|
|
||||||
|
const OKAY = 0x59414b4f;
|
||||||
|
|
||||||
|
// Start tracking devices connecting and disconnecting from the host.
|
||||||
|
// We can't reuse runCommand here because we keep the socket alive.
|
||||||
|
// @return The socket used.
|
||||||
|
const trackDevices = function() {
|
||||||
|
dumpn("trackDevices");
|
||||||
|
const socket = client.connect();
|
||||||
|
let waitForFirst = true;
|
||||||
|
const devices = {};
|
||||||
|
|
||||||
|
socket.s.onopen = function() {
|
||||||
|
dumpn("trackDevices onopen");
|
||||||
|
Services.obs.notifyObservers(null, "adb-track-devices-start");
|
||||||
|
const req = client.createRequest("host:track-devices");
|
||||||
|
socket.send(req);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.onerror = function(event) {
|
||||||
|
dumpn("trackDevices onerror: " + event);
|
||||||
|
Services.obs.notifyObservers(null, "adb-track-devices-stop");
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.onclose = function() {
|
||||||
|
dumpn("trackDevices onclose");
|
||||||
|
|
||||||
|
// Report all devices as disconnected
|
||||||
|
for (const dev in devices) {
|
||||||
|
devices[dev] = false;
|
||||||
|
EventEmitter.emit(ADB, "device-disconnected", dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.obs.notifyObservers(null, "adb-track-devices-stop");
|
||||||
|
|
||||||
|
// When we lose connection to the server,
|
||||||
|
// and the adb is still on, we most likely got our server killed
|
||||||
|
// by local adb. So we do try to reconnect to it.
|
||||||
|
setTimeout(function() { // Give some time to the new adb to start
|
||||||
|
if (ADB.ready) { // Only try to reconnect/restart if the add-on is still enabled
|
||||||
|
ADB.start().then(function() { // try to connect to the new local adb server
|
||||||
|
// or, spawn a new one
|
||||||
|
trackDevices(); // Re-track devices
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.s.ondata = function(event) {
|
||||||
|
dumpn("trackDevices ondata");
|
||||||
|
const data = event.data;
|
||||||
|
dumpn("length=" + data.byteLength);
|
||||||
|
const dec = new TextDecoder();
|
||||||
|
dumpn(dec.decode(new Uint8Array(data)).trim());
|
||||||
|
|
||||||
|
// check the OKAY or FAIL on first packet.
|
||||||
|
if (waitForFirst) {
|
||||||
|
if (!client.checkResponse(data, OKAY)) {
|
||||||
|
socket.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const packet = client.unpackPacket(data, !waitForFirst);
|
||||||
|
waitForFirst = false;
|
||||||
|
|
||||||
|
if (packet.data == "") {
|
||||||
|
// All devices got disconnected.
|
||||||
|
for (const dev in devices) {
|
||||||
|
devices[dev] = false;
|
||||||
|
EventEmitter.emit(ADB, "device-disconnected", dev);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// One line per device, each line being $DEVICE\t(offline|device)
|
||||||
|
const lines = packet.data.split("\n");
|
||||||
|
const newDev = {};
|
||||||
|
lines.forEach(function(line) {
|
||||||
|
if (line.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [dev, status] = line.split("\t");
|
||||||
|
newDev[dev] = status !== "offline";
|
||||||
|
});
|
||||||
|
// Check which device changed state.
|
||||||
|
for (const dev in newDev) {
|
||||||
|
if (devices[dev] != newDev[dev]) {
|
||||||
|
if (dev in devices || newDev[dev]) {
|
||||||
|
const topic = newDev[dev] ? "device-connected"
|
||||||
|
: "device-disconnected";
|
||||||
|
EventEmitter.emit(ADB, topic, dev);
|
||||||
|
}
|
||||||
|
devices[dev] = newDev[dev];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
exports.trackDevices = trackDevices;
|
@ -2,6 +2,10 @@
|
|||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
DIRS += [
|
||||||
|
'commands',
|
||||||
|
]
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
'adb-addon.js',
|
'adb-addon.js',
|
||||||
'adb-binary.js',
|
'adb-binary.js',
|
||||||
|
@ -9,6 +9,7 @@ const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
|
|||||||
const { getFileForBinary } = require("devtools/shared/adb/adb-binary");
|
const { getFileForBinary } = require("devtools/shared/adb/adb-binary");
|
||||||
const { check } = require("devtools/shared/adb/adb-running-checker");
|
const { check } = require("devtools/shared/adb/adb-running-checker");
|
||||||
const { ADB } = require("devtools/shared/adb/adb");
|
const { ADB } = require("devtools/shared/adb/adb");
|
||||||
|
const { trackDevices } = require("devtools/shared/adb/commands/index");
|
||||||
|
|
||||||
const ADB_JSON = {
|
const ADB_JSON = {
|
||||||
"Linux": {
|
"Linux": {
|
||||||
@ -229,7 +230,7 @@ add_task({
|
|||||||
EventEmitter.on(ADB, "device-connected", deviceId => {
|
EventEmitter.on(ADB, "device-connected", deviceId => {
|
||||||
resolve(deviceId);
|
resolve(deviceId);
|
||||||
});
|
});
|
||||||
ADB.trackDevices();
|
trackDevices();
|
||||||
});
|
});
|
||||||
|
|
||||||
equal(receivedDeviceId, "1234567890");
|
equal(receivedDeviceId, "1234567890");
|
||||||
|
Loading…
Reference in New Issue
Block a user