tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

track-devices.js (5068B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // Wrapper around the ADB utility.
      6 
      7 "use strict";
      8 
      9 const EventEmitter = require("resource://devtools/shared/event-emitter.js");
     10 const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
     11 const { setTimeout } = ChromeUtils.importESModule(
     12  "resource://gre/modules/Timer.sys.mjs"
     13 );
     14 
     15 const {
     16  adbProcess,
     17 } = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
     18 const client = require("resource://devtools/client/shared/remote-debugging/adb/adb-client.js");
     19 
     20 // All states considered as valid online states for ADB.
     21 // Per https://android.googlesource.com/platform/packages/modules/adb/+/a8ab5ceccb41035804ad83bfa625adac4a8ccf83/adb.h#127
     22 const ONLINE_STATES = new Set([
     23  "bootloader",
     24  "device",
     25  "host",
     26  "recovery",
     27  "rescue",
     28  "sideload",
     29 ]);
     30 
     31 const OKAY = 0x59414b4f;
     32 
     33 // Start tracking devices connecting and disconnecting from the host.
     34 // We can't reuse runCommand here because we keep the socket alive.
     35 class TrackDevicesCommand extends EventEmitter {
     36  run() {
     37    this._waitForFirst = true;
     38    // Hold device statuses. key: device id, value: status.
     39    this._devices = new Map();
     40    this._socket = client.connect();
     41 
     42    this._socket.s.onopen = this._onOpen.bind(this);
     43    this._socket.s.onerror = this._onError.bind(this);
     44    this._socket.s.onclose = this._onClose.bind(this);
     45    this._socket.s.ondata = this._onData.bind(this);
     46  }
     47 
     48  stop() {
     49    if (this._socket) {
     50      this._socket.close();
     51 
     52      this._socket.s.onopen = null;
     53      this._socket.s.onerror = null;
     54      this._socket.s.onclose = null;
     55      this._socket.s.ondata = null;
     56    }
     57  }
     58 
     59  _onOpen() {
     60    dumpn("trackDevices onopen");
     61    const req = client.createRequest("host:track-devices");
     62    this._socket.send(req);
     63  }
     64 
     65  _onError(event) {
     66    dumpn("trackDevices onerror: " + event);
     67  }
     68 
     69  _onClose() {
     70    dumpn("trackDevices onclose");
     71 
     72    // Report all devices as disconnected
     73    this._disconnectAllDevices();
     74 
     75    // When we lose connection to the server,
     76    // and the adb is still on, we most likely got our server killed
     77    // by local adb. So we do try to reconnect to it.
     78 
     79    // Give some time to the new adb to start
     80    setTimeout(() => {
     81      // Only try to reconnect/restart if the add-on is still enabled
     82      if (adbProcess.ready) {
     83        // try to connect to the new local adb server or spawn a new one
     84        adbProcess.start().then(() => {
     85          // Re-track devices
     86          this.run();
     87        });
     88      }
     89    }, 2000);
     90  }
     91 
     92  _onData(event) {
     93    dumpn("trackDevices ondata");
     94    const data = event.data;
     95    dumpn("length=" + data.byteLength);
     96    const dec = new TextDecoder();
     97    dumpn(dec.decode(new Uint8Array(data)).trim());
     98 
     99    // check the OKAY or FAIL on first packet.
    100    if (this._waitForFirst) {
    101      if (!client.checkResponse(data, OKAY)) {
    102        this._socket.close();
    103        return;
    104      }
    105    }
    106 
    107    const packet = client.unpackPacket(data, !this._waitForFirst);
    108    this._waitForFirst = false;
    109 
    110    if (packet.data == "") {
    111      // All devices got disconnected.
    112      this._disconnectAllDevices();
    113    } else {
    114      // One line per device, each line being $DEVICE\t(offline|device)
    115      const lines = packet.data.split("\n");
    116      const newDevices = new Map();
    117      lines.forEach(function (line) {
    118        if (!line.length) {
    119          return;
    120        }
    121 
    122        const [deviceId, status] = line.split("\t");
    123        newDevices.set(deviceId, status);
    124      });
    125 
    126      // Fire events if needed.
    127      const deviceIds = new Set([
    128        ...this._devices.keys(),
    129        ...newDevices.keys(),
    130      ]);
    131      for (const deviceId of deviceIds) {
    132        const currentStatus = this._devices.get(deviceId);
    133        const newStatus = newDevices.get(deviceId);
    134        this._fireConnectionEventIfNeeded(deviceId, currentStatus, newStatus);
    135      }
    136 
    137      // Update devices.
    138      this._devices = newDevices;
    139    }
    140  }
    141 
    142  _disconnectAllDevices() {
    143    if (this._devices.size === 0) {
    144      // If no devices were detected, fire an event to let consumer resume.
    145      this.emit("no-devices-detected");
    146    } else {
    147      for (const [deviceId, status] of this._devices.entries()) {
    148        if (!this._isStatusOnline(status)) {
    149          this.emit("device-disconnected", deviceId);
    150        }
    151      }
    152    }
    153    this._devices = new Map();
    154  }
    155 
    156  _fireConnectionEventIfNeeded(deviceId, currentStatus, newStatus) {
    157    const isCurrentOnline = this._isStatusOnline(currentStatus);
    158    const isNewOnline = this._isStatusOnline(newStatus);
    159 
    160    if (isCurrentOnline === isNewOnline) {
    161      return;
    162    }
    163 
    164    if (isNewOnline) {
    165      this.emit("device-connected", deviceId);
    166    } else {
    167      this.emit("device-disconnected", deviceId);
    168    }
    169  }
    170 
    171  _isStatusOnline(status) {
    172    return ONLINE_STATES.has(status);
    173  }
    174 }
    175 exports.TrackDevicesCommand = TrackDevicesCommand;