@react-native-harness/platform-android 1.2.0-rc.1 → 1.3.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/adb.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type AndroidAppLaunchOptions } from '@react-native-harness/platforms';
2
+ import { type Subprocess } from '@react-native-harness/tools';
2
3
  import type { ChildProcessByStdio } from 'node:child_process';
3
4
  import type { Readable } from 'node:stream';
4
5
  import { type EmulatorBootMode } from './emulator-startup.js';
@@ -42,6 +43,7 @@ export declare const isAppRunning: (adbId: string, bundleId: string) => Promise<
42
43
  export declare const getAppUid: (adbId: string, bundleId: string) => Promise<number>;
43
44
  export declare const setHideErrorDialogs: (adbId: string, hide: boolean) => Promise<void>;
44
45
  export declare const getLogcatTimestamp: (adbId: string) => Promise<string>;
46
+ export declare const startLogcat: (adbId: string, args: readonly string[]) => Subprocess;
45
47
  export declare const getAvds: () => Promise<string[]>;
46
48
  export type AdbDevice = {
47
49
  id: string;
package/dist/adb.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../src/adb.ts"],"names":[],"mappings":"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"}
package/dist/adb.js CHANGED
@@ -1,4 +1,4 @@
1
- import { logger, spawn, SubprocessError } from '@react-native-harness/tools';
1
+ import { logger, spawn, SubprocessError, } from '@react-native-harness/tools';
2
2
  import { spawn as nodeSpawn } from 'node:child_process';
3
3
  import { access, rm } from 'node:fs/promises';
4
4
  import { getAvdConfigPath, getAvdDirectory, readAvdConfig, } from './avd-config.js';
@@ -490,6 +490,10 @@ export const getLogcatTimestamp = async (adbId) => {
490
490
  ]);
491
491
  return stdout.trim().replace(/^'+|'+$/g, '');
492
492
  };
493
+ export const startLogcat = (adbId, args) => spawn(getAdbBinaryPath(), ['-s', adbId, ...args], {
494
+ stdout: 'pipe',
495
+ stderr: 'pipe',
496
+ });
493
497
  export const getAvds = async () => {
494
498
  try {
495
499
  const emulatorBinaryPath = await ensureEmulatorInstalled();
@@ -1 +1 @@
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;AAgTzC,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,iBAgLH,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
+ {"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,4 +1,4 @@
1
- import { escapeRegExp, getEmitter, logger, spawn, SubprocessError, } from '@react-native-harness/tools';
1
+ import { escapeRegExp, getEmitter, logger, SubprocessError, } from '@react-native-harness/tools';
2
2
  import * as adb from './adb.js';
3
3
  import { androidCrashParser } from './crash-parser.js';
4
4
  const androidAppMonitorLogger = logger.child('android-app-monitor');
@@ -281,10 +281,7 @@ export const createAndroidAppMonitor = ({ adbId, bundleId, appUid, crashArtifact
281
281
  };
282
282
  const startLogcat = async () => {
283
283
  const logcatTimestamp = await adb.getLogcatTimestamp(adbId);
284
- logcatProcess = spawn('adb', ['-s', adbId, ...getLogcatArgs(appUid, logcatTimestamp)], {
285
- stdout: 'pipe',
286
- stderr: 'pipe',
287
- });
284
+ logcatProcess = adb.startLogcat(adbId, getLogcatArgs(appUid, logcatTimestamp));
288
285
  const currentProcess = logcatProcess;
289
286
  if (!currentProcess) {
290
287
  return;
@@ -0,0 +1,6 @@
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
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,142 @@
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
+ };