microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
dev/v-peq/remove-ios-relative-project-path

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/androidTargetManager.ts

222lines · 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 * as nls from "vscode-nls";
5import { MobileTargetManager } from "../mobileTargetManager";
6import { ChildProcess } from "../../common/node/childProcess";
7import { OutputChannelLogger } from "../log/OutputChannelLogger";
8import { IDebuggableMobileTarget, IMobileTarget, MobileTarget } from "../mobileTarget";
9import { TargetType } from "../generalPlatform";
10import { PromiseUtil } from "../../common/node/promise";
11import { InternalErrorCode } from "../../common/error/internalErrorCode";
12import { ErrorHelper } from "../../common/error/errorHelper";
13import { AdbHelper } from "./adb";
14
15nls.config({
16 messageFormat: nls.MessageFormat.bundle,
17 bundleFormat: nls.BundleFormat.standalone,
18})();
19const localize = nls.loadMessageBundle();
20
21export class AndroidTarget extends MobileTarget {
22 public static fromInterface(obj: IDebuggableMobileTarget): AndroidTarget {
23 return new AndroidTarget(obj.isOnline, obj.isVirtualTarget, obj.id, obj.name);
24 }
25
26 constructor(isOnline: boolean, isVirtualTarget: boolean, id: string, name?: string) {
27 super(isOnline, isVirtualTarget, id, name ? name : id);
28 }
29}
30
31export class AndroidTargetManager extends MobileTargetManager {
32 private static readonly EMULATOR_COMMAND = "emulator";
33 private static readonly EMULATOR_AVD_START_COMMAND = "-avd";
34 private static readonly EMULATOR_START_TIMEOUT = 120;
35
36 private logger: OutputChannelLogger;
37 private adbHelper: AdbHelper;
38 private childProcess: ChildProcess;
39
40 constructor(adbHelper: AdbHelper) {
41 super();
42 this.adbHelper = adbHelper;
43 this.logger = OutputChannelLogger.getChannel(OutputChannelLogger.MAIN_CHANNEL_NAME, true);
44 this.childProcess = new ChildProcess();
45 }
46
47 public async isVirtualTarget(target: string): Promise<boolean> {
48 try {
49 if (target === TargetType.Device) {
50 return false;
51 } else if (
52 target === TargetType.Simulator ||
53 target.match(AdbHelper.AndroidSDKEmulatorPattern)
54 ) {
55 return true;
56 }
57 const onlineTarget = await this.adbHelper.findOnlineTargetById(target);
58 if (onlineTarget) {
59 return onlineTarget.isVirtualTarget;
60 } else if ((await this.adbHelper.getAvdsNames()).includes(target)) {
61 return true;
62 }
63 throw new Error("There is no such target");
64 } catch (error) {
65 throw ErrorHelper.getNestedError(
66 error as Error,
67 InternalErrorCode.CouldNotRecognizeTargetType,
68 target,
69 );
70 }
71 }
72
73 public async selectAndPrepareTarget(
74 filter?: (el: IMobileTarget) => boolean,
75 ): Promise<AndroidTarget | undefined> {
76 const selectedTarget = await this.startSelection(filter);
77 if (selectedTarget) {
78 if (!selectedTarget.isOnline && selectedTarget.isVirtualTarget) {
79 return this.launchSimulator(selectedTarget);
80 }
81 if (selectedTarget.id) {
82 return AndroidTarget.fromInterface(<IDebuggableMobileTarget>selectedTarget);
83 }
84 }
85 return undefined;
86 }
87
88 public async collectTargets(targetType?: TargetType): Promise<void> {
89 const targetList: IMobileTarget[] = [];
90 const collectSimulators = !targetType || targetType === TargetType.Simulator;
91 const collectDevices = !targetType || targetType === TargetType.Device;
92
93 try {
94 if (collectSimulators) {
95 const emulatorsNames: string[] = await this.adbHelper.getAvdsNames();
96 targetList.push(
97 ...emulatorsNames.map(name => ({
98 name,
99 isOnline: false,
100 isVirtualTarget: true,
101 })),
102 );
103 }
104 } catch (error) {
105 // We throw an exception only if the target type is explicitly specified,
106 // otherwise we collect only those targets that we can collect
107 if (targetType === TargetType.Simulator) {
108 throw error;
109 }
110 this.logger.warning(
111 localize(
112 "CouldNotUseEmulators",
113 "An error occurred while trying to get installed emulators: {0}\nContinue using only online targets",
114 error instanceof Error ? (error as Error).message : (error as Error).toString(),
115 ),
116 );
117 }
118
119 const onlineTargets = await this.adbHelper.getOnlineTargets();
120 for (const device of onlineTargets) {
121 if (device.isVirtualTarget && collectSimulators) {
122 const avdName = await this.adbHelper.getAvdNameById(device.id);
123 const emulatorTarget = targetList.find(target => target.name === avdName);
124 if (emulatorTarget) {
125 emulatorTarget.isOnline = true;
126 emulatorTarget.id = device.id;
127 }
128 } else if (!device.isVirtualTarget && collectDevices) {
129 targetList.push({ id: device.id, isOnline: true, isVirtualTarget: false });
130 }
131 }
132
133 this.targets = targetList;
134 }
135
136 protected async startSelection(
137 filter?: (el: IMobileTarget) => boolean,
138 ): Promise<IMobileTarget | undefined> {
139 return this.selectTarget(filter);
140 }
141
142 protected async launchSimulator(emulatorTarget: IMobileTarget): Promise<AndroidTarget> {
143 return new Promise<AndroidTarget>((resolve, reject) => {
144 let emulatorLaunchFailed = false;
145 const emulatorProcess = this.childProcess.spawn(
146 AndroidTargetManager.EMULATOR_COMMAND,
147 [AndroidTargetManager.EMULATOR_AVD_START_COMMAND, emulatorTarget.name as string],
148 {
149 detached: true,
150 },
151 true,
152 );
153 emulatorProcess.outcome.catch(error => {
154 emulatorLaunchFailed = true;
155 if (
156 process.platform == "win32" &&
157 process.env.SESSIONNAME &&
158 process.env.SESSIONNAME.toLowerCase().includes("rdp-tcp")
159 ) {
160 this.logger.warning(
161 localize(
162 "RDPEmulatorWarning",
163 "Android emulator was launched from the Windows RDP session, this might lead to failures.",
164 ),
165 );
166 }
167 reject(
168 new Error(`Virtual device launch finished with an exception: ${String(error)}`),
169 );
170 });
171 emulatorProcess.spawnedProcess.unref();
172
173 const condition = async () => {
174 if (emulatorLaunchFailed)
175 throw new Error("Android emulator launch failed unexpectedly");
176 const connectedDevices = await this.adbHelper.getOnlineTargets();
177 for (const target of connectedDevices) {
178 const onlineAvdName = await this.adbHelper.getAvdNameById(target.id);
179 if (onlineAvdName === emulatorTarget.name) {
180 return target.id;
181 }
182 }
183 return null;
184 };
185
186 void PromiseUtil.waitUntil<string>(
187 condition,
188 1000,
189 AndroidTargetManager.EMULATOR_START_TIMEOUT * 1000,
190 ).then(
191 emulatorId => {
192 if (emulatorId) {
193 emulatorTarget.id = emulatorId;
194 emulatorTarget.isOnline = true;
195 this.logger.info(
196 localize(
197 "EmulatorLaunched",
198 "Launched Android emulator {0}",
199 emulatorTarget.name,
200 ),
201 );
202 resolve(
203 AndroidTarget.fromInterface(<IDebuggableMobileTarget>emulatorTarget),
204 );
205 } else {
206 reject(
207 new Error(
208 `Virtual device launch finished with an exception: ${localize(
209 "EmulatorStartWarning",
210 "Could not start the emulator {0} within {1} seconds.",
211 emulatorTarget.name,
212 AndroidTargetManager.EMULATOR_START_TIMEOUT,
213 )}`,
214 ),
215 );
216 }
217 },
218 () => {},
219 );
220 });
221 }
222}
223