UNPKG

mobile-cli-lib

Version:
485 lines (484 loc) 23.2 kB
"use strict"; var Fiber = require("fibers"); var Future = require("fibers/future"); var iconv = require("iconv-lite"); var os_1 = require("os"); var osenv = require("osenv"); var path = require("path"); var helpers = require("../../helpers"); var net = require("net"); var device_android_debug_bridge_1 = require("./device-android-debug-bridge"); var VirtualMachine = (function () { function VirtualMachine(name, identifier) { this.name = name; this.identifier = identifier; } return VirtualMachine; }()); var AndroidEmulatorServices = (function () { function AndroidEmulatorServices($logger, $emulatorSettingsService, $errors, $childProcess, $fs, $staticConfig, $devicePlatformsConstants, $logcatHelper, $options, $utils, $injector) { this.$logger = $logger; this.$emulatorSettingsService = $emulatorSettingsService; this.$errors = $errors; this.$childProcess = $childProcess; this.$fs = $fs; this.$staticConfig = $staticConfig; this.$devicePlatformsConstants = $devicePlatformsConstants; this.$logcatHelper = $logcatHelper; this.$options = $options; this.$utils = $utils; this.$injector = $injector; iconv.extendNodeEncodings(); this.adbFilePath = this.$staticConfig.getAdbFilePath().wait(); } Object.defineProperty(AndroidEmulatorServices.prototype, "pathToEmulatorExecutable", { get: function () { if (!this._pathToEmulatorExecutable) { var androidHome = process.env.ANDROID_HOME; var emulatorExecutableName = "emulator"; this._pathToEmulatorExecutable = androidHome ? path.join(androidHome, "tools", emulatorExecutableName) : emulatorExecutableName; } return this._pathToEmulatorExecutable; }, enumerable: true, configurable: true }); AndroidEmulatorServices.prototype.getEmulatorId = function () { var _this = this; return (function () { var image = _this.getEmulatorImage().wait(); if (!image) { _this.$errors.fail("Could not find an emulator image to run your project."); } var emulatorId = _this.startEmulatorInstance(image).wait(); return emulatorId; }).future()(); }; AndroidEmulatorServices.prototype.checkDependencies = function () { var _this = this; return (function () { _this.checkAndroidSDKConfiguration().wait(); if (_this.$options.geny) { _this.checkGenymotionConfiguration().wait(); } }).future()(); }; AndroidEmulatorServices.prototype.checkAvailability = function () { var _this = this; return (function () { if (!_this.getEmulatorImage().wait()) { _this.$errors.failWithoutHelp("You do not have any Android emulators installed. Please install at least one."); } var platform = _this.$devicePlatformsConstants.Android; if (!_this.$emulatorSettingsService.canStart(platform).wait()) { _this.$errors.fail("The current project does not target Android and cannot be run in the Android emulator."); } }).future()(); }; AndroidEmulatorServices.prototype.startEmulator = function () { var _this = this; return (function () { if (_this.$options.avd && _this.$options.geny) { _this.$errors.fail("You cannot specify both --avd and --geny options. Please use only one of them."); } var emulatorId = null; var image = _this.getEmulatorImage().wait(); if (image) { emulatorId = _this.startEmulatorInstance(image).wait(); _this.waitForEmulatorBootToComplete(emulatorId).wait(); _this.unlockScreen(emulatorId).wait(); } else { _this.$errors.fail("Could not find an emulator image to run your project."); } return emulatorId; }).future()(); }; AndroidEmulatorServices.prototype.runApplicationOnEmulator = function (app, emulatorOptions) { var _this = this; return (function () { var emulatorId = _this.startEmulator().wait(); _this.runApplicationOnEmulatorCore(app, emulatorOptions.appId, emulatorId).wait(); }).future()(); }; AndroidEmulatorServices.prototype.checkAndroidSDKConfiguration = function () { var _this = this; return (function () { try { _this.$childProcess.tryExecuteApplication(_this.pathToEmulatorExecutable, ['-help'], "exit", AndroidEmulatorServices.MISSING_SDK_MESSAGE).wait(); } catch (err) { _this.$logger.trace("Error while checking Android SDK configuration: " + err); _this.$errors.failWithoutHelp("Android SDK is not configured properly. Make sure you have added tools and platform-tools to your PATH environment variable."); } }).future()(); }; AndroidEmulatorServices.prototype.getDeviceAndroidDebugBridge = function (deviceIdentifier) { return this.$injector.resolve(device_android_debug_bridge_1.DeviceAndroidDebugBridge, { identifier: deviceIdentifier }); }; AndroidEmulatorServices.prototype.checkGenymotionConfiguration = function () { var _this = this; return (function () { try { var condition = function (childProcess) { return childProcess.stderr && !_.startsWith(childProcess.stderr, "Usage:"); }; _this.$childProcess.tryExecuteApplication("player", [], "exit", AndroidEmulatorServices.MISSING_GENYMOTION_MESSAGE, condition).wait(); } catch (err) { _this.$logger.trace("Error while checking Genymotion configuration: " + err); _this.$errors.failWithoutHelp("Genymotion is not configured properly. Make sure you have added its installation directory to your PATH environment variable."); } }).future()(); }; AndroidEmulatorServices.prototype.getEmulatorImage = function () { var _this = this; return (function () { var image = _this.$options.avd || _this.$options.geny || _this.getBestFit().wait(); return image; }).future()(); }; AndroidEmulatorServices.prototype.runApplicationOnEmulatorCore = function (app, appId, emulatorId) { var _this = this; return (function () { _this.$logger.info("installing %s through adb", app); var adb = _this.getDeviceAndroidDebugBridge(emulatorId); var childProcess = adb.executeCommand(["install", "-r", app], { returnChildProcess: true }).wait(); _this.$fs.futureFromEvent(childProcess, "close").wait(); _this.unlockScreen(emulatorId).wait(); _this.$logger.info("running %s through adb", app); var androidDebugBridgeCommandOptions = { childProcessOptions: { stdio: "ignore", detached: true }, returnChildProcess: true }; childProcess = adb.executeShellCommand(["monkey", "-p", appId, "-c", "android.intent.category.LAUNCHER", "1"], androidDebugBridgeCommandOptions).wait(); _this.$fs.futureFromEvent(childProcess, "close").wait(); if (!_this.$options.justlaunch) { _this.$logcatHelper.start(emulatorId); } }).future()(); }; AndroidEmulatorServices.prototype.unlockScreen = function (emulatorId) { var adb = this.getDeviceAndroidDebugBridge(emulatorId); var childProcess = adb.executeShellCommand(["input", "keyevent", "82"], { returnChildProcess: true }).wait(); return this.$fs.futureFromEvent(childProcess, "close"); }; AndroidEmulatorServices.prototype.sleep = function (ms) { var fiber = Fiber.current; setTimeout(function () { return fiber.run(); }, ms); Fiber.yield(); }; AndroidEmulatorServices.prototype.getRunningEmulatorId = function (image) { var _this = this; return (function () { var runningEmulators = _this.getRunningEmulators().wait(); if (runningEmulators.length === 0) { return ""; } var getNameFunction = _this.$options.geny ? _this.getNameFromGenymotionEmulatorId : _this.getNameFromSDKEmulatorId; var emulatorId = _(runningEmulators).find(function (emulator) { return getNameFunction.apply(_this, [emulator]).wait() === image; }); return emulatorId; }).future()(); }; AndroidEmulatorServices.prototype.getNameFromGenymotionEmulatorId = function (emulatorId) { var _this = this; return (function () { var modelOutputLines = _this.$childProcess.execFile(_this.adbFilePath, ["-s", emulatorId, "shell", "getprop", "ro.product.model"]).wait(); _this.$logger.trace(modelOutputLines); var model = _.first(modelOutputLines.split(os_1.EOL)).trim(); return model; }).future()(); }; AndroidEmulatorServices.prototype.getNameFromSDKEmulatorId = function (emulatorId) { var _this = this; var match = emulatorId.match(/^emulator-(\d+)/); var portNumber; if (match && match[1]) { portNumber = match[1]; } else { return Future.fromResult(""); } var future = new Future(); var output = ""; var client = net.connect(portNumber, function () { client.write("avd name" + os_1.EOL); }); client.on('data', function (data) { output += data.toString(); var name = _this.getEmulatorNameFromClientOutput(output); if (name && !future.isResolved()) { future.return(name); } client.end(); }); return future; }; AndroidEmulatorServices.prototype.getEmulatorNameFromClientOutput = function (output) { var lines = _.map(output.split(os_1.EOL), function (line) { return line.trim(); }); var name; var firstIndexOfOk = _.indexOf(lines, "OK"); if (firstIndexOfOk < 0) { return null; } var secondIndexOfOk = _.indexOf(lines, "OK", firstIndexOfOk + 1); if (secondIndexOfOk < 0) { return null; } name = lines[secondIndexOfOk - 1].trim(); return name; }; AndroidEmulatorServices.prototype.startEmulatorInstance = function (image) { var _this = this; return (function () { var emulatorId = _this.getRunningEmulatorId(image).wait(); _this.endTimeEpoch = helpers.getCurrentEpochTime() + _this.$utils.getMilliSecondsTimeout(AndroidEmulatorServices.TIMEOUT_SECONDS); if (emulatorId) { return emulatorId; } _this.$logger.info("Starting Android emulator with image %s", image); if (_this.$options.geny) { _this.$childProcess.spawn("player", ["--vm-name", image], { stdio: "ignore", detached: true }).unref(); } else { _this.$childProcess.spawn(_this.pathToEmulatorExecutable, ['-avd', image], { stdio: "ignore", detached: true }).unref(); } var isInfiniteWait = _this.$utils.getMilliSecondsTimeout(AndroidEmulatorServices.TIMEOUT_SECONDS) === 0; var hasTimeLeft = helpers.getCurrentEpochTime() < _this.endTimeEpoch; while (hasTimeLeft || isInfiniteWait) { emulatorId = _this.getRunningEmulatorId(image).wait(); if (emulatorId) { return emulatorId; } _this.sleep(10000); hasTimeLeft = helpers.getCurrentEpochTime() < _this.endTimeEpoch; } if (!hasTimeLeft && !isInfiniteWait) { _this.$errors.fail(AndroidEmulatorServices.UNABLE_TO_START_EMULATOR_MESSAGE); } return emulatorId; }).future()(); }; AndroidEmulatorServices.prototype.getRunningGenymotionEmulators = function (adbDevicesOutput) { var _this = this; return (function () { var futures = (_(adbDevicesOutput).filter(function (r) { return !r.match(AndroidEmulatorServices.RUNNING_ANDROID_EMULATOR_REGEX); }) .map(function (row) { var match = row.match(/^(.+?)\s+device$/); if (match && match[1]) { var emulatorId = match[1]; return Future.fromResult(_this.isGenymotionEmulator(emulatorId).wait() ? emulatorId : undefined); } return Future.fromResult(undefined); }).value()); Future.wait(futures); return _(futures).filter(function (future) { return !!future.get(); }) .map(function (f) { return f.get().toString(); }) .value(); }).future()(); }; AndroidEmulatorServices.prototype.getRunningAvdEmulators = function (adbDevicesOutput) { return (function () { var emulatorDevices = []; _.each(adbDevicesOutput, function (device) { var rx = device.match(AndroidEmulatorServices.RUNNING_ANDROID_EMULATOR_REGEX); if (rx && rx[1]) { emulatorDevices.push(rx[1]); } }); return emulatorDevices; }).future()(); }; AndroidEmulatorServices.prototype.isGenymotionEmulator = function (emulatorId) { var _this = this; return (function () { var manufacturer = _this.$childProcess.execFile(_this.adbFilePath, ["-s", emulatorId, "shell", "getprop", "ro.product.manufacturer"]).wait(); if (manufacturer.match(/^Genymotion/i)) { return true; } var buildProduct = _this.$childProcess.execFile(_this.adbFilePath, ["-s", emulatorId, "shell", "getprop", "ro.build.product"]).wait(); if (buildProduct && _.includes(buildProduct.toLowerCase(), "vbox")) { return true; } return false; }).future()(); }; AndroidEmulatorServices.prototype.getAllRunningEmulators = function () { var _this = this; return (function () { var outputRaw = _this.$childProcess.execFile(_this.adbFilePath, ['devices']).wait().split(os_1.EOL); var emulators = _this.getRunningAvdEmulators(outputRaw).wait().concat(_this.getRunningGenymotionEmulators(outputRaw).wait()); return emulators; }).future()(); }; AndroidEmulatorServices.prototype.getRunningEmulators = function () { var _this = this; return (function () { var outputRaw = _this.$childProcess.execFile(_this.adbFilePath, ['devices']).wait().split(os_1.EOL); if (_this.$options.geny) { return _this.getRunningGenymotionEmulators(outputRaw).wait(); } else { return _this.getRunningAvdEmulators(outputRaw).wait(); } }).future()(); }; AndroidEmulatorServices.prototype.getBestFit = function () { var _this = this; return (function () { var minVersion = _this.$emulatorSettingsService.minVersion; var best = _(_this.getAvds().wait()) .map(function (avd) { return _this.getInfoFromAvd(avd).wait(); }) .maxBy(function (avd) { return avd.targetNum; }); return (best && best.targetNum >= minVersion) ? best.name : null; }).future()(); }; AndroidEmulatorServices.prototype.getInfoFromAvd = function (avdName) { var _this = this; return (function () { var iniFile = path.join(_this.avdDir, avdName + ".ini"); var avdInfo = _this.parseAvdFile(avdName, iniFile).wait(); if (avdInfo.path && _this.$fs.exists(avdInfo.path).wait()) { iniFile = path.join(avdInfo.path, "config.ini"); avdInfo = _this.parseAvdFile(avdName, iniFile, avdInfo).wait(); } return avdInfo; }).future()(); }; AndroidEmulatorServices.prototype.parseAvdFile = function (avdName, avdFileName, avdInfo) { var _this = this; if (avdInfo === void 0) { avdInfo = null; } return (function () { if (!_this.$fs.exists(avdFileName).wait()) { return null; } var encoding = _this.getAvdEncoding(avdFileName).wait(); var contents = _this.$fs.readText(avdFileName, encoding).wait().split("\n"); avdInfo = _.reduce(contents, function (result, line) { var parsedLine = line.split("="); var key = parsedLine[0]; switch (key) { case "target": result.target = parsedLine[1]; result.targetNum = _this.readTargetNum(result.target); break; case "path": result.path = parsedLine[1]; break; case "hw.device.name": result.device = parsedLine[1]; break; case "abi.type": result.abi = parsedLine[1]; break; case "skin.name": result.skin = parsedLine[1]; break; case "sdcard.size": result.sdcard = parsedLine[1]; break; } return result; }, avdInfo || Object.create(null)); avdInfo.name = avdName; return avdInfo; }).future()(); }; AndroidEmulatorServices.prototype.readTargetNum = function (target) { var platform = target.replace('android-', ''); var platformNumber = +platform; if (isNaN(platformNumber)) { var googlePlatform = target.split(":")[2]; if (googlePlatform) { platformNumber = +googlePlatform; } else if (platform === "L") { platformNumber = 20; } else if (platform === "MNC") { platformNumber = 22; } } return platformNumber; }; AndroidEmulatorServices.prototype.getAvdEncoding = function (avdName) { var _this = this; return (function () { var encoding = "utf8"; var contents = _this.$fs.readText(avdName, "ascii").wait(); if (contents.length > 0) { contents = contents.split("\n", 1)[0]; if (contents.length > 0) { var matches = contents.match(AndroidEmulatorServices.ENCODING_MASK); if (matches) { encoding = matches[1]; } } } return encoding; }).future()(); }; Object.defineProperty(AndroidEmulatorServices.prototype, "androidHomeDir", { get: function () { return path.join(osenv.home(), AndroidEmulatorServices.ANDROID_DIR_NAME); }, enumerable: true, configurable: true }); Object.defineProperty(AndroidEmulatorServices.prototype, "avdDir", { get: function () { return path.join(this.androidHomeDir, AndroidEmulatorServices.AVD_DIR_NAME); }, enumerable: true, configurable: true }); AndroidEmulatorServices.prototype.getAvds = function () { var _this = this; return (function () { var result = []; if (_this.$fs.exists(_this.avdDir).wait()) { var entries = _this.$fs.readDirectory(_this.avdDir).wait(); result = _.filter(entries, function (e) { return e.match(AndroidEmulatorServices.INI_FILES_MASK) !== null; }) .map(function (e) { return e.match(AndroidEmulatorServices.INI_FILES_MASK)[1]; }); } return result; }).future()(); }; AndroidEmulatorServices.prototype.waitForEmulatorBootToComplete = function (emulatorId) { var _this = this; return (function () { _this.$logger.printInfoMessageOnSameLine("Waiting for emulator device initialization..."); var isInfiniteWait = _this.$utils.getMilliSecondsTimeout(AndroidEmulatorServices.TIMEOUT_SECONDS) === 0; while (helpers.getCurrentEpochTime() < _this.endTimeEpoch || isInfiniteWait) { var isEmulatorBootCompleted = _this.isEmulatorBootCompleted(emulatorId).wait(); if (isEmulatorBootCompleted) { _this.$logger.printInfoMessageOnSameLine(os_1.EOL); return; } _this.$logger.printInfoMessageOnSameLine("."); _this.sleep(10000); } _this.$logger.printInfoMessageOnSameLine(os_1.EOL); _this.$errors.fail(AndroidEmulatorServices.UNABLE_TO_START_EMULATOR_MESSAGE); }).future()(); }; AndroidEmulatorServices.prototype.isEmulatorBootCompleted = function (emulatorId) { var _this = this; return (function () { var output = _this.$childProcess.execFile(_this.adbFilePath, ["-s", emulatorId, "shell", "getprop", "dev.bootcomplete"]).wait(); var matches = output.match("1"); return matches && matches.length > 0; }).future()(); }; AndroidEmulatorServices.ANDROID_DIR_NAME = ".android"; AndroidEmulatorServices.AVD_DIR_NAME = "avd"; AndroidEmulatorServices.INI_FILES_MASK = /^(.*)\.ini$/i; AndroidEmulatorServices.ENCODING_MASK = /^avd\.ini\.encoding=(.*)$/; AndroidEmulatorServices.TIMEOUT_SECONDS = 120; AndroidEmulatorServices.UNABLE_TO_START_EMULATOR_MESSAGE = "Cannot run your app in the native emulator. Increase the timeout of the operation with the --timeout option or try to restart your adb server with 'adb kill-server' command. Alternatively, run the Android Virtual Device manager and increase the allocated RAM for the virtual device."; AndroidEmulatorServices.RUNNING_ANDROID_EMULATOR_REGEX = /^(emulator-\d+)\s+device$/; AndroidEmulatorServices.MISSING_SDK_MESSAGE = "The Android SDK is not configured properly. " + "Verify that you have installed the Android SDK and that you have configured it as described in System Requirements."; AndroidEmulatorServices.MISSING_GENYMOTION_MESSAGE = "Genymotion is not configured properly. " + "Verify that you have installed Genymotion and that you have added its installation directory to your PATH environment variable."; return AndroidEmulatorServices; }()); $injector.register("androidEmulatorServices", AndroidEmulatorServices);