@@ -17,28 +17,22 @@ import android.content.Context
1717import android.util.Log
1818import com.beust.klaxon.Klaxon
1919import com.wireguard.android.backend.*
20- import com.wireguard.crypto.Key
2120import com.wireguard.crypto.KeyPair
21+ import io.flutter.plugin.common.EventChannel
2222import kotlinx.coroutines.*
23- import java.util.*
2423
2524
2625import kotlinx.coroutines.launch
2726import java.io.ByteArrayInputStream
2827
29- /* * WireguardDartPlugin */
30-
3128const val PERMISSIONS_REQUEST_CODE = 10014
32- const val METHOD_CHANNEL_NAME = " wireguard_dart"
3329
3430class 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+
4033 private lateinit var channel: MethodChannel
41- private lateinit var tunnelName: String
34+ private lateinit var statusChannel: EventChannel
35+ private lateinit var statusBroadcaster: ConnectionStatusBroadcaster
4236 private val futureBackend = CompletableDeferred <Backend >()
4337 private val scope = CoroutineScope (Job () + Dispatchers .Main .immediate)
4438 private var backend: Backend ? = null
@@ -47,15 +41,21 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
4741 private var activity: Activity ? = null
4842 private var config: com.wireguard.config.Config ? = null
4943 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+ }
5151
5252 companion object {
5353 const val TAG = " MainActivity"
5454 }
5555
5656 override fun onActivityResult (requestCode : Int , resultCode : Int , data : Intent ? ): Boolean {
5757 havePermission =
58- (requestCode == PERMISSIONS_REQUEST_CODE ) && (resultCode == Activity .RESULT_OK )
58+ (requestCode == PERMISSIONS_REQUEST_CODE ) && (resultCode == Activity .RESULT_OK )
5959 return havePermission
6060 }
6161
@@ -76,7 +76,10 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
7676 }
7777
7878 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)
8083 context = flutterPluginBinding.applicationContext
8184
8285 scope.launch(Dispatchers .IO ) {
@@ -121,115 +124,109 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
121124 " setupTunnel" -> setupTunnel(call.argument<String >(" bundleId" ).toString(), result)
122125 " connect" -> connect(call.argument<String >(" cfg" ).toString(), result)
123126 " disconnect" -> disconnect(result)
124- " getStats" -> handleGetStats(call.arguments, result)
127+ " status" -> status(result)
128+ " statistics" -> statistics(result)
125129 else -> flutterNotImplemented(result)
126130 }
127131 }
128132
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+ }
135142
143+ private fun setupTunnel (bundleId : String , result : Result ) {
136144 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+ }
137157
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 ) {
138165 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, " " )
152174 } 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()
154177 flutterError(result, e.message.toString())
155178 }
156179 }
157180 }
158181
159182 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+ }
161187 scope.launch(Dispatchers .IO ) {
162188 try {
163189 if (futureBackend.await().runningTunnelNames.isEmpty()) {
164190 throw Exception (" Tunnel is not running" )
165191 }
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)
178193 flutterSuccess(result, " " )
179- } catch (e: BackendException ) {
180- Log .e(TAG , " Disconnect - BackendException - ERROR - ${e.reason} " , e)
181- flutterError(result, e.reason.toString())
182194 } catch (e: Throwable ) {
183195 Log .e(TAG , " Disconnect - Can't disconnect from tunnel: ${e.message} " )
196+ status = queryStatus()
184197 flutterError(result, e.message.toString())
185198 }
186199 }
187200 }
188201
189- private fun connect (cfg : String , result : Result ) {
190202
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+ }
191208 scope.launch(Dispatchers .IO ) {
192209 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
208216 )
209- Log .i(TAG , " Connect - success!" )
210- flutterSuccess(result, " " )
217+ )
218+ Log .i(TAG , " Statistics - ${stats.totalDownload} ${stats.totalUpload} " )
219+
211220 } catch (e: BackendException ) {
212- Log .e(TAG , " Connect - BackendException - ERROR - ${e.reason} " , e)
221+ Log .e(TAG , " Statistics - BackendException - ERROR - ${e.reason} " , e)
213222 flutterError(result, e.reason.toString())
214223 } 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)
216225 flutterError(result, e.message.toString())
217226 }
218227 }
219228 }
220229
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-
233230 private fun checkPermission () {
234231 val intent = GoBackend .VpnService .prepare(this .activity)
235232 if (intent != null ) {
@@ -240,32 +237,31 @@ class WireguardDartPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
240237 }
241238 }
242239
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-
253240 override fun onDetachedFromEngine (binding : FlutterPlugin .FlutterPluginBinding ) {
254241 channel.setMethodCallHandler(null )
255242 }
256243
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 {
258250 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
260257 }
261- return tunnel as WireguardTunnel
262258 }
263259}
264260
265261typealias StateChangeCallback = (Tunnel .State ) -> Unit
266262
267263class WireguardTunnel (
268- private val name : String , private val onStateChanged : StateChangeCallback ? = null
264+ private val name : String , private val onStateChanged : StateChangeCallback ? = null
269265) : Tunnel {
270266
271267 override fun getName () = name
@@ -277,8 +273,8 @@ class WireguardTunnel(
277273}
278274
279275class Stats (
280- val totalDownload : Long ,
281- val totalUpload : Long ,
276+ val totalDownload : Long ,
277+ val totalUpload : Long ,
282278)
283279
284280
0 commit comments