microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
c78564627d05eb04bd03fb8d3978871935d0c077

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/androidTargetManager.ts

212lines · 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 { AdbHelper } from "./adb";
7import { ChildProcess } from "../../common/node/childProcess";
8import { OutputChannelLogger } from "../log/OutputChannelLogger";
9import { IDebuggableMobileTarget, IMobileTarget, MobileTarget } from "../mobileTarget";
10import { TargetType } from "../generalPlatform";
11import { PromiseUtil } from "../../common/node/promise";
12import { InternalErrorCode } from "../../common/error/internalErrorCode";
13import { ErrorHelper } from "../../common/error/errorHelper";
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 } else {
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 } else {
63 throw new Error("There is no such target");
64 }
65 }
66 } catch (error) {
67 throw ErrorHelper.getNestedError(
68 error,
69 InternalErrorCode.CouldNotRecognizeTargetType,
70 target,
71 );
72 }
73 }
74
75 public async selectAndPrepareTarget(
76 filter?: (el: IMobileTarget) => boolean,
77 ): Promise<AndroidTarget | undefined> {
78 const selectedTarget = await this.startSelection(filter);
79 if (selectedTarget) {
80 if (!selectedTarget.isOnline && selectedTarget.isVirtualTarget) {
81 return this.launchSimulator(selectedTarget);
82 } else {
83 if (selectedTarget.id) {
84 return AndroidTarget.fromInterface(<IDebuggableMobileTarget>selectedTarget);
85 }
86 }
87 }
88 return undefined;
89 }
90
91 public async collectTargets(targetType?: TargetType): Promise<void> {
92 const targetList: IMobileTarget[] = [];
93 const collectSimulators = !targetType || targetType === TargetType.Simulator;
94 const collectDevices = !targetType || targetType === TargetType.Device;
95
96 try {
97 if (collectSimulators) {
98 const emulatorsNames: string[] = await this.adbHelper.getAvdsNames();
99 targetList.push(
100 ...emulatorsNames.map(name => {
101 return { name, isOnline: false, isVirtualTarget: true };
102 }),
103 );
104 }
105 } catch (error) {
106 // We throw an exception only if the target type is explicitly specified,
107 // otherwise we collect only those targets that we can collect
108 if (targetType === TargetType.Simulator) {
109 throw error;
110 }
111 this.logger.warning(
112 localize(
113 "CouldNotUseEmulators",
114 "An error occurred while trying to get installed emulators: {0}\nContinue using only online targets",
115 error instanceof Error ? error.message : error.toString(),
116 ),
117 );
118 }
119
120 const onlineTargets = await this.adbHelper.getOnlineTargets();
121 for (let device of onlineTargets) {
122 if (device.isVirtualTarget && collectSimulators) {
123 const avdName = await this.adbHelper.getAvdNameById(device.id);
124 const emulatorTarget = targetList.find(target => target.name === avdName);
125 if (emulatorTarget) {
126 emulatorTarget.isOnline = true;
127 emulatorTarget.id = device.id;
128 }
129 } else if (!device.isVirtualTarget && collectDevices) {
130 targetList.push({ id: device.id, isOnline: true, isVirtualTarget: false });
131 }
132 }
133
134 this.targets = targetList;
135 }
136
137 protected async startSelection(
138 filter?: (el: IMobileTarget) => boolean,
139 ): Promise<IMobileTarget | undefined> {
140 return this.selectTarget(filter);
141 }
142
143 protected async launchSimulator(emulatorTarget: IMobileTarget): Promise<AndroidTarget> {
144 return new Promise<AndroidTarget>((resolve, reject) => {
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 if (
155 process.platform == "win32" &&
156 process.env.SESSIONNAME &&
157 process.env.SESSIONNAME.toLowerCase().includes("rdp-tcp")
158 ) {
159 this.logger.warning(
160 localize(
161 "RDPEmulatorWarning",
162 "Android emulator was launched from the Windows RDP session, this might lead to failures.",
163 ),
164 );
165 }
166 reject(new Error(`Virtual device launch finished with an exception: ${error}`));
167 });
168 emulatorProcess.spawnedProcess.unref();
169
170 const condition = async () => {
171 const connectedDevices = await this.adbHelper.getOnlineTargets();
172 for (let target of connectedDevices) {
173 const onlineAvdName = await this.adbHelper.getAvdNameById(target.id);
174 if (onlineAvdName === emulatorTarget.name) {
175 return target.id;
176 }
177 }
178 return null;
179 };
180
181 return PromiseUtil.waitUntil<string>(
182 condition,
183 1000,
184 AndroidTargetManager.EMULATOR_START_TIMEOUT * 1000,
185 ).then(emulatorId => {
186 if (emulatorId) {
187 emulatorTarget.id = emulatorId;
188 emulatorTarget.isOnline = true;
189 this.logger.info(
190 localize(
191 "EmulatorLaunched",
192 "Launched Android emulator {0}",
193 emulatorTarget.name,
194 ),
195 );
196 resolve(AndroidTarget.fromInterface(<IDebuggableMobileTarget>emulatorTarget));
197 } else {
198 reject(
199 new Error(
200 `Virtual device launch finished with an exception: ${localize(
201 "EmulatorStartWarning",
202 "Could not start the emulator {0} within {1} seconds.",
203 emulatorTarget.name,
204 AndroidTargetManager.EMULATOR_START_TIMEOUT,
205 )}`,
206 ),
207 );
208 }
209 });
210 });
211 }
212}
213