package com.plugin.vpnservice import android.content.Intent import android.net.VpnService import android.os.Build import android.os.ParcelFileDescriptor import android.os.Bundle import java.net.InetAddress import java.util.Arrays import app.tauri.plugin.JSObject class TauriVpnService : VpnService() { companion object { @JvmField var triggerCallback: (String, JSObject) -> Unit = { _, _ -> } @JvmField var self: TauriVpnService? = null const val IPV4_ADDR = "IPV4_ADDR" const val ROUTES = "ROUTES" const val DNS = "DNS" const val DISALLOWED_APPLICATIONS = "DISALLOWED_APPLICATIONS" const val MTU = "MTU" } private lateinit var vpnInterface: ParcelFileDescriptor override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { println("vpn on start command ${intent?.getExtras()} $intent") var args = intent?.getExtras() vpnInterface = createVpnInterface(args) println("vpn created ${vpnInterface.fd}") var event_data = JSObject() event_data.put("fd", vpnInterface.fd) triggerCallback("vpn_service_start", event_data) return START_STICKY } override fun onCreate() { super.onCreate() self = this println("vpn on create") } override fun onDestroy() { println("vpn on destroy") super.onDestroy() disconnect() self = null } override fun onRevoke() { println("vpn on revoke") super.onRevoke() disconnect() self = null } private fun disconnect() { if (self == this && this::vpnInterface.isInitialized) { triggerCallback("vpn_service_stop", JSObject()) vpnInterface.close() } } private fun createVpnInterface(args: Bundle?): ParcelFileDescriptor { var builder = Builder() .setSession("TauriVpnService") .setBlocking(false) var mtu = args?.getInt(MTU) ?: 1500 var ipv4Addr = args?.getString(IPV4_ADDR) ?: "10.126.126.1/24" var dns = args?.getString(DNS) ?: "114.114.114.114" var routes = args?.getStringArray(ROUTES) ?: emptyArray() var disallowedApplications = args?.getStringArray(DISALLOWED_APPLICATIONS) ?: emptyArray() println("vpn create vpn interface. mtu: $mtu, ipv4Addr: $ipv4Addr, dns:" + "$dns, routes: ${java.util.Arrays.toString(routes)}," + "disallowedApplications: ${java.util.Arrays.toString(disallowedApplications)}") val ipParts = ipv4Addr.split("/") if (ipParts.size != 2) throw IllegalArgumentException("Invalid IP addr string") builder.addAddress(ipParts[0], ipParts[1].toInt()) builder.setMtu(mtu) builder.addDnsServer(dns) for (route in routes) { val ipParts = route.split("/") if (ipParts.size != 2) throw IllegalArgumentException("Invalid route cidr string") builder.addRoute(ipParts[0], ipParts[1].toInt()) } for (app in disallowedApplications) { builder.addDisallowedApplication(app) } return builder.also { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { it.setMetered(false) } } .establish() ?: throw IllegalStateException("Failed to init VpnService") } }