microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.11.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/androidPlatform.ts

389lines · 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
64private 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];
0d77292aJiglioNero4 years ago211}
212} catch (error) {
4cd25962JiglioNero4 years ago213if (!targetId) {
214targetId = await this.getTargetIdForRunApp(onlineTargetsIds);
215}
0d77292aJiglioNero4 years ago216if (
217error.message ===
218ErrorHelper.getInternalError(
219InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator,
220).message &&
4cd25962JiglioNero4 years ago221onlineTargetsIds.length >= 1 &&
222targetId
0d77292aJiglioNero4 years ago223) {
224/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
225this.needsToLaunchApps = true;
4cd25962JiglioNero4 years ago226devicesIdsForLaunch = shouldLaunchInAllDevices ? onlineTargetsIds : [targetId];
0d77292aJiglioNero4 years ago227} else {
228throw error;
229}
230}
231
4cd25962JiglioNero4 years ago232await PromiseUtil.forEach(devicesIdsForLaunch, deviceId =>
233this.launchAppWithADBReverseAndLogCat(deviceId),
0d77292aJiglioNero4 years ago234);
52f3873ddigeff10 years ago235});
236}
237
4cd25962JiglioNero4 years ago238public async enableJSDebuggingMode(): Promise<void> {
34472878RedMickey5 years ago239return this.adbHelper.switchDebugMode(
240this.runOptions.projectRoot,
241this.packageName,
242true,
4cd25962JiglioNero4 years ago243(await this.getTarget()).id,
69ad2ab3RedMickey5 years ago244this.getAppIdSuffixFromRunArgumentsIfExists(),
34472878RedMickey5 years ago245);
b57ea017Artem Egorov8 years ago246}
247
4cd25962JiglioNero4 years ago248public async disableJSDebuggingMode(): Promise<void> {
34472878RedMickey5 years ago249return this.adbHelper.switchDebugMode(
250this.runOptions.projectRoot,
251this.packageName,
252false,
4cd25962JiglioNero4 years ago253(await this.getTarget()).id,
69ad2ab3RedMickey5 years ago254this.getAppIdSuffixFromRunArgumentsIfExists(),
34472878RedMickey5 years ago255);
52f3873ddigeff10 years ago256}
257
ce5e88eeYuri Skorokhodov5 years ago258public prewarmBundleCache(): Promise<void> {
259c018fYuri Skorokhodov5 years ago259return this.packager.prewarmBundleCache(PlatformType.Android);
299b0557Patricio Beltran10 years ago260}
261
cbc7ac5bArtem Egorov7 years ago262public getRunArguments(): string[] {
8022afdfVladimir Kotikov8 years ago263let runArguments: string[] = [];
264
8df5011eYuri Skorokhodov5 years ago265if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
8022afdfVladimir Kotikov8 years ago266runArguments = this.runOptions.runArguments;
267} else {
268if (this.runOptions.variant) {
269runArguments.push("--variant", this.runOptions.variant);
270}
271if (this.runOptions.target) {
34472878RedMickey5 years ago272if (
4cd25962JiglioNero4 years ago273this.runOptions.target !== TargetType.Device &&
274this.runOptions.target !== TargetType.Simulator
34472878RedMickey5 years ago275) {
cbc7ac5bArtem Egorov7 years ago276runArguments.push("--deviceId", this.runOptions.target);
277}
8022afdfVladimir Kotikov8 years ago278}
279}
280
281return runArguments;
282}
283
34472878RedMickey5 years ago284public dispose(): void {
8df5011eYuri Skorokhodov5 years ago285if (this.logCatMonitor) {
286LogCatMonitorManager.delMonitor(this.logCatMonitor.deviceId);
287this.logCatMonitor = null;
288}
289}
290
4cd25962JiglioNero4 years ago291public async getTargetFromRunArgs(): Promise<AndroidTarget | undefined> {
292if (this.runOptions.runArguments && this.runOptions.runArguments.length) {
293const deviceId = GeneralMobilePlatform.getOptFromRunArgs(
294this.runOptions.runArguments,
295"--deviceId",
296);
297if (deviceId) {
298return new AndroidTarget(true, this.adbHelper.isVirtualTarget(deviceId), deviceId);
299}
300}
301return undefined;
302}
303
304private async getTargetIdForRunApp(onlineTargetsIds: string[]): Promise<string> {
305let deviceId: string | undefined;
306if (this.runOptions.runArguments && this.runOptions.runArguments.length) {
307deviceId = GeneralMobilePlatform.getOptFromRunArgs(
308this.runOptions.runArguments,
309"--deviceId",
310);
311}
312return deviceId
313? deviceId
314: this.runOptions.target &&
315this.runOptions.target !== TargetType.Simulator &&
316this.runOptions.target !== TargetType.Device &&
317onlineTargetsIds.find(id => id === this.runOptions.target)
318? this.runOptions.target
319: (await this.getTarget()).id;
320}
321
69ad2ab3RedMickey5 years ago322private getAppIdSuffixFromRunArgumentsIfExists(): string | undefined {
323const appIdSuffixIndex = this.runArguments.indexOf("--appIdSuffix");
324if (appIdSuffixIndex > -1) {
325return this.runArguments[appIdSuffixIndex + 1];
326}
327return undefined;
328}
329
4cd25962JiglioNero4 years ago330private async launchAppWithADBReverseAndLogCat(deviceId: string): Promise<void> {
331await this.configureADBReverseWhenApplicable(deviceId);
0d77292aJiglioNero4 years ago332if (this.needsToLaunchApps) {
4cd25962JiglioNero4 years ago333await this.adbHelper.launchApp(this.runOptions.projectRoot, this.packageName, deviceId);
0d77292aJiglioNero4 years ago334}
4cd25962JiglioNero4 years ago335return this.startMonitoringLogCat(deviceId, this.runOptions.logCatArguments);
52f3873ddigeff10 years ago336}
337
4cd25962JiglioNero4 years ago338private async configureADBReverseWhenApplicable(deviceId: string): Promise<void> {
0d77292aJiglioNero4 years ago339// For other emulators and devices we try to enable adb reverse
4cd25962JiglioNero4 years ago340const apiVersion = await this.adbHelper.apiVersion(deviceId);
0d77292aJiglioNero4 years ago341if (apiVersion >= AndroidAPILevel.LOLLIPOP) {
342// If we support adb reverse
343try {
09f6024fHeniker4 years ago344void this.adbHelper.reverseAdb(deviceId, Number(this.runOptions.packagerPort));
0d77292aJiglioNero4 years ago345} catch (error) {
346// "adb reverse" command could work incorrectly with remote devices, then skip the error and try to go on
347if (
4cd25962JiglioNero4 years ago348this.adbHelper.isRemoteTarget(deviceId) &&
0d77292aJiglioNero4 years ago349error.message.includes(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS[3].pattern)
350) {
351this.logger.warning(error.message);
b57ea017Artem Egorov8 years ago352} else {
0d77292aJiglioNero4 years ago353throw error;
b57ea017Artem Egorov8 years ago354}
0d77292aJiglioNero4 years ago355}
356} else {
357this.logger.warning(
358localize(
359"DeviceSupportsOnlyAPILevel",
360"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 ago361deviceId,
0d77292aJiglioNero4 years ago362apiVersion,
363AndroidAPILevel.LOLLIPOP,
364),
365);
366}
52f3873ddigeff10 years ago367}
368
0d77292aJiglioNero4 years ago369private async getPackageName(): Promise<string> {
370const appName = await new Package(this.runOptions.projectRoot).name();
371return new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot);
52f3873ddigeff10 years ago372}
373
4cd25962JiglioNero4 years ago374private startMonitoringLogCat(deviceId: string, logCatArguments: string[]): void {
375LogCatMonitorManager.delMonitor(deviceId); // Stop previous logcat monitor if it's running
0a68f8dbArtem Egorov8 years ago376
377// this.logCatMonitor can be mutated, so we store it locally too
4cd25962JiglioNero4 years ago378this.logCatMonitor = new LogCatMonitor(deviceId, this.adbHelper, logCatArguments);
8df5011eYuri Skorokhodov5 years ago379LogCatMonitorManager.addMonitor(this.logCatMonitor);
34472878RedMickey5 years ago380this.logCatMonitor
381.start() // The LogCat will continue running forever, so we don't wait for it
382.catch(error => {
383this.logger.warning(error);
384this.logger.warning(
385localize("ErrorWhileMonitoringLogCat", "Error while monitoring LogCat"),
386);
387}); // The LogCatMonitor failing won't stop the debugging experience
0a68f8dbArtem Egorov8 years ago388}
ef902673Vladimir Kotikov9 years ago389}