public final class Vpn

  1. Object
  2. Vpn

Installing and controlling a VPN configuration the operating system runs for you.

if (Vpn.isSupported()) {
    Vpn.install(new VpnProfile("vpn.example.com")
            .usernamePassword("alice", secret))
       .ready(v -> Vpn.start());
}

The tunnel is the platform’s, not this app’s

Nothing here carries packets. The configuration is handed to the operating system, which runs its own IKEv2 or IPsec client; the app starts it, stops it and watches it. Implementing the tunnel itself is a different and much larger undertaking – see the note in the com.codename1.vpn package documentation – and this API stays useful around it either way, for the install, the status and the control.

The user may be asked, and the app cannot skip the asking

install shows a system prompt when the platform wants consent, and there is no way around it: a declined prompt fails with VpnError.USER_DECLINED, which is an ordinary outcome and not an error to report to the user again.

It is NOT a prompt on every install. Android asks once and remembers, so replacing a profile in an app the user has already approved provisions and answers with nothing shown – the port takes that path whenever provisionVpnProfile returns no consent intent. An app that treated each install as a fresh checkpoint, or that told the user a dialog was coming, was wrong in that case.

So: be ready for the prompt, and do not depend on it appearing.

One configuration per app

Both platforms give an app a single managed configuration, so install replaces whatever was there rather than adding to it.

Methods

public static boolean isSupported()Whether this platform can install and control a VPN configuration.
public static int getCapabilities()The VpnBridge.CAPABILITY_* mask this platform supports.
public static VpnStatus getStatus()Where the tunnel is right now.
public static AsyncResource<Boolean> install(VpnProfile profile)Installs or replaces this app’s configuration, after asking the user.
public static AsyncResource<Boolean> remove()Removes this app’s configuration.
public static AsyncResource<VpnProfile> load()Reads back the installed configuration, resolving null when none is installed.
public static AsyncResource<Boolean> start()Brings the tunnel up.
public static AsyncResource<Boolean> stop()Takes the tunnel down.
public static void addStatusListener(VpnStatusListener l)Adds a status listener.
public static void removeStatusListener(VpnStatusListener l)Removes a status listener, stopping delivery when the last one goes.

Inherited methods

Method details

isSupported

public static boolean isSupported()

Whether this platform can install and control a VPN configuration.

False on Android below API 30, and on every port with no VPN machinery. Note this is a different question from whether a VPN is runningcom.codename1.io.NetworkManager#isVPNActive() answers that one, on many more platforms and with no entitlement.

getCapabilities

public static int getCapabilities()
The VpnBridge.CAPABILITY_* mask this platform supports.

getStatus

public static VpnStatus getStatus()
Where the tunnel is right now.

install

public static AsyncResource<Boolean> install(VpnProfile profile)
Installs or replaces this app’s configuration, after asking the user.

remove

public static AsyncResource<Boolean> remove()
Removes this app’s configuration.

load

public static AsyncResource<VpnProfile> load()

Reads back the installed configuration, resolving null when none is installed.

The result never carries the password – the platform keeps it. See VpnProfile.getPassword().

On Android this is this app’s RECORD, not a platform read

VpnManager provisions, deletes, starts and stops; it has no call that hands a profile back. So the description here is the one this app kept when it installed, and it can outlive the real thing – the user removing the VPN in Settings, or app data restored onto a device that never had it, both leave the record saying a profile exists.

It corrects itself the first time the platform contradicts it: a start or a stop that comes back “nothing is provisioned” drops the record and moves the status to NOT_CONFIGURED. An app that must be certain should start rather than trust a description, and watch the status. iOS reads the real configuration and has no such gap.

start

public static AsyncResource<Boolean> start()
Brings the tunnel up.

stop

public static AsyncResource<Boolean> stop()
Takes the tunnel down.

addStatusListener

public static void addStatusListener(VpnStatusListener l)
Adds a status listener. Arrives on the EDT.

removeStatusListener

public static void removeStatusListener(VpnStatusListener l)
Removes a status listener, stopping delivery when the last one goes.