Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ group 'billion.group.wireguard_flutter'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.8.10'
ext.kotlin_version = '1.6.0'
repositories {
google()
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,11 @@ class WireguardFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
}
}

override fun onMethodCall(call: MethodCall, result: Result) {

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"initialize" -> setupTunnel(call.argument<String>("localizedDescription").toString(), result)
"initialize" -> setupTunnel(call.argument<String>("localizedDescription") ?: "", result)
"start" -> {
connect(call.argument<String>("wgQuickConfig").toString(), result)
connect(call.argument<String>("wgQuickConfig") ?: "", result)

if (!isVpnChecked) {
if (isVpnActive()) {
Expand All @@ -163,11 +162,19 @@ class WireguardFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
checkPermission()
result.success(null)
}
"getStats" -> {
if(tunnelName.isEmpty()){
flutterError(result, "Invalid argument type for tunnel name")
}else{
handleGetStats(result)
}

"getDownloadData" -> {
getDownloadData(result)
}
"getUploadData" -> {
getUploadData(result)

}
else -> flutterNotImplemented(result)
}
Expand Down Expand Up @@ -290,6 +297,34 @@ class WireguardFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
}
}

private fun handleGetStats(result: MethodChannel.Result) {

if (tunnelName.isEmpty()) {
flutterError(result, "Provide tunnel name to get statistics")
return
}

scope.launch(Dispatchers.IO) {
try {
val stats = futureBackend.await().getStatistics(tunnel(tunnelName))

var latestHandshake = 0L

for (key in stats.peers()) {
val peerStats = stats.peer(key)
if (peerStats != null && peerStats.latestHandshakeEpochMillis > latestHandshake) {
latestHandshake = peerStats.latestHandshakeEpochMillis
}
}
flutterSuccess(result, Klaxon().toJsonString(
Stats(stats.totalRx(), stats.totalTx(), latestHandshake)
))
} catch (e: BackendException) {
Log.e(TAG, "handleGetStats - BackendException - ERROR - ${e.reason}")
flutterError(result, e.reason.toString())
} catch (e: Throwable) {
Log.e(TAG, "handleGetStats - Can't get stats: $e")

private fun getDownloadData(result: Result) {
scope.launch(Dispatchers.IO) {
try {
Expand Down Expand Up @@ -341,3 +376,11 @@ class WireGuardTunnel(
}

}


class Stats(
val totalDownload: Long,
val totalUpload: Long,
val lastHandshake: Long
)

41 changes: 36 additions & 5 deletions lib/linux/wireguard_flutter_linux.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path_provider/path_provider.dart';
import 'package:process_run/shell.dart';
import 'package:wireguard_flutter/model/stats.dart';

import '../wireguard_flutter_platform_interface.dart';

Expand Down Expand Up @@ -105,13 +106,43 @@ class WireGuardFlutterLinux extends WireGuardFlutterInterface {
}

@override
Future<bool> isConnected() async {
Future<Stats?> getStats() async {
assert(
name != null,
'Bad state: not initialized. Call "initialize" before calling this command',
(await isConnected()),
'Bad state: vpn has not been started. Call startVpn',
);
final processResultList = await shell.run('sudo wg');

final processResultList = await shell.run('sudo wg show $name');
final process = processResultList.first;
return process.outLines.any((line) => line.trim() == 'interface: $name');
final lines = process.outLines;

if (lines.isEmpty) return null;

num totalDownload = 0;
num totalUpload = 0;
int lastHandshake = 0;

for (var line in lines) {
if (line.contains('transfer:')) {
var transferData = line.split(': ')[1].split(', ');
totalDownload +=
int.tryParse(transferData[0].split(' ')[0].trim()) ?? 0;
totalUpload += int.tryParse(transferData[1].split(' ')[0].trim()) ?? 0;
}
if (line.contains('latest handshake:')) {
var handshakeData = line.split(': ')[1].trim();
if (handshakeData != '0') {
// Parse the date and time
var dateTime = DateTime.parse(handshakeData);
lastHandshake = dateTime.millisecondsSinceEpoch;
}
}
}

return Stats(
totalDownload: totalDownload,
totalUpload: totalUpload,
lastHandshake: lastHandshake,
);
}
}
28 changes: 28 additions & 0 deletions lib/model/stats.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Stats {
final num totalDownload;
final num totalUpload;
final num lastHandshake;
/// Constructor of the [Stats] class that receives [totalDownload] where total downloaded data is stored,
/// [totalUpload] where uploaded data is stored.
Stats({
required this.totalDownload,
required this.totalUpload,
required this.lastHandshake
});

/// Method [toJson] to convert the class to JSON.
Map<String, dynamic> toJson() => {
'totalDownload': totalDownload,
'totalUpload': totalUpload,
'lastHanshake' : lastHandshake
};

/// Method [Stats.fromJson] to convert the JSON to class.
factory Stats.fromJson(Map<String, dynamic> json) {
return Stats(
totalDownload: json['totalDownload'] as num,
totalUpload: json['totalUpload'] as num,
lastHandshake: json['lastHandshake'] as num
);
}
}
4 changes: 4 additions & 0 deletions lib/wireguard_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:wireguard_flutter/linux/wireguard_flutter_linux.dart';
import 'package:wireguard_flutter/model/stats.dart';
import 'package:wireguard_flutter/wireguard_flutter_method_channel.dart';

import 'wireguard_flutter_platform_interface.dart';
Expand Down Expand Up @@ -59,4 +60,7 @@ class WireGuardFlutter extends WireGuardFlutterInterface {

@override
Future<VpnStage> stage() => _instance.stage();

@override
Future<Stats?> getStats() => _instance.getStats();
}
14 changes: 14 additions & 0 deletions lib/wireguard_flutter_method_channel.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'dart:convert';

import 'package:flutter/services.dart';
import 'package:wireguard_flutter/model/stats.dart';

import 'wireguard_flutter_platform_interface.dart';

Expand Down Expand Up @@ -57,4 +60,15 @@ class WireGuardFlutterMethodChannel extends WireGuardFlutterInterface {
)
: VpnStage.disconnected,
);

@override
Future<Stats?> getStats() async {
try {
final result = await _methodChannel.invokeMethod('getStats');
final stats = Stats.fromJson(jsonDecode(result));
return stats;
} on Exception catch (e) {
throw Exception(e);
}
}
}
2 changes: 2 additions & 0 deletions lib/wireguard_flutter_platform_interface.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'model/stats.dart';
abstract class WireGuardFlutterInterface {
Stream<VpnStage> get vpnStageSnapshot;

Expand All @@ -15,6 +16,7 @@ abstract class WireGuardFlutterInterface {
Future<VpnStage> stage();
Future<bool> isConnected() =>
stage().then((stage) => stage == VpnStage.connected);
Future<Stats?> getStats();
}

enum VpnStage {
Expand Down