@@ -17,28 +17,22 @@ import android.content.Context
17
17
import android.util.Log
18
18
import com.beust.klaxon.Klaxon
19
19
import com.wireguard.android.backend.*
20
- import com.wireguard.crypto.Key
21
20
import com.wireguard.crypto.KeyPair
21
+ import io.flutter.plugin.common.EventChannel
22
22
import kotlinx.coroutines.*
23
- import java.util.*
24
23
25
24
26
25
import kotlinx.coroutines.launch
27
26
import java.io.ByteArrayInputStream
28
27
29
- /* * WireguardDartPlugin */
30
-
31
28
const val PERMISSIONS_REQUEST_CODE = 10014
32
- const val METHOD_CHANNEL_NAME = " wireguard_dart"
33
29
34
30
class WireguardDartPlugin : FlutterPlugin , MethodCallHandler , ActivityAware ,
35
- PluginRegistry .ActivityResultListener {
36
- // / The MethodChannel that will the communication between Flutter and native Android
37
- // /
38
- // / This local reference serves to register the plugin with the Flutter Engine and unregister it
39
- // / when the Flutter Engine is detached from the Activity
31
+ PluginRegistry .ActivityResultListener {
32
+
40
33
private lateinit var channel: MethodChannel
41
- private lateinit var tunnelName: String
34
+ private lateinit var statusChannel: EventChannel
35
+ private lateinit var statusBroadcaster: ConnectionStatusBroadcaster
42
36
private val futureBackend = CompletableDeferred <Backend >()
43
37
private val scope = CoroutineScope (Job () + Dispatchers .Main .immediate)
44
38
private var backend: Backend ? = null
@@ -47,15 +41,21 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
47
41
private var activity: Activity ? = null
48
42
private var config: com.wireguard.config.Config ? = null
49
43
private var tunnel: WireguardTunnel ? = null
50
-
44
+ private var status: ConnectionStatus = ConnectionStatus .disconnected
45
+ set(value) {
46
+ field = value
47
+ if (::statusBroadcaster.isInitialized) {
48
+ statusBroadcaster.send(value)
49
+ }
50
+ }
51
51
52
52
companion object {
53
53
const val TAG = " MainActivity"
54
54
}
55
55
56
56
override fun onActivityResult (requestCode : Int , resultCode : Int , data : Intent ? ): Boolean {
57
57
havePermission =
58
- (requestCode == PERMISSIONS_REQUEST_CODE ) && (resultCode == Activity .RESULT_OK )
58
+ (requestCode == PERMISSIONS_REQUEST_CODE ) && (resultCode == Activity .RESULT_OK )
59
59
return havePermission
60
60
}
61
61
@@ -76,7 +76,10 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
76
76
}
77
77
78
78
override fun onAttachedToEngine (flutterPluginBinding : FlutterPlugin .FlutterPluginBinding ) {
79
- 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)
80
83
context = flutterPluginBinding.applicationContext
81
84
82
85
scope.launch(Dispatchers .IO ) {
@@ -121,115 +124,109 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
121
124
" setupTunnel" -> setupTunnel(call.argument<String >(" bundleId" ).toString(), result)
122
125
" connect" -> connect(call.argument<String >(" cfg" ).toString(), result)
123
126
" disconnect" -> disconnect(result)
124
- " getStats" -> handleGetStats(call.arguments, result)
127
+ " status" -> status(result)
128
+ " statistics" -> statistics(result)
125
129
else -> flutterNotImplemented(result)
126
130
}
127
131
}
128
132
129
- private fun handleGetStats (arguments : Any? , result : Result ) {
130
- val tunnelName = arguments?.toString()
131
- if (tunnelName.isNullOrEmpty()) {
132
- flutterError(result, " Tunnel has not been initialized" )
133
- return
134
- }
133
+ private fun generateKeyPair (result : Result ) {
134
+ val keyPair = KeyPair ()
135
+ result.success(
136
+ hashMapOf(
137
+ " privateKey" to keyPair.privateKey.toBase64(),
138
+ " publicKey" to keyPair.publicKey.toBase64()
139
+ )
140
+ )
141
+ }
135
142
143
+ private fun setupTunnel (bundleId : String , result : Result ) {
136
144
scope.launch(Dispatchers .IO ) {
145
+ if (Tunnel .isNameInvalid(bundleId)) {
146
+ flutterError(result, " Invalid Name" )
147
+ return @launch
148
+ }
149
+ checkPermission()
150
+ tunnel = WireguardTunnel (bundleId) { state ->
151
+ status = ConnectionStatus .fromTunnelState(state)
152
+ }
153
+ status = ConnectionStatus .fromTunnelState(backend?.getState(tunnel!! ))
154
+ result.success(null )
155
+ }
156
+ }
137
157
158
+ private fun connect (cfg : String , result : Result ) {
159
+ val tun = tunnel ? : run {
160
+ result.error(" err_setup_tunnel" , " Tunnel is not initialized" , null )
161
+ return
162
+ }
163
+ status = ConnectionStatus .connecting
164
+ scope.launch(Dispatchers .IO ) {
138
165
try {
139
- val statistics = futureBackend.await().getStatistics(tunnel(tunnelName))
140
- val stats = Stats (statistics.totalRx(), statistics.totalTx())
141
-
142
- flutterSuccess(
143
- result, Klaxon ().toJsonString(
144
- stats
145
- )
146
- )
147
- Log .i(TAG , " Statistics - ${stats.totalDownload} ${stats.totalUpload} " )
148
-
149
- } catch (e: BackendException ) {
150
- Log .e(TAG , " Statistics - BackendException - ERROR - ${e.reason} " , e)
151
- flutterError(result, e.reason.toString())
166
+ if (! havePermission) {
167
+ checkPermission()
168
+ throw Exception (" Permissions are not given" )
169
+ }
170
+ val inputStream = ByteArrayInputStream (cfg.toByteArray())
171
+ config = com.wireguard.config.Config .parse(inputStream)
172
+ futureBackend.await().setState(tun, Tunnel .State .UP , config);
173
+ flutterSuccess(result, " " )
152
174
} catch (e: Throwable ) {
153
- Log .e(TAG , " Statistics - Can't get stats: ${e.message} " , e)
175
+ Log .e(TAG , " Connect - Can't connect to tunnel: $e " , e)
176
+ status = queryStatus()
154
177
flutterError(result, e.message.toString())
155
178
}
156
179
}
157
180
}
158
181
159
182
private fun disconnect (result : Result ) {
160
-
183
+ val tun = tunnel ? : run {
184
+ result.error(" err_setup_tunnel" , " Tunnel is not initialized" , null )
185
+ return
186
+ }
161
187
scope.launch(Dispatchers .IO ) {
162
188
try {
163
189
if (futureBackend.await().runningTunnelNames.isEmpty()) {
164
190
throw Exception (" Tunnel is not running" )
165
191
}
166
-
167
- futureBackend.await().setState(
168
- tunnel(tunnelName) { state ->
169
- scope.launch(Dispatchers .Main ) {
170
- Log .i(TAG , " onStateChange - $state " )
171
- channel.invokeMethod(
172
- " onStateChange" , state == Tunnel .State .UP
173
- )
174
- }
175
- }, Tunnel .State .DOWN , config
176
- )
177
- Log .i(TAG , " Disconnect - success!" )
192
+ futureBackend.await().setState(tun, Tunnel .State .DOWN , config)
178
193
flutterSuccess(result, " " )
179
- } catch (e: BackendException ) {
180
- Log .e(TAG , " Disconnect - BackendException - ERROR - ${e.reason} " , e)
181
- flutterError(result, e.reason.toString())
182
194
} catch (e: Throwable ) {
183
195
Log .e(TAG , " Disconnect - Can't disconnect from tunnel: ${e.message} " )
196
+ status = queryStatus()
184
197
flutterError(result, e.message.toString())
185
198
}
186
199
}
187
200
}
188
201
189
- private fun connect (cfg : String , result : Result ) {
190
202
203
+ private fun statistics (result : Result ) {
204
+ val tun = tunnel ? : run {
205
+ result.error(" err_setup_tunnel" , " Tunnel is not initialized" , null )
206
+ return
207
+ }
191
208
scope.launch(Dispatchers .IO ) {
192
209
try {
193
- if (! havePermission) {
194
- checkPermission()
195
- throw Exception (" Permissions are not given" )
196
- }
197
- val inputStream = ByteArrayInputStream (cfg.toByteArray())
198
- config = com.wireguard.config.Config .parse(inputStream)
199
- futureBackend.await().setState(
200
- tunnel(tunnelName) { state ->
201
- scope.launch(Dispatchers .Main ) {
202
- Log .i(TAG , " onStateChange - $state " )
203
- channel.invokeMethod(
204
- " onStateChange" , state == Tunnel .State .UP
205
- )
206
- }
207
- }, Tunnel .State .UP , config
210
+ val statistics = futureBackend.await().getStatistics(tun)
211
+ val stats = Stats (statistics.totalRx(), statistics.totalTx())
212
+
213
+ flutterSuccess(
214
+ result, Klaxon ().toJsonString(
215
+ stats
208
216
)
209
- Log .i(TAG , " Connect - success!" )
210
- flutterSuccess(result, " " )
217
+ )
218
+ Log .i(TAG , " Statistics - ${stats.totalDownload} ${stats.totalUpload} " )
219
+
211
220
} catch (e: BackendException ) {
212
- Log .e(TAG , " Connect - BackendException - ERROR - ${e.reason} " , e)
221
+ Log .e(TAG , " Statistics - BackendException - ERROR - ${e.reason} " , e)
213
222
flutterError(result, e.reason.toString())
214
223
} catch (e: Throwable ) {
215
- Log .e(TAG , " Connect - Can't connect to tunnel : $e " , e)
224
+ Log .e(TAG , " Statistics - Can't get stats : ${e.message} " , e)
216
225
flutterError(result, e.message.toString())
217
226
}
218
227
}
219
228
}
220
229
221
- private fun setupTunnel (bundleId : String , result : Result ) {
222
- scope.launch(Dispatchers .IO ) {
223
- if (Tunnel .isNameInvalid(bundleId)) {
224
- flutterError(result, " Invalid Name" )
225
- return @launch
226
- }
227
- tunnelName = bundleId
228
- checkPermission()
229
- result.success(null )
230
- }
231
- }
232
-
233
230
private fun checkPermission () {
234
231
val intent = GoBackend .VpnService .prepare(this .activity)
235
232
if (intent != null ) {
@@ -240,32 +237,31 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
240
237
}
241
238
}
242
239
243
- private fun generateKeyPair (result : Result ) {
244
- val keyPair = KeyPair ()
245
- val privateKey = keyPair.privateKey.toBase64()
246
- val publicKey = KeyPair (Key .fromBase64(privateKey)).publicKey.toBase64()
247
- val map: HashMap <String , String > =
248
- hashMapOf(" privateKey" to privateKey, " publicKey" to publicKey)
249
- result.success(map)
250
- return
251
- }
252
-
253
240
override fun onDetachedFromEngine (binding : FlutterPlugin .FlutterPluginBinding ) {
254
241
channel.setMethodCallHandler(null )
255
242
}
256
243
257
- private fun tunnel (name : String , callback : StateChangeCallback ? = null): WireguardTunnel {
244
+ private fun status (result : Result ) {
245
+ val status = queryStatus()
246
+ result.success(hashMapOf(" status" to status.name))
247
+ }
248
+
249
+ private fun queryStatus (): ConnectionStatus {
258
250
if (tunnel == null ) {
259
- tunnel = WireguardTunnel (name, callback)
251
+ return ConnectionStatus .unknown
252
+ }
253
+ return when (backend?.getState(tunnel!! )) {
254
+ Tunnel .State .DOWN -> ConnectionStatus .disconnected
255
+ Tunnel .State .UP -> ConnectionStatus .connected
256
+ else -> ConnectionStatus .unknown
260
257
}
261
- return tunnel as WireguardTunnel
262
258
}
263
259
}
264
260
265
261
typealias StateChangeCallback = (Tunnel .State ) -> Unit
266
262
267
263
class WireguardTunnel (
268
- private val name : String , private val onStateChanged : StateChangeCallback ? = null
264
+ private val name : String , private val onStateChanged : StateChangeCallback ? = null
269
265
) : Tunnel {
270
266
271
267
override fun getName () = name
@@ -277,8 +273,8 @@ class WireguardTunnel(
277
273
}
278
274
279
275
class Stats (
280
- val totalDownload : Long ,
281
- val totalUpload : Long ,
276
+ val totalDownload : Long ,
277
+ val totalUpload : Long ,
282
278
)
283
279
284
280
0 commit comments