@react-native-harness/platform-android 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/adb-wait.test.d.ts +2 -0
- package/dist/__tests__/adb-wait.test.d.ts.map +1 -0
- package/dist/__tests__/adb-wait.test.js +69 -0
- package/dist/__tests__/app-session.test.d.ts +2 -0
- package/dist/__tests__/app-session.test.d.ts.map +1 -0
- package/dist/__tests__/app-session.test.js +57 -0
- package/dist/__tests__/avd-config.test.js +58 -0
- package/dist/__tests__/crash-diagnostics.test.d.ts +2 -0
- package/dist/__tests__/crash-diagnostics.test.d.ts.map +1 -0
- package/dist/__tests__/crash-diagnostics.test.js +119 -0
- package/dist/__tests__/crash-reporter.test.d.ts +2 -0
- package/dist/__tests__/crash-reporter.test.d.ts.map +1 -0
- package/dist/__tests__/crash-reporter.test.js +150 -0
- package/dist/__tests__/emulator-startup.test.js +30 -3
- package/dist/__tests__/instance.test.js +139 -24
- package/dist/adb.d.ts +7 -1
- package/dist/adb.d.ts.map +1 -1
- package/dist/adb.js +72 -23
- package/dist/app-session.d.ts +26 -0
- package/dist/app-session.d.ts.map +1 -0
- package/dist/app-session.js +214 -0
- package/dist/avd-config.d.ts +1 -0
- package/dist/avd-config.d.ts.map +1 -1
- package/dist/avd-config.js +11 -0
- package/dist/crash-diagnostics.d.ts +40 -0
- package/dist/crash-diagnostics.d.ts.map +1 -0
- package/dist/crash-diagnostics.js +256 -0
- package/dist/crash-reporter.d.ts +12 -0
- package/dist/crash-reporter.d.ts.map +1 -0
- package/dist/crash-reporter.js +150 -0
- package/dist/emulator-startup.d.ts +23 -0
- package/dist/emulator-startup.d.ts.map +1 -1
- package/dist/emulator-startup.js +58 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/instance.d.ts +1 -1
- package/dist/instance.d.ts.map +1 -1
- package/dist/instance.js +51 -72
- package/dist/runner.d.ts +2 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/__tests__/adb-wait.test.ts +95 -0
- package/src/__tests__/app-session.test.ts +76 -0
- package/src/__tests__/avd-config.test.ts +70 -0
- package/src/__tests__/crash-diagnostics.test.ts +144 -0
- package/src/__tests__/crash-reporter.test.ts +167 -0
- package/src/__tests__/emulator-startup.test.ts +53 -6
- package/src/__tests__/instance.test.ts +226 -84
- package/src/adb.ts +129 -63
- package/src/app-session.ts +316 -0
- package/src/avd-config.ts +17 -0
- package/src/crash-diagnostics.ts +471 -0
- package/src/crash-reporter.ts +263 -0
- package/src/emulator-startup.ts +74 -0
- package/src/index.ts +1 -0
- package/src/instance.ts +84 -105
- package/src/runner.ts +11 -11
- package/test/setup.ts +1 -0
- package/src/app-monitor.ts +0 -577
package/src/adb.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type AndroidAppLaunchOptions } from '@react-native-harness/platforms';
|
|
2
2
|
import {
|
|
3
|
+
delay,
|
|
3
4
|
logger,
|
|
4
5
|
spawn,
|
|
5
6
|
SubprocessError,
|
|
7
|
+
waitForAbort,
|
|
6
8
|
type Subprocess,
|
|
7
9
|
} from '@react-native-harness/tools';
|
|
8
10
|
import { spawn as nodeSpawn } from 'node:child_process';
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
getRequiredAndroidSdkPackages,
|
|
26
28
|
} from './environment.js';
|
|
27
29
|
import {
|
|
30
|
+
getEmulatorCpuCores,
|
|
28
31
|
getEmulatorStartupArgs,
|
|
29
32
|
type EmulatorBootMode,
|
|
30
33
|
} from './emulator-startup.js';
|
|
@@ -33,37 +36,26 @@ import {
|
|
|
33
36
|
AdbPermissionGrantError,
|
|
34
37
|
} from './adb-errors.js';
|
|
35
38
|
|
|
36
|
-
const wait = async (ms: number): Promise<void> => {
|
|
37
|
-
await new Promise((resolve) => {
|
|
38
|
-
setTimeout(resolve, ms);
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const waitForAbort = (signal: AbortSignal): Promise<never> => {
|
|
43
|
-
if (signal.aborted) {
|
|
44
|
-
return Promise.reject(signal.reason);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return new Promise((_, reject) => {
|
|
48
|
-
signal.addEventListener(
|
|
49
|
-
'abort',
|
|
50
|
-
() => {
|
|
51
|
-
reject(signal.reason);
|
|
52
|
-
},
|
|
53
|
-
{ once: true },
|
|
54
|
-
);
|
|
55
|
-
});
|
|
56
|
-
};
|
|
57
|
-
|
|
58
39
|
const waitWithSignal = async (
|
|
59
40
|
ms: number,
|
|
60
|
-
signal: AbortSignal
|
|
41
|
+
signal: AbortSignal
|
|
61
42
|
): Promise<void> => {
|
|
62
43
|
if (signal.aborted) {
|
|
63
44
|
throw signal.reason;
|
|
64
45
|
}
|
|
65
46
|
|
|
66
|
-
|
|
47
|
+
// Both sides of this race must be torn down explicitly when they lose:
|
|
48
|
+
// an uncancelled timer keeps a ref'd Timeout alive, and an uncancelled
|
|
49
|
+
// abort wait leaks a listener on `signal`, which is a session-lifetime
|
|
50
|
+
// signal here and gets polled against once per second.
|
|
51
|
+
const timer = delay(ms);
|
|
52
|
+
const abort = waitForAbort(signal);
|
|
53
|
+
try {
|
|
54
|
+
await Promise.race([timer.promise, abort.promise]);
|
|
55
|
+
} finally {
|
|
56
|
+
timer.cancel();
|
|
57
|
+
abort.cancel();
|
|
58
|
+
}
|
|
67
59
|
};
|
|
68
60
|
|
|
69
61
|
const EMULATOR_STARTUP_OBSERVATION_TIMEOUT_MS = 5000;
|
|
@@ -73,7 +65,7 @@ const androidAdbLogger = logger.child('android-adb');
|
|
|
73
65
|
export const emulatorProcess = {
|
|
74
66
|
startDetachedProcess: (
|
|
75
67
|
file: string,
|
|
76
|
-
args: readonly string[]
|
|
68
|
+
args: readonly string[]
|
|
77
69
|
): ChildProcessByStdio<null, Readable, Readable> =>
|
|
78
70
|
nodeSpawn(file, args, {
|
|
79
71
|
detached: true,
|
|
@@ -84,7 +76,7 @@ export const emulatorProcess = {
|
|
|
84
76
|
const appendBoundedOutput = (
|
|
85
77
|
output: string,
|
|
86
78
|
chunk: string,
|
|
87
|
-
limit: number = EMULATOR_OUTPUT_BUFFER_LIMIT
|
|
79
|
+
limit: number = EMULATOR_OUTPUT_BUFFER_LIMIT
|
|
88
80
|
): string => {
|
|
89
81
|
const nextOutput = output + chunk;
|
|
90
82
|
|
|
@@ -165,7 +157,7 @@ export const getRequiredEmulatorPackages = (apiLevel: number): string[] => {
|
|
|
165
157
|
};
|
|
166
158
|
|
|
167
159
|
export const verifyAndroidEmulatorSdk = async (
|
|
168
|
-
apiLevel: number
|
|
160
|
+
apiLevel: number
|
|
169
161
|
): Promise<void> => {
|
|
170
162
|
await ensureAndroidSdkPackages(getRequiredEmulatorPackages(apiLevel));
|
|
171
163
|
};
|
|
@@ -192,7 +184,7 @@ const ensureAvdProfileAvailable = async (profile: string): Promise<void> => {
|
|
|
192
184
|
: 'None reported by avdmanager.';
|
|
193
185
|
|
|
194
186
|
throw new Error(
|
|
195
|
-
`Android AVD profile "${profile}" is not available on this machine. Available profiles: ${availableProfilesList}
|
|
187
|
+
`Android AVD profile "${profile}" is not available on this machine. Available profiles: ${availableProfilesList}`
|
|
196
188
|
);
|
|
197
189
|
};
|
|
198
190
|
|
|
@@ -201,7 +193,7 @@ const ensureAvdConfigExists = async (name: string): Promise<string> => {
|
|
|
201
193
|
|
|
202
194
|
if ((await readAvdConfig(name)) == null) {
|
|
203
195
|
throw new Error(
|
|
204
|
-
`Android AVD "${name}" was created, but config.ini was not found at ${configPath}
|
|
196
|
+
`Android AVD "${name}" was created, but config.ini was not found at ${configPath}.`
|
|
205
197
|
);
|
|
206
198
|
}
|
|
207
199
|
|
|
@@ -235,7 +227,7 @@ const ensureAvdIniExists = async ({
|
|
|
235
227
|
export const getStartAppArgs = (
|
|
236
228
|
bundleId: string,
|
|
237
229
|
activityName: string,
|
|
238
|
-
options?: AndroidAppLaunchOptions
|
|
230
|
+
options?: AndroidAppLaunchOptions
|
|
239
231
|
): string[] => {
|
|
240
232
|
const args = [
|
|
241
233
|
'shell',
|
|
@@ -264,7 +256,7 @@ export const getStartAppArgs = (
|
|
|
264
256
|
|
|
265
257
|
if (!Number.isSafeInteger(value)) {
|
|
266
258
|
throw new Error(
|
|
267
|
-
`Android app launch option "${key}" must be a safe integer
|
|
259
|
+
`Android app launch option "${key}" must be a safe integer.`
|
|
268
260
|
);
|
|
269
261
|
}
|
|
270
262
|
|
|
@@ -276,7 +268,7 @@ export const getStartAppArgs = (
|
|
|
276
268
|
|
|
277
269
|
export const isAppInstalled = async (
|
|
278
270
|
adbId: string,
|
|
279
|
-
bundleId: string
|
|
271
|
+
bundleId: string
|
|
280
272
|
): Promise<boolean> => {
|
|
281
273
|
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
282
274
|
'-s',
|
|
@@ -293,7 +285,7 @@ export const isAppInstalled = async (
|
|
|
293
285
|
export const reversePort = async (
|
|
294
286
|
adbId: string,
|
|
295
287
|
port: number,
|
|
296
|
-
hostPort: number = port
|
|
288
|
+
hostPort: number = port
|
|
297
289
|
): Promise<void> => {
|
|
298
290
|
await spawn(getAdbBinaryPath(), [
|
|
299
291
|
'-s',
|
|
@@ -306,7 +298,7 @@ export const reversePort = async (
|
|
|
306
298
|
|
|
307
299
|
export const stopApp = async (
|
|
308
300
|
adbId: string,
|
|
309
|
-
bundleId: string
|
|
301
|
+
bundleId: string
|
|
310
302
|
): Promise<void> => {
|
|
311
303
|
await spawn(getAdbBinaryPath(), [
|
|
312
304
|
'-s',
|
|
@@ -322,7 +314,7 @@ export const startApp = async (
|
|
|
322
314
|
adbId: string,
|
|
323
315
|
bundleId: string,
|
|
324
316
|
activityName: string,
|
|
325
|
-
options?: AndroidAppLaunchOptions
|
|
317
|
+
options?: AndroidAppLaunchOptions
|
|
326
318
|
): Promise<void> => {
|
|
327
319
|
await spawn(getAdbBinaryPath(), [
|
|
328
320
|
'-s',
|
|
@@ -341,7 +333,7 @@ export const getDeviceIds = async (): Promise<string[]> => {
|
|
|
341
333
|
};
|
|
342
334
|
|
|
343
335
|
export const getEmulatorName = async (
|
|
344
|
-
adbId: string
|
|
336
|
+
adbId: string
|
|
345
337
|
): Promise<string | null> => {
|
|
346
338
|
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
347
339
|
'-s',
|
|
@@ -355,7 +347,7 @@ export const getEmulatorName = async (
|
|
|
355
347
|
|
|
356
348
|
export const getShellProperty = async (
|
|
357
349
|
adbId: string,
|
|
358
|
-
property: string
|
|
350
|
+
property: string
|
|
359
351
|
): Promise<string | null> => {
|
|
360
352
|
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
361
353
|
'-s',
|
|
@@ -377,7 +369,7 @@ export type DeviceInfo = {
|
|
|
377
369
|
};
|
|
378
370
|
|
|
379
371
|
export const getDeviceInfo = async (
|
|
380
|
-
adbId: string
|
|
372
|
+
adbId: string
|
|
381
373
|
): Promise<DeviceInfo | null> => {
|
|
382
374
|
const manufacturer = await getShellProperty(adbId, 'ro.product.manufacturer');
|
|
383
375
|
const model = await getShellProperty(adbId, 'ro.product.model');
|
|
@@ -386,7 +378,7 @@ export const getDeviceInfo = async (
|
|
|
386
378
|
|
|
387
379
|
const getRequestedPermissions = async (
|
|
388
380
|
adbId: string,
|
|
389
|
-
bundleId: string
|
|
381
|
+
bundleId: string
|
|
390
382
|
): Promise<string[]> => {
|
|
391
383
|
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
392
384
|
'-s',
|
|
@@ -476,14 +468,14 @@ export const stopEmulator = async (adbId: string): Promise<void> => {
|
|
|
476
468
|
|
|
477
469
|
export const installApp = async (
|
|
478
470
|
adbId: string,
|
|
479
|
-
appPath: string
|
|
471
|
+
appPath: string
|
|
480
472
|
): Promise<void> => {
|
|
481
473
|
await spawn(getAdbBinaryPath(), ['-s', adbId, 'install', '-r', appPath]);
|
|
482
474
|
};
|
|
483
475
|
|
|
484
476
|
export const uninstallApp = async (
|
|
485
477
|
adbId: string,
|
|
486
|
-
bundleId: string
|
|
478
|
+
bundleId: string
|
|
487
479
|
): Promise<void> => {
|
|
488
480
|
await spawn(getAdbBinaryPath(), ['-s', adbId, 'uninstall', bundleId]);
|
|
489
481
|
};
|
|
@@ -502,20 +494,27 @@ export const createAvd = async ({
|
|
|
502
494
|
}: CreateAvdOptions): Promise<void> => {
|
|
503
495
|
const systemImagePackage = getAndroidSystemImagePackage(
|
|
504
496
|
apiLevel,
|
|
505
|
-
getHostAndroidSystemImageArch()
|
|
497
|
+
getHostAndroidSystemImageArch()
|
|
506
498
|
);
|
|
507
499
|
|
|
508
500
|
await verifyAndroidEmulatorSdk(apiLevel);
|
|
509
501
|
await ensureAvdProfileAvailable(profile);
|
|
510
502
|
await spawn('bash', [
|
|
511
503
|
'-lc',
|
|
512
|
-
`printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}" -p "${getAvdDirectory(
|
|
504
|
+
`printf 'no\n' | "${getAvdManagerBinaryPath()}" create avd --force --name "${name}" --package "${systemImagePackage}" --device "${profile}" -p "${getAvdDirectory(
|
|
505
|
+
name
|
|
506
|
+
)}"`,
|
|
513
507
|
]);
|
|
514
508
|
await ensureAvdIniExists({ name, apiLevel });
|
|
515
509
|
const configPath = await ensureAvdConfigExists(name);
|
|
510
|
+
// hw.cpu.ncore is baked into config.ini (rather than passed as a `-cores`
|
|
511
|
+
// boot flag) so it's part of the AVD's persisted hardware profile: boot
|
|
512
|
+
// snapshots require an identical hardware config to load, so the vCPU
|
|
513
|
+
// count must be fixed at AVD-creation time and stay consistent across
|
|
514
|
+
// boots.
|
|
516
515
|
await spawn('bash', [
|
|
517
516
|
'-lc',
|
|
518
|
-
`printf '%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' >> "${configPath}"`,
|
|
517
|
+
`printf '%s\n%s\n%s\n' 'disk.dataPartition.size=${diskSize}' 'vm.heapSize=${heapSize}' 'hw.cpu.ncore=${getEmulatorCpuCores()}' >> "${configPath}"`,
|
|
519
518
|
]);
|
|
520
519
|
};
|
|
521
520
|
|
|
@@ -527,7 +526,7 @@ export const deleteAvd = async (name: string): Promise<void> => {
|
|
|
527
526
|
{
|
|
528
527
|
force: true,
|
|
529
528
|
recursive: true,
|
|
530
|
-
}
|
|
529
|
+
}
|
|
531
530
|
);
|
|
532
531
|
await rm(
|
|
533
532
|
`${
|
|
@@ -535,18 +534,18 @@ export const deleteAvd = async (name: string): Promise<void> => {
|
|
|
535
534
|
}/${name}.ini`,
|
|
536
535
|
{
|
|
537
536
|
force: true,
|
|
538
|
-
}
|
|
537
|
+
}
|
|
539
538
|
);
|
|
540
539
|
};
|
|
541
540
|
|
|
542
541
|
export const startEmulator = async (
|
|
543
542
|
name: string,
|
|
544
|
-
mode: EmulatorBootMode = 'default-boot'
|
|
543
|
+
mode: EmulatorBootMode = 'default-boot'
|
|
545
544
|
): Promise<void> => {
|
|
546
545
|
const emulatorBinaryPath = await ensureEmulatorInstalled();
|
|
547
546
|
const childProcess = emulatorProcess.startDetachedProcess(
|
|
548
547
|
emulatorBinaryPath,
|
|
549
|
-
getEmulatorStartupArgs(name, mode)
|
|
548
|
+
getEmulatorStartupArgs(name, mode)
|
|
550
549
|
);
|
|
551
550
|
|
|
552
551
|
let stdout = '';
|
|
@@ -582,7 +581,7 @@ export const startEmulator = async (
|
|
|
582
581
|
stdout,
|
|
583
582
|
stderr,
|
|
584
583
|
error,
|
|
585
|
-
})
|
|
584
|
+
})
|
|
586
585
|
);
|
|
587
586
|
});
|
|
588
587
|
|
|
@@ -594,7 +593,7 @@ export const startEmulator = async (
|
|
|
594
593
|
stderr,
|
|
595
594
|
exitCode,
|
|
596
595
|
signal,
|
|
597
|
-
})
|
|
596
|
+
})
|
|
598
597
|
);
|
|
599
598
|
});
|
|
600
599
|
});
|
|
@@ -609,13 +608,19 @@ export const startEmulator = async (
|
|
|
609
608
|
throw error;
|
|
610
609
|
});
|
|
611
610
|
|
|
612
|
-
const
|
|
613
|
-
|
|
611
|
+
const observationTimer = delay(EMULATOR_STARTUP_OBSERVATION_TIMEOUT_MS);
|
|
612
|
+
const observationTimeout = observationTimer.promise.then(
|
|
613
|
+
() => 'timeout' as const
|
|
614
614
|
);
|
|
615
615
|
|
|
616
616
|
try {
|
|
617
617
|
await Promise.race([earlyExit, observedBoot, observationTimeout]);
|
|
618
618
|
} finally {
|
|
619
|
+
// If `earlyExit` or `observedBoot` won the race, this timer would
|
|
620
|
+
// otherwise sit ref'd in the event loop for the rest of its 5s;
|
|
621
|
+
// cancelling here is a no-op on the happy "proceed detached" path
|
|
622
|
+
// where the timer itself won.
|
|
623
|
+
observationTimer.cancel();
|
|
619
624
|
cleanup();
|
|
620
625
|
}
|
|
621
626
|
|
|
@@ -626,7 +631,7 @@ export const startEmulator = async (
|
|
|
626
631
|
|
|
627
632
|
export const waitForEmulator = async (
|
|
628
633
|
name: string,
|
|
629
|
-
signal: AbortSignal
|
|
634
|
+
signal: AbortSignal
|
|
630
635
|
): Promise<string> => {
|
|
631
636
|
while (!signal.aborted) {
|
|
632
637
|
const adbIds = await getDeviceIds();
|
|
@@ -651,7 +656,7 @@ export const waitForEmulator = async (
|
|
|
651
656
|
|
|
652
657
|
export const waitForEmulatorDisconnect = async (
|
|
653
658
|
adbId: string,
|
|
654
|
-
signal: AbortSignal
|
|
659
|
+
signal: AbortSignal
|
|
655
660
|
): Promise<void> => {
|
|
656
661
|
while (!signal.aborted) {
|
|
657
662
|
const adbIds = await getDeviceIds();
|
|
@@ -668,7 +673,7 @@ export const waitForEmulatorDisconnect = async (
|
|
|
668
673
|
|
|
669
674
|
export const waitForBoot = async (
|
|
670
675
|
name: string,
|
|
671
|
-
signal: AbortSignal
|
|
676
|
+
signal: AbortSignal
|
|
672
677
|
): Promise<string> => {
|
|
673
678
|
while (!signal.aborted) {
|
|
674
679
|
const adbIds = await getDeviceIds();
|
|
@@ -697,8 +702,15 @@ export const waitForBoot = async (
|
|
|
697
702
|
|
|
698
703
|
export const isAppRunning = async (
|
|
699
704
|
adbId: string,
|
|
700
|
-
bundleId: string
|
|
705
|
+
bundleId: string
|
|
701
706
|
): Promise<boolean> => {
|
|
707
|
+
return (await getAppPid(adbId, bundleId)) != null;
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
export const getAppPid = async (
|
|
711
|
+
adbId: string,
|
|
712
|
+
bundleId: string
|
|
713
|
+
): Promise<number | null> => {
|
|
702
714
|
try {
|
|
703
715
|
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
704
716
|
'-s',
|
|
@@ -707,10 +719,15 @@ export const isAppRunning = async (
|
|
|
707
719
|
'pidof',
|
|
708
720
|
bundleId,
|
|
709
721
|
]);
|
|
710
|
-
|
|
722
|
+
const pid = stdout
|
|
723
|
+
.trim()
|
|
724
|
+
.split(/\s+/)
|
|
725
|
+
.find((value) => value !== '');
|
|
726
|
+
|
|
727
|
+
return pid ? Number(pid) : null;
|
|
711
728
|
} catch (error) {
|
|
712
729
|
if (error instanceof SubprocessError && error.exitCode === 1) {
|
|
713
|
-
return
|
|
730
|
+
return null;
|
|
714
731
|
}
|
|
715
732
|
|
|
716
733
|
throw error;
|
|
@@ -719,7 +736,7 @@ export const isAppRunning = async (
|
|
|
719
736
|
|
|
720
737
|
export const getAppUid = async (
|
|
721
738
|
adbId: string,
|
|
722
|
-
bundleId: string
|
|
739
|
+
bundleId: string
|
|
723
740
|
): Promise<number> => {
|
|
724
741
|
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
725
742
|
'-s',
|
|
@@ -744,7 +761,7 @@ export const getAppUid = async (
|
|
|
744
761
|
|
|
745
762
|
export const setHideErrorDialogs = async (
|
|
746
763
|
adbId: string,
|
|
747
|
-
hide: boolean
|
|
764
|
+
hide: boolean
|
|
748
765
|
): Promise<void> => {
|
|
749
766
|
await spawn(getAdbBinaryPath(), [
|
|
750
767
|
'-s',
|
|
@@ -773,12 +790,61 @@ export const getLogcatTimestamp = async (adbId: string): Promise<string> => {
|
|
|
773
790
|
export const startLogcat = (
|
|
774
791
|
adbId: string,
|
|
775
792
|
args: readonly string[],
|
|
793
|
+
options?: { signal?: AbortSignal }
|
|
776
794
|
): Subprocess =>
|
|
777
795
|
spawn(getAdbBinaryPath(), ['-s', adbId, ...args], {
|
|
778
796
|
stdout: 'pipe',
|
|
779
797
|
stderr: 'pipe',
|
|
798
|
+
signal: options?.signal,
|
|
780
799
|
});
|
|
781
800
|
|
|
801
|
+
export const DROPBOX_CRASH_TAGS = [
|
|
802
|
+
'data_app_crash',
|
|
803
|
+
'data_app_native_crash',
|
|
804
|
+
] as const;
|
|
805
|
+
|
|
806
|
+
export const getDropboxPrint = async (
|
|
807
|
+
adbId: string,
|
|
808
|
+
tags: readonly string[] = DROPBOX_CRASH_TAGS
|
|
809
|
+
): Promise<string> => {
|
|
810
|
+
// Android treats multiple args after --print as one search string, so each
|
|
811
|
+
// tag must be queried separately and merged on the host.
|
|
812
|
+
const outputs = await Promise.all(
|
|
813
|
+
tags.map(async (tag) => {
|
|
814
|
+
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
815
|
+
'-s',
|
|
816
|
+
adbId,
|
|
817
|
+
'shell',
|
|
818
|
+
'dumpsys',
|
|
819
|
+
'dropbox',
|
|
820
|
+
'--print',
|
|
821
|
+
tag,
|
|
822
|
+
]);
|
|
823
|
+
|
|
824
|
+
return stdout;
|
|
825
|
+
})
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
return outputs.join('\n');
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
export const getActivityExitInfo = async (
|
|
832
|
+
adbId: string,
|
|
833
|
+
bundleId: string
|
|
834
|
+
): Promise<string> => {
|
|
835
|
+
const { stdout } = await spawn(getAdbBinaryPath(), [
|
|
836
|
+
'-s',
|
|
837
|
+
adbId,
|
|
838
|
+
'shell',
|
|
839
|
+
'dumpsys',
|
|
840
|
+
'activity',
|
|
841
|
+
'exit-info',
|
|
842
|
+
bundleId,
|
|
843
|
+
]);
|
|
844
|
+
|
|
845
|
+
return stdout;
|
|
846
|
+
};
|
|
847
|
+
|
|
782
848
|
export const getAvds = async (): Promise<string[]> => {
|
|
783
849
|
try {
|
|
784
850
|
const emulatorBinaryPath = await ensureEmulatorInstalled();
|
|
@@ -832,7 +898,7 @@ export const getConnectedDevices = async (): Promise<AdbDevice[]> => {
|
|
|
832
898
|
|
|
833
899
|
export const grantPermissions = async (
|
|
834
900
|
adbId: string,
|
|
835
|
-
bundleId: string
|
|
901
|
+
bundleId: string
|
|
836
902
|
): Promise<void> => {
|
|
837
903
|
androidAdbLogger.debug('grantPermissions:start %o', {
|
|
838
904
|
adbId,
|
|
@@ -849,7 +915,7 @@ export const grantPermissions = async (
|
|
|
849
915
|
getDangerousPermissions(adbId),
|
|
850
916
|
]);
|
|
851
917
|
const permissions = requestedPermissions.filter((permission) =>
|
|
852
|
-
dangerousPermissions.has(permission)
|
|
918
|
+
dangerousPermissions.has(permission)
|
|
853
919
|
);
|
|
854
920
|
|
|
855
921
|
androidAdbLogger.debug('grantPermissions:resolved %o', {
|
|
@@ -885,7 +951,7 @@ export const grantPermissions = async (
|
|
|
885
951
|
});
|
|
886
952
|
|
|
887
953
|
await Promise.all(
|
|
888
|
-
grantCommands.map((args) => spawn(getAdbBinaryPath(), args as string[]))
|
|
954
|
+
grantCommands.map((args) => spawn(getAdbBinaryPath(), args as string[]))
|
|
889
955
|
);
|
|
890
956
|
|
|
891
957
|
androidAdbLogger.debug('grantPermissions:success %o', {
|