Table of Contents

Class AndroidVpnAdapter

Namespace
VpnHood.Net.VpnAdapters.AndroidTun
Assembly
VpnHood.Net.VpnAdapters.AndroidTun.dll

Provides the VPN adapter for Android, on the TUN interface that Android's VpnService gives an app: it describes the interface to a VpnService.Builder, establishes it, and reads and writes its packets through libc on its file descriptor.

public class AndroidVpnAdapter : TunVpnAdapter, IVpnAdapter, IPacketTransport, IDisposable
Inheritance
AndroidVpnAdapter
Implements
Inherited Members

Remarks

The interface stays up when the device moves to other networks, which Android handles; from Android 10, it takes its meteredness from the networks under it. It has app filters, no NAT and no metric, and keeps a socket out of the tunnel with Protect(int). Its steps run at once, and take no notice of their cancellation tokens.

Constructors

AndroidVpnAdapter(VpnService, AndroidVpnAdapterSettings)

Provides the VPN adapter for Android, on the TUN interface that Android's VpnService gives an app: it describes the interface to a VpnService.Builder, establishes it, and reads and writes its packets through libc on its file descriptor.

public AndroidVpnAdapter(VpnService vpnService, AndroidVpnAdapterSettings vpnAdapterSettings)

Parameters

vpnService VpnService

The app's VPN service, which the user has allowed to run a VPN (Prepare(Context)).

vpnAdapterSettings AndroidVpnAdapterSettings

The adapter's settings.

Remarks

The interface stays up when the device moves to other networks, which Android handles; from Android 10, it takes its meteredness from the networks under it. It has app filters, no NAT and no metric, and keeps a socket out of the tunnel with Protect(int). Its steps run at once, and take no notice of their cancellation tokens.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

Properties

AppPackageId

Gets the ID the app filter knows this app by, which it always lets into the tunnel, or null where there is no app filter.

protected override string AppPackageId { get; }

Property Value

string

The app's package name.

Exceptions

Exception

Android gave the service no application context.

CheckAdapterInterfaceAfterNetworkChanged

Gets a value indicating whether the adapter checks, at each change of the host's network, that its interface is still there, and stops when it is not. The check is IsAdapterInterfaceEnabled(), which by default looks for the interface by name or address only.

protected override bool CheckAdapterInterfaceAfterNetworkChanged { get; }

Property Value

bool

Always false.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

IsAppFilterSupported

Gets a value indicating whether the adapter can include or exclude apps from the tunnel (IncludeApps, ExcludeApps).

public override bool IsAppFilterSupported { get; }

Property Value

bool

Always true.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

IsSocketProtectedByBind

Gets a value indicating whether the adapter keeps a socket out of the tunnel by binding it to the primary address, rather than by an OS call of its own.

protected override bool IsSocketProtectedByBind { get; }

Property Value

bool

Always false: a socket is protected with Protect(int).

Remarks

When true, the routes leave out the probe networks the adapter aims at to find the primary addresses, and that search runs on an unprotected socket after a network change.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

RestartAfterNetworkAddressChanged

Gets a value indicating whether the adapter restarts when the host moves to other networks. When false, it takes the new primary addresses and raises PrimaryNetworkChanged.

protected override bool RestartAfterNetworkAddressChanged { get; }

Property Value

bool

Always false: Android moves the interface to the new network.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

Methods

AdapterAdd(CancellationToken)

Makes the interface: the first step of a start.

protected override Task AdapterAdd(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It starts the description of the interface: a non-blocking VpnService.Builder that, from Android 10, takes its meteredness from the networks under it. The interface itself comes with AdapterOpen(CancellationToken).

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

AdapterClose()

Closes the interface for reading and writing: the first step of a stop, under the base's stop lock. It may run after a partial start.

protected override void AdapterClose()

Remarks

It invalidates the file descriptor while no read or write is under way, then closes it, which ends the VPN. A failure to close is logged as an error, and the descriptor's Java object is disposed all the same.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

AdapterOpen(CancellationToken)

Opens the interface for reading and writing packets: the last step of a start, before the reader starts.

protected override Task AdapterOpen(CancellationToken cancellationToken)

Parameters

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It establishes the interface the builder describes, and sets its file descriptor non-blocking.

Exceptions

ArgumentNullException

The builder is not made: AdapterAdd(CancellationToken) has not run.

Exception

Android did not establish the interface, as when the app is not prepared to run a VPN or its VPN was revoked.

PInvokeException

The file descriptor's flags could not be read or set.

AdapterRemove()

Removes the interface and what the start added with it: the last step of a stop, after AdapterClose(). When the base calls it, it runs under the base's stop lock and must not wait for the reader or a sender to take that lock. It may run after a partial start, and after a close that failed.

protected override void AdapterRemove()

Remarks

It closes the interface, as AdapterClose() does.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

AddAddress(IpNetwork, CancellationToken)

Gives the interface its address in a virtual network. A start calls it once per configured IP version.

protected override Task AddAddress(IpNetwork ipNetwork, CancellationToken cancellationToken)

Parameters

ipNetwork IpNetwork

The interface's address with its network.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It adds the address to the builder, for AdapterOpen(CancellationToken).

Exceptions

ArgumentNullException

The builder is not made: AdapterAdd(CancellationToken) has not run.

AddNat(IpNetwork, CancellationToken)

Translates a virtual network to the host's primary address (NAT). A start calls it with UseNat, for each IP version the adapter's NAT translates and the host has a primary address of.

protected override Task AddNat(IpNetwork ipNetwork, CancellationToken cancellationToken)

Parameters

ipNetwork IpNetwork

The virtual network.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Exceptions

NotSupportedException

Always: Android has no NAT for an app.

AddRoute(IpNetwork, CancellationToken)

Routes a network through the interface. A start calls it once per network, for each IP version the interface carries.

protected override Task AddRoute(IpNetwork ipNetwork, CancellationToken cancellationToken)

Parameters

ipNetwork IpNetwork

The network.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It adds the route to the builder, for AdapterOpen(CancellationToken).

Exceptions

ArgumentNullException

The builder is not made: AdapterAdd(CancellationToken) has not run.

DisposeUnmanaged()

Releases unmanaged resources, such as native handles. A derived class must catch its own errors.

protected override void DisposeUnmanaged()

Remarks

It closes an interface still open.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

GetPrimaryAdapterDnsServers()

Gets the DNS servers of the networks under the VPN, as .NET lists none on Android: those of every network that has the internet, is not a VPN, and that Android has validated.

public override IReadOnlyList<IPAddress> GetPrimaryAdapterDnsServers()

Returns

IReadOnlyList<IPAddress>

The servers, without repeats, in this adapter's order of their networks' transports: Ethernet, Wi-Fi, USB, satellite, Bluetooth, cellular, then the rest. A network whose link properties Android does not give is skipped, and link-local IPv6 servers are left out, as their interface's scope is lost here.

Remarks

A network Android has not validated is left out: a captive portal's resolver may answer every name with the portal's address.

Exceptions

InvalidOperationException

Android gave no connectivity service.

IsNatSupported(IpVersion)

Determines whether the adapter's NAT translates an IP version. A server forwards a version the adapter does not translate by its own forwarders.

public override bool IsNatSupported(IpVersion ipVersion)

Parameters

ipVersion IpVersion

The IP version.

Returns

bool

true if the adapter translates it; otherwise, false.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

ProtectSocket(Socket)

Keeps a socket's traffic out of the tunnel with Protect(int). Call it before the socket connects.

public override bool ProtectSocket(Socket socket)

Parameters

socket Socket

The socket.

Returns

bool

true if Android protected the socket; otherwise, false.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

ProtectSocket(Socket, IPAddress)

Keeps a socket's traffic out of the tunnel, as ProtectSocket(Socket) does: the remote address does not matter. Call it before the socket connects.

public override bool ProtectSocket(Socket socket, IPAddress ipAddress)

Parameters

socket Socket

The socket.

ipAddress IPAddress

Not used.

Returns

bool

true if Android protected the socket; otherwise, false.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

ReadPacket(byte[])

Reads one packet from the interface into a buffer, without waiting for one.

protected override int ReadPacket(byte[] buffer)

Parameters

buffer byte[]

The buffer, at least the MTU long.

Returns

int

The number of bytes read, from the buffer's start; 0 if no packet is ready.

Remarks

Only the bytes read are parsed, and a packet whose header claims more is refused: the buffer is rented, so past them it holds an earlier read's bytes.

Exceptions

InvalidOperationException

The adapter is not open.

IOException

The read returned no bytes, as when the interface has closed.

PInvokeException

The read failed.

SetAllowedApps(IEnumerable<string>, CancellationToken)

Lets only these apps' traffic into the tunnel. A start calls it with IncludeApps, where IsAppFilterSupported is true.

protected override Task SetAllowedApps(IEnumerable<string> packageIds, CancellationToken cancellationToken)

Parameters

packageIds IEnumerable<string>

The apps' IDs, this app's own included.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It adds each app to the builder; one Android refuses, as an app that is not installed, is logged as an error and left out.

Exceptions

ArgumentNullException

The builder is not made: AdapterAdd(CancellationToken) has not run.

SetDisallowedApps(IEnumerable<string>, CancellationToken)

Keeps these apps' traffic out of the tunnel. A start calls it with ExcludeApps, where IsAppFilterSupported is true.

protected override Task SetDisallowedApps(IEnumerable<string> packageIds, CancellationToken cancellationToken)

Parameters

packageIds IEnumerable<string>

The apps' IDs, never this app's own.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It adds each app to the builder; one Android refuses, as an app that is not installed, is logged as an error and left out.

Exceptions

ArgumentNullException

The builder is not made: AdapterAdd(CancellationToken) has not run.

SetDnsServers(IReadOnlyList<IPAddress>, CancellationToken)

Sets the DNS servers the interface gives the host. A start calls it every time.

protected override Task SetDnsServers(IReadOnlyList<IPAddress> dnsServers, CancellationToken cancellationToken)

Parameters

dnsServers IReadOnlyList<IPAddress>

The servers, only of the IP versions the interface carries; may be empty.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It adds the servers to the builder, for AdapterOpen(CancellationToken).

Exceptions

ArgumentNullException

The builder is not made: AdapterAdd(CancellationToken) has not run.

SetMetric(int, bool, bool, CancellationToken)

Sets the interface's route metric. A start calls it when Metric is set.

protected override Task SetMetric(int metric, bool ipV4, bool ipV6, CancellationToken cancellationToken)

Parameters

metric int

The metric.

ipV4 bool

true if the interface carries IPv4 in this run.

ipV6 bool

true if the interface carries IPv6 in this run.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It does nothing: Android gives an app's VPN no metric.

Exceptions

ArgumentOutOfRangeException

The settings' blocking timeout is negative or too long, or their queue capacity is not above zero.

SetMtu(int, bool, bool, CancellationToken)

Sets the interface's MTU. A start calls it when Mtu is set.

protected override Task SetMtu(int mtu, bool ipV4, bool ipV6, CancellationToken cancellationToken)

Parameters

mtu int

The MTU in bytes.

ipV4 bool

true if the interface carries IPv4 in this run.

ipV6 bool

true if the interface carries IPv6 in this run.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It sets the MTU on the builder, for both IP versions.

Exceptions

ArgumentNullException

The builder is not made: AdapterAdd(CancellationToken) has not run.

SetSessionName(string, CancellationToken)

Names the session where the OS shows it. A start calls it when SessionName is not empty.

protected override Task SetSessionName(string sessionName, CancellationToken cancellationToken)

Parameters

sessionName string

The name.

cancellationToken CancellationToken

The token to monitor for cancellation requests.

Returns

Task

A task that represents the asynchronous operation.

Remarks

It names the session on the builder, which Android shows in the VPN's settings and notification.

Exceptions

ArgumentNullException

The builder is not made: AdapterAdd(CancellationToken) has not run.

WaitForTunRead()

Blocks until a packet may be ready, after a read found none. It runs on the reader's task.

protected override void WaitForTunRead()

Remarks

It polls the file descriptor for up to a second at a time, so a reader whose adapter closed meanwhile wakes to see it, and polls again when a signal interrupts it. It returns at once when the adapter was closed before the wait began.

Exceptions

PInvokeException

The poll failed.

WaitForTunWrite()

Blocks until the interface may take a packet again, between the retries of a write it refused.

protected override void WaitForTunWrite()

Remarks

It polls the file descriptor for up to a second at a time, so a writer whose adapter closed meanwhile wakes to see it, and polls again when a signal interrupts it. It returns at once when the adapter was closed before the wait began.

Exceptions

PInvokeException

The poll failed.

WritePacket(IpPacket)

Writes one packet to the interface, without waiting for room.

protected override bool WritePacket(IpPacket ipPacket)

Parameters

ipPacket IpPacket

The packet.

Returns

bool

true if the interface took it; false if it cannot take it now, which the adapter retries with doubling waits (see MaxPacketSendDelay).

Exceptions

InvalidOperationException

The adapter is not open.

IOException

The interface took only part of the packet.

PInvokeException

The write failed.