Skip to content

Commit 6ddd405

Browse files
committed
refactor android debugger service to work correctly with the new android debugger that uses unix named sockets
related to pull #273
1 parent e0aa429 commit 6ddd405

File tree

1 file changed

+143
-98
lines changed

1 file changed

+143
-98
lines changed

lib/services/android-debug-service.ts

+143-98
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import * as helpers from "../common/helpers";
44
import * as path from "path";
55
import * as util from "util";
6+
import * as net from "net";
7+
import Future = require("fibers/future");
68

79
class AndroidDebugService implements IDebugService {
810
private static ENV_DEBUG_IN_FILENAME = "envDebug.in";
@@ -43,14 +45,64 @@ class AndroidDebugService implements IDebugService {
4345
: this.debugOnDevice();
4446
}
4547

46-
public debugOnEmulator(): IFuture<void> {
48+
private debugOnEmulator(): IFuture<void> {
4749
return (() => {
4850
this.$platformService.deployOnEmulator(this.platform).wait();
4951
this.debugOnDevice().wait();
5052
}).future<void>()();
5153
}
54+
55+
56+
private isPortAvailable(candidatetPort: number): IFuture<boolean> {
57+
return (() => {
58+
59+
let future = new Future<boolean>();
60+
var server = net.createServer();
61+
server.listen(candidatetPort, function (err: any) {
62+
server.once('close', function () {
63+
future.return(true);
64+
})
65+
server.close();
66+
});
67+
68+
server.on('error', function (err: any) {
69+
future.return(false);
70+
});
71+
72+
return future;
73+
}).future<boolean>()();
74+
}
75+
76+
77+
private getForwardedLocalDebugPortForPackageName(deviceId: string, packageName: string): number {
78+
var port = -1;
79+
var forwardsResult = this.device.adb.executeCommand(["forward", "--list"]).wait();
80+
81+
//this gets the port number without supporting forwards on multiple devices on the same package name
82+
//let match = forwardsResult.match(new RegExp("(?! tcp:)([\\d])+(?= localabstract:" + packageName + "-debug)", "g"));
83+
84+
//matches 123a188909e6czzc tcp:40001 localabstract:org.nativescript.testUnixSockets-debug
85+
let match = forwardsResult.match(new RegExp("(" + deviceId + " tcp:)([\\d])+(?= localabstract:" + packageName + "-debug)", "g"));
86+
if (match) {
87+
port = parseInt(match[0].substring(match[0].length - 5));
88+
}
89+
else {
90+
var candidatePort = 40000;
91+
while (!this.isPortAvailable(candidatePort++).wait()) {
92+
if (candidatePort > 65534) {
93+
this.$errors.failWithoutHelp("Unable to find free local port.");
94+
}
95+
}
96+
port = candidatePort;
97+
98+
this.unixSocketForward(port, packageName + "-debug").wait();
99+
}
100+
101+
return port;
102+
}
103+
52104

53-
public debugOnDevice(): IFuture<void> {
105+
private debugOnDevice(): IFuture<void> {
54106
return (() => {
55107
let packageFile = "";
56108

@@ -82,9 +134,9 @@ class AndroidDebugService implements IDebugService {
82134
this.device = device;
83135

84136
if (this.$options.getPort) {
85-
this.printDebugPort(packageName).wait();
137+
this.printDebugPort(device.deviceInfo.identifier, packageName).wait();
86138
} else if (this.$options.start) {
87-
this.attachDebugger(packageName);
139+
this.attachDebugger(device.deviceInfo.identifier, packageName);
88140
} else if (this.$options.stop) {
89141
this.detachDebugger(packageName).wait();
90142
} else if (this.$options.debugBrk) {
@@ -93,40 +145,27 @@ class AndroidDebugService implements IDebugService {
93145
}).future<void>()();
94146
}
95147

96-
private printDebugPort(packageName: string): IFuture<void> {
148+
private printDebugPort(deviceId: string, packageName: string): IFuture<void> {
97149
return (() => {
98-
let res = this.device.adb.executeShellCommand(["am", "broadcast", "-a", packageName + "-GetDbgPort"]).wait();
99-
this.$logger.info(res);
150+
var port = this.getForwardedLocalDebugPortForPackageName(deviceId, packageName);
151+
this.$logger.info(port);
100152
}).future<void>()();
101153
}
102154

103-
private attachDebugger(packageName: string): void {
104-
let startDebuggerCommand = ["am", "broadcast", "-a", '\"${packageName}-Debug\"', "--ez", "enable", "true"];
105-
let port = this.$options.debugPort;
106-
107-
if (port > 0) {
108-
startDebuggerCommand.push("--ei", "debuggerPort", port.toString());
109-
this.device.adb.executeShellCommand(startDebuggerCommand).wait();
110-
} else {
111-
let res = this.device.adb.executeShellCommand(["am", "broadcast", "-a", packageName + "-Debug", "--ez", "enable", "true"]).wait();
112-
let match = res.match(/result=(\d)+/);
113-
if (match) {
114-
port = match[0].substring(7);
115-
} else {
116-
port = 0;
117-
}
118-
}
119-
if ((0 < port) && (port < 65536)) {
120-
this.tcpForward(port, port).wait();
121-
this.startDebuggerClient(port).wait();
122-
this.openDebuggerClient(AndroidDebugService.DEFAULT_NODE_INSPECTOR_URL + "?port=" + port);
123-
} else {
124-
this.$logger.info("Cannot detect debug port.");
125-
}
155+
private attachDebugger(deviceId: string,packageName: string): void {
156+
157+
//let startDebuggerCommand = ["am", "broadcast", "-a", '\"${packageName}-debug\"', "--ez", "enable", "true"];
158+
//this.device.adb.executeShellCommand(startDebuggerCommand).wait();
159+
160+
var port = this.getForwardedLocalDebugPortForPackageName(deviceId, packageName);
161+
162+
163+
this.startDebuggerClient(port).wait();
164+
this.openDebuggerClient(AndroidDebugService.DEFAULT_NODE_INSPECTOR_URL + "?port=" + port);
126165
}
127166

128167
private detachDebugger(packageName: string): IFuture<void> {
129-
return this.device.adb.executeShellCommand(["am", "broadcast", "-a", `${packageName}-Debug`, "--ez", "enable", "false"]);
168+
return this.device.adb.executeShellCommand(["am", "broadcast", "-a", `${packageName}-debug`, "--ez", "enable", "false"]);
130169
}
131170

132171
private startAppWithDebugger(packageFile: string, packageName: string): IFuture<void> {
@@ -153,29 +192,35 @@ class AndroidDebugService implements IDebugService {
153192
private debugStartCore(): IFuture<void> {
154193
return (() => {
155194
let packageName = this.$projectData.projectId;
156-
let packageDir = util.format(AndroidDebugService.PACKAGE_EXTERNAL_DIR_TEMPLATE, packageName);
157-
let envDebugOutFullpath = this.$mobileHelper.buildDevicePath(packageDir, AndroidDebugService.ENV_DEBUG_OUT_FILENAME);
195+
196+
//TODO: Removed these...
197+
//let packageDir = util.format(AndroidDebugService.PACKAGE_EXTERNAL_DIR_TEMPLATE, packageName);
198+
//let envDebugOutFullpath = this.$mobileHelper.buildDevicePath(packageDir, AndroidDebugService.ENV_DEBUG_OUT_FILENAME);
158199

159-
this.device.adb.executeShellCommand(["rm", `${envDebugOutFullpath}`]).wait();
160-
this.device.adb.executeShellCommand(["mkdir", "-p", `${packageDir}`]).wait();
200+
//this.device.adb.executeShellCommand(["rm", `${envDebugOutFullpath}`]).wait();
201+
//this.device.adb.executeShellCommand(["mkdir", "-p", `${packageDir}`]).wait();
161202

162-
let debugBreakPath = this.$mobileHelper.buildDevicePath(packageDir, "debugbreak");
163-
this.device.adb.executeShellCommand([`cat /dev/null > ${debugBreakPath}`]).wait();
203+
//let debugBreakPath = this.$mobileHelper.buildDevicePath(packageDir, "debugbreak");
204+
//this.device.adb.executeShellCommand([`cat /dev/null > ${debugBreakPath}`]).wait();
205+
206+
this.device.adb.executeShellCommand([`cat /dev/null > /data/local/tmp/${packageName}-debugbreak`]).wait();
164207

165208
this.device.applicationManager.stopApplication(packageName).wait();
166209
this.device.applicationManager.startApplication(packageName).wait();
167210

168-
let dbgPort = this.startAndGetPort(packageName).wait();
169-
if (dbgPort > 0) {
170-
this.tcpForward(dbgPort, dbgPort).wait();
171-
this.startDebuggerClient(dbgPort).wait();
172-
this.openDebuggerClient(AndroidDebugService.DEFAULT_NODE_INSPECTOR_URL + "?port=" + dbgPort);
173-
}
211+
var localDebugPort = this.getForwardedLocalDebugPortForPackageName(this.device.deviceInfo.identifier, packageName);
212+
this.startDebuggerClient(localDebugPort).wait();
213+
this.openDebuggerClient(AndroidDebugService.DEFAULT_NODE_INSPECTOR_URL + "?port=" + localDebugPort);
214+
174215
}).future<void>()();
175216
}
176217

177-
private tcpForward(src: Number, dest: Number): IFuture<void> {
178-
return this.device.adb.executeCommand(["forward", `tcp:${src.toString()}`, `tcp:${dest.toString()}`]);
218+
// private tcpForward(src: Number, dest: Number): IFuture<void> {
219+
// return this.device.adb.executeCommand(["forward", `tcp:${src.toString()}`, `tcp:${dest.toString()}`]);
220+
// }
221+
222+
private unixSocketForward(local: Number, remote: String): IFuture<void> {
223+
return this.device.adb.executeCommand(["forward", `tcp:${local.toString()}`, `localabstract:${remote.toString()}`]);
179224
}
180225

181226
private startDebuggerClient(port: Number): IFuture<void> {
@@ -203,58 +248,58 @@ class AndroidDebugService implements IDebugService {
203248
}
204249
}
205250

206-
private checkIfRunning(packageName: string): boolean {
207-
let packageDir = util.format(AndroidDebugService.PACKAGE_EXTERNAL_DIR_TEMPLATE, packageName);
208-
let envDebugOutFullpath = packageDir + AndroidDebugService.ENV_DEBUG_OUT_FILENAME;
209-
let isRunning = this.checkIfFileExists(envDebugOutFullpath).wait();
210-
return isRunning;
211-
}
212-
213-
private checkIfFileExists(filename: string): IFuture<boolean> {
214-
return (() => {
215-
let res = this.device.adb.executeShellCommand([`test -f ${filename} && echo 'yes' || echo 'no'`]).wait();
216-
let exists = res.indexOf('yes') > -1;
217-
return exists;
218-
}).future<boolean>()();
219-
}
220-
221-
private startAndGetPort(packageName: string): IFuture<number> {
222-
return (() => {
223-
let port = -1;
224-
let timeout = this.$utils.getParsedTimeout(90);
225-
226-
let packageDir = util.format(AndroidDebugService.PACKAGE_EXTERNAL_DIR_TEMPLATE, packageName);
227-
let envDebugInFullpath = packageDir + AndroidDebugService.ENV_DEBUG_IN_FILENAME;
228-
this.device.adb.executeShellCommand(["rm", `${envDebugInFullpath}`]).wait();
229-
230-
let isRunning = false;
231-
for (let i = 0; i < timeout; i++) {
232-
helpers.sleep(1000 /* ms */);
233-
isRunning = this.checkIfRunning(packageName);
234-
if (isRunning) {
235-
break;
236-
}
237-
}
238-
239-
if (isRunning) {
240-
this.device.adb.executeShellCommand([`cat /dev/null > ${envDebugInFullpath}`]).wait();
241-
242-
for (let i = 0; i < timeout; i++) {
243-
helpers.sleep(1000 /* ms */);
244-
let envDebugOutFullpath = packageDir + AndroidDebugService.ENV_DEBUG_OUT_FILENAME;
245-
let exists = this.checkIfFileExists(envDebugOutFullpath).wait();
246-
if (exists) {
247-
let res = this.device.adb.executeShellCommand(["cat", envDebugOutFullpath]).wait();
248-
let match = res.match(/PORT=(\d)+/);
249-
if (match) {
250-
port = parseInt(match[0].substring(5), 10);
251-
break;
252-
}
253-
}
254-
}
255-
}
256-
return port;
257-
}).future<number>()();
258-
}
251+
// private checkIfRunning(packageName: string): boolean {
252+
// let packageDir = util.format(AndroidDebugService.PACKAGE_EXTERNAL_DIR_TEMPLATE, packageName);
253+
// let envDebugOutFullpath = packageDir + AndroidDebugService.ENV_DEBUG_OUT_FILENAME;
254+
// let isRunning = this.checkIfFileExists(envDebugOutFullpath).wait();
255+
// return isRunning;
256+
// }
257+
258+
// private checkIfFileExists(filename: string): IFuture<boolean> {
259+
// return (() => {
260+
// let res = this.device.adb.executeShellCommand([`test -f ${filename} && echo 'yes' || echo 'no'`]).wait();
261+
// let exists = res.indexOf('yes') > -1;
262+
// return exists;
263+
// }).future<boolean>()();
264+
// }
265+
266+
// private startAndGetPort(packageName: string): IFuture<number> {
267+
// return (() => {
268+
// let port = -1;
269+
// let timeout = this.$utils.getParsedTimeout(90);
270+
271+
// let packageDir = util.format(AndroidDebugService.PACKAGE_EXTERNAL_DIR_TEMPLATE, packageName);
272+
// let envDebugInFullpath = packageDir + AndroidDebugService.ENV_DEBUG_IN_FILENAME;
273+
// this.device.adb.executeShellCommand(["rm", `${envDebugInFullpath}`]).wait();
274+
275+
// let isRunning = false;
276+
// for (let i = 0; i < timeout; i++) {
277+
// helpers.sleep(1000 /* ms */);
278+
// isRunning = this.checkIfRunning(packageName);
279+
// if (isRunning) {
280+
// break;
281+
// }
282+
// }
283+
284+
// if (isRunning) {
285+
// this.device.adb.executeShellCommand([`cat /dev/null > ${envDebugInFullpath}`]).wait();
286+
287+
// for (let i = 0; i < timeout; i++) {
288+
// helpers.sleep(1000 /* ms */);
289+
// let envDebugOutFullpath = packageDir + AndroidDebugService.ENV_DEBUG_OUT_FILENAME;
290+
// let exists = this.checkIfFileExists(envDebugOutFullpath).wait();
291+
// if (exists) {
292+
// let res = this.device.adb.executeShellCommand(["cat", envDebugOutFullpath]).wait();
293+
// let match = res.match(/PORT=(\d)+/);
294+
// if (match) {
295+
// port = parseInt(match[0].substring(5), 10);
296+
// break;
297+
// }
298+
// }
299+
// }
300+
// }
301+
// return port;
302+
// }).future<number>()();
303+
// }
259304
}
260305
$injector.register("androidDebugService", AndroidDebugService);

0 commit comments

Comments
 (0)