OmerAti adlı üyeden alıntı: mesajı görüntüle
kullandığın paketi ve vpn bağlantısı yaptığın kodların bir kısmını paylaşırmısın
çünkü direkt olarak VPN bağlantısını kesmek mümkün değildir, kullanıcı tarafından yapılması gereklidir.
Şöyle tunnelservice.kt dosyam

package com.sail_tunnel.sail

import android.app.Service
import android.content.Intent
import android.net.*
import android.os.Build
import android.os.ParcelFileDescriptor
import com.sail_tunnel.sail.net.DefaultNetworkListener
import com.sail_tunnel.sail.net.DnsResolverCompat
import com.sail_tunnel.sail.services.VpnState
import com.sail_tunnel.sail.services.LocalDnsWorker
import com.sail_tunnel.sail.services.TunnelInstance
import com.sail_tunnel.sail.utils.readableMessage
import kotlinx.coroutines.*
import java.io.File
import java.io.IOException

class TunnelService : VpnService() {
companion object {
private const val CORE_NAME = "leaf"
private const val VPN_MTU = 1500
private const val PRIVATE_VLAN4_CLIENT = "194.36.88.50"
private const val PRIVATE_VLAN4_ROUTER = "1.1.1.1"
}

inner class NullConnectionException : NullPointerException() {
override fun getLocalizedMessage() =
"Failed to start VPN service. You might need to reboot your device."
}

private val data = VpnState.Data()

private fun startRunner() {
if (Build.VERSION.SDK_INT >= 26) startForegroundService(Intent(this, javaClass))
else startService(Intent(this, javaClass))
}

private var pfd: ParcelFileDescriptor? = null
private var active = false
private var metered = false

@Volatile
private var underlyingNetwork: Network? = null
set(value) {
field = value
if (active) setUnderlyingNetworks(underlyingNetworks)
}
private val underlyingNetworks
get() =
// clearing underlyingNetworks makes Android 9 consider the network to be metered
if (Build.VERSION.SDK_INT == 28 && metered) null else underlyingNetwork?.let {
arrayOf(
it
)
}

override fun onRevoke() = stopRunner()

private fun killProcesses(scope: CoroutineScope) {
data.proxy?.run {
shutdown(scope)
data.proxy = null
}
data.localDns?.shutdown(scope)
data.localDns = null

active = false
scope.launch { DefaultNetworkListener.stop(this) }
pfd?.close()
pfd = null
}

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val data = data
if (data.state != VpnState.State.Stopped) return Service.START_NOT_STICKY

try {
data.proxy = TunnelInstance()
} catch (e: IllegalArgumentException) {
stopRunner(false, e.message)
return Service.START_NOT_STICKY
}

data.changeState(VpnState.State.Connecting)
data.connectingJob = GlobalScope.launch(Dispatchers.Main) {
try {
preInit()

startVpn()

data.changeState(VpnState.State.Connected)
} catch (_: CancellationException) {
// if the job was cancelled, it is canceller's responsibility to call stopRunner
} catch (exc: Throwable) {
stopRunner(false, exc.readableMessage)
} finally {
data.connectingJob = null
}
}

return START_NOT_STICKY
}

private suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it }
private suspend fun rawResolver(query: ByteArray) =
// no need to listen for network here as this is only used for forwarding local DNS queries.
// retries should be attempted by client.
DnsResolverCompat.resolveRaw(underlyingNetwork ?: throw IOException("no network"), query)

private suspend fun startVpn() {
val builder = Builder()
.setConfigureIntent(Core.configureIntent(this))
.setSession(CORE_NAME)
.setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN4_CLIENT, 24)
.addDnsServer(PRIVATE_VLAN4_ROUTER)
.addRoute("0.0.0.0", 0)

active = true // possible race condition here?
builder.setUnderlyingNetworks(underlyingNetworks)
if (Build.VERSION.SDK_INT >= 29) builder.setMetered(metered)

this.pfd = builder.establish() ?: throw NullConnectionException()

val context = Core.deviceStorage
val configRoot = context.noBackupFilesDir

data.localDns = LocalDnsWorker(this::rawResolver).apply { start() }

val configFile = File(configRoot, VpnState.CONFIG_FILE)
val configContent = configFile
.readText()
.replace("{{leafLogFile}}", File(configRoot, VpnState.LEAF_LOG_FILE).absolutePath)
.replace("{{tunFd}}", this.pfd?.fd?.toLong().toString())

configFile.writeText(configContent)

data.proxy!!.start(
this,
File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
configFile,
)
}

private fun stopRunner(restart: Boolean = false, msg: String? = null) {
if (data.state == VpnState.State.Stopping) return
// channge the state
data.changeState(VpnState.State.Stopping)
GlobalScope.launch(Dispatchers.Main.immediate) {
data.connectingJob?.cancelAndJoin() // ensure stop connecting first
// we use a coroutineScope here to allow clean-up in parallel
coroutineScope {
killProcesses(GlobalScope)
}

// change the state
data.changeState(VpnState.State.Stopped, msg)

// stop the service if nothing has bound to it
if (restart) startRunner() else {
stopSelf()
}
}
}
}


bu da app model kodum. Buton işlevi

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:sail/adapters/leaf_ffi/config.dart';
import 'package:sail/channels/vpn_manager.dart';
import 'package:sail/constant/app_colors.dart';
import 'package:sail/constant/app_strings.dart';
import 'package:sail/models/base_model.dart';
import 'package:sail/models/server_model.dart';
import 'package:sail/models/user_model.dart';
import 'package:sail/utils/common_util.dart';

class AppModel extends BaseModel {
VpnManager vpnManager = VpnManager();
VpnStatus vpnStatus = VpnStatus.disconnected;
bool isOn = false;
DateTime? connectedDate;
PageController pageController = PageController(initialPage: 0);
String appTitle = AppStrings.appName;
Config config = Config();
ThemeData themeData = ThemeData(
primarySwatch: AppColors.themeColor,
visualDensity: VisualDensity.adaptivePlatformDensity,
);

AppModel() {
General general = General(
loglevel: 'info',
logoutput: '{{leafLogFile}}',
dnsServer: ['223.5.5.5', '114.114.114.114'],
tunFd: '{{tunFd}}',
routingDomainResolve: true);

List<Rule> rules = [];
// rules.add(Rule(typeField: 'EXTERNAL', target: 'Direct', filter: 'site:cn'));
    rules.add(Rule(typeField: 'FINAL', target: 'Direct'));

config.general = general;
config.rules = rules;
}

final Map _tabMap = {
0: AppStrings.appName,
1: 'Paketler',
2: 'Sunucular',
3: 'Hesap Ayarları',
};

void jumpToPage(int page) {
pageController.jumpToPage(page);
appTitle = _tabMap[page];

notifyListeners();
}

void getStatus() async {
vpnStatus = await vpnManager.getStatus();

if (vpnStatus == VpnStatus.connected) {
isOn = true;


notifyListeners();
} else if (vpnStatus == VpnStatus.disconnected) {
isOn = false;
notifyListeners();
}
}



void togglePowerButton() async {
if (vpnStatus == VpnStatus.connecting) {
Fluttertoast.showToast(
msg: "Bağlanıyor, lütfen bekleyin...",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 2,
textColor: Colors.white,
fontSize: 14.0);
return;
}

if (vpnStatus == VpnStatus.disconnecting) {
Fluttertoast.showToast(
msg: "Bağlantı kesiliyor, lütfen bekleyin...",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 2,
textColor: Colors.white,
fontSize: 14.0);
return;
}

if (vpnStatus == VpnStatus.connected) {
vpnStatus = VpnStatus.disconnecting;
}

if (vpnStatus == VpnStatus.disconnected) {
vpnStatus = VpnStatus.connecting;
}

await vpnManager.toggle();

notifyListeners();
}

void getTunnelLog() async {
var log = await vpnManager.getTunnelLog();

print("log: $log");
}

void getTunnelConfiguration() async {
var conf = await vpnManager.getTunnelConfiguration();

print("config: $conf");
}

void setConfigProxies(UserModel userModel, ServerModel serverModel) async {
List<Proxy> proxies = [];
List<ProxyGroup> proxyGroups = [];
List<String> actors = [];

for (var server in serverModel.serverEntityList) {
Proxy proxy = Proxy(
tag: server.name,
protocol: server.type,
address: server.host,
port: server.port,
encryptMethod: server.cipher,
password: userModel.userEntity!.uuid);
proxies.add(proxy);
actors.add(server.name);
}

if (actors.isNotEmpty) {
proxyGroups.add(ProxyGroup(
tag: "UrlTest",
protocol: 'url-test',
actors: actors,
checkInterval: 600));

config.rules?.last.target = "UrlTest";
}

config.proxies = proxies;
config.proxyGroups = proxyGroups;

print("-----------------config-----------------");
print(config);
print("-----------------config-----------------");

vpnManager.setTunnelConfiguration(config.toString());
}

void setConfigRule(String tag) async {
// var proxy = config.proxies?.where((proxies) => proxies.tag == tag);
    //
    // if (proxy == null || proxy.isEmpty) {
    //   return;
    // }
    //
    // config.rules?.last.target = tag;
    //
    // print("-----------------config-----------------");
    // print(config);
    // print("-----------------config-----------------");
    //
    // vpnManager.setTunnelConfiguration(config.toString());
  }
}