1import * as Log from '../../../log';
2import { installExitHooks } from '../../../utils/exit';
3import { adbArgs, Device, getAttachedDevicesAsync, getServer, logUnauthorized } from './adb';
4
5let removeExitHook: (() => void) | null = null;
6
7export async function startAdbReverseAsync(ports: number[]): Promise<boolean> {
8  // Install cleanup automatically...
9  removeExitHook = installExitHooks(() => {
10    stopAdbReverseAsync(ports);
11  });
12
13  const devices = await getAttachedDevicesAsync();
14  for (const device of devices) {
15    for (const port of ports) {
16      if (!(await adbReverseAsync(device, port))) {
17        Log.debug(`[ADB] Failed to start reverse port ${port} on device "${device.name}"`);
18        return false;
19      }
20    }
21  }
22  return true;
23}
24
25export async function stopAdbReverseAsync(ports: number[]): Promise<void> {
26  removeExitHook?.();
27
28  const devices = await getAttachedDevicesAsync();
29  for (const device of devices) {
30    for (const port of ports) {
31      await adbReverseRemoveAsync(device, port);
32    }
33  }
34}
35
36async function adbReverseAsync(device: Device, port: number): Promise<boolean> {
37  if (!device.isAuthorized) {
38    logUnauthorized(device);
39    return false;
40  }
41
42  try {
43    await getServer().runAsync(adbArgs(device.pid, 'reverse', `tcp:${port}`, `tcp:${port}`));
44    return true;
45  } catch (error: any) {
46    Log.warn(`[ADB] Couldn't reverse port ${port}: ${error.message}`);
47    return false;
48  }
49}
50
51async function adbReverseRemoveAsync(device: Device, port: number): Promise<boolean> {
52  if (!device.isAuthorized) {
53    return false;
54  }
55
56  try {
57    await getServer().runAsync(adbArgs(device.pid, 'reverse', '--remove', `tcp:${port}`));
58    return true;
59  } catch (error: any) {
60    // Don't send this to warn because we call this preemptively sometimes
61    Log.debug(`[ADB] Couldn't reverse remove port ${port}: ${error.message}`);
62    return false;
63  }
64}
65