55
66package com .wireguard .android .backend ;
77
8+ import android .app .ForegroundServiceStartNotAllowedException ;
9+ import android .app .Notification ;
10+ import android .app .Service ;
811import android .content .Context ;
912import android .content .Intent ;
13+ import android .content .pm .ServiceInfo ;
1014import android .os .Build ;
1115import android .os .ParcelFileDescriptor ;
1216import android .system .OsConstants ;
2428import com .wireguard .util .NonNullForAll ;
2529
2630import java .net .InetAddress ;
27- import java .time .Instant ;
2831import java .util .Collections ;
2932import java .util .Set ;
3033import java .util .concurrent .ExecutionException ;
3538
3639import androidx .annotation .Nullable ;
3740import androidx .collection .ArraySet ;
41+ import androidx .core .app .NotificationCompat ;
42+ import androidx .core .app .ServiceCompat ;
43+
44+ import static android .content .pm .ServiceInfo .FOREGROUND_SERVICE_TYPE_NONE ;
45+ import static android .content .pm .ServiceInfo .FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED ;
3846
3947/**
4048 * Implementation of {@link Backend} that uses the wireguard-go userspace implementation to provide
@@ -256,7 +264,8 @@ private void setStateInternal(final Tunnel tunnel, @Nullable final Config config
256264 }
257265
258266
259- dnsRetry : for (int i = 0 ; i < DNS_RESOLUTION_RETRIES ; ++i ) {
267+ dnsRetry :
268+ for (int i = 0 ; i < DNS_RESOLUTION_RETRIES ; ++i ) {
260269 // Pre-resolve IPs so they're cached when building the userspace string
261270 for (final Peer peer : config .getPeers ()) {
262271 final InetEndpoint ep = peer .getEndpoint ().orElse (null );
@@ -345,7 +354,8 @@ private void setStateInternal(final Tunnel tunnel, @Nullable final Config config
345354 wgTurnOff (handleToClose );
346355 try {
347356 vpnService .get (0 , TimeUnit .NANOSECONDS ).stopSelf ();
348- } catch (final TimeoutException ignored ) { }
357+ } catch (final TimeoutException ignored ) {
358+ }
349359 }
350360
351361 tunnel .onStateChange (state );
@@ -392,6 +402,9 @@ public GhettoCompletableFuture<V> newIncompleteFuture() {
392402 * {@link android.net.VpnService} implementation for {@link GoBackend}
393403 */
394404 public static class VpnService extends android .net .VpnService {
405+
406+ private static final int NOTIFICATION_ID = 999 ;
407+ private static final String CHANNEL_ID = "VPN_CHANNEL" ;
395408 @ Nullable private GoBackend owner ;
396409
397410 public Builder getBuilder () {
@@ -423,6 +436,7 @@ public void onDestroy() {
423436
424437 @ Override
425438 public int onStartCommand (@ Nullable final Intent intent , final int flags , final int startId ) {
439+ startForeground ();
426440 vpnService .complete (this );
427441 if (intent == null || intent .getComponent () == null || !intent .getComponent ().getPackageName ().equals (getPackageName ())) {
428442 Log .d (TAG , "Service started by Always-on VPN feature" );
@@ -435,5 +449,20 @@ public int onStartCommand(@Nullable final Intent intent, final int flags, final
435449 public void setOwner (final GoBackend owner ) {
436450 this .owner = owner ;
437451 }
452+
453+ private void startForeground () {
454+ try {
455+ final Notification notification = new NotificationCompat
456+ .Builder (this , CHANNEL_ID )
457+ .build ();
458+ ServiceCompat .startForeground (this , NOTIFICATION_ID , notification , FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED );
459+ } catch (final Exception ex ) {
460+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .S &&
461+ ex instanceof ForegroundServiceStartNotAllowedException
462+ ) {
463+ Log .d (TAG , "App not in a valid state to start foreground service" );
464+ }
465+ }
466+ }
438467 }
439468}
0 commit comments