summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/remote-debugging/adb/commands
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /devtools/client/shared/remote-debugging/adb/commands
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/shared/remote-debugging/adb/commands')
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/index.js29
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/list-devices.js29
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/moz.build12
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js46
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/run-command.js66
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/shell.js107
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/track-devices.js163
7 files changed, 452 insertions, 0 deletions
diff --git a/devtools/client/shared/remote-debugging/adb/commands/index.js b/devtools/client/shared/remote-debugging/adb/commands/index.js
new file mode 100644
index 0000000000..1032f1a4b8
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/index.js
@@ -0,0 +1,29 @@
+/* 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("resource://devtools/client/shared/remote-debugging/adb/commands/list-devices.js");
+const {
+ prepareTCPConnection,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js");
+const {
+ runCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/run-command.js");
+const {
+ shell,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/shell.js");
+const {
+ TrackDevicesCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/track-devices.js");
+
+module.exports = {
+ listDevices,
+ prepareTCPConnection,
+ runCommand,
+ shell,
+ TrackDevicesCommand,
+};
diff --git a/devtools/client/shared/remote-debugging/adb/commands/list-devices.js b/devtools/client/shared/remote-debugging/adb/commands/list-devices.js
new file mode 100644
index 0000000000..c04ba9eb1e
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/list-devices.js
@@ -0,0 +1,29 @@
+/* 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("resource://devtools/shared/DevToolsUtils.js");
+/**
+ * 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) {
+ return;
+ }
+ const [device] = line.split("\t");
+ res.push(device);
+ });
+ return res;
+ });
+};
+exports.listDevices = listDevices;
diff --git a/devtools/client/shared/remote-debugging/adb/commands/moz.build b/devtools/client/shared/remote-debugging/adb/commands/moz.build
new file mode 100644
index 0000000000..ecd3428a1b
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/moz.build
@@ -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",
+)
diff --git a/devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js b/devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js
new file mode 100644
index 0000000000..dc9103f56b
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js
@@ -0,0 +1,46 @@
+/* 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("resource://devtools/shared/DevToolsUtils.js");
+const {
+ runCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/run-command.js");
+
+// sends adb forward deviceId, localPort and devicePort
+const forwardPort = function (deviceId, localPort, devicePort) {
+ dumpn("forwardPort " + localPort + " -- " + devicePort);
+ // Send "host-serial:<serial-number>:<request>",
+ // with <request> set to "forward:<local>;<remote>"
+ // See https://android.googlesource.com/platform/system/core/+/jb-dev/adb/SERVICES.TXT
+ return runCommand(
+ `host-serial:${deviceId}:forward:${localPort};${devicePort}`
+ ).then(function onSuccess(data) {
+ return data;
+ });
+};
+
+const getFreeTCPPort = function () {
+ const serv = Cc["@mozilla.org/network/server-socket;1"].createInstance(
+ Ci.nsIServerSocket
+ );
+ serv.init(-1, true, -1);
+ const port = serv.port;
+ serv.close();
+ return port;
+};
+
+// Prepare TCP connection for provided device id and socket path.
+// The returned value is a port number of localhost for the connection.
+const prepareTCPConnection = async function (deviceId, socketPath) {
+ const port = getFreeTCPPort();
+ const local = `tcp:${port}`;
+ const remote = socketPath.startsWith("@")
+ ? `localabstract:${socketPath.substring(1)}`
+ : `localfilesystem:${socketPath}`;
+ await forwardPort(deviceId, local, remote);
+ return port;
+};
+exports.prepareTCPConnection = prepareTCPConnection;
diff --git a/devtools/client/shared/remote-debugging/adb/commands/run-command.js b/devtools/client/shared/remote-debugging/adb/commands/run-command.js
new file mode 100644
index 0000000000..5fc521b5e7
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/run-command.js
@@ -0,0 +1,66 @@
+/* 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("resource://devtools/shared/DevToolsUtils.js");
+const { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+const {
+ adbProcess,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
+const client = require("resource://devtools/client/shared/remote-debugging/adb/adb-client.js");
+
+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 (!adbProcess.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;
diff --git a/devtools/client/shared/remote-debugging/adb/commands/shell.js b/devtools/client/shared/remote-debugging/adb/commands/shell.js
new file mode 100644
index 0000000000..03f4bfcf78
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/shell.js
@@ -0,0 +1,107 @@
+/* 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("resource://devtools/shared/DevToolsUtils.js");
+const client = require("resource://devtools/client/shared/remote-debugging/adb/adb-client.js");
+
+const OKAY = 0x59414b4f;
+
+const shell = async function (deviceId, command) {
+ if (!deviceId) {
+ throw new Error("ADB shell command needs the device id");
+ }
+
+ let state;
+ let stdout = "";
+
+ dumpn("shell " + command + " on " + deviceId);
+
+ 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:" + deviceId);
+ 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;
diff --git a/devtools/client/shared/remote-debugging/adb/commands/track-devices.js b/devtools/client/shared/remote-debugging/adb/commands/track-devices.js
new file mode 100644
index 0000000000..2d796668ea
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/track-devices.js
@@ -0,0 +1,163 @@
+/* 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("resource://devtools/shared/event-emitter.js");
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+const { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+const {
+ adbProcess,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
+const client = require("resource://devtools/client/shared/remote-debugging/adb/adb-client.js");
+
+const ADB_STATUS_OFFLINE = "offline";
+const OKAY = 0x59414b4f;
+
+// Start tracking devices connecting and disconnecting from the host.
+// We can't reuse runCommand here because we keep the socket alive.
+class TrackDevicesCommand extends EventEmitter {
+ run() {
+ this._waitForFirst = true;
+ // Hold device statuses. key: device id, value: status.
+ this._devices = new Map();
+ this._socket = client.connect();
+
+ this._socket.s.onopen = this._onOpen.bind(this);
+ this._socket.s.onerror = this._onError.bind(this);
+ this._socket.s.onclose = this._onClose.bind(this);
+ this._socket.s.ondata = this._onData.bind(this);
+ }
+
+ stop() {
+ if (this._socket) {
+ this._socket.close();
+
+ this._socket.s.onopen = null;
+ this._socket.s.onerror = null;
+ this._socket.s.onclose = null;
+ this._socket.s.ondata = null;
+ }
+ }
+
+ _onOpen() {
+ dumpn("trackDevices onopen");
+ const req = client.createRequest("host:track-devices");
+ this._socket.send(req);
+ }
+
+ _onError(event) {
+ dumpn("trackDevices onerror: " + event);
+ }
+
+ _onClose() {
+ dumpn("trackDevices onclose");
+
+ // Report all devices as disconnected
+ this._disconnectAllDevices();
+
+ // 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.
+
+ // Give some time to the new adb to start
+ setTimeout(() => {
+ // Only try to reconnect/restart if the add-on is still enabled
+ if (adbProcess.ready) {
+ // try to connect to the new local adb server or spawn a new one
+ adbProcess.start().then(() => {
+ // Re-track devices
+ this.run();
+ });
+ }
+ }, 2000);
+ }
+
+ _onData(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 (this._waitForFirst) {
+ if (!client.checkResponse(data, OKAY)) {
+ this._socket.close();
+ return;
+ }
+ }
+
+ const packet = client.unpackPacket(data, !this._waitForFirst);
+ this._waitForFirst = false;
+
+ if (packet.data == "") {
+ // All devices got disconnected.
+ this._disconnectAllDevices();
+ } else {
+ // One line per device, each line being $DEVICE\t(offline|device)
+ const lines = packet.data.split("\n");
+ const newDevices = new Map();
+ lines.forEach(function (line) {
+ if (!line.length) {
+ return;
+ }
+
+ const [deviceId, status] = line.split("\t");
+ newDevices.set(deviceId, status);
+ });
+
+ // Fire events if needed.
+ const deviceIds = new Set([
+ ...this._devices.keys(),
+ ...newDevices.keys(),
+ ]);
+ for (const deviceId of deviceIds) {
+ const currentStatus = this._devices.get(deviceId);
+ const newStatus = newDevices.get(deviceId);
+ this._fireConnectionEventIfNeeded(deviceId, currentStatus, newStatus);
+ }
+
+ // Update devices.
+ this._devices = newDevices;
+ }
+ }
+
+ _disconnectAllDevices() {
+ if (this._devices.size === 0) {
+ // If no devices were detected, fire an event to let consumer resume.
+ this.emit("no-devices-detected");
+ } else {
+ for (const [deviceId, status] of this._devices.entries()) {
+ if (status !== ADB_STATUS_OFFLINE) {
+ this.emit("device-disconnected", deviceId);
+ }
+ }
+ }
+ this._devices = new Map();
+ }
+
+ _fireConnectionEventIfNeeded(deviceId, currentStatus, newStatus) {
+ const isCurrentOnline = !!(
+ currentStatus && currentStatus !== ADB_STATUS_OFFLINE
+ );
+ const isNewOnline = !!(newStatus && newStatus !== ADB_STATUS_OFFLINE);
+
+ if (isCurrentOnline === isNewOnline) {
+ return;
+ }
+
+ if (isNewOnline) {
+ this.emit("device-connected", deviceId);
+ } else {
+ this.emit("device-disconnected", deviceId);
+ }
+ }
+}
+exports.TrackDevicesCommand = TrackDevicesCommand;