1、权限
在AndroidManifest.xml里配置相关权限
<uses-permission android:name="android.permission.BIND_VPN_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.VPN" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<service
android:name="com.snltty.linker.app.VpnServiceLinker"
android:exported="false"
android:permission="android.permission.BIND_VPN_SERVICE" android:foregroundServiceType="connectedDevice">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
<service
android:name="com.snltty.linker.app.ForegroundService"
android:exported="false" android:foregroundServiceType="dataSync">
<intent-filter>
<action android:name="com.snltty.linker.app.ForegroundService" />
</intent-filter>
</service>
2、服务
前台服务,用来运行我们的业务和显示通知
[Service(Label = "ForegroundService", Name = "com.snltty.linker.app.ForegroundService", Exported = true)]
[IntentFilter(new string[] { "com.snltty.linker.app.ForegroundService" })]
public sealed class ForegroundService : Service
{
private static readonly int SERVICE_ID = 10000;
private static readonly string CHANNEL_ID = "linker.app";
private static readonly string CHANNEL_NAME = "linker.app";
private static readonly string CHANNEL_CONTENT = "linker.app are running";
Intent intent;
Intent vpnIntent;
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnCreate()
{
base.OnCreate();
//运行一些我们的业务
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
intent.SetFlags(ActivityFlags.NewTask);
this.intent = intent;
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationImportance.High);
notificationChannel.EnableLights(true);
notificationChannel.SetShowBadge(true);
notificationChannel.LockscreenVisibility = NotificationVisibility.Public;
NotificationManager manager = (NotificationManager)GetSystemService(NotificationService);
manager.CreateNotificationChannel(notificationChannel);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
{
StartForeground(SERVICE_ID, CreateNotification(), Android.Content.PM.ForegroundService.TypeDataSync);
}
else
{
StartForeground(SERVICE_ID, CreateNotification());
}
return StartCommandResult.Sticky;
}
private Notification CreateNotification()
{
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.Mutable);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.logo)
.SetChannelId(CHANNEL_ID)
.SetContentIntent(pendingIntent)
.SetContentTitle(CHANNEL_NAME)
.SetContentText(CHANNEL_CONTENT)
.SetOngoing(true).SetPriority(0)
.Build();
notification.Flags |= NotificationFlags.NoClear;
return notification;
}
}
VPN 服务VpnService,用来创建网卡
[Service(Label = "VpnServiceLinker", Name = "com.snltty.linker.app.VpnServiceLinker", Enabled = true, Permission = "android.permission.BIND_VPN_SERVICE")]
public class VpnServiceLinker : VpnService
{
public VpnServiceLinker()
{
}
public override void OnCreate()
{
base.OnCreate();
VpnService.Builder builder = new VpnService.Builder(this);
builder.SetMtu(1420).AddAddress("10.18.18.2", 24).AddDnsServer("8.8.8.8").SetBlocking(false);
if (OperatingSystem.IsAndroidVersionAtLeast(29))
builder.SetMetered(false);
ParcelFileDescriptor vpnInterface = builder.SetSession("linker").Establish();
FileInputStream vpnInput = new FileInputStream(vpnInterface.FileDescriptor);
FileOutputStream vpnOutput = new FileOutputStream(vpnInterface.FileDescriptor);
int fd = vpnInterface.Fd;
//读取
//int length = vpnInput.Read(new byte[2048], 0, 2048);
//写入
//vpnOutput.Write(buffer, 0, buffer.Length);
//vpnOutput.Flush();
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
return StartCommandResult.Sticky;
}
public override void OnDestroy()
{
base.OnDestroy();
}
}
3、启动
在MainActivity,app启动后申请一下VPN权限,让用户同意创建VPN
public const int VPN_RESULT_CODE = 0x0F;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
//申请一下VPN权限,申请到权限后再开始运行我们的业务
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.BindVpnService) != Permission.Granted)
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.BindVpnService }, 0);
}
var intent = VpnService.Prepare(this);
if (intent != null)
{
StartActivityForResult(intent, VPN_RESULT_CODE);
}
else
{
OnActivityResult(VPN_RESULT_CODE, Result.Ok, null);
}
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
//申请到了VPN权限,启动前台服务,用来运行一些我们的业务
if (requestCode == VPN_RESULT_CODE && resultCode == Result.Ok)
{
intent = new Intent(this, typeof(ForegroundService));
StartForegroundService(intent);
}
}
然后在别的地方,合适的时机,需要创建网卡的时候,启动一下VPN服务,就可以创建网卡了
Intent vpnIntent = new Intent(Android.App.Application.Context, typeof(VpnServiceLinker));
Android.App.Application.Context.StartService(vpnIntent);