amnezia-client/client/android/src/org/amnezia/vpn/VPNServiceBinder.kt
Josh Soref 7351fe9633
Spelling (#214)
Spelling fixed
2023-04-11 14:50:44 +01:00

210 lines
7.2 KiB
Kotlin

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.amnezia.vpn
import android.os.Binder
import android.os.DeadObjectException
import android.os.IBinder
import android.os.Parcel
import com.wireguard.config.*
import org.json.JSONObject
import java.lang.Exception
class VPNServiceBinder(service: VPNService) : Binder() {
private val mService = service
private val tag = "VPNServiceBinder"
private var mListener: IBinder? = null
private var mResumeConfig: JSONObject? = null
private var mImportedConfig: String? = null
/**
* The codes this Binder does accept in [onTransact]
*/
object ACTIONS {
const val activate = 1
const val deactivate = 2
const val registerEventListener = 3
const val requestStatistic = 4
const val requestGetLog = 5
const val requestCleanupLog = 6
const val resumeActivate = 7
const val setNotificationText = 8
const val setFallBackNotification = 9
const val importConfig = 11
}
/**
* Gets called when the VPNServiceBinder gets a request from a Client.
* The [code] determines what action is requested. - see [ACTIONS]
* [data] may contain a utf-8 encoded json string with optional args or is null.
* [reply] is a pointer to a buffer in the clients memory, to reply results.
* we use this to send result data.
*
* returns true if the [code] was accepted
*/
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
Log.i(tag, "GOT TRANSACTION " + code)
when (code) {
ACTIONS.activate -> {
try {
Log.i(tag, "Activation Requested, parsing Config")
// [data] is here a json containing the wireguard/openvpn conf
val buffer = data.createByteArray()
val json = buffer?.let { String(it) }
val config = JSONObject(json)
Log.v(tag, "Stored new Tunnel config in Service")
Log.i(tag, "Config: $config")
if (!mService.checkPermissions()) {
mResumeConfig = config
// The Permission prompt was already
// send, in case it's accepted we will
// receive ACTIONS.resumeActivate
return true
}
this.mService.turnOn(config)
} catch (e: Exception) {
Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}")
dispatchEvent(EVENTS.activationError, e.localizedMessage)
}
return true
}
ACTIONS.resumeActivate -> {
// [data] is empty
// Activate the current tunnel
Log.i(tag, "resume activate")
try {
mResumeConfig?.let { this.mService.turnOn(it) }
} catch (e: Exception) {
Log.e(tag, "An Error occurred while enabling the VPN: ${e.localizedMessage}")
}
return true
}
ACTIONS.deactivate -> {
// [data] here is empty
this.mService.turnOff()
return true
}
ACTIONS.registerEventListener -> {
Log.i(tag, "register: start")
// [data] contains the Binder that we need to dispatch the Events
val binder = data.readStrongBinder()
mListener = binder
val obj = JSONObject()
obj.put("connected", mService.isUp)
obj.put("time", mService.connectionTime)
dispatchEvent(EVENTS.init, obj.toString())
////
if (mImportedConfig != null) {
Log.i(tag, "register: config not null")
dispatchEvent(EVENTS.configImport, mImportedConfig)
mImportedConfig = null
} else {
Log.i(tag, "register: config is null")
}
return true
}
ACTIONS.requestStatistic -> {
dispatchEvent(EVENTS.statisticUpdate, mService.status.toString())
return true
}
ACTIONS.requestGetLog -> {
// Grabs all the Logs and dispatch new Log Event
dispatchEvent(EVENTS.backendLogs, Log.getContent())
return true
}
ACTIONS.requestCleanupLog -> {
Log.clearFile()
return true
}
ACTIONS.setNotificationText -> {
NotificationUtil.update(data)
return true
}
ACTIONS.setFallBackNotification -> {
NotificationUtil.saveFallBackMessage(data, mService)
return true
}
ACTIONS.importConfig -> {
val buffer = data.readString()
val obj = JSONObject()
obj.put("config", buffer)
val resultString = obj.toString()
Log.i(tag, "Transact import config request")
if (mListener != null) {
dispatchEvent(EVENTS.configImport, resultString)
} else {
mImportedConfig = resultString
}
}
IBinder.LAST_CALL_TRANSACTION -> {
Log.e(tag, "The OS Requested to shut down the VPN")
this.mService.turnOff()
return true
}
else -> {
Log.e(tag, "Received invalid bind request \t Code -> $code")
// If we're hitting this there is probably something wrong in the client.
return false
}
}
return false
}
/**
* Dispatches an Event to all registered Binders
* [code] the Event that happened - see [EVENTS]
* To register an Eventhandler use [onTransact] with
* [ACTIONS.registerEventListener]
*/
fun dispatchEvent(code: Int, payload: String?) {
try {
mListener?.let {
if (it.isBinderAlive) {
val data = Parcel.obtain()
data.writeByteArray(payload?.toByteArray(charset("UTF-8")))
it.transact(code, data, Parcel.obtain(), 0)
} else {
Log.i(tag, "Dispatching event: binder NOT alive")
}
}
} catch (e: DeadObjectException) {
// If the QT Process is killed (not just inactive)
// we cant access isBinderAlive, so nothing to do here.
}
}
/**
* The codes we Are Using in case of [dispatchEvent]
*/
object EVENTS {
const val init = 0
const val connected = 1
const val disconnected = 2
const val statisticUpdate = 3
const val backendLogs = 4
const val activationError = 5
const val permissionRequired = 6
const val configImport = 7
}
}