# MAUI Android 使用VpnService创建网卡

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);