microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
ffdf159274093d12e32a09aafae6d0de3099bb4a

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/extension/android/androidEmulatorManager.ts

161lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import { AdbHelper } from "./adb";
5import { ChildProcess } from "../../common/node/childProcess";
6import { IVirtualDevice, VirtualDeviceManager } from "../VirtualDeviceManager";
7import { OutputChannelLogger } from "../log/OutputChannelLogger";
8import * as nls from "vscode-nls";
9import { ErrorHelper } from "../../common/error/errorHelper";
10import { InternalErrorCode } from "../../common/error/internalErrorCode";
11import { QuickPickOptions, window } from "vscode";
12nls.config({
13 messageFormat: nls.MessageFormat.bundle,
14 bundleFormat: nls.BundleFormat.standalone,
15})();
16const localize = nls.loadMessageBundle();
17
18// eslint-disable-next-line @typescript-eslint/no-empty-interface
19export interface IAndroidEmulator extends IVirtualDevice {}
20
21export class AndroidEmulatorManager extends VirtualDeviceManager {
22 private static readonly EMULATOR_COMMAND = "emulator";
23 private static readonly EMULATOR_LIST_AVDS_COMMAND = `-list-avds`;
24 private static readonly EMULATOR_AVD_START_COMMAND = `-avd`;
25
26 private static readonly EMULATOR_START_TIMEOUT = 120;
27
28 private logger: OutputChannelLogger = OutputChannelLogger.getChannel(
29 OutputChannelLogger.MAIN_CHANNEL_NAME,
30 true,
31 );
32
33 private adbHelper: AdbHelper;
34 private childProcess: ChildProcess;
35
36 constructor(adbHelper: AdbHelper) {
37 super();
38 this.adbHelper = adbHelper;
39 this.childProcess = new ChildProcess();
40 }
41
42 public async startEmulator(target: string): Promise<IAndroidEmulator | null> {
43 const onlineDevices = await this.adbHelper.getOnlineDevices();
44 for (let i = 0; i < onlineDevices.length; i++) {
45 if (onlineDevices[i].id === target) {
46 return { id: onlineDevices[i].id };
47 }
48 }
49 if (target && (await this.adbHelper.getOnlineDevices()).length === 0) {
50 if (target === "simulator") {
51 const newEmulator = await this.startSelection();
52 if (newEmulator) {
53 const emulatorId = await this.tryLaunchEmulatorByName(newEmulator);
54 return { name: newEmulator, id: emulatorId };
55 }
56 } else if (!target.includes("device")) {
57 const emulatorId = await this.tryLaunchEmulatorByName(target);
58 return { name: target, id: emulatorId };
59 }
60 }
61 return null;
62 }
63
64 public async tryLaunchEmulatorByName(emulatorName: string): Promise<string> {
65 return new Promise((resolve, reject) => {
66 const emulatorProcess = this.childProcess.spawn(
67 AndroidEmulatorManager.EMULATOR_COMMAND,
68 [AndroidEmulatorManager.EMULATOR_AVD_START_COMMAND, emulatorName],
69 {
70 detached: true,
71 },
72 true,
73 );
74 emulatorProcess.outcome.catch(error => {
75 if (
76 process.platform == "win32" &&
77 process.env.SESSIONNAME &&
78 process.env.SESSIONNAME.toLowerCase().includes("rdp-tcp")
79 ) {
80 this.logger.warning(
81 localize(
82 "RDPEmulatorWarning",
83 "Android emulator was launched from the Windows RDP session, this might lead to failures.",
84 ),
85 );
86 }
87 reject(
88 ErrorHelper.getInternalError(
89 InternalErrorCode.VirtualDeviceSelectionError,
90 error,
91 ),
92 );
93 });
94 emulatorProcess.spawnedProcess.unref();
95
96 const rejectTimeout = setTimeout(() => {
97 cleanup();
98 reject(
99 ErrorHelper.getInternalError(
100 InternalErrorCode.VirtualDeviceSelectionError,
101 localize(
102 "EmulatorStartWarning",
103 "Could not start the emulator {0} within {1} seconds.",
104 emulatorName,
105 AndroidEmulatorManager.EMULATOR_START_TIMEOUT,
106 ),
107 ),
108 );
109 }, AndroidEmulatorManager.EMULATOR_START_TIMEOUT * 1000);
110
111 const bootCheckInterval = setInterval(async () => {
112 const connectedDevices = await this.adbHelper.getOnlineDevices();
113 if (connectedDevices.length > 0) {
114 this.logger.info(
115 localize("EmulatorLaunched", "Launched emulator {0}", emulatorName),
116 );
117 cleanup();
118 resolve(connectedDevices[0].id);
119 }
120 }, 1000);
121
122 const cleanup = () => {
123 clearTimeout(rejectTimeout);
124 clearInterval(bootCheckInterval);
125 };
126 });
127 }
128
129 public startSelection(): Promise<string | undefined> {
130 return this.selectVirtualDevice();
131 }
132
133 public async selectOnlineDevice(): Promise<string | undefined> {
134 const emulatorsList = (await this.adbHelper.getOnlineDevices()).map(device => device.id);
135 const quickPickOptions: QuickPickOptions = {
136 ignoreFocusOut: true,
137 canPickMany: false,
138 placeHolder: localize("SelectOnlineDevice", "Select online Android device"),
139 };
140 let result: string | undefined = emulatorsList[0];
141 if (emulatorsList.length > 1) {
142 result = await window.showQuickPick(emulatorsList, quickPickOptions);
143 }
144 return result?.toString();
145 }
146
147 protected async getVirtualDevicesNamesList(): Promise<string[]> {
148 const res = await this.childProcess.execToString(
149 `${AndroidEmulatorManager.EMULATOR_COMMAND} ${AndroidEmulatorManager.EMULATOR_LIST_AVDS_COMMAND}`,
150 );
151 let emulatorsList: string[] = [];
152 if (res) {
153 emulatorsList = res.split(/\r?\n|\r/g);
154 const indexOfBlank = emulatorsList.indexOf("");
155 if (emulatorsList.indexOf("") >= 0) {
156 emulatorsList.splice(indexOfBlank, 1);
157 }
158 }
159 return emulatorsList;
160 }
161}
162