Skip to main content

VPN Service

On Android the tunnel runs inside an android.net.VpnService. The VPN service is an ordinary component of your app process, registered in your AndroidManifest.xml. The SDK provides KapeVpnService; the minimal integration is to subclass it (or use it directly):

// KapeVpnTunnelService.kt
import com.kape.platformsdk.vpn.KapeVpnService

class KapeVpnTunnelService : KapeVpnService()

The base class handles:

  • SDK initialization with the shared, encrypted credential store
  • Endpoint discovery and authentication
  • Tunnel setup and handshake (WireGuard, Lightway, or OpenVPN)
  • Running as a foreground service with the required ongoing notification
  • Connection status and traffic stats updates back to the app

Supplying Your Own Configurations​

Most integrations never touch this. If you depend on KapeVPN-VpnService directly and drive KapeSessionController yourself, you supply a VpnConfigurationGenerator:

fun interface VpnConfigurationGenerator {
suspend fun nextConfiguration(): VpnConfiguration?
}

The session controller calls it once per connection attempt and connects to whatever comes back, so your generator owns discovery, ordering, credentials, and re-fetching when it runs out of endpoints. Return null when you have nothing available right now — the session backs off and asks again rather than tearing the tunnel down. Throw KapeVpnTunnelException for a failure the app can act on; the reason is recorded in lastTunnelError and the session retries with backoff.

A configuration you return has to be ready to connect. For WireGuard that means authorizing it first, which your WireGuardAuthenticator does for you:

override suspend fun nextConfiguration(): VpnConfiguration? {
val config = endpoints.removeFirstOrNull() ?: return null
return authenticator.authorize(config)
}

authorize throws when authorization fails. The connection controller no longer sees that failure, so log it yourself: toAuthFailureLog() includes the HTTP status of a WireGuardAuthenticationException and redacts public IP addresses, which your logger may not do. The built-in WireGuard authenticator throws one, with the KapeError as its cause, when the auth server rejects the request; any other failure is a KapeError.

Manifest Registration​

Register the service in your app's AndroidManifest.xml. The system requires the BIND_VPN_SERVICE permission on the <service> element and the android.net.VpnService intent filter:

<service
android:name=".KapeVpnTunnelService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:foregroundServiceType="specialUse"
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>

Your app must also declare the supporting permissions (see Installation): INTERNET, ACCESS_NETWORK_STATE, FOREGROUND_SERVICE (plus FOREGROUND_SERVICE_SPECIAL_USE on API 34+), and POST_NOTIFICATIONS (API 33+).

How the App and Service Communicate​

DirectionMechanism
App → ServiceContext.startService(...) / service binding
Service → AppStatus broadcasts / bound-service callbacks
Shared configkape_sdk_settings.xml (read by both)
Shared stateDataStore (selected location, protocol, last error)

You don't interact with these directly. KapeVpnManager and KapeVpnService handle all inter-component communication.