Server Locations
Fetching Locations
Fetch the list of available VPN servers after authentication:
val locations = vpnManager.createLocationService()
locations.fetchLocations() // suspend
Only locations serving the user's selected VPN protocol come back — a location that serves only
Lightway is not offered while OpenVPN is selected. The selection is read once per call and nothing
observes it, so call fetchLocations() again after setSelectedProtocol(...). A consumer without
KapeVPN supplies the protocol directly; see VPN Locations.
Locations are exposed as StateFlows either as a flat list or as a grouping by continent:
// All locations
locations.allLocations // StateFlow<List<KapeServerLocation>>
// Grouped by continent
locations.locationsByContinent // StateFlow<Map<Continent, List<KapeServerLocation>>>
// e.g. {Continent.Europe: [...], Continent.Americas: [...]}
viewModelScope.launch {
locations.allLocations.collect { servers -> /* update UI */ }
}
Each KapeServerLocation provides:
| Property | Type | Description |
|---|---|---|
id | String | Location ID (pass to connect(...)) |
name | String | Human-readable name (e.g. "Frankfurt") |
country | String | ISO 3166-1 alpha-2 country code |
continent | String | Continent or region name |
latitude | Double | Latitude coordinate |
longitude | Double | Longitude coordinate |
rating | KapeLocationRating? | Quality rating (smart locations only) |
KapeLocationRating values: Excellent, Good, Ok, Degraded, Poor, Unknown. The enum is
Comparable which means that higher ratings sort greater.
Smart Locations
Smart locations are the SDK's recommended servers for the current user. The rating combines several signals: ping latency (min, avg, median, p90), short HTTP download and upload measurements, and geographic distance. Results are cached per network. When the user reconnects to a known WiFi network, cached results are returned immediately without re-running the full measurement.
Fetch smart locations after fetchLocations():
locations.fetchSmartLocations()
locations.smartLocations // StateFlow<List<KapeServerLocation>> — sorted by best rating
To disable HTTP tests (recommended on cellular to save data and reduce measurement time):
locations.fetchSmartLocations(extendedTest = false)
extendedTest applies to this path only — a progress-tracked scan (below) measures latency and
never runs the HTTP tests.
To receive per-location progress updates while the measurement runs:
locations.fetchSmartLocations { progress ->
println("${progress.locationId}: ${progress.pingMs ?: 0} ms")
}
Call fetchLocations() before fetchSmartLocations(). Smart locations are resolved by matching
against allLocations, which is scoped to the selected protocol — so a highly-rated location that
does not serve that protocol is not recommended. With a narrow protocol selection smartLocations
can come back empty even though the rating ran.
Network Monitoring
KapeNetworkHelper (part of KapeCore) keeps the SDK aware of the current network and exposes the
WiFi identity for display in your UI. It also enables per-network SmartLocation caching. When the
user reconnects to a known network, cached results are returned immediately.
Setup
Create one instance and call start() once after the SDK handle is ready. Typically in your
Application class or a top-level view model:
import com.kape.platformsdk.core.KapeNetworkHelper
val networkHelper = KapeNetworkHelper(handle)
networkHelper.start()
KapeNetworkHelper uses ConnectivityManager to detect network changes. On each change it reads the
current SSID and BSSID (via WifiManager), pushes them into the SDK configuration, and signals a
network change so the SDK can invalidate or restore cached SmartLocation results for that network.
Call stop() when the helper is no longer needed:
networkHelper.stop()
Observing network info
KapeNetworkHelper exposes two StateFlows:
| Property | Type | Description |
|---|---|---|
ssid | StateFlow<String?> | WiFi network name. null on cellular or when the location permission is missing. |
bssid | StateFlow<String?> | WiFi access point MAC address. null on cellular or when the location permission is missing. |
viewModelScope.launch {
networkHelper.ssid.collect { ssid -> /* show ssid ?: "–" */ }
}
Runtime permission
On Android, reading the SSID and BSSID requires the ACCESS_FINE_LOCATION runtime permission (and
NEARBY_WIFI_DEVICES on API 33+). These are declared and requested by your app, not by the SDK —
the SDK does not bundle a location permission in its library manifest, to avoid forcing a sensitive
permission on every integrator.
<!-- In your app's AndroidManifest.xml -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
Request the permission at runtime before calling start(). Without it, ssid and bssid are always
null. The helper still functions — network-change signals are sent to the SDK — but per-network
caching has no benefit since all networks appear identical.
Do not instantiate KapeNetworkHelper inside KapeVpnService. The service does not need
SmartLocation caching and runs under a tighter resource budget.