summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/remote-debugging/adb/commands/shell.js
blob: e891ab218d5d899c9d700ccdc9558e8231b9162c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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 () {
      dumpn("shell onerror");
      reject("SOCKET_ERROR");
    };

    socket.s.onopen = function () {
      dumpn("shell onopen");
      state = "start";
      runFSM();
    };

    socket.s.onclose = function () {
      resolve(stdout);
      dumpn("shell onclose");
    };

    socket.s.ondata = function (event) {
      dumpn("shell ondata");
      runFSM(event.data);
    };
  });
};

exports.shell = shell;