Source code
Revision control
Copy as Markdown
Other Tools
/* 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
"use strict";
const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
const {
getFileForBinary,
} = require("resource://devtools/client/shared/remote-debugging/adb/adb-binary.js");
const { setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
loader.lazyRequireGetter(
this,
"runCommand",
"resource://devtools/client/shared/remote-debugging/adb/commands/index.js",
true
);
loader.lazyRequireGetter(
this,
"check",
"resource://devtools/client/shared/remote-debugging/adb/adb-running-checker.js",
true
);
// Waits until a predicate returns true or re-tries the predicate calls
// |retry| times, we wait for 100ms between each calls.
async function waitUntil(predicate, retry = 20) {
let count = 0;
while (count++ < retry) {
if (await predicate()) {
return true;
}
// Wait for 100 milliseconds.
await new Promise(resolve => setTimeout(resolve, 100));
}
// Timed out after trying too many times.
return false;
}
// Class representing the ADB process.
class AdbProcess extends EventEmitter {
constructor() {
super();
this._ready = false;
this._didRunInitially = false;
}
get ready() {
return this._ready;
}
_getAdbFile() {
if (this._adbFilePromise) {
return this._adbFilePromise;
}
this._adbFilePromise = getFileForBinary();
return this._adbFilePromise;
}
async _runProcess(process, params) {
return new Promise((resolve, reject) => {
process.runAsync(
params,
params.length,
{
observe(subject, topic) {
switch (topic) {
case "process-finished":
resolve();
break;
case "process-failed":
reject();
break;
}
},
},
false
);
});
}
// We startup by launching adb in server mode, and setting
// the tcp socket preference to |true|
async start() {
const onSuccessfulStart = () => {
this._ready = true;
this.emit("adb-ready");
};
const isAdbRunning = await check();
if (isAdbRunning) {
dumpn("Found ADB process running, not restarting");
onSuccessfulStart();
return;
}
dumpn("Didn't find ADB process running, restarting");
this._didRunInitially = true;
const process = Cc["@mozilla.org/process/util;1"].createInstance(
Ci.nsIProcess
);
const adbFile = await this._getAdbFile();
process.init(adbFile);
// Hide command prompt window on Windows
process.startHidden = true;
process.noShell = true;
const params = ["start-server"];
let isStarted = false;
try {
await this._runProcess(process, params);
isStarted = await waitUntil(check);
} catch (e) {}
if (isStarted) {
onSuccessfulStart();
} else {
this._ready = false;
throw new Error("ADB Process didn't start");
}
}
/**
* Stop the ADB server, but only if we started it. If it was started before
* us, we return immediately.
*/
async stop() {
if (!this._didRunInitially) {
return; // We didn't start the server, nothing to do
}
await this.kill();
}
/**
* Kill the ADB server.
*/
async kill() {
try {
await runCommand("host:kill");
} catch (e) {
dumpn("Failed to send host:kill command");
}
dumpn("adb server was terminated by host:kill");
this._ready = false;
this._didRunInitially = false;
}
}
exports.adbProcess = new AdbProcess();