adb-process.js (3932B)
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 "use strict"; 6 7 const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js"); 8 const EventEmitter = require("resource://devtools/shared/event-emitter.js"); 9 const { 10 getFileForBinary, 11 } = require("resource://devtools/client/shared/remote-debugging/adb/adb-binary.js"); 12 const { setTimeout } = ChromeUtils.importESModule( 13 "resource://gre/modules/Timer.sys.mjs" 14 ); 15 16 loader.lazyRequireGetter( 17 this, 18 "runCommand", 19 "resource://devtools/client/shared/remote-debugging/adb/commands/index.js", 20 true 21 ); 22 loader.lazyRequireGetter( 23 this, 24 "check", 25 "resource://devtools/client/shared/remote-debugging/adb/adb-running-checker.js", 26 true 27 ); 28 29 // Waits until a predicate returns true or re-tries the predicate calls 30 // |retry| times, we wait for 100ms between each calls. 31 async function waitUntil(predicate, retry = 20) { 32 let count = 0; 33 while (count++ < retry) { 34 if (await predicate()) { 35 return true; 36 } 37 // Wait for 100 milliseconds. 38 await new Promise(resolve => setTimeout(resolve, 100)); 39 } 40 // Timed out after trying too many times. 41 return false; 42 } 43 44 // Class representing the ADB process. 45 class AdbProcess extends EventEmitter { 46 constructor() { 47 super(); 48 49 this._ready = false; 50 this._didRunInitially = false; 51 } 52 53 get ready() { 54 return this._ready; 55 } 56 57 _getAdbFile() { 58 if (this._adbFilePromise) { 59 return this._adbFilePromise; 60 } 61 this._adbFilePromise = getFileForBinary(); 62 return this._adbFilePromise; 63 } 64 65 async _runProcess(process, params) { 66 return new Promise((resolve, reject) => { 67 process.runAsync( 68 params, 69 params.length, 70 { 71 observe(subject, topic) { 72 switch (topic) { 73 case "process-finished": 74 resolve(); 75 break; 76 case "process-failed": 77 reject(); 78 break; 79 } 80 }, 81 }, 82 false 83 ); 84 }); 85 } 86 87 // We startup by launching adb in server mode, and setting 88 // the tcp socket preference to |true| 89 async start() { 90 const onSuccessfulStart = () => { 91 this._ready = true; 92 this.emit("adb-ready"); 93 }; 94 95 const isAdbRunning = await check(); 96 if (isAdbRunning) { 97 dumpn("Found ADB process running, not restarting"); 98 onSuccessfulStart(); 99 return; 100 } 101 dumpn("Didn't find ADB process running, restarting"); 102 103 this._didRunInitially = true; 104 const process = Cc["@mozilla.org/process/util;1"].createInstance( 105 Ci.nsIProcess 106 ); 107 108 // FIXME: Bug 1481691 - We should avoid extracting files every time. 109 const adbFile = await this._getAdbFile(); 110 process.init(adbFile); 111 // Hide command prompt window on Windows 112 process.startHidden = true; 113 process.noShell = true; 114 const params = ["start-server"]; 115 let isStarted = false; 116 try { 117 await this._runProcess(process, params); 118 isStarted = await waitUntil(check); 119 } catch (e) {} 120 121 if (isStarted) { 122 onSuccessfulStart(); 123 } else { 124 this._ready = false; 125 throw new Error("ADB Process didn't start"); 126 } 127 } 128 129 /** 130 * Stop the ADB server, but only if we started it. If it was started before 131 * us, we return immediately. 132 */ 133 async stop() { 134 if (!this._didRunInitially) { 135 return; // We didn't start the server, nothing to do 136 } 137 await this.kill(); 138 } 139 140 /** 141 * Kill the ADB server. 142 */ 143 async kill() { 144 try { 145 await runCommand("host:kill"); 146 } catch (e) { 147 dumpn("Failed to send host:kill command"); 148 } 149 dumpn("adb server was terminated by host:kill"); 150 this._ready = false; 151 this._didRunInitially = false; 152 } 153 } 154 155 exports.adbProcess = new AdbProcess();