in src/debugger/cordovaDebugSession.ts [1448:1586]
private attachAndroid(attachArgs: ICordovaAttachRequestArgs): Promise<ICordovaAttachRequestArgs> {
let errorLogger = (message: string) => this.outputLogger(message, true);
// Determine which device/emulator we are targeting
let resolveTagetPromise = new Promise<string>(async (resolve, reject) => {
try {
const devicesOutput = await this.runAdbCommand(["devices"], errorLogger);
try {
const result = await this.resolveAndroidTarget(attachArgs, true);
if (!result) {
errorLogger(devicesOutput);
reject(new Error(`Unable to find target ${attachArgs.target}`));
}
resolve(result.id);
} catch (error) {
reject(error);
}
} catch (error) {
let errorCode: string = (<any>error).code;
if (errorCode && errorCode === "ENOENT") {
throw new Error(localize("UnableToFindAdb", "Unable to find adb. Please ensure it is in your PATH and re-open Visual Studio Code"));
}
throw error;
}
});
let packagePromise: Promise<string> = fs.promises.readFile(path.join(attachArgs.cwd, ANDROID_MANIFEST_PATH))
.catch((err) => {
if (err && err.code === "ENOENT") {
return fs.promises.readFile(path.join(attachArgs.cwd, ANDROID_MANIFEST_PATH_8));
}
throw err;
})
.then((manifestContents) => {
let parsedFile = elementtree.XML(manifestContents.toString());
let packageKey = "package";
return parsedFile.attrib[packageKey];
});
return Promise.all([packagePromise, resolveTagetPromise])
.then(([appPackageName, targetDevice]) => {
let pidofCommandArguments = ["-s", targetDevice, "shell", "pidof", appPackageName];
let getPidCommandArguments = ["-s", targetDevice, "shell", "ps"];
let getSocketsCommandArguments = ["-s", targetDevice, "shell", "cat /proc/net/unix"];
let findAbstractNameFunction = () =>
// Get the pid from app package name
this.runAdbCommand(pidofCommandArguments, errorLogger)
.then((pid) => {
if (pid && /^[0-9]+$/.test(pid.trim())) {
return pid.trim();
}
throw Error(CordovaDebugSession.pidofNotFoundError);
}).catch((err) => {
if (err.message !== CordovaDebugSession.pidofNotFoundError) {
return;
}
return this.runAdbCommand(getPidCommandArguments, errorLogger)
.then((psResult) => {
const lines = psResult.split("\n");
const keys = lines.shift().split(PS_FIELDS_SPLITTER_RE);
const nameIdx = keys.indexOf("NAME");
const pidIdx = keys.indexOf("PID");
for (const line of lines) {
const fields = line.trim().split(PS_FIELDS_SPLITTER_RE).filter(field => !!field);
if (fields.length < nameIdx) {
continue;
}
if (fields[nameIdx] === appPackageName) {
return fields[pidIdx];
}
}
});
})
// Get the "_devtools_remote" abstract name by filtering /proc/net/unix with process inodes
.then(pid =>
this.runAdbCommand(getSocketsCommandArguments, errorLogger)
.then((getSocketsResult) => {
const lines = getSocketsResult.split("\n");
const keys = lines.shift().split(/[\s\r]+/);
const flagsIdx = keys.indexOf("Flags");
const stIdx = keys.indexOf("St");
const pathIdx = keys.indexOf("Path");
for (const line of lines) {
const fields = line.split(/[\s\r]+/);
if (fields.length < 8) {
continue;
}
// flag = 00010000 (16) -> accepting connection
// state = 01 (1) -> unconnected
if (fields[flagsIdx] !== "00010000" || fields[stIdx] !== "01") {
continue;
}
const pathField = fields[pathIdx];
if (pathField.length < 1 || pathField[0] !== "@") {
continue;
}
if (pathField.indexOf("_devtools_remote") === -1) {
continue;
}
if (pathField === `@webview_devtools_remote_${pid}`) {
// Matches the plain cordova webview format
return pathField.substr(1);
}
if (pathField === `@${appPackageName}_devtools_remote`) {
// Matches the crosswalk format of "@PACKAGENAME_devtools_remote
return pathField.substr(1);
}
// No match, keep searching
}
})
);
return retryAsync(
findAbstractNameFunction,
(match) => !!match,
5,
1,
5000,
localize("UnableToFindLocalAbstractName", "Unable to find 'localabstract' name of Cordova app"),
this.cancellationTokenSource.token
)
.then((abstractName) => {
// Configure port forwarding to the app
let forwardSocketCommandArguments = ["-s", targetDevice, "forward", `tcp:${attachArgs.port}`, `localabstract:${abstractName}`];
this.outputLogger(localize("ForwardingDebugPort", "Forwarding debug port"));
return this.runAdbCommand(forwardSocketCommandArguments, errorLogger).then(() => {
this.adbPortForwardingInfo = { targetDevice, port: attachArgs.port };
});
});
}).then(() => {
return attachArgs;
});
}