Files
android-tether-api/app/src/main/java/dev/itsh/tetherapi/receiver/TetherStateReceiver.kt
Giovanni Harting 017172f48a Initial commit: Android Tether API app
Exposes phone connection status via HTTP API for tethered devices.

Features:
- Auto-start/stop server when tethering is enabled/disabled
- REST API with /status and /health endpoints
- Connection type, signal strength, carrier, battery info
- Foreground service with persistent notification

Stack: Kotlin 2.3, AGP 8.13, Gradle 8.13, compileSdk 36
2025-12-18 21:08:56 +01:00

62 lines
2.1 KiB
Kotlin

package dev.itsh.tetherapi.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.os.Build
import androidx.preference.PreferenceManager
import dev.itsh.tetherapi.service.TetherApiService
class TetherStateReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != ACTION_TETHER_STATE_CHANGED) return
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
val autoStartEnabled = prefs.getBoolean(PREF_AUTO_START, true)
if (!autoStartEnabled) return
val isTethering = isTetherActive(context, intent)
val port = prefs.getInt(PREF_PORT, TetherApiService.DEFAULT_PORT)
if (isTethering && !TetherApiService.isRunning) {
TetherApiService.start(context, port)
} else if (!isTethering && TetherApiService.isRunning) {
TetherApiService.stop(context)
}
}
@Suppress("DEPRECATION")
private fun isTetherActive(context: Context, intent: Intent): Boolean {
val extras = intent.extras ?: return false
val activeList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
extras.getStringArrayList("activeArray")
} else {
@Suppress("UNCHECKED_CAST")
extras.get("activeArray") as? ArrayList<String>
}
if (!activeList.isNullOrEmpty()) {
return true
}
return try {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val method = cm.javaClass.getDeclaredMethod("getTetheredIfaces")
val tetheredIfaces = method.invoke(cm) as? Array<*>
tetheredIfaces != null && tetheredIfaces.isNotEmpty()
} catch (e: Exception) {
false
}
}
companion object {
const val ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"
const val PREF_AUTO_START = "auto_start_on_tether"
const val PREF_PORT = "api_port"
}
}