UNPKG

@expo/cli

Version:
327 lines (326 loc) 13.1 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: ()=>DeviceABI, getServer: ()=>getServer, logUnauthorized: ()=>logUnauthorized, isPackageInstalledAsync: ()=>isPackageInstalledAsync, launchActivityAsync: ()=>launchActivityAsync, openAppIdAsync: ()=>openAppIdAsync, openUrlAsync: ()=>openUrlAsync, uninstallAsync: ()=>uninstallAsync, getPackageInfoAsync: ()=>getPackageInfoAsync, installAsync: ()=>installAsync, adbArgs: ()=>adbArgs, getAttachedDevicesAsync: ()=>getAttachedDevicesAsync, getAdbNameForDeviceIdAsync: ()=>getAdbNameForDeviceIdAsync, isDeviceBootedAsync: ()=>isDeviceBootedAsync, isBootAnimationCompleteAsync: ()=>isBootAnimationCompleteAsync, getDeviceABIsAsync: ()=>getDeviceABIsAsync, getPropertyDataForDeviceAsync: ()=>getPropertyDataForDeviceAsync, sanitizeAdbDeviceName: ()=>sanitizeAdbDeviceName }); function _chalk() { const data = /*#__PURE__*/ _interopRequireDefault(require("chalk")); _chalk = function() { return data; }; return data; } function _os() { const data = /*#__PURE__*/ _interopRequireDefault(require("os")); _os = function() { return data; }; return data; } const _adbserver = require("./ADBServer"); const _log = /*#__PURE__*/ _interopRequireWildcard(require("../../../log")); const _env = require("../../../utils/env"); const _errors = require("../../../utils/errors"); const _link = require("../../../utils/link"); function _interopRequireDefault(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 _interopRequireWildcard(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 = {}; 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; (function(DeviceABI) { 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]. "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"; })(DeviceABI || (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 != null ? _server : _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 }) { return openAsync(adbArgs(device.pid, "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)); } 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 { var ref; // Given an emulator pid, get the emulator name which can be used to start the emulator later. name = (ref = await getAdbNameForDeviceIdAsync({ pid })) != null ? ref : ""; } 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); } var ref; return (ref = sanitizeAdbDeviceName(results)) != null ? ref : null; } async function isDeviceBootedAsync({ name } = {}) { const devices = await getAttachedDevicesAsync(); if (!name) { var ref; return (ref = devices[0]) != null ? ref : null; } var ref1; return (ref1 = devices.find((device)=>device.name === name)) != null ? ref1 : 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