Skip to content

Commit 44e51d0

Browse files
committed
Added foreground service support
Signed-off-by: ddeviatilov <[email protected]>
1 parent f0b45d5 commit 44e51d0

File tree

4 files changed

+39
-4
lines changed

4 files changed

+39
-4
lines changed

tunnel/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ android {
6868
dependencies {
6969
implementation(libs.androidx.annotation)
7070
implementation(libs.androidx.collection)
71+
implementation(libs.androidx.core.ktx)
7172
compileOnly(libs.jsr305)
7273
testImplementation(libs.junit)
7374
}

tunnel/src/main/AndroidManifest.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55

66
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
77

8+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
9+
810
<application>
911
<service
1012
android:name="com.wireguard.android.backend.GoBackend$VpnService"
1113
android:permission="android.permission.BIND_VPN_SERVICE"
12-
android:exported="false">
14+
android:exported="false"
15+
android:persistent="true"
16+
android:foregroundServiceType="systemExempted">
1317
<intent-filter>
1418
<action android:name="android.net.VpnService" />
1519
</intent-filter>

tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,12 @@
55

66
package com.wireguard.android.backend;
77

8+
import android.app.ForegroundServiceStartNotAllowedException;
9+
import android.app.Notification;
10+
import android.app.Service;
811
import android.content.Context;
912
import android.content.Intent;
13+
import android.content.pm.ServiceInfo;
1014
import android.os.Build;
1115
import android.os.ParcelFileDescriptor;
1216
import android.system.OsConstants;
@@ -24,7 +28,6 @@
2428
import com.wireguard.util.NonNullForAll;
2529

2630
import java.net.InetAddress;
27-
import java.time.Instant;
2831
import java.util.Collections;
2932
import java.util.Set;
3033
import java.util.concurrent.ExecutionException;
@@ -35,6 +38,11 @@
3538

3639
import androidx.annotation.Nullable;
3740
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;
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
}

wireguard-android

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit f0b45d50f332cd532ee9f62cd51582da6fea7e49

0 commit comments

Comments
 (0)