Tenants
| Name | Engine | Allowlist | Interval | Devices | Blocked | |
|---|---|---|---|---|---|---|
| Loading… | ||||||
Admin Users
Loading…
SDK
Open full pageSpeedTester SDK
Run speed tests, monitor network status, and manage Wi-Fi — all through the SpeedTester TV app, with a clean Kotlin API. No AIDL. No service binding. No boilerplate.
Overview
The SpeedTester SDK wraps the IPC layer between your app and the SpeedTester TV service. Under the hood it uses AIDL, but you never touch it — the SDK handles service binding, reconnection, and marshalling automatically.
| Requirement | Minimum |
|---|---|
| Android SDK | 24 (Android 7.0) |
| Kotlin | 1.9+ |
| Coroutines | 1.7+ |
| SpeedTester TV app | Installed on device |
Authentication
Every call into the SpeedTester service is authorised. Two mechanisms are available — whichever passes first is used.
📦 Package allowlist
Register your app's package name via this portal (Tenants → Allowed Packages). The service verifies the calling UID automatically — no code needed on your side.
🔑 Tenant API key
Request a key from 3SS and pass it to SpeedTesterSdk.init(). Useful for multi-app or white-label scenarios where package registration isn't practical.
Setup
Add the dependency
// settings.gradle.kts
dependencyResolutionManagement {
repositories {
maven { url = uri("https://maven.3ss.tv/releases") }
}
}
// app/build.gradle.kts
dependencies {
implementation("tv.threess:speedtest-sdk:1.0.0")
}
Declare package visibility (Android 11+)
<!-- AndroidManifest.xml -->
<queries>
<package android:name="tv.threess.speedtester" />
</queries>
Initialise the SDK
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
SpeedTesterSdk.init(
context = this,
apiKey = "YOUR_TENANT_API_KEY" // omit if using package allowlist
)
}
}
init() throws an IllegalStateException at runtime.Speed test
Use SpeedTestClient to trigger a test and observe progress as a Flow. Cancelling the coroutine automatically cancels the test.
val client = SpeedTestClient(context)
client.startTest().collect { event ->
when (event) {
is SpeedTestClient.Event.Progress -> showProgress(event.downloadMbps, event.percent)
is SpeedTestClient.Event.Completed -> showResult(event.result)
is SpeedTestClient.Event.Error -> showError(event.message)
}
}
client.release() // call in onDestroy / onCleared
Network status
Observe live network conditions as a Flow. Emits immediately with the current state, then on every change.
val client = NetworkStatusClient(context)
client.networkStatus().collect { status ->
println("${status.connectionType} — ${status.ssid} (${status.signalStrengthDbm} dBm)")
}
Wi-Fi
val client = WifiClient(context)
when (val result = client.connect("MyNetwork", "password123")) {
is WifiConnectResult.Connected -> println("Connected to ${result.ssid}")
is WifiConnectResult.Failed -> println("Failed: ${result.reason}")
}
Data models
SpeedTestResult
| Field | Type | Description |
|---|---|---|
sessionId | String | Unique test identifier |
engine | String | "ookla" or "librespeed" |
timestampEpochMs | Long | Completion time (Unix ms) |
downloadMbps | Float | Download speed |
uploadMbps | Float | Upload speed |
pingMs | Float | Latency |
jitterMs | Float | Jitter |
packetLossPercent | Float | Packet loss % |
network | NetworkStatus | Network context at test time |
NetworkStatus
| Field | Type | Description |
|---|---|---|
connectionType | ConnectionType | WIFI, CELLULAR, ETHERNET, UNKNOWN |
ssid | String? | Wi-Fi SSID (null on cellular) |
bssid | String? | Access point MAC address |
signalStrengthDbm | Int | Signal in dBm |
linkSpeedMbps | Int | PHY link speed |
ipAddress | String? | IPv4 address |
visibleSsids | List<String> | Nearby network names |
Error reference
| Error | Cause | Fix |
|---|---|---|
"Not authorised" |
Package not on allowlist; API key missing or invalid | Add package in Tenants → Allowed Packages, or pass a valid API key |
IllegalStateException: init()… |
Client created before SpeedTesterSdk.init() |
Move init() to Application.onCreate() |
Bind never completes |
SpeedTester app not installed or not running | Ensure SpeedTester app is installed and has been launched at least once |
Permissions
All sensitive permissions are held by the SpeedTester app. Your AndroidManifest.xml does not need ACCESS_FINE_LOCATION, ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, or INTERNET. You only need the <queries> entry from step 2.