Skip to content

[AdbRunner] Add ADB forward port management (follow-up to #305)#351

Draft
rmarinho wants to merge 1 commit intodotnet:mainfrom
rmarinho:feature/adb-forward-port
Draft

[AdbRunner] Add ADB forward port management (follow-up to #305)#351
rmarinho wants to merge 1 commit intodotnet:mainfrom
rmarinho:feature/adb-forward-port

Conversation

@rmarinho
Copy link
Copy Markdown
Member

Summary

Adds the symmetric forward-port pair to the reverse-port methods that landed in #305. Same AdbPortSpec / AdbPortRule / AdbProtocol types — just four new methods. Discussed in #305 (comment).

Method adb command
ForwardPortAsync(serial, local, remote) adb -s <serial> forward <local> <remote>
RemoveForwardPortAsync(serial, local) adb -s <serial> forward --remove <local>
RemoveAllForwardPortsAsync(serial) adb -s <serial> forward --remove-all
ListForwardPortsAsync(serial) adb forward --list (filtered by serial)

Why we need this in addition to reverse

adb forward and adb reverse are not interchangeable — they connect opposite directions:

  • reverse <remote-on-device> <local-on-host> — device-side socket forwards to host-side. Used by hot reload (the device app connects to a "device" port that's actually tunnelled to the IDE host).
  • forward <local-on-host> <remote-on-device> — host-side socket forwards to device-side. Used when the IDE / harness needs to connect to a service running on the device:
    • JDWP debugger attachforward tcp:N jdwp:<pid>
    • Performance-tracing endpoints exposed by the runtime
    • DevFlow agent connect — when the in-app agent listens on a device port and the host needs a stable host-side port to reach it

--list output format note

adb forward --list emits one line per rule across all devices in the form <serial> <local> <remote> (different from (reverse) <remote> <local>). ListForwardPortsAsync uses the unscoped adb forward --list and filters to the requested serial in ParseForwardListOutput. Serial match is case-sensitive to match adb's behaviour.

Tests

  • 12 new parser tests (ParseForwardListOutput_*) mirroring the reverse parser tests:
    • single rule, multiple rules, serial filtering, empty output, no lines, empty serial, no matching device, malformed line, non-tcp specs, Windows line endings, tab separation, case-sensitivity.
  • 7 new parameter-validation tests covering empty serial / null spec for the four new public methods.

All Forward + Reverse tests pass locally (36/36). Full suite has one pre-existing unrelated JdkDirectory_JavaHome failure on main that's not touched by this PR.

Consumers

  • VS Code MAUI extension ServiceHub→CLI migration — forwardPort in MauiAndroidPlatform.ts (debugger configurations, perf tooling).
  • MAUI DevTools CLI (dotnet/maui-labs#197) — maui android port forward … group, sibling of the existing reverse surface.
  • Visual Studio ClientTools.Platform — same paths that drive reverse today have parallel forward call-sites.

Public API additions

virtual Xamarin.Android.Tools.AdbRunner.ForwardPortAsync(string! serial, AdbPortSpec! local, AdbPortSpec! remote, CancellationToken cancellationToken = default) -> Task!
virtual Xamarin.Android.Tools.AdbRunner.ListForwardPortsAsync(string! serial, CancellationToken cancellationToken = default) -> Task<IReadOnlyList<AdbPortRule!>!>!
virtual Xamarin.Android.Tools.AdbRunner.RemoveAllForwardPortsAsync(string! serial, CancellationToken cancellationToken = default) -> Task!
virtual Xamarin.Android.Tools.AdbRunner.RemoveForwardPortAsync(string! serial, AdbPortSpec! local, CancellationToken cancellationToken = default) -> Task!

Adds the symmetric forward-port pair to the reverse-port methods that landed
in dotnet#305. Same `AdbPortSpec` / `AdbPortRule` / `AdbProtocol` types — just four
new methods.

| Method                                          | adb command                                |
|-------------------------------------------------|--------------------------------------------|
| ForwardPortAsync(serial, local, remote)         | adb -s <serial> forward <local> <remote>   |
| RemoveForwardPortAsync(serial, local)           | adb -s <serial> forward --remove <local>   |
| RemoveAllForwardPortsAsync(serial)              | adb -s <serial> forward --remove-all       |
| ListForwardPortsAsync(serial)                   | adb forward --list (filtered by serial)    |

`adb forward` and `adb reverse` are not interchangeable — they connect
opposite directions. `forward` is host->device (the IDE/harness reaches a
service running on the device) and is the path used for JDWP debugger
attach (`forward tcp:N jdwp:<pid>`), perf-tracing endpoints exposed by the
runtime, and host-side DevFlow agent connect when the agent listens on a
device port.

Output-format note for `--list`: `adb forward --list` emits one line per
rule across all devices in the form `<serial> <local> <remote>` (different
from `(reverse) <remote> <local>`). `ListForwardPortsAsync` uses the
unscoped `adb forward --list` and filters to the requested serial in
`ParseForwardListOutput`. Serial match is case-sensitive (matches adb).

### Tests

- 12 new parser tests in `ParseForwardListOutput_*` mirroring the reverse
  parser tests (single rule, multiple rules, serial filtering, empty
  output, malformed lines, non-tcp specs, Windows line endings, tab
  separation, case sensitivity).
- 7 new parameter-validation tests covering empty serial / null spec for
  the four new public methods.

### Consumers

- VS Code MAUI extension ServiceHub->CLI migration (`forwardPort` in
  `MauiAndroidPlatform.ts` — debugger configurations, perf tooling).
- MAUI DevTools CLI (dotnet/maui-labs#197) — `maui android port forward`
  group, sibling of the existing `reverse` surface.
- Visual Studio `ClientTools.Platform` — same paths that drive reverse
  today.

Discussed in
dotnet#305 (comment)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copilot `copilot-cli` or other AIs were used to author this enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant