Skip to content

Commit 827ef0a

Browse files
committed
fix: resolve Android SDK tool paths from SDK root
1 parent 4f862ef commit 827ef0a

2 files changed

Lines changed: 82 additions & 20 deletions

File tree

packages/platform-android/src/adb.ts

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { type AndroidAppLaunchOptions } from '@react-native-harness/platforms';
22
import { spawn, SubprocessError } from '@react-native-harness/tools';
3+
import {
4+
getAdbBinaryPath,
5+
getAvdManagerBinaryPath,
6+
getEmulatorBinaryPath,
7+
getSdkManagerBinaryPath,
8+
} from './environment.js';
39

410
const wait = async (ms: number): Promise<void> => {
511
await new Promise((resolve) => {
@@ -11,6 +17,11 @@ const getSystemImagePackage = (apiLevel: number): string => {
1117
return `system-images;android-${apiLevel};default;x86_64`;
1218
};
1319

20+
const getAvdConfigPath = (name: string): string =>
21+
`${
22+
process.env.ANDROID_AVD_HOME ?? `${process.env.HOME}/.android/avd`
23+
}/${name}.avd/config.ini`;
24+
1425
export type CreateAvdOptions = {
1526
name: string;
1627
apiLevel: number;
@@ -65,7 +76,7 @@ export const isAppInstalled = async (
6576
adbId: string,
6677
bundleId: string
6778
): Promise<boolean> => {
68-
const { stdout } = await spawn('adb', [
79+
const { stdout } = await spawn(getAdbBinaryPath(), [
6980
'-s',
7081
adbId,
7182
'shell',
@@ -82,7 +93,7 @@ export const reversePort = async (
8293
port: number,
8394
hostPort: number = port
8495
): Promise<void> => {
85-
await spawn('adb', [
96+
await spawn(getAdbBinaryPath(), [
8697
'-s',
8798
adbId,
8899
'reverse',
@@ -95,7 +106,14 @@ export const stopApp = async (
95106
adbId: string,
96107
bundleId: string
97108
): Promise<void> => {
98-
await spawn('adb', ['-s', adbId, 'shell', 'am', 'force-stop', bundleId]);
109+
await spawn(getAdbBinaryPath(), [
110+
'-s',
111+
adbId,
112+
'shell',
113+
'am',
114+
'force-stop',
115+
bundleId,
116+
]);
99117
};
100118

101119
export const startApp = async (
@@ -104,15 +122,15 @@ export const startApp = async (
104122
activityName: string,
105123
options?: AndroidAppLaunchOptions
106124
): Promise<void> => {
107-
await spawn('adb', [
125+
await spawn(getAdbBinaryPath(), [
108126
'-s',
109127
adbId,
110128
...getStartAppArgs(bundleId, activityName, options),
111129
]);
112130
};
113131

114132
export const getDeviceIds = async (): Promise<string[]> => {
115-
const { stdout } = await spawn('adb', ['devices']);
133+
const { stdout } = await spawn(getAdbBinaryPath(), ['devices']);
116134
return stdout
117135
.split('\n')
118136
.slice(1) // Skip header
@@ -123,15 +141,21 @@ export const getDeviceIds = async (): Promise<string[]> => {
123141
export const getEmulatorName = async (
124142
adbId: string
125143
): Promise<string | null> => {
126-
const { stdout } = await spawn('adb', ['-s', adbId, 'emu', 'avd', 'name']);
144+
const { stdout } = await spawn(getAdbBinaryPath(), [
145+
'-s',
146+
adbId,
147+
'emu',
148+
'avd',
149+
'name',
150+
]);
127151
return stdout.split('\n')[0].trim() || null;
128152
};
129153

130154
export const getShellProperty = async (
131155
adbId: string,
132156
property: string
133157
): Promise<string | null> => {
134-
const { stdout } = await spawn('adb', [
158+
const { stdout } = await spawn(getAdbBinaryPath(), [
135159
'-s',
136160
adbId,
137161
'shell',
@@ -160,14 +184,14 @@ export const isBootCompleted = async (adbId: string): Promise<boolean> => {
160184
};
161185

162186
export const stopEmulator = async (adbId: string): Promise<void> => {
163-
await spawn('adb', ['-s', adbId, 'emu', 'kill']);
187+
await spawn(getAdbBinaryPath(), ['-s', adbId, 'emu', 'kill']);
164188
};
165189

166190
export const installApp = async (
167191
adbId: string,
168192
appPath: string
169193
): Promise<void> => {
170-
await spawn('adb', ['-s', adbId, 'install', '-r', appPath]);
194+
await spawn(getAdbBinaryPath(), ['-s', adbId, 'install', '-r', appPath]);
171195
};
172196

173197
export const hasAvd = async (name: string): Promise<boolean> => {
@@ -184,20 +208,22 @@ export const createAvd = async ({
184208
}: CreateAvdOptions): Promise<void> => {
185209
const systemImagePackage = getSystemImagePackage(apiLevel);
186210

187-
await spawn('sdkmanager', [systemImagePackage]);
211+
await spawn(getSdkManagerBinaryPath(), [systemImagePackage]);
188212
await spawn('bash', [
189213
'-lc',
190-
`printf 'no\n' | avdmanager create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}"`,
214+
`printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}"`,
191215
]);
192216
await spawn('bash', [
193217
'-lc',
194-
`printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "$HOME/.android/avd/${name}.avd/config.ini"`,
218+
`printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${getAvdConfigPath(
219+
name
220+
)}"`,
195221
]);
196222
};
197223

198224
export const startEmulator = async (name: string): Promise<void> => {
199225
void spawn(
200-
'emulator',
226+
getEmulatorBinaryPath(),
201227
[
202228
`@${name}`,
203229
'-no-snapshot-save',
@@ -266,7 +292,7 @@ export const isAppRunning = async (
266292
bundleId: string
267293
): Promise<boolean> => {
268294
try {
269-
const { stdout } = await spawn('adb', [
295+
const { stdout } = await spawn(getAdbBinaryPath(), [
270296
'-s',
271297
adbId,
272298
'shell',
@@ -287,7 +313,7 @@ export const getAppUid = async (
287313
adbId: string,
288314
bundleId: string
289315
): Promise<number> => {
290-
const { stdout } = await spawn('adb', [
316+
const { stdout } = await spawn(getAdbBinaryPath(), [
291317
'-s',
292318
adbId,
293319
'shell',
@@ -312,7 +338,7 @@ export const setHideErrorDialogs = async (
312338
adbId: string,
313339
hide: boolean
314340
): Promise<void> => {
315-
await spawn('adb', [
341+
await spawn(getAdbBinaryPath(), [
316342
'-s',
317343
adbId,
318344
'shell',
@@ -325,7 +351,7 @@ export const setHideErrorDialogs = async (
325351
};
326352

327353
export const getLogcatTimestamp = async (adbId: string): Promise<string> => {
328-
const { stdout } = await spawn('adb', [
354+
const { stdout } = await spawn(getAdbBinaryPath(), [
329355
'-s',
330356
adbId,
331357
'shell',
@@ -338,7 +364,7 @@ export const getLogcatTimestamp = async (adbId: string): Promise<string> => {
338364

339365
export const getAvds = async (): Promise<string[]> => {
340366
try {
341-
const { stdout } = await spawn('emulator', ['-list-avds']);
367+
const { stdout } = await spawn(getEmulatorBinaryPath(), ['-list-avds']);
342368
return stdout
343369
.split('\n')
344370
.map((line) => line.trim())
@@ -355,7 +381,7 @@ export type AdbDevice = {
355381
};
356382

357383
export const getConnectedDevices = async (): Promise<AdbDevice[]> => {
358-
const { stdout } = await spawn('adb', ['devices', '-l']);
384+
const { stdout } = await spawn(getAdbBinaryPath(), ['devices', '-l']);
359385
const lines = stdout.split('\n').slice(1);
360386
const devices: AdbDevice[] = [];
361387

packages/platform-android/src/environment.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,24 @@ import path from 'node:path';
33

44
const CMDLINE_TOOLS_PATH_SEGMENTS = ['cmdline-tools', 'latest'];
55

6-
const getAndroidSdkRoot = (env: NodeJS.ProcessEnv): string | null => {
6+
export const getAndroidSdkRoot = (
7+
env: NodeJS.ProcessEnv = process.env
8+
): string | null => {
79
return env.ANDROID_HOME ?? env.ANDROID_SDK_ROOT ?? null;
810
};
911

12+
const getRequiredAndroidSdkRoot = (): string => {
13+
const sdkRoot = getAndroidSdkRoot();
14+
15+
if (!sdkRoot) {
16+
throw new Error(
17+
'Android SDK root is not configured. Set ANDROID_HOME or ANDROID_SDK_ROOT.'
18+
);
19+
}
20+
21+
return sdkRoot;
22+
};
23+
1024
export const getAndroidProcessEnv = (
1125
env: NodeJS.ProcessEnv = process.env
1226
): NodeJS.ProcessEnv => {
@@ -41,3 +55,25 @@ export const getAndroidProcessEnv = (
4155
export const initializeAndroidProcessEnv = (): void => {
4256
Object.assign(process.env, getAndroidProcessEnv());
4357
};
58+
59+
export const getAdbBinaryPath = (): string =>
60+
path.join(getRequiredAndroidSdkRoot(), 'platform-tools', 'adb');
61+
62+
export const getEmulatorBinaryPath = (): string =>
63+
path.join(getRequiredAndroidSdkRoot(), 'emulator', 'emulator');
64+
65+
export const getSdkManagerBinaryPath = (): string =>
66+
path.join(
67+
getRequiredAndroidSdkRoot(),
68+
...CMDLINE_TOOLS_PATH_SEGMENTS,
69+
'bin',
70+
'sdkmanager'
71+
);
72+
73+
export const getAvdManagerBinaryPath = (): string =>
74+
path.join(
75+
getRequiredAndroidSdkRoot(),
76+
...CMDLINE_TOOLS_PATH_SEGMENTS,
77+
'bin',
78+
'avdmanager'
79+
);

0 commit comments

Comments
 (0)