microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.13.3

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/androidPlatform.ts

427lines · modeblame

52f3873ddigeff10 years ago1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
df8c800dArtem Egorov8 years ago4import * as semver from "semver";
09f6024fHeniker4 years ago5import * as nls from "vscode-nls";
4cd25962JiglioNero4 years ago6import { MobilePlatformDeps, TargetType } from "../generalPlatform";
8df5011eYuri Skorokhodov5 years ago7import { IAndroidRunOptions, PlatformType } from "../launchArgs";
8import { Package } from "../../common/node/package";
9import { OutputVerifier, PatternToFailure } from "../../common/outputVerifier";
10import { TelemetryHelper } from "../../common/telemetryHelper";
11import { CommandExecutor } from "../../common/commandExecutor";
d7d405aeYuri Skorokhodov7 years ago12import { InternalErrorCode } from "../../common/error/internalErrorCode";
13import { ErrorHelper } from "../../common/error/errorHelper";
4bb0956eRedMickey5 years ago14import { notNullOrUndefined } from "../../common/utils";
ce5e88eeYuri Skorokhodov5 years ago15import { PromiseUtil } from "../../common/node/promise";
09f6024fHeniker4 years ago16import { GeneralMobilePlatform } from "../generalMobilePlatform";
47927908RedMickey4 years ago17import { ProjectVersionHelper } from "../../common/projectVersionHelper";
8df5011eYuri Skorokhodov5 years ago18import { LogCatMonitorManager } from "./logCatMonitorManager";
4cd25962JiglioNero4 years ago19import { AndroidTarget, AndroidTargetManager } from "./androidTargetManager";
20import { AdbHelper, AndroidAPILevel } from "./adb";
09f6024fHeniker4 years ago21import { LogCatMonitor } from "./logCatMonitor";
22import { PackageNameResolver } from "./packageNameResolver";
23
34472878RedMickey5 years ago24nls.config({
25messageFormat: nls.MessageFormat.bundle,
26bundleFormat: nls.BundleFormat.standalone,
27})();
d7d405aeYuri Skorokhodov7 years ago28const localize = nls.loadMessageBundle();
5c8365a6Artem Egorov8 years ago29
52f3873ddigeff10 years ago30/**
31* Android specific platform implementation for debugging RN applications.
32*/
299b0557Patricio Beltran10 years ago33export class AndroidPlatform extends GeneralMobilePlatform {
0a68f8dbArtem Egorov8 years ago34// We should add the common Android build/run errors we find to this list
34472878RedMickey5 years ago35private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure[] = [
36{
37pattern: "Failed to install on any devices",
38errorCode: InternalErrorCode.AndroidCouldNotInstallTheAppOnAnyAvailibleDevice,
39},
40{
41pattern: "com.android.ddmlib.ShellCommandUnresponsiveException",
42errorCode: InternalErrorCode.AndroidShellCommandTimedOut,
43},
44{
45pattern: "Android project not found",
46errorCode: InternalErrorCode.AndroidProjectNotFound,
47},
48{
49pattern: "error: more than one device/emulator",
50errorCode: InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator,
51},
52{
09f6024fHeniker4 years ago53pattern: /^Error: Activity class {.*} does not exist\.$/m,
34472878RedMickey5 years ago54errorCode: InternalErrorCode.AndroidFailedToLaunchTheSpecifiedActivity,
55},
56];
57
58private static RUN_ANDROID_SUCCESS_PATTERNS: string[] = [
59"BUILD SUCCESSFUL",
60"Starting the app",
61"Starting: Intent",
62];
52f3873ddigeff10 years ago63
176f99c8ConnorQi013 months ago64private packageName!: string;
db6fd42aRuslan Bikkinin7 years ago65private adbHelper: AdbHelper;
8df5011eYuri Skorokhodov5 years ago66private logCatMonitor: LogCatMonitor | null = null;
52f3873ddigeff10 years ago67private needsToLaunchApps: boolean = false;
db6fd42aRuslan Bikkinin7 years ago68
4cd25962JiglioNero4 years ago69protected targetManager: AndroidTargetManager;
70protected target?: AndroidTarget;
52f3873ddigeff10 years ago71
299b0557Patricio Beltran10 years ago72// We set remoteExtension = null so that if there is an instance of androidPlatform that wants to have it's custom remoteExtension it can. This is specifically useful for tests.
7daed3fcArtem Egorov8 years ago73constructor(protected runOptions: IAndroidRunOptions, platformDeps: MobilePlatformDeps = {}) {
0a68f8dbArtem Egorov8 years ago74super(runOptions, platformDeps);
4dfb1c4cetatanova5 years ago75this.adbHelper = new AdbHelper(
76this.runOptions.projectRoot,
77runOptions.nodeModulesRoot,
78this.logger,
79);
4cd25962JiglioNero4 years ago80this.targetManager = new AndroidTargetManager(this.adbHelper);
db6fd42aRuslan Bikkinin7 years ago81}
43e1a996Ruslan Bikkinin7 years ago82
4cd25962JiglioNero4 years ago83public showDevMenu(deviceId?: string): Promise<void> {
84return this.adbHelper.showDevMenu(deviceId);
52f3873ddigeff10 years ago85}
86
4cd25962JiglioNero4 years ago87public reloadApp(deviceId?: string): Promise<void> {
88return this.adbHelper.reloadApp(deviceId);
89}
90
91public async getTarget(): Promise<AndroidTarget> {
92if (!this.target) {
93const onlineTargets = await this.adbHelper.getOnlineTargets();
94const target = await this.getTargetFromRunArgs();
95if (target) {
96this.target = target;
97} else {
98const onlineTargetsBySpecifiedType = onlineTargets.filter(target => {
99switch (this.runOptions.target) {
100case TargetType.Simulator:
101return target.isVirtualTarget;
102case TargetType.Device:
103return !target.isVirtualTarget;
104case undefined:
105case "":
106return true;
107default:
108return target.id === this.runOptions.target;
109}
110});
111if (onlineTargetsBySpecifiedType.length) {
112this.target = AndroidTarget.fromInterface(onlineTargetsBySpecifiedType[0]);
113} else if (onlineTargets.length) {
114this.logger.warning(
115localize(
116"ThereIsNoOnlineTargetWithSpecifiedTargetType",
117"There is no any online target with specified target type '{0}'. Continue with any online target.",
118this.runOptions.target,
119),
120);
121this.target = AndroidTarget.fromInterface(onlineTargets[0]);
122} else {
123throw ErrorHelper.getInternalError(
124InternalErrorCode.AndroidThereIsNoAnyOnlineDebuggableTarget,
125);
126}
0d77292aJiglioNero4 years ago127}
68a5b8d5JiglioNero5 years ago128}
4cd25962JiglioNero4 years ago129return this.target;
68a5b8d5JiglioNero5 years ago130}
131
0d77292aJiglioNero4 years ago132public async runApp(shouldLaunchInAllDevices: boolean = false): Promise<void> {
549baae2RedMickey6 years ago133let extProps: any = {
031832ffArtem Egorov8 years ago134platform: {
259c018fYuri Skorokhodov5 years ago135value: PlatformType.Android,
031832ffArtem Egorov8 years ago136isPii: false,
137},
138};
139
549baae2RedMickey6 years ago140if (this.runOptions.isDirect) {
141extProps.isDirect = {
142value: true,
143isPii: false,
144};
e7a2c40dRedMickey4 years ago145this.projectObserver?.updateRNAndroidHermesProjectState(true);
549baae2RedMickey6 years ago146}
147
34472878RedMickey5 years ago148extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
149this.runOptions,
150this.runOptions.reactNativeVersions,
151extProps,
152);
ba953e9fRedMickey6 years ago153
0d77292aJiglioNero4 years ago154await TelemetryHelper.generate("AndroidPlatform.runApp", extProps, async () => {
34472878RedMickey5 years ago155const env = GeneralMobilePlatform.getEnvArgument(
156process.env,
157this.runOptions.env,
158this.runOptions.envFile,
159);
78c2b4deRedMickey6 years ago160
e3706a1cRedMickey6 years ago161if (
34472878RedMickey5 years ago162!semver.valid(
163this.runOptions.reactNativeVersions.reactNativeVersion,
09f6024fHeniker4 years ago164) /* Custom RN implementations should support this flag*/ ||
34472878RedMickey5 years ago165semver.gte(
166this.runOptions.reactNativeVersions.reactNativeVersion,
167AndroidPlatform.NO_PACKAGER_VERSION,
47927908RedMickey4 years ago168) ||
169ProjectVersionHelper.isCanaryVersion(
170this.runOptions.reactNativeVersions.reactNativeVersion,
34472878RedMickey5 years ago171)
e3706a1cRedMickey6 years ago172) {
173this.runArguments.push("--no-packager");
174}
175
09f6024fHeniker4 years ago176const mainActivity = GeneralMobilePlatform.getOptFromRunArgs(
34472878RedMickey5 years ago177this.runArguments,
178"--main-activity",
179);
e3706a1cRedMickey6 years ago180
181if (mainActivity) {
182this.adbHelper.setLaunchActivity(mainActivity);
4bb0956eRedMickey5 years ago183} else if (notNullOrUndefined(this.runOptions.debugLaunchActivity)) {
e3706a1cRedMickey6 years ago184this.runArguments.push("--main-activity", this.runOptions.debugLaunchActivity);
185this.adbHelper.setLaunchActivity(this.runOptions.debugLaunchActivity);
186}
187
34472878RedMickey5 years ago188const runAndroidSpawn = new CommandExecutor(
4dfb1c4cetatanova5 years ago189this.runOptions.nodeModulesRoot,
34472878RedMickey5 years ago190this.projectPath,
191this.logger,
192).spawnReactCommand("run-android", this.runArguments, { env });
e3706a1cRedMickey6 years ago193const output = new OutputVerifier(
34472878RedMickey5 years ago194() => Promise.resolve(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
195() => Promise.resolve(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS),
196PlatformType.Android,
197).process(runAndroidSpawn);
e3706a1cRedMickey6 years ago198
4cd25962JiglioNero4 years ago199let devicesIdsForLaunch: string[] = [];
200const onlineTargetsIds = (await this.adbHelper.getOnlineTargets()).map(
201target => target.id,
202);
203let targetId: string | undefined;
0d77292aJiglioNero4 years ago204try {
205try {
206await output;
207} finally {
4cd25962JiglioNero4 years ago208targetId = await this.getTargetIdForRunApp(onlineTargetsIds);
209this.packageName = await this.getPackageName();
210devicesIdsForLaunch = [targetId];
80dcd561ConnorQi013 months ago211
212// Save target info for status indicator
213if (targetId) {
214const onlineTargets = await this.adbHelper.getOnlineTargets();
215const targetInfo = onlineTargets.find(t => t.id === targetId);
216if (targetInfo) {
217let deviceName = targetId;
218try {
219// For emulators, try to get AVD name first
220if (targetInfo.isVirtualTarget) {
221const avdName = await this.adbHelper.getAvdNameById(targetId);
222if (avdName) {
223deviceName = `${avdName} (${targetId})`;
224}
225} else {
226// For physical devices, get model name
227const modelResult = await this.adbHelper.executeQuery(
228targetId,
229"shell getprop ro.product.model",
230);
231const model = modelResult.trim();
232if (model) {
233deviceName = `${model} (${targetId})`;
234}
235}
236} catch (error) {}
237this.target = new AndroidTarget(
238targetInfo.isOnline,
239targetInfo.isVirtualTarget,
240targetInfo.id,
241deviceName,
242);
243}
244}
0d77292aJiglioNero4 years ago245}
246} catch (error) {
4cd25962JiglioNero4 years ago247if (!targetId) {
248targetId = await this.getTargetIdForRunApp(onlineTargetsIds);
249}
0d77292aJiglioNero4 years ago250if (
176f99c8ConnorQi013 months ago251(error as Error).message ===
0d77292aJiglioNero4 years ago252ErrorHelper.getInternalError(
253InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator,
254).message &&
4cd25962JiglioNero4 years ago255onlineTargetsIds.length >= 1 &&
256targetId
0d77292aJiglioNero4 years ago257) {
258/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
259this.needsToLaunchApps = true;
4cd25962JiglioNero4 years ago260devicesIdsForLaunch = shouldLaunchInAllDevices ? onlineTargetsIds : [targetId];
0d77292aJiglioNero4 years ago261} else {
262throw error;
263}
264}
265
4cd25962JiglioNero4 years ago266await PromiseUtil.forEach(devicesIdsForLaunch, deviceId =>
267this.launchAppWithADBReverseAndLogCat(deviceId),
0d77292aJiglioNero4 years ago268);
52f3873ddigeff10 years ago269});
270}
271
4cd25962JiglioNero4 years ago272public async enableJSDebuggingMode(): Promise<void> {
34472878RedMickey5 years ago273return this.adbHelper.switchDebugMode(
274this.runOptions.projectRoot,
275this.packageName,
276true,
4cd25962JiglioNero4 years ago277(await this.getTarget()).id,
69ad2ab3RedMickey5 years ago278this.getAppIdSuffixFromRunArgumentsIfExists(),
34472878RedMickey5 years ago279);
b57ea017Artem Egorov8 years ago280}
281
4cd25962JiglioNero4 years ago282public async disableJSDebuggingMode(): Promise<void> {
34472878RedMickey5 years ago283return this.adbHelper.switchDebugMode(
284this.runOptions.projectRoot,
285this.packageName,
286false,
4cd25962JiglioNero4 years ago287(await this.getTarget()).id,
69ad2ab3RedMickey5 years ago288this.getAppIdSuffixFromRunArgumentsIfExists(),
34472878RedMickey5 years ago289);
52f3873ddigeff10 years ago290}
291
ce5e88eeYuri Skorokhodov5 years ago292public prewarmBundleCache(): Promise<void> {
259c018fYuri Skorokhodov5 years ago293return this.packager.prewarmBundleCache(PlatformType.Android);
299b0557Patricio Beltran10 years ago294}
295
cbc7ac5bArtem Egorov7 years ago296public getRunArguments(): string[] {
8022afdfVladimir Kotikov8 years ago297let runArguments: string[] = [];
298
8df5011eYuri Skorokhodov5 years ago299if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
8022afdfVladimir Kotikov8 years ago300runArguments = this.runOptions.runArguments;
301} else {
302if (this.runOptions.variant) {
303runArguments.push("--variant", this.runOptions.variant);
304}
305if (this.runOptions.target) {
34472878RedMickey5 years ago306if (
4cd25962JiglioNero4 years ago307this.runOptions.target !== TargetType.Device &&
308this.runOptions.target !== TargetType.Simulator
34472878RedMickey5 years ago309) {
cbc7ac5bArtem Egorov7 years ago310runArguments.push("--deviceId", this.runOptions.target);
311}
8022afdfVladimir Kotikov8 years ago312}
313}
314
315return runArguments;
316}
317
34472878RedMickey5 years ago318public dispose(): void {
8df5011eYuri Skorokhodov5 years ago319if (this.logCatMonitor) {
320LogCatMonitorManager.delMonitor(this.logCatMonitor.deviceId);
321this.logCatMonitor = null;
322}
323}
324
4cd25962JiglioNero4 years ago325public async getTargetFromRunArgs(): Promise<AndroidTarget | undefined> {
326if (this.runOptions.runArguments && this.runOptions.runArguments.length) {
327const deviceId = GeneralMobilePlatform.getOptFromRunArgs(
328this.runOptions.runArguments,
329"--deviceId",
330);
331if (deviceId) {
332return new AndroidTarget(true, this.adbHelper.isVirtualTarget(deviceId), deviceId);
333}
334}
335return undefined;
336}
337
338private async getTargetIdForRunApp(onlineTargetsIds: string[]): Promise<string> {
339let deviceId: string | undefined;
340if (this.runOptions.runArguments && this.runOptions.runArguments.length) {
341deviceId = GeneralMobilePlatform.getOptFromRunArgs(
342this.runOptions.runArguments,
343"--deviceId",
344);
345}
346return deviceId
347? deviceId
348: this.runOptions.target &&
349this.runOptions.target !== TargetType.Simulator &&
350this.runOptions.target !== TargetType.Device &&
351onlineTargetsIds.find(id => id === this.runOptions.target)
352? this.runOptions.target
353: (await this.getTarget()).id;
354}
355
69ad2ab3RedMickey5 years ago356private getAppIdSuffixFromRunArgumentsIfExists(): string | undefined {
357const appIdSuffixIndex = this.runArguments.indexOf("--appIdSuffix");
358if (appIdSuffixIndex > -1) {
359return this.runArguments[appIdSuffixIndex + 1];
360}
361return undefined;
362}
363
4cd25962JiglioNero4 years ago364private async launchAppWithADBReverseAndLogCat(deviceId: string): Promise<void> {
365await this.configureADBReverseWhenApplicable(deviceId);
0d77292aJiglioNero4 years ago366if (this.needsToLaunchApps) {
4cd25962JiglioNero4 years ago367await this.adbHelper.launchApp(this.runOptions.projectRoot, this.packageName, deviceId);
0d77292aJiglioNero4 years ago368}
4cd25962JiglioNero4 years ago369return this.startMonitoringLogCat(deviceId, this.runOptions.logCatArguments);
52f3873ddigeff10 years ago370}
371
4cd25962JiglioNero4 years ago372private async configureADBReverseWhenApplicable(deviceId: string): Promise<void> {
0d77292aJiglioNero4 years ago373// For other emulators and devices we try to enable adb reverse
4cd25962JiglioNero4 years ago374const apiVersion = await this.adbHelper.apiVersion(deviceId);
0d77292aJiglioNero4 years ago375if (apiVersion >= AndroidAPILevel.LOLLIPOP) {
376// If we support adb reverse
377try {
09f6024fHeniker4 years ago378void this.adbHelper.reverseAdb(deviceId, Number(this.runOptions.packagerPort));
0d77292aJiglioNero4 years ago379} catch (error) {
380// "adb reverse" command could work incorrectly with remote devices, then skip the error and try to go on
381if (
4cd25962JiglioNero4 years ago382this.adbHelper.isRemoteTarget(deviceId) &&
176f99c8ConnorQi013 months ago383(error as Error).message.includes(
384AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS[3].pattern instanceof RegExp
385? AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS[3].pattern.source
386: AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS[3].pattern,
387)
0d77292aJiglioNero4 years ago388) {
176f99c8ConnorQi013 months ago389this.logger.warning((error as Error).message);
b57ea017Artem Egorov8 years ago390} else {
0d77292aJiglioNero4 years ago391throw error;
b57ea017Artem Egorov8 years ago392}
0d77292aJiglioNero4 years ago393}
394} else {
395this.logger.warning(
396localize(
397"DeviceSupportsOnlyAPILevel",
398"Device {0} supports only API Level {1}. \n Level {2} is needed to support port forwarding via adb reverse. \n For debugging to work you'll need <Shake or press menu button> for the dev menu, \n go into <Dev Settings> and configure <Debug Server host & port for Device> to be \n an IP address of your computer that the Device can reach. More info at: \n https://facebook.github.io/react-native/docs/debugging.html#debugging-react-native-apps",
4cd25962JiglioNero4 years ago399deviceId,
0d77292aJiglioNero4 years ago400apiVersion,
401AndroidAPILevel.LOLLIPOP,
402),
403);
404}
52f3873ddigeff10 years ago405}
406
0d77292aJiglioNero4 years ago407private async getPackageName(): Promise<string> {
408const appName = await new Package(this.runOptions.projectRoot).name();
409return new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot);
52f3873ddigeff10 years ago410}
411
4cd25962JiglioNero4 years ago412private startMonitoringLogCat(deviceId: string, logCatArguments: string[]): void {
413LogCatMonitorManager.delMonitor(deviceId); // Stop previous logcat monitor if it's running
0a68f8dbArtem Egorov8 years ago414
415// this.logCatMonitor can be mutated, so we store it locally too
4cd25962JiglioNero4 years ago416this.logCatMonitor = new LogCatMonitor(deviceId, this.adbHelper, logCatArguments);
8df5011eYuri Skorokhodov5 years ago417LogCatMonitorManager.addMonitor(this.logCatMonitor);
34472878RedMickey5 years ago418this.logCatMonitor
419.start() // The LogCat will continue running forever, so we don't wait for it
420.catch(error => {
421this.logger.warning(error);
422this.logger.warning(
423localize("ErrorWhileMonitoringLogCat", "Error while monitoring LogCat"),
424);
425}); // The LogCatMonitor failing won't stop the debugging experience
0a68f8dbArtem Egorov8 years ago426}
ef902673Vladimir Kotikov9 years ago427}