UNPKG

nativescript

Version:

Command-line interface for building NativeScript projects

252 lines • 13.4 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AndroidProcessService = void 0; const os_1 = require("os"); const device_android_debug_bridge_1 = require("../android/device-android-debug-bridge"); const constants_1 = require("../../constants"); const decorators_1 = require("../../decorators"); const yok_1 = require("../../yok"); const _ = require("lodash"); class AndroidProcessService { constructor($errors, $cleanupService, $injector, $net, $staticConfig) { this.$errors = $errors; this.$cleanupService = $cleanupService; this.$injector = $injector; this.$net = $net; this.$staticConfig = $staticConfig; this._devicesAdbs = {}; this._forwardedLocalPorts = {}; } async forwardFreeTcpToAbstractPort(portForwardInputData) { const adb = await this.setupForPortForwarding(portForwardInputData); return this.forwardPort(portForwardInputData, adb); } async mapAbstractToTcpPort(deviceIdentifier, appIdentifier, framework) { const adb = await this.setupForPortForwarding({ deviceIdentifier, appIdentifier, }); const processId = (await this.getProcessIds(adb, [appIdentifier]))[appIdentifier]; const applicationNotStartedErrorMessage = `The application is not started on the device with identifier ${deviceIdentifier}.`; if (!processId) { this.$errors.fail(applicationNotStartedErrorMessage); } const abstractPortsInformation = await this.getAbstractPortsInformation(adb); const abstractPort = await this.getAbstractPortForApplication(adb, processId, appIdentifier, abstractPortsInformation, framework); if (!abstractPort) { this.$errors.fail(applicationNotStartedErrorMessage); } const forwardedTcpPort = await this.forwardPort({ deviceIdentifier, appIdentifier, abstractPort: `localabstract:${abstractPort}`, }, adb); return forwardedTcpPort && forwardedTcpPort.toString(); } async getMappedAbstractToTcpPorts(deviceIdentifier, appIdentifiers, framework) { const adb = this.getAdb(deviceIdentifier), abstractPortsInformation = await this.getAbstractPortsInformation(adb), processIds = await this.getProcessIds(adb, appIdentifiers), adbForwardList = await adb.executeCommand(["forward", "--list"]), localPorts = {}; await Promise.all(_.map(appIdentifiers, async (appIdentifier) => { localPorts[appIdentifier] = null; const processId = processIds[appIdentifier]; if (!processId) { return; } const abstractPort = await this.getAbstractPortForApplication(adb, processId, appIdentifier, abstractPortsInformation, framework); if (!abstractPort) { return; } const localPort = await this.getAlreadyMappedPort(adb, deviceIdentifier, abstractPort, adbForwardList); if (localPort) { localPorts[appIdentifier] = localPort; } })); return localPorts; } async getDebuggableApps(deviceIdentifier) { const adb = this.getAdb(deviceIdentifier); const androidWebViewPortInformation = (await this.getAbstractPortsInformation(adb)).split(os_1.EOL); // TODO: Add tests and make sure only unique names are returned. Input before groupBy is: // [ { deviceIdentifier: 'SH26BW100473', // appIdentifier: 'com.telerik.EmptyNS', // framework: 'NativeScript' }, // { deviceIdentifier: 'SH26BW100473', // appIdentifier: 'com.telerik.EmptyNS', // framework: 'Cordova' }, // { deviceIdentifier: 'SH26BW100473', // appIdentifier: 'chrome', // framework: 'Cordova' }, // { deviceIdentifier: 'SH26BW100473', // appIdentifier: 'chrome', // framework: 'Cordova' } ] const portInformation = await Promise.all(_.map(androidWebViewPortInformation, async (line) => (await this.getApplicationInfoFromWebViewPortInformation(adb, deviceIdentifier, line)) || (await this.getNativeScriptApplicationInformation(adb, deviceIdentifier, line)))); return _(portInformation) .filter((deviceAppInfo) => !!deviceAppInfo) .groupBy((element) => element.framework) .map((group) => _.uniqBy(group, (g) => g.appIdentifier)) .flatten() .value(); } async getAppProcessId(deviceIdentifier, appIdentifier) { const adb = this.getAdb(deviceIdentifier); const processId = (await this.getProcessIds(adb, [appIdentifier]))[appIdentifier]; return processId ? processId.toString() : null; } async forwardPort(portForwardInputData, adb) { let localPort = await this.getAlreadyMappedPort(adb, portForwardInputData.deviceIdentifier, portForwardInputData.abstractPort); if (!localPort) { localPort = await this.$net.getFreePort(); await adb.executeCommand(["forward", `tcp:${localPort}`, portForwardInputData.abstractPort], { deviceIdentifier: portForwardInputData.deviceIdentifier }); } this._forwardedLocalPorts[portForwardInputData.deviceIdentifier] = localPort; await this.$cleanupService.addCleanupCommand({ command: await this.$staticConfig.getAdbFilePath(), args: [ "-s", portForwardInputData.deviceIdentifier, "forward", "--remove", `tcp:${localPort}`, ], }); return localPort && +localPort; } async setupForPortForwarding(portForwardInputData) { const adb = this.getAdb(portForwardInputData.deviceIdentifier); return adb; } async getApplicationInfoFromWebViewPortInformation(adb, deviceIdentifier, information) { // Need to search by processId to check for old Android webviews (@webview_devtools_remote_<processId>). const processIdRegExp = /@webview_devtools_remote_(.+)/g; const processIdMatches = processIdRegExp.exec(information); let cordovaAppIdentifier; if (processIdMatches) { const processId = processIdMatches[1]; cordovaAppIdentifier = await this.getApplicationIdentifierFromPid(adb, processId); } else { // Search for appIdentifier (@<appIdentifier>_devtools_remote). const chromeAppIdentifierRegExp = /@(.+)_devtools_remote\s?/g; const chromeAppIdentifierMatches = chromeAppIdentifierRegExp.exec(information); if (chromeAppIdentifierMatches && chromeAppIdentifierMatches.length > 0) { cordovaAppIdentifier = chromeAppIdentifierMatches[1]; } } if (cordovaAppIdentifier) { return { deviceIdentifier: deviceIdentifier, appIdentifier: cordovaAppIdentifier, framework: constants_1.TARGET_FRAMEWORK_IDENTIFIERS.Cordova, }; } return null; } async getNativeScriptApplicationInformation(adb, deviceIdentifier, information) { // Search for appIdentifier (@<appIdentifier-debug>). const nativeScriptAppIdentifierRegExp = /@(.+)-(debug|inspectorServer)/g; const nativeScriptAppIdentifierMatches = nativeScriptAppIdentifierRegExp.exec(information); if (nativeScriptAppIdentifierMatches && nativeScriptAppIdentifierMatches.length > 0) { const appIdentifier = nativeScriptAppIdentifierMatches[1]; return { deviceIdentifier: deviceIdentifier, appIdentifier: appIdentifier, framework: constants_1.TARGET_FRAMEWORK_IDENTIFIERS.NativeScript, }; } return null; } async getAbstractPortForApplication(adb, processId, appIdentifier, abstractPortsInformation, framework) { // The result will look like this (without the columns names): // Num RefCount Protocol Flags Type St Inode Path // 0000000000000000: 00000002 00000000 00010000 0001 01 189004 @webview_devtools_remote_25512 // The Path column is the abstract port. framework = framework || ""; switch (framework.toLowerCase()) { case constants_1.TARGET_FRAMEWORK_IDENTIFIERS.Cordova.toLowerCase(): return this.getCordovaPortInformation(abstractPortsInformation, appIdentifier, processId); case constants_1.TARGET_FRAMEWORK_IDENTIFIERS.NativeScript.toLowerCase(): return this.getNativeScriptPortInformation(abstractPortsInformation, appIdentifier); default: return (this.getCordovaPortInformation(abstractPortsInformation, appIdentifier, processId) || this.getNativeScriptPortInformation(abstractPortsInformation, appIdentifier)); } } getCordovaPortInformation(abstractPortsInformation, appIdentifier, processId) { return (this.getPortInformation(abstractPortsInformation, `${appIdentifier}_devtools_remote`) || this.getPortInformation(abstractPortsInformation, processId)); } getNativeScriptPortInformation(abstractPortsInformation, appIdentifier) { return this.getPortInformation(abstractPortsInformation, `${appIdentifier}-debug`); } async getAbstractPortsInformation(adb) { return adb.executeShellCommand(["cat", "/proc/net/unix"]); } getPortInformation(abstractPortsInformation, searchedInfo) { const processRegExp = new RegExp(`\\w+:\\s+(?:\\w+\\s+){1,6}@(.*?${searchedInfo})$`, "gm"); const match = processRegExp.exec(abstractPortsInformation); return match && match[1]; } async getProcessIds(adb, appIdentifiers) { // Process information will look like this (without the columns names): // USER PID PPID VSIZE RSS WCHAN PC NAME // u0_a63 25512 1334 1519560 96040 ffffffff f76a8f75 S com.telerik.appbuildertabstest const result = {}; const processIdInformation = await adb.executeShellCommand(["ps"]); _.each(appIdentifiers, (appIdentifier) => { const processIdRegExp = new RegExp(`^\\w*\\s*(\\d+).*?${appIdentifier}$`); result[appIdentifier] = this.getFirstMatchingGroupFromMultilineResult(processIdInformation, processIdRegExp); }); return result; } async getAlreadyMappedPort(adb, deviceIdentifier, abstractPort, adbForwardList) { const allForwardedPorts = adbForwardList || (await adb.executeCommand(["forward", "--list"])) || ""; // Sample output: // 5e2e580b tcp:62503 localabstract:webview_devtools_remote_7985 // 5e2e580b tcp:62524 localabstract:webview_devtools_remote_7986 // 5e2e580b tcp:63160 localabstract:webview_devtools_remote_7987 // 5e2e580b tcp:57577 localabstract:com.telerik.nrel-debug const regex = new RegExp(`${deviceIdentifier}\\s+?tcp:(\\d+?)\\s+?.*?${abstractPort}$`); return this.getFirstMatchingGroupFromMultilineResult(allForwardedPorts, regex); } getAdb(deviceIdentifier) { if (!this._devicesAdbs[deviceIdentifier]) { this._devicesAdbs[deviceIdentifier] = this.$injector.resolve(device_android_debug_bridge_1.DeviceAndroidDebugBridge, { identifier: deviceIdentifier, }); } return this._devicesAdbs[deviceIdentifier]; } async getApplicationIdentifierFromPid(adb, pid, psData) { psData = psData || (await adb.executeShellCommand(["ps"])); // Process information will look like this (without the columns names): // USER PID PPID VSIZE RSS WCHAN PC NAME // u0_a63 25512 1334 1519560 96040 ffffffff f76a8f75 S com.telerik.appbuildertabstest return this.getFirstMatchingGroupFromMultilineResult(psData, new RegExp(`\\s+${pid}(?:\\s+\\d+){3}\\s+.*\\s+(.*?)$`)); } getFirstMatchingGroupFromMultilineResult(input, regex) { let result; _((input || "").split("\n")) .map((line) => line.trim()) .filter((line) => !!line) .each((line) => { const matches = line.match(regex); if (matches && matches[1]) { result = matches[1]; return false; } }); return result; } } exports.AndroidProcessService = AndroidProcessService; __decorate([ (0, decorators_1.exported)("androidProcessService") ], AndroidProcessService.prototype, "getAppProcessId", null); yok_1.injector.register("androidProcessService", AndroidProcessService); //# sourceMappingURL=android-process-service.js.map