File: /var/www/vhost/disk-apps/pwa.sports-crowd.com/node_modules/native-run/dist/android/utils/adb.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.execAdb = exports.unforwardPorts = exports.forwardPorts = exports.parseAdbDevices = exports.startActivity = exports.parseAdbInstallOutput = exports.ADBEvent = exports.uninstallApp = exports.closeApp = exports.installApk = exports.waitForClose = exports.waitForBoot = exports.waitForDevice = exports.getDeviceProperties = exports.getDeviceProperty = exports.getDevices = void 0;
const child_process_1 = require("child_process");
const Debug = require("debug");
const os = require("os");
const path = require("path");
const split2 = require("split2");
const through2 = require("through2");
const errors_1 = require("../../errors");
const process_1 = require("../../utils/process");
const sdk_1 = require("./sdk");
const modulePrefix = 'native-run:android:utils:adb';
const ADB_GETPROP_MAP = new Map([
['ro.product.manufacturer', 'manufacturer'],
['ro.product.model', 'model'],
['ro.product.name', 'product'],
['ro.build.version.sdk', 'sdkVersion'],
]);
async function getDevices(sdk) {
const debug = Debug(`${modulePrefix}:${getDevices.name}`);
const args = ['devices', '-l'];
debug('Invoking adb with args: %O', args);
const stdout = await execAdb(sdk, args, { timeout: 5000 });
const devices = parseAdbDevices(stdout);
await Promise.all(devices.map(async (device) => {
const properties = await getDeviceProperties(sdk, device);
for (const [prop, deviceProp] of ADB_GETPROP_MAP.entries()) {
const value = properties[prop];
if (value) {
device[deviceProp] = value;
}
}
}));
debug('Found adb devices: %O', devices);
return devices;
}
exports.getDevices = getDevices;
async function getDeviceProperty(sdk, device, property) {
const debug = Debug(`${modulePrefix}:${getDeviceProperty.name}`);
const args = ['-s', device.serial, 'shell', 'getprop', property];
debug('Invoking adb with args: %O', args);
const stdout = await execAdb(sdk, args, { timeout: 5000 });
return stdout.trim();
}
exports.getDeviceProperty = getDeviceProperty;
async function getDeviceProperties(sdk, device) {
const debug = Debug(`${modulePrefix}:${getDeviceProperties.name}`);
const args = ['-s', device.serial, 'shell', 'getprop'];
debug('Invoking adb with args: %O', args);
const stdout = await execAdb(sdk, args, { timeout: 5000 });
const re = /^\[([a-z0-9.]+)\]: \[(.*)\]$/;
const propAllowList = [...ADB_GETPROP_MAP.keys()];
const properties = {};
for (const line of stdout.split(os.EOL)) {
const m = line.match(re);
if (m) {
const [, key, value] = m;
if (propAllowList.includes(key)) {
properties[key] = value;
}
}
}
return properties;
}
exports.getDeviceProperties = getDeviceProperties;
async function waitForDevice(sdk, serial) {
const debug = Debug(`${modulePrefix}:${waitForDevice.name}`);
const args = ['-s', serial, 'wait-for-any-device'];
debug('Invoking adb with args: %O', args);
await execAdb(sdk, args);
debug('Device %s is connected to ADB!', serial);
}
exports.waitForDevice = waitForDevice;
async function waitForBoot(sdk, device) {
const debug = Debug(`${modulePrefix}:${waitForBoot.name}`);
return new Promise((resolve) => {
const interval = setInterval(async () => {
const booted = await getDeviceProperty(sdk, device, 'dev.bootcomplete');
if (booted) {
debug('Device %s is booted!', device.serial);
clearInterval(interval);
resolve();
}
}, 100);
});
}
exports.waitForBoot = waitForBoot;
async function waitForClose(sdk, device, app) {
const debug = Debug(`${modulePrefix}:${waitForClose.name}`);
const args = ['-s', device.serial, 'shell', `ps | grep ${app}`];
return new Promise((resolve) => {
const interval = setInterval(async () => {
try {
debug('Invoking adb with args: %O', args);
await execAdb(sdk, args);
}
catch (e) {
debug('Error received from adb: %O', e);
debug('App %s no longer found in process list for %s', app, device.serial);
clearInterval(interval);
resolve();
}
}, 500);
});
}
exports.waitForClose = waitForClose;
async function installApk(sdk, device, apk) {
const debug = Debug(`${modulePrefix}:${installApk.name}`);
const platformTools = await (0, sdk_1.getSDKPackage)(path.join(sdk.root, 'platform-tools'));
const adbBin = path.join(platformTools.location, 'adb');
const args = ['-s', device.serial, 'install', '-r', '-t', apk];
debug('Invoking adb with args: %O', args);
const p = (0, child_process_1.spawn)(adbBin, args, {
stdio: 'pipe',
env: (0, sdk_1.supplementProcessEnv)(sdk),
});
return new Promise((resolve, reject) => {
p.on('close', (code) => {
if (code === 0) {
resolve();
}
else {
reject(new errors_1.ADBException(`Non-zero exit code from adb: ${code}`));
}
});
p.on('error', (err) => {
debug('adb install error: %O', err);
reject(err);
});
p.stderr.pipe(split2()).pipe(through2((chunk, enc, cb) => {
const line = chunk.toString();
debug('adb install: %O', line);
const event = parseAdbInstallOutput(line);
if (event === ADBEvent.IncompatibleUpdateFailure) {
reject(new errors_1.ADBException(`Encountered adb error: ${ADBEvent[event]}.`, errors_1.ERR_INCOMPATIBLE_UPDATE));
}
else if (event === ADBEvent.NewerVersionOnDeviceFailure) {
reject(new errors_1.ADBException(`Encountered adb error: ${ADBEvent[event]}.`, errors_1.ERR_VERSION_DOWNGRADE));
}
else if (event === ADBEvent.NewerSdkRequiredOnDeviceFailure) {
reject(new errors_1.ADBException(`Encountered adb error: ${ADBEvent[event]}.`, errors_1.ERR_MIN_SDK_VERSION));
}
else if (event === ADBEvent.NoCertificates) {
reject(new errors_1.ADBException(`Encountered adb error: ${ADBEvent[event]}.`, errors_1.ERR_NO_CERTIFICATES));
}
else if (event === ADBEvent.NotEnoughSpace) {
reject(new errors_1.ADBException(`Encountered adb error: ${ADBEvent[event]}.`, errors_1.ERR_NOT_ENOUGH_SPACE));
}
else if (event === ADBEvent.DeviceOffline) {
reject(new errors_1.ADBException(`Encountered adb error: ${ADBEvent[event]}.`, errors_1.ERR_DEVICE_OFFLINE));
}
cb();
}));
});
}
exports.installApk = installApk;
async function closeApp(sdk, device, app) {
const debug = Debug(`${modulePrefix}:${closeApp.name}`);
const args = ['-s', device.serial, 'shell', 'am', 'force-stop', app];
debug('Invoking adb with args: %O', args);
await execAdb(sdk, args);
}
exports.closeApp = closeApp;
async function uninstallApp(sdk, device, app) {
const debug = Debug(`${modulePrefix}:${uninstallApp.name}`);
const args = ['-s', device.serial, 'uninstall', app];
debug('Invoking adb with args: %O', args);
await execAdb(sdk, args);
}
exports.uninstallApp = uninstallApp;
var ADBEvent;
(function (ADBEvent) {
ADBEvent[ADBEvent["IncompatibleUpdateFailure"] = 0] = "IncompatibleUpdateFailure";
ADBEvent[ADBEvent["NewerVersionOnDeviceFailure"] = 1] = "NewerVersionOnDeviceFailure";
ADBEvent[ADBEvent["NewerSdkRequiredOnDeviceFailure"] = 2] = "NewerSdkRequiredOnDeviceFailure";
ADBEvent[ADBEvent["NoCertificates"] = 3] = "NoCertificates";
ADBEvent[ADBEvent["NotEnoughSpace"] = 4] = "NotEnoughSpace";
ADBEvent[ADBEvent["DeviceOffline"] = 5] = "DeviceOffline";
})(ADBEvent = exports.ADBEvent || (exports.ADBEvent = {}));
function parseAdbInstallOutput(line) {
const debug = Debug(`${modulePrefix}:${parseAdbInstallOutput.name}`);
let event;
if (line.includes('INSTALL_FAILED_UPDATE_INCOMPATIBLE')) {
event = ADBEvent.IncompatibleUpdateFailure;
}
else if (line.includes('INSTALL_FAILED_VERSION_DOWNGRADE')) {
event = ADBEvent.NewerVersionOnDeviceFailure;
}
else if (line.includes('INSTALL_FAILED_OLDER_SDK')) {
event = ADBEvent.NewerSdkRequiredOnDeviceFailure;
}
else if (line.includes('INSTALL_PARSE_FAILED_NO_CERTIFICATES')) {
event = ADBEvent.NoCertificates;
}
else if (line.includes('INSTALL_FAILED_INSUFFICIENT_STORAGE') || line.includes('not enough space')) {
event = ADBEvent.NotEnoughSpace;
}
else if (line.includes('device offline')) {
event = ADBEvent.DeviceOffline;
}
if (typeof event !== 'undefined') {
debug('Parsed event from adb install output: %s', ADBEvent[event]);
}
return event;
}
exports.parseAdbInstallOutput = parseAdbInstallOutput;
async function startActivity(sdk, device, packageName, activityName) {
const debug = Debug(`${modulePrefix}:${startActivity.name}`);
const args = ['-s', device.serial, 'shell', 'am', 'start', '-W', '-n', `${packageName}/${activityName}`];
debug('Invoking adb with args: %O', args);
await execAdb(sdk, args, { timeout: 5000 });
}
exports.startActivity = startActivity;
function parseAdbDevices(output) {
const debug = Debug(`${modulePrefix}:${parseAdbDevices.name}`);
const re = /^([\S]+)\s+([a-z\s]+)\s+(.*)$/;
const ipRe = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/;
const lines = output.split(os.EOL);
debug('Parsing adb devices from output lines: %O', lines);
const devices = [];
for (const line of lines) {
if (line && !line.startsWith('List')) {
const m = line.match(re);
if (m) {
const [, serial, state, description] = m;
const properties = description
.split(/\s+/)
.map((prop) => (prop.includes(':') ? prop.split(':') : undefined))
.filter((kv) => typeof kv !== 'undefined' && kv.length >= 2)
.reduce((acc, [k, v]) => {
if (k && v) {
acc[k.trim()] = v.trim();
}
return acc;
}, {});
const isIP = !!serial.match(ipRe);
const isGenericDevice = (properties['device'] || '').startsWith('generic');
const type = 'usb' in properties || isIP || !serial.startsWith('emulator') || !isGenericDevice ? 'hardware' : 'emulator';
const connection = 'usb' in properties ? 'usb' : isIP ? 'tcpip' : null;
devices.push({
serial,
state,
type,
connection,
properties,
// We might not know these yet
manufacturer: '',
model: properties['model'] || '',
product: properties['product'] || '',
sdkVersion: '',
});
}
else {
debug('adb devices output line does not match expected regex: %O', line);
}
}
}
return devices;
}
exports.parseAdbDevices = parseAdbDevices;
async function forwardPorts(sdk, device, ports) {
const debug = Debug(`${modulePrefix}:${forwardPorts.name}`);
const args = ['-s', device.serial, 'reverse', `tcp:${ports.device}`, `tcp:${ports.host}`];
debug('Invoking adb with args: %O', args);
await execAdb(sdk, args, { timeout: 5000 });
}
exports.forwardPorts = forwardPorts;
async function unforwardPorts(sdk, device, ports) {
const debug = Debug(`${modulePrefix}:${unforwardPorts.name}`);
const args = ['-s', device.serial, 'reverse', '--remove', `tcp:${ports.device}`];
debug('Invoking adb with args: %O', args);
await execAdb(sdk, args, { timeout: 5000 });
}
exports.unforwardPorts = unforwardPorts;
async function execAdb(sdk, args, options = {}) {
const debug = Debug(`${modulePrefix}:${execAdb.name}`);
let timer;
const retry = async () => {
const msg = `ADBs is unresponsive after ${options.timeout}ms, killing server and retrying...\n`;
if (process.argv.includes('--json')) {
debug(msg);
}
else {
process.stderr.write(msg);
}
debug('ADB timeout of %O reached, killing server and retrying...', options.timeout);
debug('Invoking adb with args: %O', ['kill-server']);
await execAdb(sdk, ['kill-server']);
debug('Invoking adb with args: %O', ['start-server']);
await execAdb(sdk, ['start-server']);
debug('Retrying...');
return run();
};
const run = async () => {
const platformTools = await (0, sdk_1.getSDKPackage)(path.join(sdk.root, 'platform-tools'));
const adbBin = path.join(platformTools.location, 'adb');
const { stdout } = await (0, process_1.execFile)(adbBin, args, {
env: (0, sdk_1.supplementProcessEnv)(sdk),
});
if (timer) {
clearTimeout(timer);
timer = undefined;
}
return stdout;
};
return new Promise((resolve, reject) => {
if (options.timeout) {
timer = setTimeout(() => retry().then(resolve, reject), options.timeout);
}
run().then(resolve, (err) => {
if (!timer) {
reject(err);
}
});
});
}
exports.execAdb = execAdb;