5
5
6
6
package com .wireguard .android .backend ;
7
7
8
+ import android .app .ForegroundServiceStartNotAllowedException ;
9
+ import android .app .Notification ;
10
+ import android .app .Service ;
8
11
import android .content .Context ;
9
12
import android .content .Intent ;
13
+ import android .content .pm .ServiceInfo ;
10
14
import android .os .Build ;
11
15
import android .os .ParcelFileDescriptor ;
12
16
import android .system .OsConstants ;
24
28
import com .wireguard .util .NonNullForAll ;
25
29
26
30
import java .net .InetAddress ;
27
- import java .time .Instant ;
28
31
import java .util .Collections ;
29
32
import java .util .Set ;
30
33
import java .util .concurrent .ExecutionException ;
35
38
36
39
import androidx .annotation .Nullable ;
37
40
import 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 ;
38
46
39
47
/**
40
48
* 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
256
264
}
257
265
258
266
259
- dnsRetry : for (int i = 0 ; i < DNS_RESOLUTION_RETRIES ; ++i ) {
267
+ dnsRetry :
268
+ for (int i = 0 ; i < DNS_RESOLUTION_RETRIES ; ++i ) {
260
269
// Pre-resolve IPs so they're cached when building the userspace string
261
270
for (final Peer peer : config .getPeers ()) {
262
271
final InetEndpoint ep = peer .getEndpoint ().orElse (null );
@@ -345,7 +354,8 @@ private void setStateInternal(final Tunnel tunnel, @Nullable final Config config
345
354
wgTurnOff (handleToClose );
346
355
try {
347
356
vpnService .get (0 , TimeUnit .NANOSECONDS ).stopSelf ();
348
- } catch (final TimeoutException ignored ) { }
357
+ } catch (final TimeoutException ignored ) {
358
+ }
349
359
}
350
360
351
361
tunnel .onStateChange (state );
@@ -392,6 +402,9 @@ public GhettoCompletableFuture<V> newIncompleteFuture() {
392
402
* {@link android.net.VpnService} implementation for {@link GoBackend}
393
403
*/
394
404
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" ;
395
408
@ Nullable private GoBackend owner ;
396
409
397
410
public Builder getBuilder () {
@@ -423,6 +436,7 @@ public void onDestroy() {
423
436
424
437
@ Override
425
438
public int onStartCommand (@ Nullable final Intent intent , final int flags , final int startId ) {
439
+ startForeground ();
426
440
vpnService .complete (this );
427
441
if (intent == null || intent .getComponent () == null || !intent .getComponent ().getPackageName ().equals (getPackageName ())) {
428
442
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
435
449
public void setOwner (final GoBackend owner ) {
436
450
this .owner = owner ;
437
451
}
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
+ }
438
467
}
439
468
}
0 commit comments