如何在 Android 中实现本地 VPN 进行内容过滤而不依赖服务器?

问题描述 投票:0回答:1

我正在开发一个 Android 应用程序,该应用程序需要创建本地 VPN 来路由所有设备网络流量以实现内容过滤目的。我的目标不是使用远程 VPN 服务器,而是开发在设备上本地运行的 VPN 服务。

以下是我想要实现的目标的关键方面:

  1. 本地 VPN 设置:

    • 使用 Kotlin 在 Android 设备上本地创建和管理 VPN 服务。
    • 配置 VPN 设置,例如 IP 地址、路由和 DNS 服务器。
  2. 内容过滤:

    • 拦截并过滤通过本地 VPN 路由的 HTTP 流量。
    • 实施过滤规则以阻止或允许特定内容。
  3. VPN 生命周期管理:

    • 根据用户交互(即“启用 VPN”和“禁用 VPN”按钮)启动和停止 VPN 服务。

当前实施:

这是我的

MainActivity
MyVpnService
的简化版本:

主要活动:

package com.example.adblocker

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.net.VpnService
import android.os.Bundle
import android.preference.PreferenceManager
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.adblocker.ui.theme.AdblockerTheme

class MainActivity : ComponentActivity() {
    private var websiteList by mutableStateOf(emptyList<String>())
    private lateinit var sharedPreferences: SharedPreferences

    private val websiteReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val newWebsites = intent?.getStringArrayListExtra("websiteList") ?: emptyList()
            websiteList = newWebsites
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        setContent {
            AdblockerTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    VpnScreen(
                        websiteList = websiteList,
                        onStartVpnClick = { startVpnService() },
                        onStopVpnClick = { stopVpnService() }
                    )
                }
            }
        }
        registerReceiver(websiteReceiver, IntentFilter("com.example.adblocker.UPDATE_WEBSITES"))
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(websiteReceiver)
    }

    private fun startVpnService() {
        val vpnIntent = VpnService.prepare(this)
        if (vpnIntent != null) {
            startActivityForResult(vpnIntent, 0)
        } else {
            onActivityResult(0, RESULT_OK, null)
        }
    }

    private fun stopVpnService() {
        val intent = Intent(this, MyVpnService::class.java)
        stopService(intent)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 0 && resultCode == RESULT_OK) {
            val intent = Intent(this, MyVpnService::class.java)
            startService(intent)
        }
    }
}

@Composable
fun VpnScreen(
    websiteList: List<String>,
    onStartVpnClick: () -> Unit,
    onStopVpnClick: () -> Unit
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "AdBlocker VPN",
            style = MaterialTheme.typography.titleLarge,
            modifier = Modifier.padding(bottom = 20.dp)
        )
        Button(onClick = onStartVpnClick) {
            Text(text = "Enable VPN")
        }
        Spacer(modifier = Modifier.height(20.dp))
        Button(onClick = onStopVpnClick) {
            Text(text = "Disable VPN")
        }
        Spacer(modifier = Modifier.height(20.dp))
        LazyColumn(
            modifier = Modifier
                .fillMaxWidth()
                .weight(1f)
        ) {
            items(websiteList) { website ->
                Text(text = website)
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun VpnScreenPreview() {
    AdblockerTheme {
        VpnScreen(websiteList = emptyList(), onStartVpnClick = {}, onStopVpnClick = {})
    }
}

我的VPN服务:

package com.example.adblocker

import android.net.VpnService
import android.os.ParcelFileDescriptor
import android.util.Log

class MyVpnService : VpnService() {
    private var vpnInterface: ParcelFileDescriptor? = null

    override fun onCreate() {
        super.onCreate()
        startVpn()
    }

    override fun onDestroy() {
        super.onDestroy()
        vpnInterface?.close()
    }

    private fun startVpn() {
        try {
            val builder = Builder()
            builder.setSession("My VPN Service")
            builder.addAddress("10.0.0.2", 24)   // Local VPN IP address
            builder.addRoute("0.0.0.0", 0)       // Route all traffic through the VPN
            builder.addDnsServer("8.8.8.8")      // DNS server for name resolution
            builder.addDnsServer("8.8.4.4")      // Optional additional DNS server

            vpnInterface = builder.establish()
            Log.d("MyVpnService", "VPN started with address 10.0.0.2")
        } catch (e: Exception) {
            e.printStackTrace()
            Log.e("MyVpnService", "Error starting VPN: ${e.message}")
        }
    }
}

AndroidmanifestXml: `

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.Adblocker">
    <activity android:name=".MainActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service android:name=".MyVpnService"
        android:permission="android.permission.BIND_VPN_SERVICE"
        android:exported="true">
        <intent-filter>
            <action android:name="android.net.VpnService"/>
        </intent-filter>
    </service>
</application>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


<uses-feature android:name="android.hardware.faketouch" android:required="false" />
`

问题:

启动 VPN 服务后,网络连接无法正常工作,并且我看到一条错误消息,指示“无互联网连接”。 VPN 服务已启动,但设备无法访问互联网或正确路由流量。 在此输入图片描述

问题:

如何解决启动VPN服务后网络无法连接的问题?是否需要特定配置或额外步骤来确保 VPN 正确路由和过滤设备上的网络流量?


请根据需要随意调整或扩展问题。

采取的故障排除步骤:

  1. 已验证 VPN 配置(IP 地址、路由和 DNS)。
  2. 检查日志中是否有与 VPN 设置相关的错误。
  3. 测试了最低限度的 VPN 配置以隔离问题。
  4. 确保 VPN 处于活动状态时设备网络设置正确。
android kotlin network-programming vpn android-vpn-service
1个回答
0
投票

我和你的问题有同样的问题。仍在努力寻找一些东西...... 如果您或某人有答案或指导,请告诉我。 谢谢。

© www.soinside.com 2019 - 2024. All rights reserved.