@react-native-harness/platform-android 1.4.0 → 1.5.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.
Files changed (77) hide show
  1. package/dist/adb.d.ts +2 -4
  2. package/dist/adb.d.ts.map +1 -1
  3. package/dist/adb.js +2 -3
  4. package/dist/app-session.d.ts +3 -12
  5. package/dist/app-session.d.ts.map +1 -1
  6. package/dist/app-session.js +23 -33
  7. package/dist/instance.d.ts.map +1 -1
  8. package/dist/instance.js +2 -4
  9. package/package.json +4 -4
  10. package/src/adb.ts +5 -6
  11. package/src/app-session.ts +14 -31
  12. package/src/instance.ts +2 -4
  13. package/dist/__tests__/adb-id.test.d.ts +0 -2
  14. package/dist/__tests__/adb-id.test.d.ts.map +0 -1
  15. package/dist/__tests__/adb-id.test.js +0 -41
  16. package/dist/__tests__/adb-wait.test.d.ts +0 -2
  17. package/dist/__tests__/adb-wait.test.d.ts.map +0 -1
  18. package/dist/__tests__/adb-wait.test.js +0 -69
  19. package/dist/__tests__/adb.test.d.ts +0 -2
  20. package/dist/__tests__/adb.test.d.ts.map +0 -1
  21. package/dist/__tests__/adb.test.js +0 -552
  22. package/dist/__tests__/app-monitor.test.d.ts +0 -2
  23. package/dist/__tests__/app-monitor.test.d.ts.map +0 -1
  24. package/dist/__tests__/app-monitor.test.js +0 -257
  25. package/dist/__tests__/app-session.test.d.ts +0 -2
  26. package/dist/__tests__/app-session.test.d.ts.map +0 -1
  27. package/dist/__tests__/app-session.test.js +0 -57
  28. package/dist/__tests__/avd-config.test.d.ts +0 -2
  29. package/dist/__tests__/avd-config.test.d.ts.map +0 -1
  30. package/dist/__tests__/avd-config.test.js +0 -232
  31. package/dist/__tests__/ci-action.test.d.ts +0 -2
  32. package/dist/__tests__/ci-action.test.d.ts.map +0 -1
  33. package/dist/__tests__/ci-action.test.js +0 -46
  34. package/dist/__tests__/crash-diagnostics.test.d.ts +0 -2
  35. package/dist/__tests__/crash-diagnostics.test.d.ts.map +0 -1
  36. package/dist/__tests__/crash-diagnostics.test.js +0 -119
  37. package/dist/__tests__/crash-parser.test.d.ts +0 -2
  38. package/dist/__tests__/crash-parser.test.d.ts.map +0 -1
  39. package/dist/__tests__/crash-parser.test.js +0 -45
  40. package/dist/__tests__/crash-reporter.test.d.ts +0 -2
  41. package/dist/__tests__/crash-reporter.test.d.ts.map +0 -1
  42. package/dist/__tests__/crash-reporter.test.js +0 -150
  43. package/dist/__tests__/emulator-startup.test.d.ts +0 -2
  44. package/dist/__tests__/emulator-startup.test.d.ts.map +0 -1
  45. package/dist/__tests__/emulator-startup.test.js +0 -47
  46. package/dist/__tests__/environment.test.d.ts +0 -2
  47. package/dist/__tests__/environment.test.d.ts.map +0 -1
  48. package/dist/__tests__/environment.test.js +0 -117
  49. package/dist/__tests__/instance.test.d.ts +0 -2
  50. package/dist/__tests__/instance.test.d.ts.map +0 -1
  51. package/dist/__tests__/instance.test.js +0 -599
  52. package/dist/__tests__/shared-prefs.test.d.ts +0 -2
  53. package/dist/__tests__/shared-prefs.test.d.ts.map +0 -1
  54. package/dist/__tests__/shared-prefs.test.js +0 -87
  55. package/dist/__tests__/targets.test.d.ts +0 -2
  56. package/dist/__tests__/targets.test.d.ts.map +0 -1
  57. package/dist/__tests__/targets.test.js +0 -49
  58. package/dist/app-monitor.d.ts +0 -13
  59. package/dist/app-monitor.d.ts.map +0 -1
  60. package/dist/app-monitor.js +0 -376
  61. package/dist/device-state.d.ts +0 -6
  62. package/dist/device-state.d.ts.map +0 -1
  63. package/dist/device-state.js +0 -142
  64. package/dist/tsconfig.lib.tsbuildinfo +0 -1
  65. package/src/__tests__/adb-id.test.ts +0 -51
  66. package/src/__tests__/adb-wait.test.ts +0 -95
  67. package/src/__tests__/app-session.test.ts +0 -76
  68. package/src/__tests__/avd-config.test.ts +0 -276
  69. package/src/__tests__/ci-action.test.ts +0 -81
  70. package/src/__tests__/crash-diagnostics.test.ts +0 -144
  71. package/src/__tests__/crash-parser.test.ts +0 -52
  72. package/src/__tests__/crash-reporter.test.ts +0 -167
  73. package/src/__tests__/emulator-startup.test.ts +0 -82
  74. package/src/__tests__/instance.test.ts +0 -844
  75. package/src/__tests__/shared-prefs.test.ts +0 -144
  76. package/src/__tests__/targets.test.ts +0 -53
  77. package/tsconfig.tsbuildinfo +0 -1
@@ -1,87 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import * as tools from '@react-native-harness/tools';
3
- import { applyHarnessDebugHttpHost, clearHarnessDebugHttpHost, } from '../shared-prefs.js';
4
- const bundleId = 'com.example.app';
5
- const adbId = 'emulator-5554';
6
- const getWrittenContent = (calls) => {
7
- const writeCall = calls.find(([, , options]) => {
8
- if (!options || typeof options !== 'object' || !('stdin' in options)) {
9
- return false;
10
- }
11
- return Boolean(options.stdin);
12
- });
13
- const options = writeCall?.[2];
14
- if (!options || typeof options !== 'object' || !('stdin' in options)) {
15
- throw new Error('Expected write call options.');
16
- }
17
- const content = options.stdin;
18
- if (!content ||
19
- typeof content !== 'object' ||
20
- !('string' in content) ||
21
- typeof content.string !== 'string') {
22
- throw new Error('Expected write call with string stdin.');
23
- }
24
- return content.string;
25
- };
26
- const getWrittenContents = (calls) => calls
27
- .filter(([, , options]) => {
28
- if (!options || typeof options !== 'object' || !('stdin' in options)) {
29
- return false;
30
- }
31
- return Boolean(options.stdin);
32
- })
33
- .map((call) => getWrittenContent([call]));
34
- describe('Android shared preferences Metro host override', () => {
35
- afterEach(() => {
36
- vi.restoreAllMocks();
37
- });
38
- it('handles empty self-closing map files', async () => {
39
- const spawnSpy = vi
40
- .spyOn(tools, 'spawn')
41
- .mockResolvedValueOnce({
42
- stdout: '<?xml version="1.0" encoding="utf-8"?>\n<map />\n',
43
- })
44
- .mockResolvedValueOnce({});
45
- await applyHarnessDebugHttpHost(adbId, bundleId, 'localhost:9090');
46
- expect(getWrittenContent(spawnSpy.mock.calls)).toContain('<map>');
47
- expect(getWrittenContent(spawnSpy.mock.calls)).toContain('</map>');
48
- expect(getWrittenContent(spawnSpy.mock.calls)).toContain('<string name="debug_http_host">localhost:9090</string>');
49
- });
50
- it('restores the previous debug host on cleanup', async () => {
51
- const spawnSpy = vi
52
- .spyOn(tools, 'spawn')
53
- .mockResolvedValueOnce({
54
- stdout: [
55
- '<?xml version="1.0" encoding="utf-8"?>',
56
- '<map>',
57
- ' <string name="debug_http_host">10.0.2.2:8081</string>',
58
- '</map>',
59
- ].join('\n'),
60
- })
61
- .mockResolvedValueOnce({})
62
- .mockResolvedValueOnce({
63
- stdout: [
64
- '<?xml version="1.0" encoding="utf-8"?>',
65
- '<map>',
66
- ' <string name="debug_http_host">10.0.2.2:8081</string>',
67
- ' <!-- react-native-harness:debug_http_host:start -->',
68
- ' <string name="debug_http_host">localhost:9090</string>',
69
- ' <!-- react-native-harness:debug_http_host:end -->',
70
- '</map>',
71
- ].join('\n'),
72
- })
73
- .mockResolvedValueOnce({});
74
- await applyHarnessDebugHttpHost(adbId, bundleId, 'localhost:9090');
75
- await clearHarnessDebugHttpHost(adbId, bundleId);
76
- const writes = getWrittenContents(spawnSpy.mock.calls);
77
- const firstWrite = writes[0];
78
- const secondWrite = writes[1];
79
- expect(firstWrite).toEqual(expect.stringContaining('<string name="harness_debug_http_host_backup">10.0.2.2:8081</string>'));
80
- expect(firstWrite).toEqual(expect.stringContaining('<string name="debug_http_host">localhost:9090</string>'));
81
- expect(firstWrite).toEqual(expect.not.stringContaining('<string name="debug_http_host">10.0.2.2:8081</string>'));
82
- expect(secondWrite).toEqual(expect.stringContaining('<string name="debug_http_host">10.0.2.2:8081</string>'));
83
- expect(secondWrite).toEqual(expect.not.stringContaining('<string name="harness_debug_http_host_backup">10.0.2.2:8081</string>'));
84
- expect(secondWrite).toEqual(expect.not.stringContaining('<!-- react-native-harness:debug_http_host:start -->'));
85
- expect(secondWrite).toEqual(expect.not.stringContaining('<string name="debug_http_host">localhost:9090</string>'));
86
- });
87
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=targets.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"targets.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/targets.test.ts"],"names":[],"mappings":""}
@@ -1,49 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { getRunTargets } from '../targets.js';
3
- import * as adb from '../adb.js';
4
- import * as environment from '../environment.js';
5
- describe('Android target discovery', () => {
6
- beforeEach(() => {
7
- vi.restoreAllMocks();
8
- vi.unstubAllEnvs();
9
- });
10
- it('installs adb and emulator only for discovery', async () => {
11
- const ensureAndroidAdbAvailable = vi
12
- .spyOn(environment, 'ensureAndroidAdbAvailable')
13
- .mockResolvedValue('/tmp/android-sdk');
14
- const ensureAndroidEmulatorAvailable = vi
15
- .spyOn(environment, 'ensureAndroidEmulatorAvailable')
16
- .mockResolvedValue('/tmp/android-sdk');
17
- vi.spyOn(adb, 'getAvds').mockResolvedValue(['Pixel_8_API_35']);
18
- vi.spyOn(adb, 'getConnectedDevices').mockResolvedValue([
19
- {
20
- id: 'device-1',
21
- manufacturer: 'Google',
22
- model: 'Pixel 8',
23
- },
24
- ]);
25
- await expect(getRunTargets()).resolves.toEqual([
26
- {
27
- type: 'emulator',
28
- name: 'Pixel_8_API_35',
29
- platform: 'android',
30
- description: 'Android emulator',
31
- device: {
32
- name: 'Pixel_8_API_35',
33
- },
34
- },
35
- {
36
- type: 'physical',
37
- name: 'Google Pixel 8',
38
- platform: 'android',
39
- description: 'Physical device (device-1)',
40
- device: {
41
- manufacturer: 'Google',
42
- model: 'Pixel 8',
43
- },
44
- },
45
- ]);
46
- expect(ensureAndroidAdbAvailable).toHaveBeenCalledTimes(1);
47
- expect(ensureAndroidEmulatorAvailable).toHaveBeenCalledTimes(1);
48
- });
49
- });
@@ -1,13 +0,0 @@
1
- import { type AppMonitor, type AppCrashDetails, type CrashArtifactWriter, type CrashDetailsLookupOptions, type AppMonitorEvent } from '@react-native-harness/platforms';
2
- declare const createAndroidLogEvent: (line: string, bundleId: string) => AppMonitorEvent | null;
3
- export declare const createAndroidAppMonitor: ({ adbId, bundleId, appUid, crashArtifactWriter, }: {
4
- adbId: string;
5
- bundleId: string;
6
- appUid: number;
7
- crashArtifactWriter?: CrashArtifactWriter;
8
- }) => AndroidAppMonitor;
9
- export { createAndroidLogEvent };
10
- export type AndroidAppMonitor = AppMonitor & {
11
- getCrashDetails: (options: CrashDetailsLookupOptions) => Promise<AppCrashDetails | null>;
12
- };
13
- //# sourceMappingURL=app-monitor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"app-monitor.d.ts","sourceRoot":"","sources":["../src/app-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,eAAe,EAErB,MAAM,iCAAiC,CAAC;AA+SzC,QAAA,MAAM,qBAAqB,GACzB,MAAM,MAAM,EACZ,UAAU,MAAM,KACf,eAAe,GAAG,IAwEpB,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,mDAKrC;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C,KAAG,iBA4KH,CAAC;AAEF,OAAO,EAAE,qBAAqB,EAAE,CAAC;AACjC,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG;IAC3C,eAAe,EAAE,CACf,OAAO,EAAE,yBAAyB,KAC/B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;CACtC,CAAC"}
@@ -1,376 +0,0 @@
1
- import { escapeRegExp, getEmitter, logger, SubprocessError, } from '@react-native-harness/tools';
2
- import * as adb from './adb.js';
3
- import { androidCrashParser } from './crash-parser.js';
4
- const androidAppMonitorLogger = logger.child('android-app-monitor');
5
- const getLogcatArgs = (uid, fromTime) => [
6
- 'logcat',
7
- '-v',
8
- 'threadtime',
9
- '-b',
10
- 'crash',
11
- `--uid=${uid}`,
12
- '-T',
13
- fromTime,
14
- ];
15
- const MAX_RECENT_LOG_LINES = 200;
16
- const MAX_RECENT_CRASH_ARTIFACTS = 10;
17
- const CRASH_ARTIFACT_SETTLE_DELAY_MS = 100;
18
- const startProcPattern = (bundleId) => new RegExp(`Start proc (\\d+):${escapeRegExp(bundleId)}(?:/|\\s)`);
19
- const processPattern = (bundleId) => new RegExp(`Process:\\s*${escapeRegExp(bundleId)},\\s*PID:\\s*(\\d+)`);
20
- const nativeCrashPattern = (bundleId) => new RegExp(`>>>\\s*${escapeRegExp(bundleId)}\\s*<<<`);
21
- const processDiedPattern = (bundleId) => new RegExp(`Process\\s+${escapeRegExp(bundleId)}\\s+\\(pid\\s+(\\d+)\\)\\s+has\\s+died`, 'i');
22
- const getSignal = (line) => {
23
- const namedSignalMatch = line.match(/\b(SIG[A-Z0-9]+)\b/);
24
- if (namedSignalMatch) {
25
- return namedSignalMatch[1];
26
- }
27
- const signalNumberMatch = line.match(/signal\s+(\d+)/i);
28
- if (signalNumberMatch) {
29
- return `signal ${signalNumberMatch[1]}`;
30
- }
31
- return undefined;
32
- };
33
- const getAndroidLogLineCrashDetails = ({ line, bundleId, pid, }) => {
34
- const fatalExceptionMatch = line.match(/FATAL EXCEPTION:\s*(.+)$/i);
35
- const processMatch = line.match(processPattern(bundleId));
36
- return {
37
- source: 'logs',
38
- summary: line.trim(),
39
- signal: getSignal(line),
40
- exceptionType: fatalExceptionMatch?.[1]?.trim(),
41
- processName: processMatch
42
- ? bundleId
43
- : line.includes(bundleId)
44
- ? bundleId
45
- : undefined,
46
- pid: pid ?? (processMatch ? Number(processMatch[1]) : undefined),
47
- rawLines: [line],
48
- };
49
- };
50
- const CRASH_BLOCK_HEADER = '--------- beginning of crash';
51
- const getLatestCrashBlock = (recentLogLines) => {
52
- const lines = recentLogLines.map(({ line }) => line);
53
- let latestCrashHeaderIndex = -1;
54
- for (let index = lines.length - 1; index >= 0; index -= 1) {
55
- if (/FATAL EXCEPTION:|Process:\s+.+,\s+PID:/i.test(lines[index])) {
56
- latestCrashHeaderIndex = index;
57
- break;
58
- }
59
- }
60
- const blockStartIndex = Math.max(lines.lastIndexOf(CRASH_BLOCK_HEADER), latestCrashHeaderIndex);
61
- if (blockStartIndex === -1) {
62
- return lines;
63
- }
64
- return lines.slice(blockStartIndex);
65
- };
66
- const getCrashBlockForArtifact = ({ artifact, recentLogLines, }) => {
67
- const targetIndex = recentLogLines.findIndex(({ line, occurredAt }) => line === artifact.triggerLine &&
68
- (artifact.triggerOccurredAt === undefined ||
69
- occurredAt === artifact.triggerOccurredAt));
70
- if (targetIndex === -1) {
71
- return artifact.rawLines ?? [];
72
- }
73
- let blockStartIndex = targetIndex;
74
- for (let index = targetIndex; index >= 0; index -= 1) {
75
- const { line } = recentLogLines[index];
76
- if (line === CRASH_BLOCK_HEADER) {
77
- blockStartIndex = index;
78
- break;
79
- }
80
- }
81
- let blockEndIndex = recentLogLines.length;
82
- for (let index = targetIndex + 1; index < recentLogLines.length; index += 1) {
83
- if (recentLogLines[index].line === CRASH_BLOCK_HEADER) {
84
- blockEndIndex = index;
85
- break;
86
- }
87
- }
88
- return recentLogLines
89
- .slice(blockStartIndex, blockEndIndex)
90
- .map(({ line }) => line);
91
- };
92
- const hydrateCrashArtifact = ({ artifact, recentLogLines, }) => {
93
- const rawLines = getCrashBlockForArtifact({ artifact, recentLogLines });
94
- if (rawLines.length === 0) {
95
- return artifact;
96
- }
97
- const parsedDetails = androidCrashParser.parse({
98
- contents: rawLines.join('\n'),
99
- bundleId: artifact.processName ?? '',
100
- pid: artifact.pid,
101
- });
102
- return {
103
- ...artifact,
104
- ...parsedDetails,
105
- artifactType: artifact.artifactType,
106
- artifactPath: artifact.artifactPath,
107
- rawLines,
108
- };
109
- };
110
- const createCrashArtifact = ({ details, recentLogLines, }) => {
111
- const occurredAt = Date.now();
112
- const rawLines = getLatestCrashBlock(recentLogLines);
113
- const triggerOccurredAt = [...recentLogLines]
114
- .reverse()
115
- .find(({ line }) => line === details.summary)?.occurredAt;
116
- const contents = rawLines.length > 0
117
- ? rawLines.join('\n')
118
- : (details.rawLines ?? []).join('\n');
119
- const parsedDetails = details.processName !== undefined
120
- ? androidCrashParser.parse({
121
- contents,
122
- bundleId: details.processName,
123
- pid: details.pid,
124
- })
125
- : details;
126
- return {
127
- ...parsedDetails,
128
- occurredAt,
129
- triggerLine: details.summary ?? '',
130
- triggerOccurredAt,
131
- artifactType: 'logcat',
132
- rawLines: rawLines.length > 0
133
- ? rawLines
134
- : parsedDetails.rawLines ?? details.rawLines,
135
- };
136
- };
137
- const persistCrashArtifact = ({ details, crashArtifactWriter, }) => {
138
- if (!crashArtifactWriter || details.artifactType !== 'logcat') {
139
- return details;
140
- }
141
- const artifactBody = details.rawLines?.join('\n');
142
- if (!artifactBody) {
143
- return details;
144
- }
145
- return {
146
- ...details,
147
- artifactPath: crashArtifactWriter.persistArtifact({
148
- artifactKind: details.artifactType,
149
- source: {
150
- kind: 'text',
151
- fileName: 'logcat.txt',
152
- text: `${artifactBody}\n`,
153
- },
154
- }),
155
- };
156
- };
157
- const getLatestCrashArtifact = ({ crashArtifacts, recentLogLines, processName, pid, occurredAt, }) => {
158
- const matchingByPid = pid
159
- ? crashArtifacts.filter((artifact) => artifact.pid === pid)
160
- : [];
161
- const matchingByProcess = processName
162
- ? crashArtifacts.filter((artifact) => artifact.processName === processName)
163
- : [];
164
- const candidates = matchingByPid.length > 0
165
- ? matchingByPid
166
- : matchingByProcess.length > 0
167
- ? matchingByProcess
168
- : crashArtifacts;
169
- const sortedCandidates = [...candidates].sort((left, right) => Math.abs(left.occurredAt - occurredAt) -
170
- Math.abs(right.occurredAt - occurredAt));
171
- const artifact = sortedCandidates[0];
172
- if (!artifact) {
173
- return null;
174
- }
175
- return hydrateCrashArtifact({
176
- artifact,
177
- recentLogLines,
178
- });
179
- };
180
- const createAndroidLogEvent = (line, bundleId) => {
181
- const startMatch = line.match(startProcPattern(bundleId));
182
- if (startMatch) {
183
- return {
184
- type: 'app_started',
185
- pid: Number(startMatch[1]),
186
- source: 'logs',
187
- line,
188
- };
189
- }
190
- const processMatch = line.match(processPattern(bundleId));
191
- if (processMatch) {
192
- return {
193
- type: 'possible_crash',
194
- pid: Number(processMatch[1]),
195
- source: 'logs',
196
- line,
197
- crashDetails: getAndroidLogLineCrashDetails({
198
- line,
199
- bundleId,
200
- pid: Number(processMatch[1]),
201
- }),
202
- };
203
- }
204
- if (nativeCrashPattern(bundleId).test(line)) {
205
- return {
206
- type: 'possible_crash',
207
- source: 'logs',
208
- line,
209
- crashDetails: getAndroidLogLineCrashDetails({
210
- line,
211
- bundleId,
212
- }),
213
- };
214
- }
215
- const diedMatch = line.match(processDiedPattern(bundleId));
216
- if (diedMatch) {
217
- return {
218
- type: 'app_exited',
219
- pid: Number(diedMatch[1]),
220
- source: 'logs',
221
- line,
222
- crashDetails: getAndroidLogLineCrashDetails({
223
- line,
224
- bundleId,
225
- pid: Number(diedMatch[1]),
226
- }),
227
- };
228
- }
229
- if (line.includes(bundleId) &&
230
- /fatal|crash|signal 11|signal 6|backtrace/i.test(line)) {
231
- return {
232
- type: 'possible_crash',
233
- source: 'logs',
234
- line,
235
- crashDetails: getAndroidLogLineCrashDetails({
236
- line,
237
- bundleId,
238
- }),
239
- };
240
- }
241
- return null;
242
- };
243
- export const createAndroidAppMonitor = ({ adbId, bundleId, appUid, crashArtifactWriter, }) => {
244
- const emitter = getEmitter();
245
- let isStarted = false;
246
- let logcatProcess = null;
247
- let logTask = null;
248
- let recentLogLines = [];
249
- let recentCrashArtifacts = [];
250
- const emit = (event) => {
251
- emitter.emit(event);
252
- };
253
- const recordLogLine = (line) => {
254
- recentLogLines = [
255
- ...recentLogLines,
256
- { line, occurredAt: Date.now() },
257
- ].slice(-MAX_RECENT_LOG_LINES);
258
- };
259
- const recordCrashArtifact = (details) => {
260
- if (!details) {
261
- return;
262
- }
263
- recentCrashArtifacts = [
264
- ...recentCrashArtifacts,
265
- createCrashArtifact({
266
- details,
267
- recentLogLines,
268
- }),
269
- ].slice(-MAX_RECENT_CRASH_ARTIFACTS);
270
- };
271
- const stopProcess = async (child) => {
272
- if (!child) {
273
- return;
274
- }
275
- try {
276
- (await child.nodeChildProcess).kill();
277
- }
278
- catch {
279
- // Ignore termination failures for background monitors.
280
- }
281
- };
282
- const startLogcat = async () => {
283
- const logcatTimestamp = await adb.getLogcatTimestamp(adbId);
284
- logcatProcess = adb.startLogcat(adbId, getLogcatArgs(appUid, logcatTimestamp));
285
- const currentProcess = logcatProcess;
286
- if (!currentProcess) {
287
- return;
288
- }
289
- logTask = (async () => {
290
- try {
291
- for await (const line of currentProcess) {
292
- recordLogLine(line);
293
- emit({ type: 'log', source: 'logs', line });
294
- const event = createAndroidLogEvent(line, bundleId);
295
- if (event) {
296
- if (event.type === 'possible_crash' ||
297
- event.type === 'app_exited') {
298
- recordCrashArtifact(event.crashDetails);
299
- }
300
- emit(event);
301
- }
302
- }
303
- }
304
- catch (error) {
305
- if (!(error instanceof SubprocessError && error.signalName === 'SIGTERM')) {
306
- androidAppMonitorLogger.debug('Android logcat monitor stopped', error);
307
- }
308
- }
309
- })();
310
- };
311
- const start = async () => {
312
- if (isStarted) {
313
- return;
314
- }
315
- try {
316
- await startLogcat();
317
- isStarted = true;
318
- }
319
- catch (error) {
320
- const currentProcess = logcatProcess;
321
- const currentTask = logTask;
322
- logcatProcess = null;
323
- logTask = null;
324
- await stopProcess(currentProcess);
325
- await currentTask;
326
- throw error;
327
- }
328
- };
329
- const stop = async () => {
330
- if (!isStarted) {
331
- return;
332
- }
333
- isStarted = false;
334
- const currentProcess = logcatProcess;
335
- const currentTask = logTask;
336
- logcatProcess = null;
337
- logTask = null;
338
- await stopProcess(currentProcess);
339
- await currentTask;
340
- };
341
- const dispose = async () => {
342
- await stop();
343
- emitter.clearAllListeners();
344
- recentLogLines = [];
345
- recentCrashArtifacts = [];
346
- };
347
- const addListener = (listener) => {
348
- emitter.addListener(listener);
349
- };
350
- const removeListener = (listener) => {
351
- emitter.removeListener(listener);
352
- };
353
- return {
354
- start,
355
- stop,
356
- dispose,
357
- addListener,
358
- removeListener,
359
- getCrashDetails: async (options) => {
360
- await new Promise((resolve) => setTimeout(resolve, CRASH_ARTIFACT_SETTLE_DELAY_MS));
361
- const details = getLatestCrashArtifact({
362
- crashArtifacts: recentCrashArtifacts,
363
- recentLogLines,
364
- ...options,
365
- });
366
- if (!details) {
367
- return null;
368
- }
369
- return persistCrashArtifact({
370
- details,
371
- crashArtifactWriter,
372
- });
373
- },
374
- };
375
- };
376
- export { createAndroidLogEvent };
@@ -1,6 +0,0 @@
1
- import type { DeviceStateController } from '@react-native-harness/platforms';
2
- export declare const createAndroidDeviceState: (options: {
3
- adbId: string;
4
- bundleId: string;
5
- }) => DeviceStateController;
6
- //# sourceMappingURL=device-state.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"device-state.d.ts","sourceRoot":"","sources":["../src/device-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,qBAAqB,EAEtB,MAAM,iCAAiC,CAAC;AA0HzC,eAAO,MAAM,wBAAwB,GAAI,SAAS;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB,KAAG,qBAqFH,CAAC"}
@@ -1,142 +0,0 @@
1
- import * as adb from './adb.js';
2
- const ANDROID_PERMISSION_ALIASES = {
3
- bluetooth: [
4
- 'android.permission.BLUETOOTH_CONNECT',
5
- 'android.permission.BLUETOOTH_SCAN',
6
- ],
7
- calendar: [
8
- 'android.permission.READ_CALENDAR',
9
- 'android.permission.WRITE_CALENDAR',
10
- ],
11
- camera: ['android.permission.CAMERA'],
12
- contacts: [
13
- 'android.permission.READ_CONTACTS',
14
- 'android.permission.WRITE_CONTACTS',
15
- ],
16
- location: [
17
- 'android.permission.ACCESS_FINE_LOCATION',
18
- 'android.permission.ACCESS_COARSE_LOCATION',
19
- ],
20
- mediaLibrary: [
21
- 'android.permission.READ_MEDIA_IMAGES',
22
- 'android.permission.READ_MEDIA_VIDEO',
23
- 'android.permission.READ_MEDIA_AUDIO',
24
- ],
25
- microphone: ['android.permission.RECORD_AUDIO'],
26
- notifications: ['android.permission.POST_NOTIFICATIONS'],
27
- phone: [
28
- 'android.permission.CALL_PHONE',
29
- 'android.permission.ANSWER_PHONE_CALLS',
30
- ],
31
- sms: [
32
- 'android.permission.READ_SMS',
33
- 'android.permission.RECEIVE_SMS',
34
- 'android.permission.SEND_SMS',
35
- ],
36
- storage: [
37
- 'android.permission.READ_EXTERNAL_STORAGE',
38
- 'android.permission.WRITE_EXTERNAL_STORAGE',
39
- ],
40
- };
41
- const normalizePermissionName = (name) => {
42
- return name.trim();
43
- };
44
- const resolvePermissions = async (options) => {
45
- if (options.command.kind === 'permission-all') {
46
- return adb.getDeclaredDangerousPermissions(options.adbId, options.bundleId);
47
- }
48
- const normalized = normalizePermissionName(options.command.permission);
49
- const aliases = ANDROID_PERMISSION_ALIASES[normalized];
50
- if (aliases) {
51
- return aliases;
52
- }
53
- return [normalized];
54
- };
55
- const snapshotPermissions = async (options) => {
56
- const granted = await Promise.all(options.permissions.map(async (permission) => {
57
- const isGranted = await adb.isPermissionGranted(options.adbId, options.bundleId, permission);
58
- return isGranted ? permission : null;
59
- }));
60
- return granted.filter((permission) => permission != null);
61
- };
62
- const applyPermissionSnapshot = async (options) => {
63
- const grantedSet = new Set(options.snapshot.previousGranted);
64
- const toGrant = options.snapshot.permissions.filter((permission) => grantedSet.has(permission));
65
- const toRevoke = options.snapshot.permissions.filter((permission) => !grantedSet.has(permission));
66
- if (toGrant.length > 0) {
67
- await adb.setPermissions(options.adbId, options.bundleId, toGrant, 'grant', {
68
- bestEffort: true,
69
- });
70
- }
71
- if (toRevoke.length > 0) {
72
- await adb.setPermissions(options.adbId, options.bundleId, toRevoke, 'reset', {
73
- bestEffort: true,
74
- });
75
- }
76
- };
77
- export const createAndroidDeviceState = (options) => {
78
- const outstandingMutations = new Map();
79
- let mutationCounter = 0;
80
- const rememberMutation = (snapshot) => {
81
- const id = `android-permissions-${++mutationCounter}`;
82
- const mutation = {
83
- id,
84
- snapshot,
85
- revert: async () => {
86
- await controller.permissions.revert(id);
87
- },
88
- };
89
- outstandingMutations.set(id, mutation);
90
- return mutation;
91
- };
92
- const controller = {
93
- permissions: {
94
- apply: async (command) => {
95
- const permissions = await resolvePermissions({
96
- adbId: options.adbId,
97
- bundleId: options.bundleId,
98
- command,
99
- });
100
- if (permissions.length === 0) {
101
- return { mutation: null };
102
- }
103
- const previousGranted = await snapshotPermissions({
104
- adbId: options.adbId,
105
- bundleId: options.bundleId,
106
- permissions,
107
- });
108
- await adb.setPermissions(options.adbId, options.bundleId, permissions, command.decision, { bestEffort: command.kind === 'permission-all' });
109
- const mutation = rememberMutation({
110
- kind: 'permission-set',
111
- permissions,
112
- previousGranted,
113
- });
114
- return { mutation };
115
- },
116
- revert: async (mutationId) => {
117
- const mutation = outstandingMutations.get(mutationId);
118
- if (!mutation) {
119
- return;
120
- }
121
- outstandingMutations.delete(mutationId);
122
- await applyPermissionSnapshot({
123
- adbId: options.adbId,
124
- bundleId: options.bundleId,
125
- snapshot: mutation.snapshot,
126
- });
127
- },
128
- resetOutstanding: async () => {
129
- const mutations = [...outstandingMutations.values()].reverse();
130
- outstandingMutations.clear();
131
- for (const mutation of mutations) {
132
- await applyPermissionSnapshot({
133
- adbId: options.adbId,
134
- bundleId: options.bundleId,
135
- snapshot: mutation.snapshot,
136
- });
137
- }
138
- },
139
- },
140
- };
141
- return controller;
142
- };