55
66package com .wireguard .android .backend ;
77
8+ import android .app .ForegroundServiceStartNotAllowedException ;
9+ import android .app .Notification ;
10+ import android .app .NotificationChannel ;
11+ import android .app .NotificationManager ;
812import android .content .Context ;
913import android .content .Intent ;
14+ import android .content .pm .ServiceInfo ;
1015import android .os .Build ;
1116import android .os .ParcelFileDescriptor ;
1217import android .system .OsConstants ;
2429import com .wireguard .util .NonNullForAll ;
2530
2631import java .net .InetAddress ;
27- import java .time .Instant ;
2832import java .util .Collections ;
2933import java .util .Set ;
3034import java .util .concurrent .ExecutionException ;
3539
3640import androidx .annotation .Nullable ;
3741import androidx .collection .ArraySet ;
42+ import androidx .core .app .NotificationCompat ;
43+ import androidx .core .app .ServiceCompat ;
3844
3945/**
4046 * Implementation of {@link Backend} that uses the wireguard-go userspace implementation to provide
@@ -392,6 +398,9 @@ public GhettoCompletableFuture<V> newIncompleteFuture() {
392398 * {@link android.net.VpnService} implementation for {@link GoBackend}
393399 */
394400 public static class VpnService extends android .net .VpnService {
401+
402+ private static final int NOTIFICATION_ID = 999 ;
403+ private static final String CHANNEL_ID = "WireGuardChannel" ;
395404 @ Nullable private GoBackend owner ;
396405
397406 public Builder getBuilder () {
@@ -423,6 +432,7 @@ public void onDestroy() {
423432
424433 @ Override
425434 public int onStartCommand (@ Nullable final Intent intent , final int flags , final int startId ) {
435+ startForeground ();
426436 vpnService .complete (this );
427437 if (intent == null || intent .getComponent () == null || !intent .getComponent ().getPackageName ().equals (getPackageName ())) {
428438 Log .d (TAG , "Service started by Always-on VPN feature" );
@@ -435,5 +445,42 @@ public int onStartCommand(@Nullable final Intent intent, final int flags, final
435445 public void setOwner (final GoBackend owner ) {
436446 this .owner = owner ;
437447 }
448+
449+ private void startForeground () {
450+ try {
451+ createNotificationChannel ();
452+ final Notification notification = new NotificationCompat
453+ .Builder (this , CHANNEL_ID )
454+ .build ();
455+ ServiceCompat .startForeground (this , NOTIFICATION_ID , notification , ServiceInfo .FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED );
456+ } catch (final Exception ex ) {
457+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S &&
458+ ex instanceof ForegroundServiceStartNotAllowedException
459+ ) {
460+ Log .d (TAG , "App not in a valid state to start foreground service" );
461+ }
462+ }
463+ }
464+
465+ private void createNotificationChannel () {
466+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
467+ final NotificationManager notificationManager =
468+ (NotificationManager ) getSystemService (Context .NOTIFICATION_SERVICE );
469+
470+ if (notificationManager .getNotificationChannel (CHANNEL_ID ) == null ) {
471+ final NotificationChannel channel = new NotificationChannel (
472+ CHANNEL_ID ,
473+ "WireGuard VPN Service" ,
474+ NotificationManager .IMPORTANCE_LOW
475+ );
476+ channel .setDescription ("VPN connection status notifications" );
477+ channel .setShowBadge (false );
478+ channel .enableLights (false );
479+ channel .enableVibration (false );
480+
481+ notificationManager .createNotificationChannel (channel );
482+ }
483+ }
484+ }
438485 }
439486}
0 commit comments