microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.8.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/android/androidPlatform.ts

384lines · 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";
4cd25962JiglioNero4 years ago5import { MobilePlatformDeps, TargetType } from "../generalPlatform";
8df5011eYuri Skorokhodov5 years ago6import { IAndroidRunOptions, PlatformType } from "../launchArgs";
7import { Package } from "../../common/node/package";
8import { PackageNameResolver } from "./packageNameResolver";
9import { OutputVerifier, PatternToFailure } from "../../common/outputVerifier";
10import { TelemetryHelper } from "../../common/telemetryHelper";
11import { CommandExecutor } from "../../common/commandExecutor";
12import { LogCatMonitor } from "./logCatMonitor";
d7d405aeYuri Skorokhodov7 years ago13import * as nls from "vscode-nls";
14import { InternalErrorCode } from "../../common/error/internalErrorCode";
15import { ErrorHelper } from "../../common/error/errorHelper";
4bb0956eRedMickey5 years ago16import { notNullOrUndefined } from "../../common/utils";
ce5e88eeYuri Skorokhodov5 years ago17import { PromiseUtil } from "../../common/node/promise";
8df5011eYuri Skorokhodov5 years ago18import { LogCatMonitorManager } from "./logCatMonitorManager";
4cd25962JiglioNero4 years ago19import { AndroidTarget, AndroidTargetManager } from "./androidTargetManager";
20import { AdbHelper, AndroidAPILevel } from "./adb";
21import { GeneralMobilePlatform } from "../generalMobilePlatform";
34472878RedMickey5 years ago22nls.config({
23messageFormat: nls.MessageFormat.bundle,
24bundleFormat: nls.BundleFormat.standalone,
25})();
d7d405aeYuri Skorokhodov7 years ago26const localize = nls.loadMessageBundle();
5c8365a6Artem Egorov8 years ago27
52f3873ddigeff10 years ago28/**
29* Android specific platform implementation for debugging RN applications.
30*/
299b0557Patricio Beltran10 years ago31export class AndroidPlatform extends GeneralMobilePlatform {
0a68f8dbArtem Egorov8 years ago32// We should add the common Android build/run errors we find to this list
34472878RedMickey5 years ago33private static RUN_ANDROID_FAILURE_PATTERNS: PatternToFailure[] = [
34{
35pattern: "Failed to install on any devices",
36errorCode: InternalErrorCode.AndroidCouldNotInstallTheAppOnAnyAvailibleDevice,
37},
38{
39pattern: "com.android.ddmlib.ShellCommandUnresponsiveException",
40errorCode: InternalErrorCode.AndroidShellCommandTimedOut,
41},
42{
43pattern: "Android project not found",
44errorCode: InternalErrorCode.AndroidProjectNotFound,
45},
46{
47pattern: "error: more than one device/emulator",
48errorCode: InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator,
49},
50{
51pattern: /^Error: Activity class \{.*\} does not exist\.$/m,
52errorCode: InternalErrorCode.AndroidFailedToLaunchTheSpecifiedActivity,
53},
54];
55
56private static RUN_ANDROID_SUCCESS_PATTERNS: string[] = [
57"BUILD SUCCESSFUL",
58"Starting the app",
59"Starting: Intent",
60];
52f3873ddigeff10 years ago61
62private packageName: string;
db6fd42aRuslan Bikkinin7 years ago63private adbHelper: AdbHelper;
8df5011eYuri Skorokhodov5 years ago64private logCatMonitor: LogCatMonitor | null = null;
52f3873ddigeff10 years ago65private needsToLaunchApps: boolean = false;
db6fd42aRuslan Bikkinin7 years ago66
4cd25962JiglioNero4 years ago67protected targetManager: AndroidTargetManager;
68protected target?: AndroidTarget;
52f3873ddigeff10 years ago69
299b0557Patricio Beltran10 years ago70// 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 ago71constructor(protected runOptions: IAndroidRunOptions, platformDeps: MobilePlatformDeps = {}) {
0a68f8dbArtem Egorov8 years ago72super(runOptions, platformDeps);
4dfb1c4cetatanova5 years ago73this.adbHelper = new AdbHelper(
74this.runOptions.projectRoot,
75runOptions.nodeModulesRoot,
76this.logger,
77);
4cd25962JiglioNero4 years ago78this.targetManager = new AndroidTargetManager(this.adbHelper);
db6fd42aRuslan Bikkinin7 years ago79}
43e1a996Ruslan Bikkinin7 years ago80
4cd25962JiglioNero4 years ago81public showDevMenu(deviceId?: string): Promise<void> {
82return this.adbHelper.showDevMenu(deviceId);
52f3873ddigeff10 years ago83}
84
4cd25962JiglioNero4 years ago85public reloadApp(deviceId?: string): Promise<void> {
86return this.adbHelper.reloadApp(deviceId);
87}
88
89public async getTarget(): Promise<AndroidTarget> {
90if (!this.target) {
91const onlineTargets = await this.adbHelper.getOnlineTargets();
92const target = await this.getTargetFromRunArgs();
93if (target) {
94this.target = target;
95} else {
96const onlineTargetsBySpecifiedType = onlineTargets.filter(target => {
97switch (this.runOptions.target) {
98case TargetType.Simulator:
99return target.isVirtualTarget;
100case TargetType.Device:
101return !target.isVirtualTarget;
102case undefined:
103case "":
104return true;
105default:
106return target.id === this.runOptions.target;
107}
108});
109if (onlineTargetsBySpecifiedType.length) {
110this.target = AndroidTarget.fromInterface(onlineTargetsBySpecifiedType[0]);
111} else if (onlineTargets.length) {
112this.logger.warning(
113localize(
114"ThereIsNoOnlineTargetWithSpecifiedTargetType",
115"There is no any online target with specified target type '{0}'. Continue with any online target.",
116this.runOptions.target,
117),
118);
119this.target = AndroidTarget.fromInterface(onlineTargets[0]);
120} else {
121throw ErrorHelper.getInternalError(
122InternalErrorCode.AndroidThereIsNoAnyOnlineDebuggableTarget,
123);
124}
0d77292aJiglioNero4 years ago125}
68a5b8d5JiglioNero5 years ago126}
4cd25962JiglioNero4 years ago127return this.target;
68a5b8d5JiglioNero5 years ago128}
129
0d77292aJiglioNero4 years ago130public async runApp(shouldLaunchInAllDevices: boolean = false): Promise<void> {
549baae2RedMickey6 years ago131let extProps: any = {
031832ffArtem Egorov8 years ago132platform: {
259c018fYuri Skorokhodov5 years ago133value: PlatformType.Android,
031832ffArtem Egorov8 years ago134isPii: false,
135},
136};
137
549baae2RedMickey6 years ago138if (this.runOptions.isDirect) {
139extProps.isDirect = {
140value: true,
141isPii: false,
142};
e7a2c40dRedMickey4 years ago143this.projectObserver?.updateRNAndroidHermesProjectState(true);
549baae2RedMickey6 years ago144}
145
34472878RedMickey5 years ago146extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
147this.runOptions,
148this.runOptions.reactNativeVersions,
149extProps,
150);
ba953e9fRedMickey6 years ago151
0d77292aJiglioNero4 years ago152await TelemetryHelper.generate("AndroidPlatform.runApp", extProps, async () => {
34472878RedMickey5 years ago153const env = GeneralMobilePlatform.getEnvArgument(
154process.env,
155this.runOptions.env,
156this.runOptions.envFile,
157);
78c2b4deRedMickey6 years ago158
e3706a1cRedMickey6 years ago159if (
34472878RedMickey5 years ago160!semver.valid(
161this.runOptions.reactNativeVersions.reactNativeVersion,
162) /*Custom RN implementations should support this flag*/ ||
163semver.gte(
164this.runOptions.reactNativeVersions.reactNativeVersion,
165AndroidPlatform.NO_PACKAGER_VERSION,
166)
e3706a1cRedMickey6 years ago167) {
168this.runArguments.push("--no-packager");
169}
170
34472878RedMickey5 years ago171let mainActivity = GeneralMobilePlatform.getOptFromRunArgs(
172this.runArguments,
173"--main-activity",
174);
e3706a1cRedMickey6 years ago175
176if (mainActivity) {
177this.adbHelper.setLaunchActivity(mainActivity);
4bb0956eRedMickey5 years ago178} else if (notNullOrUndefined(this.runOptions.debugLaunchActivity)) {
e3706a1cRedMickey6 years ago179this.runArguments.push("--main-activity", this.runOptions.debugLaunchActivity);
180this.adbHelper.setLaunchActivity(this.runOptions.debugLaunchActivity);
181}
182
34472878RedMickey5 years ago183const runAndroidSpawn = new CommandExecutor(
4dfb1c4cetatanova5 years ago184this.runOptions.nodeModulesRoot,
34472878RedMickey5 years ago185this.projectPath,
186this.logger,
187).spawnReactCommand("run-android", this.runArguments, { env });
e3706a1cRedMickey6 years ago188const output = new OutputVerifier(
34472878RedMickey5 years ago189() => Promise.resolve(AndroidPlatform.RUN_ANDROID_SUCCESS_PATTERNS),
190() => Promise.resolve(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS),
191PlatformType.Android,
192).process(runAndroidSpawn);
e3706a1cRedMickey6 years ago193
4cd25962JiglioNero4 years ago194let devicesIdsForLaunch: string[] = [];
195const onlineTargetsIds = (await this.adbHelper.getOnlineTargets()).map(
196target => target.id,
197);
198let targetId: string | undefined;
0d77292aJiglioNero4 years ago199try {
200try {
201await output;
202} finally {
4cd25962JiglioNero4 years ago203targetId = await this.getTargetIdForRunApp(onlineTargetsIds);
204this.packageName = await this.getPackageName();
205devicesIdsForLaunch = [targetId];
0d77292aJiglioNero4 years ago206}
207} catch (error) {
4cd25962JiglioNero4 years ago208if (!targetId) {
209targetId = await this.getTargetIdForRunApp(onlineTargetsIds);
210}
0d77292aJiglioNero4 years ago211if (
212error.message ===
213ErrorHelper.getInternalError(
214InternalErrorCode.AndroidMoreThanOneDeviceOrEmulator,
215).message &&
4cd25962JiglioNero4 years ago216onlineTargetsIds.length >= 1 &&
217targetId
0d77292aJiglioNero4 years ago218) {
219/* If it failed due to multiple devices, we'll apply this workaround to make it work anyways */
220this.needsToLaunchApps = true;
4cd25962JiglioNero4 years ago221devicesIdsForLaunch = shouldLaunchInAllDevices ? onlineTargetsIds : [targetId];
0d77292aJiglioNero4 years ago222} else {
223throw error;
224}
225}
226
4cd25962JiglioNero4 years ago227await PromiseUtil.forEach(devicesIdsForLaunch, deviceId =>
228this.launchAppWithADBReverseAndLogCat(deviceId),
0d77292aJiglioNero4 years ago229);
52f3873ddigeff10 years ago230});
231}
232
4cd25962JiglioNero4 years ago233public async enableJSDebuggingMode(): Promise<void> {
34472878RedMickey5 years ago234return this.adbHelper.switchDebugMode(
235this.runOptions.projectRoot,
236this.packageName,
237true,
4cd25962JiglioNero4 years ago238(await this.getTarget()).id,
69ad2ab3RedMickey5 years ago239this.getAppIdSuffixFromRunArgumentsIfExists(),
34472878RedMickey5 years ago240);
b57ea017Artem Egorov8 years ago241}
242
4cd25962JiglioNero4 years ago243public async disableJSDebuggingMode(): Promise<void> {
34472878RedMickey5 years ago244return this.adbHelper.switchDebugMode(
245this.runOptions.projectRoot,
246this.packageName,
247false,
4cd25962JiglioNero4 years ago248(await this.getTarget()).id,
69ad2ab3RedMickey5 years ago249this.getAppIdSuffixFromRunArgumentsIfExists(),
34472878RedMickey5 years ago250);
52f3873ddigeff10 years ago251}
252
ce5e88eeYuri Skorokhodov5 years ago253public prewarmBundleCache(): Promise<void> {
259c018fYuri Skorokhodov5 years ago254return this.packager.prewarmBundleCache(PlatformType.Android);
299b0557Patricio Beltran10 years ago255}
256
cbc7ac5bArtem Egorov7 years ago257public getRunArguments(): string[] {
8022afdfVladimir Kotikov8 years ago258let runArguments: string[] = [];
259
8df5011eYuri Skorokhodov5 years ago260if (this.runOptions.runArguments && this.runOptions.runArguments.length > 0) {
8022afdfVladimir Kotikov8 years ago261runArguments = this.runOptions.runArguments;
262} else {
263if (this.runOptions.variant) {
264runArguments.push("--variant", this.runOptions.variant);
265}
266if (this.runOptions.target) {
34472878RedMickey5 years ago267if (
4cd25962JiglioNero4 years ago268this.runOptions.target !== TargetType.Device &&
269this.runOptions.target !== TargetType.Simulator
34472878RedMickey5 years ago270) {
cbc7ac5bArtem Egorov7 years ago271runArguments.push("--deviceId", this.runOptions.target);
272}
8022afdfVladimir Kotikov8 years ago273}
274}
275
276return runArguments;
277}
278
34472878RedMickey5 years ago279public dispose(): void {
8df5011eYuri Skorokhodov5 years ago280if (this.logCatMonitor) {
281LogCatMonitorManager.delMonitor(this.logCatMonitor.deviceId);
282this.logCatMonitor = null;
283}
284}
285
4cd25962JiglioNero4 years ago286public async getTargetFromRunArgs(): Promise<AndroidTarget | undefined> {
287if (this.runOptions.runArguments && this.runOptions.runArguments.length) {
288const deviceId = GeneralMobilePlatform.getOptFromRunArgs(
289this.runOptions.runArguments,
290"--deviceId",
291);
292if (deviceId) {
293return new AndroidTarget(true, this.adbHelper.isVirtualTarget(deviceId), deviceId);
294}
295}
296return undefined;
297}
298
299private async getTargetIdForRunApp(onlineTargetsIds: string[]): Promise<string> {
300let deviceId: string | undefined;
301if (this.runOptions.runArguments && this.runOptions.runArguments.length) {
302deviceId = GeneralMobilePlatform.getOptFromRunArgs(
303this.runOptions.runArguments,
304"--deviceId",
305);
306}
307return deviceId
308? deviceId
309: this.runOptions.target &&
310this.runOptions.target !== TargetType.Simulator &&
311this.runOptions.target !== TargetType.Device &&
312onlineTargetsIds.find(id => id === this.runOptions.target)
313? this.runOptions.target
314: (await this.getTarget()).id;
315}
316
69ad2ab3RedMickey5 years ago317private getAppIdSuffixFromRunArgumentsIfExists(): string | undefined {
318const appIdSuffixIndex = this.runArguments.indexOf("--appIdSuffix");
319if (appIdSuffixIndex > -1) {
320return this.runArguments[appIdSuffixIndex + 1];
321}
322return undefined;
323}
324
4cd25962JiglioNero4 years ago325private async launchAppWithADBReverseAndLogCat(deviceId: string): Promise<void> {
326await this.configureADBReverseWhenApplicable(deviceId);
0d77292aJiglioNero4 years ago327if (this.needsToLaunchApps) {
4cd25962JiglioNero4 years ago328await this.adbHelper.launchApp(this.runOptions.projectRoot, this.packageName, deviceId);
0d77292aJiglioNero4 years ago329}
4cd25962JiglioNero4 years ago330return this.startMonitoringLogCat(deviceId, this.runOptions.logCatArguments);
52f3873ddigeff10 years ago331}
332
4cd25962JiglioNero4 years ago333private async configureADBReverseWhenApplicable(deviceId: string): Promise<void> {
0d77292aJiglioNero4 years ago334// For other emulators and devices we try to enable adb reverse
4cd25962JiglioNero4 years ago335const apiVersion = await this.adbHelper.apiVersion(deviceId);
0d77292aJiglioNero4 years ago336if (apiVersion >= AndroidAPILevel.LOLLIPOP) {
337// If we support adb reverse
338try {
4cd25962JiglioNero4 years ago339this.adbHelper.reverseAdb(deviceId, Number(this.runOptions.packagerPort));
0d77292aJiglioNero4 years ago340} catch (error) {
341// "adb reverse" command could work incorrectly with remote devices, then skip the error and try to go on
342if (
4cd25962JiglioNero4 years ago343this.adbHelper.isRemoteTarget(deviceId) &&
0d77292aJiglioNero4 years ago344error.message.includes(AndroidPlatform.RUN_ANDROID_FAILURE_PATTERNS[3].pattern)
345) {
346this.logger.warning(error.message);
b57ea017Artem Egorov8 years ago347} else {
0d77292aJiglioNero4 years ago348throw error;
b57ea017Artem Egorov8 years ago349}
0d77292aJiglioNero4 years ago350}
351} else {
352this.logger.warning(
353localize(
354"DeviceSupportsOnlyAPILevel",
355"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 ago356deviceId,
0d77292aJiglioNero4 years ago357apiVersion,
358AndroidAPILevel.LOLLIPOP,
359),
360);
361}
52f3873ddigeff10 years ago362}
363
0d77292aJiglioNero4 years ago364private async getPackageName(): Promise<string> {
365const appName = await new Package(this.runOptions.projectRoot).name();
366return new PackageNameResolver(appName).resolvePackageName(this.runOptions.projectRoot);
52f3873ddigeff10 years ago367}
368
4cd25962JiglioNero4 years ago369private startMonitoringLogCat(deviceId: string, logCatArguments: string[]): void {
370LogCatMonitorManager.delMonitor(deviceId); // Stop previous logcat monitor if it's running
0a68f8dbArtem Egorov8 years ago371
372// this.logCatMonitor can be mutated, so we store it locally too
4cd25962JiglioNero4 years ago373this.logCatMonitor = new LogCatMonitor(deviceId, this.adbHelper, logCatArguments);
8df5011eYuri Skorokhodov5 years ago374LogCatMonitorManager.addMonitor(this.logCatMonitor);
34472878RedMickey5 years ago375this.logCatMonitor
376.start() // The LogCat will continue running forever, so we don't wait for it
377.catch(error => {
378this.logger.warning(error);
379this.logger.warning(
380localize("ErrorWhileMonitoringLogCat", "Error while monitoring LogCat"),
381);
382}); // The LogCatMonitor failing won't stop the debugging experience
0a68f8dbArtem Egorov8 years ago383}
ef902673Vladimir Kotikov9 years ago384}