UNPKG

@expo/cli

Version:
373 lines (372 loc) 13.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { DeviceABI: function() { return DeviceABI; }, adbArgs: function() { return adbArgs; }, getAdbNameForDeviceIdAsync: function() { return getAdbNameForDeviceIdAsync; }, getAttachedDevicesAsync: function() { return getAttachedDevicesAsync; }, getDeviceABIsAsync: function() { return getDeviceABIsAsync; }, getPackageInfoAsync: function() { return getPackageInfoAsync; }, getPropertyDataForDeviceAsync: function() { return getPropertyDataForDeviceAsync; }, getServer: function() { return getServer; }, installAsync: function() { return installAsync; }, isBootAnimationCompleteAsync: function() { return isBootAnimationCompleteAsync; }, isDeviceBootedAsync: function() { return isDeviceBootedAsync; }, isPackageInstalledAsync: function() { return isPackageInstalledAsync; }, launchActivityAsync: function() { return launchActivityAsync; }, logUnauthorized: function() { return logUnauthorized; }, openAppIdAsync: function() { return openAppIdAsync; }, openUrlAsync: function() { return openUrlAsync; }, sanitizeAdbDeviceName: function() { return sanitizeAdbDeviceName; }, uninstallAsync: function() { return uninstallAsync; } }); function _chalk() { const data = /*#__PURE__*/ _interop_require_default(require("chalk")); _chalk = function() { return data; }; return data; } function _os() { const data = /*#__PURE__*/ _interop_require_default(require("os")); _os = function() { return data; }; return data; } const _ADBServer = require("./ADBServer"); const _log = /*#__PURE__*/ _interop_require_wildcard(require("../../../log")); const _env = require("../../../utils/env"); const _errors = require("../../../utils/errors"); const _link = require("../../../utils/link"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = { __proto__: null }; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const debug = require('debug')('expo:start:platforms:android:adb'); var DeviceABI = /*#__PURE__*/ function(DeviceABI) { // The arch specific android target platforms are soft-deprecated. // Instead of using TargetPlatform as a combination arch + platform // the code will be updated to carry arch information in [DarwinArch] // and [AndroidArch]. DeviceABI["arm"] = "arm"; DeviceABI["arm64"] = "arm64"; DeviceABI["x64"] = "x64"; DeviceABI["x86"] = "x86"; DeviceABI["x8664"] = "x86_64"; DeviceABI["arm64v8a"] = "arm64-v8a"; DeviceABI["armeabiV7a"] = "armeabi-v7a"; DeviceABI["armeabi"] = "armeabi"; DeviceABI["universal"] = "universal"; return DeviceABI; }({}); const CANT_START_ACTIVITY_ERROR = 'Activity not started, unable to resolve Intent'; // http://developer.android.com/ndk/guides/abis.html const PROP_CPU_NAME = 'ro.product.cpu.abi'; const PROP_CPU_ABI_LIST_NAME = 'ro.product.cpu.abilist'; // Can sometimes be null // http://developer.android.com/ndk/guides/abis.html const PROP_BOOT_ANIMATION_STATE = 'init.svc.bootanim'; let _server; function getServer() { _server ??= new _ADBServer.ADBServer(); return _server; } function logUnauthorized(device) { _log.warn(`\nThis computer is not authorized for developing on ${_chalk().default.bold(device.name)}. ${_chalk().default.dim((0, _link.learnMore)('https://expo.fyi/authorize-android-device'))}`); } async function isPackageInstalledAsync(device, androidPackage) { const packages = await getServer().runAsync(adbArgs(device.pid, 'shell', 'pm', 'list', 'packages', '--user', _env.env.EXPO_ADB_USER, androidPackage)); const lines = packages.split(/\r?\n/); for(let i = 0; i < lines.length; i++){ const line = lines[i].trim(); if (line === `package:${androidPackage}`) { return true; } } return false; } async function launchActivityAsync(device, { launchActivity, url }) { const args = [ 'shell', 'am', 'start', // FLAG_ACTIVITY_SINGLE_TOP -- If set, the activity will not be launched if it is already running at the top of the history stack. '-f', '0x20000000', // Activity to open first: com.bacon.app/.MainActivity '-n', launchActivity ]; if (url) { args.push('-d', url); } return openAsync(adbArgs(device.pid, ...args)); } async function openAppIdAsync(device, { applicationId }) { return openAsync(adbArgs(device.pid, 'shell', 'monkey', '-p', applicationId, '-c', 'android.intent.category.LAUNCHER', '1')); } async function openUrlAsync(device, { url }) { return openAsync(adbArgs(device.pid, 'shell', 'am', 'start', '-a', 'android.intent.action.VIEW', '-d', // ADB requires ampersands to be escaped. url.replace(/&/g, String.raw`\&`))); } /** Runs a generic command watches for common errors in order to throw with an expected code. */ async function openAsync(args) { const results = await getServer().runAsync(args); if (results.includes(CANT_START_ACTIVITY_ERROR) || results.match(/Error: Activity class .* does not exist\./g)) { throw new _errors.CommandError('APP_NOT_INSTALLED', results.substring(results.indexOf('Error: '))); } return results; } async function uninstallAsync(device, { appId }) { return await getServer().runAsync(adbArgs(device.pid, 'uninstall', '--user', _env.env.EXPO_ADB_USER, appId)); } async function getPackageInfoAsync(device, { appId }) { return await getServer().runAsync(adbArgs(device.pid, 'shell', 'dumpsys', 'package', appId)); } async function installAsync(device, { filePath }) { // TODO: Handle the `INSTALL_FAILED_INSUFFICIENT_STORAGE` error. return await getServer().runAsync(adbArgs(device.pid, 'install', '-r', '-d', '--user', _env.env.EXPO_ADB_USER, filePath)); } function adbArgs(pid, ...options) { const args = []; if (pid) { args.push('-s', pid); } return args.concat(options); } async function getAttachedDevicesAsync() { const output = await getServer().runAsync([ 'devices', '-l' ]); const splitItems = output.trim().replace(/\n$/, '').split(_os().default.EOL)// Filter ADB trace logs from the output, e.g. // adb D 03-06 15:25:53 63677 4018815 adb_client.cpp:393] adb_query: host:devices-l // 03-04 12:29:44.557 16415 16415 D adb : commandline.cpp:1646 Using server socket: tcp:172.27.192.1:5037 // 03-04 12:29:44.557 16415 16415 D adb : adb_client.cpp:160 _adb_connect: host:version .filter((line)=>!line.match(/\.cpp:[0-9]+/)); // First line is `"List of devices attached"`, remove it // @ts-ignore: todo const attachedDevices = splitItems.slice(1, splitItems.length).map((line)=>{ // unauthorized: ['FA8251A00719', 'unauthorized', 'usb:338690048X', 'transport_id:5'] // authorized: ['FA8251A00719', 'device', 'usb:336592896X', 'product:walleye', 'model:Pixel_2', 'device:walleye', 'transport_id:4'] // emulator: ['emulator-5554', 'offline', 'transport_id:1'] const props = line.split(' ').filter(Boolean); const type = line.includes('emulator') ? 'emulator' : 'device'; let connectionType; if (type === 'device' && line.includes('usb:')) { connectionType = 'USB'; } else if (type === 'device' && line.includes('_adb-tls-connect.')) { connectionType = 'Network'; } const isBooted = type === 'emulator' || props[1] !== 'offline'; const isAuthorized = connectionType === 'Network' ? line.includes('model:') // Network connected devices show `model:<name>` when authorized : props[1] !== 'unauthorized'; return { props, type, isAuthorized, isBooted, connectionType }; }).filter(({ props: [pid] })=>!!pid); const devicePromises = attachedDevices.map(async (props)=>{ const { type, props: [pid, ...deviceInfo], isAuthorized, isBooted } = props; let name = null; if (type === 'device') { if (isAuthorized) { // Possibly formatted like `model:Pixel_2` // Transform to `Pixel_2` const modelItem = deviceInfo.find((info)=>info.includes('model:')); if (modelItem) { name = modelItem.replace('model:', ''); } } // unauthorized devices don't have a name available to read if (!name) { // Device FA8251A00719 name = `Device ${pid}`; } } else { // Given an emulator pid, get the emulator name which can be used to start the emulator later. name = await getAdbNameForDeviceIdAsync({ pid }) ?? ''; } return props.connectionType ? { pid, name, type, isAuthorized, isBooted, connectionType: props.connectionType } : { pid, name, type, isAuthorized, isBooted }; }); return Promise.all(devicePromises); } async function getAdbNameForDeviceIdAsync(device) { const results = await getServer().runAsync(adbArgs(device.pid, 'emu', 'avd', 'name')); if (results.match(/could not connect to TCP port .*: Connection refused/)) { // Can also occur when the emulator does not exist. throw new _errors.CommandError('EMULATOR_NOT_FOUND', results); } return sanitizeAdbDeviceName(results) ?? null; } async function isDeviceBootedAsync({ name } = {}) { const devices = await getAttachedDevicesAsync(); if (!name) { return devices[0] ?? null; } return devices.find((device)=>device.name === name) ?? null; } async function isBootAnimationCompleteAsync(pid) { try { const props = await getPropertyDataForDeviceAsync({ pid }, PROP_BOOT_ANIMATION_STATE); return !!props[PROP_BOOT_ANIMATION_STATE].match(/stopped/); } catch { return false; } } async function getDeviceABIsAsync(device) { const cpuAbiList = (await getPropertyDataForDeviceAsync(device, PROP_CPU_ABI_LIST_NAME))[PROP_CPU_ABI_LIST_NAME]; if (cpuAbiList) { return cpuAbiList.trim().split(','); } const abi = (await getPropertyDataForDeviceAsync(device, PROP_CPU_NAME))[PROP_CPU_NAME]; return [ abi ]; } async function getPropertyDataForDeviceAsync(device, prop) { // @ts-ignore const propCommand = adbArgs(...[ device.pid, 'shell', 'getprop', prop ].filter(Boolean)); try { // Prevent reading as UTF8. const results = await getServer().getFileOutputAsync(propCommand); // Like: // [wifi.direct.interface]: [p2p-dev-wlan0] // [wifi.interface]: [wlan0] if (prop) { debug(`Property data: (device pid: ${device.pid}, prop: ${prop}, data: ${results})`); return { [prop]: results }; } const props = parseAdbDeviceProperties(results); debug(`Parsed data:`, props); return props; } catch (error) { // TODO: Ensure error has message and not stderr throw new _errors.CommandError(`Failed to get properties for device (${device.pid}): ${error.message}`); } } function parseAdbDeviceProperties(devicePropertiesString) { const properties = {}; const propertyExp = /\[(.*?)\]: \[(.*?)\]/gm; for (const match of devicePropertiesString.matchAll(propertyExp)){ properties[match[1]] = match[2]; } return properties; } function sanitizeAdbDeviceName(deviceName) { return deviceName.trim().split(/[\r\n]+/).shift(); } //# sourceMappingURL=adb.js.map