mirror of
https://mirror.suhoan.cn/https://github.com/EasyTier/EasyTier.git
synced 2025-12-15 06:07:23 +08:00
Android Support (#166)
1. Add vpnservice tauri plugin for android. 2. add workflow for android. 3. Easytier Core support android, allow set tun fd.
This commit is contained in:
129
easytier-gui/src/composables/mobile_vpn.ts
Normal file
129
easytier-gui/src/composables/mobile_vpn.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { addPluginListener } from '@tauri-apps/api/core';
|
||||
import { prepare_vpn, start_vpn, stop_vpn } from 'tauri-plugin-vpnservice-api';
|
||||
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
interface vpnStatus {
|
||||
running: boolean
|
||||
ipv4Addr: string | null | undefined
|
||||
ipv4Cidr: number | null | undefined
|
||||
}
|
||||
|
||||
var curVpnStatus: vpnStatus = {
|
||||
running: false,
|
||||
ipv4Addr: undefined,
|
||||
ipv4Cidr: undefined,
|
||||
}
|
||||
|
||||
async function waitVpnStatus(target_status: boolean, timeout_sec: number) {
|
||||
let start_time = Date.now()
|
||||
while (curVpnStatus.running !== target_status) {
|
||||
if (Date.now() - start_time > timeout_sec * 1000) {
|
||||
throw new Error('wait vpn status timeout')
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 50))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function doStopVpn() {
|
||||
if (!curVpnStatus.running) {
|
||||
return
|
||||
}
|
||||
console.log('stop vpn')
|
||||
let stop_ret = await stop_vpn()
|
||||
console.log('stop vpn', JSON.stringify((stop_ret)))
|
||||
await waitVpnStatus(false, 3)
|
||||
|
||||
curVpnStatus.ipv4Addr = undefined
|
||||
}
|
||||
|
||||
async function doStartVpn(ipv4Addr: string, cidr: number) {
|
||||
if (curVpnStatus.running) {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('start vpn')
|
||||
let start_ret = await start_vpn({
|
||||
"ipv4Addr": ipv4Addr + '/' + cidr,
|
||||
"routes": ["0.0.0.0/0"],
|
||||
"disallowedApplications": ["com.kkrainbow.easytier"],
|
||||
"mtu": 1300,
|
||||
});
|
||||
if (start_ret?.errorMsg?.length) {
|
||||
throw new Error(start_ret.errorMsg)
|
||||
}
|
||||
await waitVpnStatus(true, 3)
|
||||
|
||||
curVpnStatus.ipv4Addr = ipv4Addr
|
||||
}
|
||||
|
||||
async function onVpnServiceStart(payload: any) {
|
||||
console.log('vpn service start', JSON.stringify(payload))
|
||||
curVpnStatus.running = true
|
||||
if (payload.fd) {
|
||||
setTunFd(networkStore.networkInstanceIds[0], payload.fd)
|
||||
}
|
||||
}
|
||||
|
||||
async function onVpnServiceStop(payload: any) {
|
||||
console.log('vpn service stop', JSON.stringify(payload))
|
||||
curVpnStatus.running = false
|
||||
networkStore.clearNetworkInstances()
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||
}
|
||||
|
||||
async function registerVpnServiceListener() {
|
||||
console.log('register vpn service listener')
|
||||
await addPluginListener(
|
||||
'vpnservice',
|
||||
'vpn_service_start',
|
||||
onVpnServiceStart
|
||||
)
|
||||
|
||||
await addPluginListener(
|
||||
'vpnservice',
|
||||
'vpn_service_stop',
|
||||
onVpnServiceStop
|
||||
)
|
||||
}
|
||||
|
||||
async function watchNetworkInstance() {
|
||||
networkStore.$subscribe(async () => {
|
||||
let insts = networkStore.networkInstanceIds
|
||||
if (!insts) {
|
||||
await doStopVpn()
|
||||
return
|
||||
}
|
||||
|
||||
const curNetworkInfo = networkStore.networkInfos[insts[0]]
|
||||
if (!curNetworkInfo || curNetworkInfo?.error_msg?.length) {
|
||||
await doStopVpn()
|
||||
return
|
||||
}
|
||||
|
||||
const virtual_ip = curNetworkInfo?.node_info?.virtual_ipv4
|
||||
if (virtual_ip !== curVpnStatus.ipv4Addr) {
|
||||
console.log('virtual ip changed', JSON.stringify(curVpnStatus), virtual_ip)
|
||||
await doStopVpn()
|
||||
if (virtual_ip.length > 0) {
|
||||
await doStartVpn(virtual_ip, 24)
|
||||
}
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function initMobileVpnService() {
|
||||
await registerVpnServiceListener()
|
||||
await watchNetworkInstance()
|
||||
}
|
||||
|
||||
export async function prepareVpnService() {
|
||||
console.log('prepare vpn')
|
||||
let prepare_ret = await prepare_vpn()
|
||||
console.log('prepare vpn', JSON.stringify((prepare_ret)))
|
||||
if (prepare_ret?.errorMsg?.length) {
|
||||
throw new Error(prepare_ret.errorMsg)
|
||||
}
|
||||
}
|
||||
@@ -29,3 +29,7 @@ export async function setAutoLaunchStatus(enable: boolean) {
|
||||
export async function setLoggingLevel(level: string) {
|
||||
return await invoke('set_logging_level', { level })
|
||||
}
|
||||
|
||||
export async function setTunFd(instanceId: string, fd: number) {
|
||||
return await invoke('set_tun_fd', { instanceId, fd })
|
||||
}
|
||||
|
||||
@@ -15,20 +15,26 @@ async function toggleVisibility() {
|
||||
}
|
||||
|
||||
export async function useTray(init: boolean = false) {
|
||||
let tray = await TrayIcon.getById(DEFAULT_TRAY_NAME)
|
||||
if (!tray) {
|
||||
tray = await TrayIcon.new({
|
||||
tooltip: `EasyTier\n${pkg.version}`,
|
||||
title: `EasyTier\n${pkg.version}`,
|
||||
id: DEFAULT_TRAY_NAME,
|
||||
menu: await Menu.new({
|
||||
id: 'main',
|
||||
items: await generateMenuItem(),
|
||||
}),
|
||||
action: async () => {
|
||||
toggleVisibility()
|
||||
}
|
||||
})
|
||||
let tray;
|
||||
try {
|
||||
tray = await TrayIcon.getById(DEFAULT_TRAY_NAME)
|
||||
if (!tray) {
|
||||
tray = await TrayIcon.new({
|
||||
tooltip: `EasyTier\n${pkg.version}`,
|
||||
title: `EasyTier\n${pkg.version}`,
|
||||
id: DEFAULT_TRAY_NAME,
|
||||
menu: await Menu.new({
|
||||
id: 'main',
|
||||
items: await generateMenuItem(),
|
||||
}),
|
||||
action: async () => {
|
||||
toggleVisibility()
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Error while creating tray icon:', error)
|
||||
return null
|
||||
}
|
||||
|
||||
if (init) {
|
||||
@@ -63,13 +69,14 @@ export async function MenuItemShow(text: string) {
|
||||
id: 'show',
|
||||
text,
|
||||
action: async () => {
|
||||
await toggleVisibility();
|
||||
await toggleVisibility();
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function setTrayMenu(items: (MenuItem | PredefinedMenuItem)[] | undefined = undefined) {
|
||||
const tray = await useTray()
|
||||
if (!tray) return
|
||||
const menu = await Menu.new({
|
||||
id: 'main',
|
||||
items: items || await generateMenuItem(),
|
||||
@@ -79,12 +86,14 @@ export async function setTrayMenu(items: (MenuItem | PredefinedMenuItem)[] | und
|
||||
|
||||
export async function setTrayRunState(isRunning: boolean = false) {
|
||||
const tray = await useTray()
|
||||
if (!tray) return
|
||||
tray.setIcon(isRunning ? 'icons/icon-inactive.ico' : 'icons/icon.ico')
|
||||
}
|
||||
|
||||
export async function setTrayTooltip(tooltip: string) {
|
||||
if (tooltip) {
|
||||
const tray = await useTray()
|
||||
if (!tray) return
|
||||
tray.setTooltip(`EasyTier\n${pkg.version}\n${tooltip}`)
|
||||
tray.setTitle(`EasyTier\n${pkg.version}\n${tooltip}`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user