Skip to content

Commit 3c347c1

Browse files
committed
Implement streaming connection status (Android)
1 parent fc19a27 commit 3c347c1

File tree

3 files changed

+75
-38
lines changed

3 files changed

+75
-38
lines changed
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
package network.mysterium.wireguard_dart
22

3+
import com.wireguard.android.backend.Tunnel
4+
35
enum class ConnectionStatus {
4-
disconnected, connected, connecting, disconnecting, unknown
6+
disconnected, connected, connecting, disconnecting, unknown;
7+
8+
companion object {
9+
fun fromTunnelState(state: Tunnel.State?): ConnectionStatus = when (state) {
10+
Tunnel.State.UP -> connected
11+
Tunnel.State.DOWN -> disconnected
12+
else -> unknown
13+
}
14+
}
515
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package network.mysterium.wireguard_dart
2+
3+
import android.os.Handler
4+
import android.os.Looper
5+
import io.flutter.plugin.common.EventChannel
6+
import io.flutter.plugin.common.EventChannel.EventSink
7+
import io.flutter.plugin.common.EventChannel.StreamHandler
8+
9+
class ConnectionStatusBroadcaster : StreamHandler {
10+
11+
private var eventSink: EventSink? = null
12+
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
13+
eventSink = events
14+
}
15+
16+
override fun onCancel(arguments: Any?) {
17+
eventSink = null
18+
}
19+
20+
fun send(status: ConnectionStatus) {
21+
Handler(Looper.getMainLooper()).post {
22+
eventSink?.success(hashMapOf("status" to status.name))
23+
}
24+
}
25+
}

android/src/main/kotlin/network/mysterium/wireguard_dart/WireguardDartPlugin.kt

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,21 @@ import android.util.Log
1818
import com.beust.klaxon.Klaxon
1919
import com.wireguard.android.backend.*
2020
import com.wireguard.crypto.KeyPair
21+
import io.flutter.plugin.common.EventChannel
2122
import kotlinx.coroutines.*
2223

2324

2425
import kotlinx.coroutines.launch
2526
import java.io.ByteArrayInputStream
2627

27-
/** WireguardDartPlugin */
28-
2928
const val PERMISSIONS_REQUEST_CODE = 10014
30-
const val METHOD_CHANNEL_NAME = "wireguard_dart"
3129

3230
class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
33-
PluginRegistry.ActivityResultListener {
34-
/// The MethodChannel that will the communication between Flutter and native Android
35-
///
36-
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
37-
/// when the Flutter Engine is detached from the Activity
31+
PluginRegistry.ActivityResultListener {
32+
3833
private lateinit var channel: MethodChannel
39-
private lateinit var tunnelName: String
34+
private lateinit var statusChannel: EventChannel
35+
private lateinit var statusBroadcaster: ConnectionStatusBroadcaster
4036
private val futureBackend = CompletableDeferred<Backend>()
4137
private val scope = CoroutineScope(Job() + Dispatchers.Main.immediate)
4238
private var backend: Backend? = null
@@ -45,15 +41,21 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
4541
private var activity: Activity? = null
4642
private var config: com.wireguard.config.Config? = null
4743
private var tunnel: WireguardTunnel? = null
48-
private lateinit var status: ConnectionStatus
44+
private var status: ConnectionStatus = ConnectionStatus.disconnected
45+
set(value) {
46+
field = value
47+
if (::statusBroadcaster.isInitialized) {
48+
statusBroadcaster.send(value)
49+
}
50+
}
4951

5052
companion object {
5153
const val TAG = "MainActivity"
5254
}
5355

5456
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
5557
havePermission =
56-
(requestCode == PERMISSIONS_REQUEST_CODE) && (resultCode == Activity.RESULT_OK)
58+
(requestCode == PERMISSIONS_REQUEST_CODE) && (resultCode == Activity.RESULT_OK)
5759
return havePermission
5860
}
5961

@@ -74,7 +76,10 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
7476
}
7577

7678
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
77-
channel = MethodChannel(flutterPluginBinding.binaryMessenger, METHOD_CHANNEL_NAME)
79+
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "wireguard_dart")
80+
statusChannel = EventChannel(flutterPluginBinding.binaryMessenger, "wireguard_dart.status")
81+
statusBroadcaster = ConnectionStatusBroadcaster()
82+
statusChannel.setStreamHandler(statusBroadcaster)
7883
context = flutterPluginBinding.applicationContext
7984

8085
scope.launch(Dispatchers.IO) {
@@ -128,10 +133,10 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
128133
private fun generateKeyPair(result: Result) {
129134
val keyPair = KeyPair()
130135
result.success(
131-
hashMapOf(
132-
"privateKey" to keyPair.privateKey.toBase64(),
133-
"publicKey" to keyPair.publicKey.toBase64()
134-
)
136+
hashMapOf(
137+
"privateKey" to keyPair.privateKey.toBase64(),
138+
"publicKey" to keyPair.publicKey.toBase64()
139+
)
135140
)
136141
}
137142

@@ -141,14 +146,11 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
141146
flutterError(result, "Invalid Name")
142147
return@launch
143148
}
144-
tunnelName = bundleId
145149
checkPermission()
146-
tunnel = WireguardTunnel(tunnelName)
147-
status = when (backend?.getState(tunnel!!)) {
148-
Tunnel.State.UP -> ConnectionStatus.connected
149-
Tunnel.State.DOWN -> ConnectionStatus.disconnected
150-
else -> ConnectionStatus.unknown
150+
tunnel = WireguardTunnel(bundleId) { state ->
151+
status = ConnectionStatus.fromTunnelState(state)
151152
}
153+
status = ConnectionStatus.fromTunnelState(backend?.getState(tunnel!!))
152154
result.success(null)
153155
}
154156
}
@@ -158,6 +160,7 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
158160
result.error("err_setup_tunnel", "Tunnel is not initialized", null)
159161
return
160162
}
163+
status = ConnectionStatus.connecting
161164
scope.launch(Dispatchers.IO) {
162165
try {
163166
if (!havePermission) {
@@ -168,11 +171,9 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
168171
config = com.wireguard.config.Config.parse(inputStream)
169172
futureBackend.await().setState(tun, Tunnel.State.UP, config);
170173
flutterSuccess(result, "")
171-
} catch (e: BackendException) {
172-
Log.e(TAG, "Connect - BackendException - ERROR - ${e.reason}", e)
173-
flutterError(result, e.reason.toString())
174174
} catch (e: Throwable) {
175175
Log.e(TAG, "Connect - Can't connect to tunnel: $e", e)
176+
status = queryStatus()
176177
flutterError(result, e.message.toString())
177178
}
178179
}
@@ -190,11 +191,9 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
190191
}
191192
futureBackend.await().setState(tun, Tunnel.State.DOWN, config)
192193
flutterSuccess(result, "")
193-
} catch (e: BackendException) {
194-
Log.e(TAG, "Disconnect - BackendException - ERROR - ${e.reason}", e)
195-
flutterError(result, e.reason.toString())
196194
} catch (e: Throwable) {
197195
Log.e(TAG, "Disconnect - Can't disconnect from tunnel: ${e.message}")
196+
status = queryStatus()
198197
flutterError(result, e.message.toString())
199198
}
200199
}
@@ -212,9 +211,9 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
212211
val stats = Stats(statistics.totalRx(), statistics.totalTx())
213212

214213
flutterSuccess(
215-
result, Klaxon().toJsonString(
214+
result, Klaxon().toJsonString(
216215
stats
217-
)
216+
)
218217
)
219218
Log.i(TAG, "Statistics - ${stats.totalDownload} ${stats.totalUpload}")
220219

@@ -243,23 +242,26 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
243242
}
244243

245244
private fun status(result: Result) {
245+
val status = queryStatus()
246+
result.success(hashMapOf("status" to status.name))
247+
}
248+
249+
private fun queryStatus(): ConnectionStatus {
246250
if (tunnel == null) {
247-
flutterError(result, "Tunnel has not been initialized")
248-
return
251+
return ConnectionStatus.unknown
249252
}
250-
val status = when (backend?.getState(tunnel!!)) {
253+
return when (backend?.getState(tunnel!!)) {
251254
Tunnel.State.DOWN -> ConnectionStatus.disconnected
252255
Tunnel.State.UP -> ConnectionStatus.connected
253256
else -> ConnectionStatus.unknown
254257
}
255-
result.success(hashMapOf("status" to status.name))
256258
}
257259
}
258260

259261
typealias StateChangeCallback = (Tunnel.State) -> Unit
260262

261263
class WireguardTunnel(
262-
private val name: String, private val onStateChanged: StateChangeCallback? = null
264+
private val name: String, private val onStateChanged: StateChangeCallback? = null
263265
) : Tunnel {
264266

265267
override fun getName() = name
@@ -271,8 +273,8 @@ class WireguardTunnel(
271273
}
272274

273275
class Stats(
274-
val totalDownload: Long,
275-
val totalUpload: Long,
276+
val totalDownload: Long,
277+
val totalUpload: Long,
276278
)
277279

278280

0 commit comments

Comments
 (0)