Skip to content

Commit ca1f17a

Browse files
committed
fix: bootstrap Android SDK tools for Harness subprocesses
1 parent 5491c87 commit ca1f17a

4 files changed

Lines changed: 189 additions & 92 deletions

File tree

packages/platform-android/src/adb.ts

Lines changed: 72 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type AndroidAppLaunchOptions } from '@react-native-harness/platforms';
22
import { spawn, SubprocessError } from '@react-native-harness/tools';
3+
import { withAndroidProcessEnv } from './environment.js';
34

45
const wait = async (ms: number): Promise<void> => {
56
await new Promise((resolve) => {
@@ -65,15 +66,11 @@ export const isAppInstalled = async (
6566
adbId: string,
6667
bundleId: string
6768
): Promise<boolean> => {
68-
const { stdout } = await spawn('adb', [
69-
'-s',
70-
adbId,
71-
'shell',
72-
'pm',
73-
'list',
74-
'packages',
75-
bundleId,
76-
]);
69+
const { stdout } = await spawn(
70+
'adb',
71+
['-s', adbId, 'shell', 'pm', 'list', 'packages', bundleId],
72+
withAndroidProcessEnv()
73+
);
7774
return stdout.trim() !== '';
7875
};
7976

@@ -82,20 +79,22 @@ export const reversePort = async (
8279
port: number,
8380
hostPort: number = port
8481
): Promise<void> => {
85-
await spawn('adb', [
86-
'-s',
87-
adbId,
88-
'reverse',
89-
`tcp:${port}`,
90-
`tcp:${hostPort}`,
91-
]);
82+
await spawn(
83+
'adb',
84+
['-s', adbId, 'reverse', `tcp:${port}`, `tcp:${hostPort}`],
85+
withAndroidProcessEnv()
86+
);
9287
};
9388

9489
export const stopApp = async (
9590
adbId: string,
9691
bundleId: string
9792
): Promise<void> => {
98-
await spawn('adb', ['-s', adbId, 'shell', 'am', 'force-stop', bundleId]);
93+
await spawn(
94+
'adb',
95+
['-s', adbId, 'shell', 'am', 'force-stop', bundleId],
96+
withAndroidProcessEnv()
97+
);
9998
};
10099

101100
export const startApp = async (
@@ -104,15 +103,15 @@ export const startApp = async (
104103
activityName: string,
105104
options?: AndroidAppLaunchOptions
106105
): Promise<void> => {
107-
await spawn('adb', [
108-
'-s',
109-
adbId,
110-
...getStartAppArgs(bundleId, activityName, options),
111-
]);
106+
await spawn(
107+
'adb',
108+
['-s', adbId, ...getStartAppArgs(bundleId, activityName, options)],
109+
withAndroidProcessEnv()
110+
);
112111
};
113112

114113
export const getDeviceIds = async (): Promise<string[]> => {
115-
const { stdout } = await spawn('adb', ['devices']);
114+
const { stdout } = await spawn('adb', ['devices'], withAndroidProcessEnv());
116115
return stdout
117116
.split('\n')
118117
.slice(1) // Skip header
@@ -123,21 +122,23 @@ export const getDeviceIds = async (): Promise<string[]> => {
123122
export const getEmulatorName = async (
124123
adbId: string
125124
): Promise<string | null> => {
126-
const { stdout } = await spawn('adb', ['-s', adbId, 'emu', 'avd', 'name']);
125+
const { stdout } = await spawn(
126+
'adb',
127+
['-s', adbId, 'emu', 'avd', 'name'],
128+
withAndroidProcessEnv()
129+
);
127130
return stdout.split('\n')[0].trim() || null;
128131
};
129132

130133
export const getShellProperty = async (
131134
adbId: string,
132135
property: string
133136
): Promise<string | null> => {
134-
const { stdout } = await spawn('adb', [
135-
'-s',
136-
adbId,
137-
'shell',
138-
'getprop',
139-
property,
140-
]);
137+
const { stdout } = await spawn(
138+
'adb',
139+
['-s', adbId, 'shell', 'getprop', property],
140+
withAndroidProcessEnv()
141+
);
141142
return stdout.trim() || null;
142143
};
143144

@@ -160,7 +161,7 @@ export const isBootCompleted = async (adbId: string): Promise<boolean> => {
160161
};
161162

162163
export const stopEmulator = async (adbId: string): Promise<void> => {
163-
await spawn('adb', ['-s', adbId, 'emu', 'kill']);
164+
await spawn('adb', ['-s', adbId, 'emu', 'kill'], withAndroidProcessEnv());
164165
};
165166

166167
export const installApp = async (
@@ -266,13 +267,11 @@ export const isAppRunning = async (
266267
bundleId: string
267268
): Promise<boolean> => {
268269
try {
269-
const { stdout } = await spawn('adb', [
270-
'-s',
271-
adbId,
272-
'shell',
273-
'pidof',
274-
bundleId,
275-
]);
270+
const { stdout } = await spawn(
271+
'adb',
272+
['-s', adbId, 'shell', 'pidof', bundleId],
273+
withAndroidProcessEnv()
274+
);
276275
return stdout.trim() !== '';
277276
} catch (error) {
278277
if (error instanceof SubprocessError && error.exitCode === 1) {
@@ -287,15 +286,11 @@ export const getAppUid = async (
287286
adbId: string,
288287
bundleId: string
289288
): Promise<number> => {
290-
const { stdout } = await spawn('adb', [
291-
'-s',
292-
adbId,
293-
'shell',
294-
'pm',
295-
'list',
296-
'packages',
297-
'-U',
298-
]);
289+
const { stdout } = await spawn(
290+
'adb',
291+
['-s', adbId, 'shell', 'pm', 'list', 'packages', '-U'],
292+
withAndroidProcessEnv()
293+
);
299294
const line = stdout
300295
.split('\n')
301296
.find((entry) => entry.includes(`package:${bundleId}`));
@@ -312,33 +307,39 @@ export const setHideErrorDialogs = async (
312307
adbId: string,
313308
hide: boolean
314309
): Promise<void> => {
315-
await spawn('adb', [
316-
'-s',
317-
adbId,
318-
'shell',
319-
'settings',
320-
'put',
321-
'global',
322-
'hide_error_dialogs',
323-
hide ? '1' : '0',
324-
]);
310+
await spawn(
311+
'adb',
312+
[
313+
'-s',
314+
adbId,
315+
'shell',
316+
'settings',
317+
'put',
318+
'global',
319+
'hide_error_dialogs',
320+
hide ? '1' : '0',
321+
],
322+
withAndroidProcessEnv()
323+
);
325324
};
326325

327326
export const getLogcatTimestamp = async (adbId: string): Promise<string> => {
328-
const { stdout } = await spawn('adb', [
329-
'-s',
330-
adbId,
331-
'shell',
332-
'date',
333-
"+'%m-%d %H:%M:%S.000'",
334-
]);
327+
const { stdout } = await spawn(
328+
'adb',
329+
['-s', adbId, 'shell', 'date', "+'%m-%d %H:%M:%S.000'"],
330+
withAndroidProcessEnv()
331+
);
335332

336333
return stdout.trim().replace(/^'+|'+$/g, '');
337334
};
338335

339336
export const getAvds = async (): Promise<string[]> => {
340337
try {
341-
const { stdout } = await spawn('emulator', ['-list-avds']);
338+
const { stdout } = await spawn(
339+
'emulator',
340+
['-list-avds'],
341+
withAndroidProcessEnv()
342+
);
342343
return stdout
343344
.split('\n')
344345
.map((line) => line.trim())
@@ -355,7 +356,11 @@ export type AdbDevice = {
355356
};
356357

357358
export const getConnectedDevices = async (): Promise<AdbDevice[]> => {
358-
const { stdout } = await spawn('adb', ['devices', '-l']);
359+
const { stdout } = await spawn(
360+
'adb',
361+
['devices', '-l'],
362+
withAndroidProcessEnv()
363+
);
359364
const lines = stdout.split('\n').slice(1);
360365
const devices: AdbDevice[] = [];
361366

packages/platform-android/src/app-monitor.ts

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,31 @@ import {
66
type AppMonitorEvent,
77
type AppMonitorListener,
88
} from '@react-native-harness/platforms';
9-
import { escapeRegExp, getEmitter, logger, spawn, SubprocessError, type Subprocess } from '@react-native-harness/tools';
9+
import {
10+
escapeRegExp,
11+
getEmitter,
12+
logger,
13+
spawn,
14+
SubprocessError,
15+
type Subprocess,
16+
} from '@react-native-harness/tools';
1017
import * as adb from './adb.js';
1118
import { androidCrashParser } from './crash-parser.js';
19+
import { withAndroidProcessEnv } from './environment.js';
1220

1321
const androidAppMonitorLogger = logger.child('android-app-monitor');
1422

1523
const getLogcatArgs = (uid: number, fromTime: string) =>
16-
['logcat', '-v', 'threadtime', '-b', 'crash', `--uid=${uid}`, '-T', fromTime] as const;
24+
[
25+
'logcat',
26+
'-v',
27+
'threadtime',
28+
'-b',
29+
'crash',
30+
`--uid=${uid}`,
31+
'-T',
32+
fromTime,
33+
] as const;
1734
const MAX_RECENT_LOG_LINES = 200;
1835
const MAX_RECENT_CRASH_ARTIFACTS = 10;
1936
const CRASH_ARTIFACT_SETTLE_DELAY_MS = 100;
@@ -29,7 +46,9 @@ const nativeCrashPattern = (bundleId: string) =>
2946

3047
const processDiedPattern = (bundleId: string) =>
3148
new RegExp(
32-
`Process\\s+${escapeRegExp(bundleId)}\\s+\\(pid\\s+(\\d+)\\)\\s+has\\s+died`,
49+
`Process\\s+${escapeRegExp(
50+
bundleId
51+
)}\\s+\\(pid\\s+(\\d+)\\)\\s+has\\s+died`,
3352
'i'
3453
);
3554

@@ -66,7 +85,11 @@ const getAndroidLogLineCrashDetails = ({
6685
summary: line.trim(),
6786
signal: getSignal(line),
6887
exceptionType: fatalExceptionMatch?.[1]?.trim(),
69-
processName: processMatch ? bundleId : line.includes(bundleId) ? bundleId : undefined,
88+
processName: processMatch
89+
? bundleId
90+
: line.includes(bundleId)
91+
? bundleId
92+
: undefined,
7093
pid: pid ?? (processMatch ? Number(processMatch[1]) : undefined),
7194
rawLines: [line],
7295
};
@@ -211,7 +234,9 @@ const createCrashArtifact = ({
211234
triggerOccurredAt,
212235
artifactType: 'logcat',
213236
rawLines:
214-
rawLines.length > 0 ? rawLines : parsedDetails.rawLines ?? details.rawLines,
237+
rawLines.length > 0
238+
? rawLines
239+
: parsedDetails.rawLines ?? details.rawLines,
215240
};
216241
};
217242

@@ -265,11 +290,12 @@ const getLatestCrashArtifact = ({
265290
matchingByPid.length > 0
266291
? matchingByPid
267292
: matchingByProcess.length > 0
268-
? matchingByProcess
269-
: crashArtifacts;
293+
? matchingByProcess
294+
: crashArtifacts;
270295
const sortedCandidates = [...candidates].sort(
271296
(left, right) =>
272-
Math.abs(left.occurredAt - occurredAt) - Math.abs(right.occurredAt - occurredAt)
297+
Math.abs(left.occurredAt - occurredAt) -
298+
Math.abs(right.occurredAt - occurredAt)
273299
);
274300

275301
const artifact = sortedCandidates[0];
@@ -385,9 +411,10 @@ export const createAndroidAppMonitor = ({
385411
};
386412

387413
const recordLogLine = (line: string) => {
388-
recentLogLines = [...recentLogLines, { line, occurredAt: Date.now() }].slice(
389-
-MAX_RECENT_LOG_LINES
390-
);
414+
recentLogLines = [
415+
...recentLogLines,
416+
{ line, occurredAt: Date.now() },
417+
].slice(-MAX_RECENT_LOG_LINES);
391418
};
392419

393420
const recordCrashArtifact = (details?: AppCrashDetails) => {
@@ -419,10 +446,15 @@ export const createAndroidAppMonitor = ({
419446
const startLogcat = async () => {
420447
const logcatTimestamp = await adb.getLogcatTimestamp(adbId);
421448

422-
logcatProcess = spawn('adb', ['-s', adbId, ...getLogcatArgs(appUid, logcatTimestamp)], {
423-
stdout: 'pipe',
424-
stderr: 'pipe',
425-
});
449+
logcatProcess = spawn(
450+
'adb',
451+
['-s', adbId, ...getLogcatArgs(appUid, logcatTimestamp)],
452+
{
453+
stdout: 'pipe',
454+
stderr: 'pipe',
455+
...withAndroidProcessEnv(),
456+
}
457+
);
426458

427459
const currentProcess = logcatProcess;
428460

@@ -439,15 +471,23 @@ export const createAndroidAppMonitor = ({
439471
const event = createAndroidLogEvent(line, bundleId);
440472

441473
if (event) {
442-
if (event.type === 'possible_crash' || event.type === 'app_exited') {
474+
if (
475+
event.type === 'possible_crash' ||
476+
event.type === 'app_exited'
477+
) {
443478
recordCrashArtifact(event.crashDetails);
444479
}
445480
emit(event);
446481
}
447482
}
448483
} catch (error) {
449-
if (!(error instanceof SubprocessError && error.signalName === 'SIGTERM')) {
450-
androidAppMonitorLogger.debug('Android logcat monitor stopped', error);
484+
if (
485+
!(error instanceof SubprocessError && error.signalName === 'SIGTERM')
486+
) {
487+
androidAppMonitorLogger.debug(
488+
'Android logcat monitor stopped',
489+
error
490+
);
451491
}
452492
}
453493
})();

0 commit comments

Comments
 (0)