summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/remote-debugging/adb/adb.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/remote-debugging/adb/adb.js')
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb.js176
1 files changed, 176 insertions, 0 deletions
diff --git a/devtools/client/shared/remote-debugging/adb/adb.js b/devtools/client/shared/remote-debugging/adb/adb.js
new file mode 100644
index 0000000000..b4c2a9377f
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb.js
@@ -0,0 +1,176 @@
+/* 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 { clearInterval, setInterval } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const {
+ adbProcess,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
+const {
+ adbAddon,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-addon.js");
+const AdbDevice = require("resource://devtools/client/shared/remote-debugging/adb/adb-device.js");
+const {
+ AdbRuntime,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-runtime.js");
+const {
+ TrackDevicesCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/track-devices.js");
+loader.lazyRequireGetter(
+ this,
+ "check",
+ "resource://devtools/client/shared/remote-debugging/adb/adb-running-checker.js",
+ true
+);
+
+// Duration in milliseconds of the runtime polling. We resort to polling here because we
+// have no event to know when a runtime started on an already discovered ADB device.
+const UPDATE_RUNTIMES_INTERVAL = 3000;
+
+class Adb extends EventEmitter {
+ constructor() {
+ super();
+
+ this._trackDevicesCommand = new TrackDevicesCommand();
+
+ this._isTrackingDevices = false;
+ this._isUpdatingRuntimes = false;
+
+ this._listeners = new Set();
+ this._devices = new Map();
+ this._runtimes = [];
+
+ this._updateAdbProcess = this._updateAdbProcess.bind(this);
+ this._onDeviceConnected = this._onDeviceConnected.bind(this);
+ this._onDeviceDisconnected = this._onDeviceDisconnected.bind(this);
+ this._onNoDevicesDetected = this._onNoDevicesDetected.bind(this);
+
+ this._trackDevicesCommand.on("device-connected", this._onDeviceConnected);
+ this._trackDevicesCommand.on(
+ "device-disconnected",
+ this._onDeviceDisconnected
+ );
+ this._trackDevicesCommand.on(
+ "no-devices-detected",
+ this._onNoDevicesDetected
+ );
+ adbAddon.on("update", this._updateAdbProcess);
+ }
+
+ registerListener(listener) {
+ this._listeners.add(listener);
+ this.on("runtime-list-updated", listener);
+ this._updateAdbProcess();
+ }
+
+ unregisterListener(listener) {
+ this._listeners.delete(listener);
+ this.off("runtime-list-updated", listener);
+ this._updateAdbProcess();
+ }
+
+ async updateRuntimes() {
+ try {
+ const devices = [...this._devices.values()];
+ const promises = devices.map(d => this._getDeviceRuntimes(d));
+ const allRuntimes = await Promise.all(promises);
+
+ this._runtimes = allRuntimes.flat();
+ this.emit("runtime-list-updated");
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
+ getRuntimes() {
+ return this._runtimes;
+ }
+
+ getDevices() {
+ return [...this._devices.values()];
+ }
+
+ async isProcessStarted() {
+ return check();
+ }
+
+ async _startTracking() {
+ this._isTrackingDevices = true;
+ await adbProcess.start();
+
+ this._trackDevicesCommand.run();
+
+ // Device runtimes are detected by running a shell command and checking for
+ // "firefox-debugger-socket" in the list of currently running processes.
+ this._timer = setInterval(
+ this.updateRuntimes.bind(this),
+ UPDATE_RUNTIMES_INTERVAL
+ );
+ }
+
+ async _stopTracking() {
+ clearInterval(this._timer);
+ this._isTrackingDevices = false;
+ this._trackDevicesCommand.stop();
+
+ this._devices = new Map();
+ this._runtimes = [];
+ this.updateRuntimes();
+ }
+
+ _shouldTrack() {
+ return adbAddon.status === "installed" && this._listeners.size > 0;
+ }
+
+ /**
+ * This method will emit "runtime-list-ready" to notify the consumer that the list of
+ * runtimes is ready to be retrieved.
+ */
+ async _updateAdbProcess() {
+ if (!this._isTrackingDevices && this._shouldTrack()) {
+ const onRuntimesUpdated = this.once("runtime-list-updated");
+ this._startTracking();
+ // If we are starting to track runtimes, the list of runtimes will only be ready
+ // once the first "runtime-list-updated" event has been processed.
+ await onRuntimesUpdated;
+ } else if (this._isTrackingDevices && !this._shouldTrack()) {
+ this._stopTracking();
+ }
+ this.emit("runtime-list-ready");
+ }
+
+ async _onDeviceConnected(deviceId) {
+ const adbDevice = new AdbDevice(deviceId);
+ await adbDevice.initialize();
+ this._devices.set(deviceId, adbDevice);
+ this.updateRuntimes();
+ }
+
+ _onDeviceDisconnected(deviceId) {
+ this._devices.delete(deviceId);
+ this.updateRuntimes();
+ }
+
+ _onNoDevicesDetected() {
+ this.updateRuntimes();
+ }
+
+ async _getDeviceRuntimes(device) {
+ const socketPaths = [...(await device.getRuntimeSocketPaths())];
+ const runtimes = [];
+ for (const socketPath of socketPaths) {
+ const runtime = new AdbRuntime(device, socketPath);
+ await runtime.init();
+ runtimes.push(runtime);
+ }
+ return runtimes;
+ }
+}
+
+exports.adb = new Adb();