/* 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;