summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorArne Schwabe <arne@rfc2549.org>2023-06-01 00:28:41 +0200
committerArne Schwabe <arne@rfc2549.org>2023-06-01 11:32:00 +0200
commit8fe897a3c7654ee0573bfb50333ebc3a319cf3f8 (patch)
tree155e15e9f706630307a03e20500f6ea5b62aebdd /main
parent3211fed0646db057c1a4bc8c3a8ac6583bbf7ca3 (diff)
Use JobScheduler as backup to keep an active VPN active
This should restart it in some instances when the app dies or gets killed.
Diffstat (limited to 'main')
-rw-r--r--main/src/main/AndroidManifest.xml5
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java9
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java106
3 files changed, 119 insertions, 1 deletions
diff --git a/main/src/main/AndroidManifest.xml b/main/src/main/AndroidManifest.xml
index 4a8348e1..50362b30 100644
--- a/main/src/main/AndroidManifest.xml
+++ b/main/src/main/AndroidManifest.xml
@@ -96,6 +96,11 @@
</intent-filter>
</receiver>
+ <service android:name=".core.keepVPNAlive"
+ android:process=":openvpn"
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
+
<activity
android:name=".LaunchVPN"
diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index 115d6319..9e42378b 100644
--- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -14,6 +14,8 @@ import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.UiModeManager;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -34,12 +36,12 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
-import android.util.Pair;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -227,6 +229,10 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
private void endVpnService() {
+ if (!isAlwaysOn()) {
+ /* if we should be an always on VPN, keep the timer running */
+ keepVPNAlive.unscheduleKeepVPNAliveJobService(this);
+ }
synchronized (mProcessLock) {
mProcessThread = null;
}
@@ -573,6 +579,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
ProfileManager.setConnectedVpnProfile(this, mProfile);
VpnStatus.setConnectedVPNProfile(mProfile.getUUIDString());
+ keepVPNAlive.scheduleKeepVPNAliveJobService(this, vp);
String nativeLibraryDirectory = getApplicationInfo().nativeLibraryDir;
String tmpDir;
diff --git a/main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java b/main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java
new file mode 100644
index 00000000..2b75b2fe
--- /dev/null
+++ b/main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2012-2023 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.core;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.VpnService;
+import android.os.PersistableBundle;
+
+import de.blinkt.openvpn.LaunchVPN;
+import de.blinkt.openvpn.VpnProfile;
+
+/**
+ * This is a task that is run periodically to restart the VPN if tit has died for
+ * some reason in the background
+ */
+public class keepVPNAlive extends JobService implements VpnStatus.StateListener {
+ private ConnectionStatus mLevel = ConnectionStatus.UNKNOWN_LEVEL;
+ private static final int JOBID_KEEPVPNALIVE = 6231;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ VpnStatus.addStateListener(this);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ VpnStatus.removeStateListener(this);
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters jobParameters) {
+ if (mLevel == ConnectionStatus.UNKNOWN_LEVEL || mLevel == ConnectionStatus.LEVEL_NOTCONNECTED) {
+ String vpnUUID = jobParameters.getExtras().getString(LaunchVPN.EXTRA_KEY);
+ VpnProfile vp = ProfileManager.get(this, vpnUUID);
+ if (vp == null) {
+ VpnStatus.logError("Keepalive service cannot find VPN");
+ unscheduleKeepVPNAliveJobService(this);
+ return false;
+ }
+ VPNLaunchHelper.startOpenVpn(vp, getApplicationContext(), "VPN keep alive Job");
+ } else {
+ VpnStatus.logDebug("Keepalive service called but VPN still connected.");
+ }
+
+ /* The job has finished */
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ /* not doing anything */
+ return true;
+ }
+
+ @Override
+ public void updateState(String state, String logmessage,
+ int localizedResId, ConnectionStatus level, Intent Intent) {
+ mLevel = level;
+ }
+
+ @Override
+ public void setConnectedVPN(String uuid) {
+
+ }
+
+ public static void scheduleKeepVPNAliveJobService(Context c, VpnProfile vp) {
+ ComponentName keepVPNAliveComponent = new ComponentName(c, keepVPNAlive.class);
+ JobInfo.Builder jib = new JobInfo.Builder(JOBID_KEEPVPNALIVE, keepVPNAliveComponent);
+
+ /* set the VPN that should be restarted if we get killed */
+ PersistableBundle extraBundle = new PersistableBundle();
+ extraBundle.putString(de.blinkt.openvpn.LaunchVPN.EXTRA_KEY, vp.getUUIDString());
+ jib.setExtras(extraBundle);
+
+ /* periodic timing */
+ /* The current limits are 15 minutes and 5 minutes for flex and periodic timer
+ * but we use a minimum of 5 minutes and 2 minutes to avoid problems if there is some
+ * strange Android build that allows lower lmits.
+ */
+ long initervalMillis = Math.max(JobInfo.getMinPeriodMillis(), 5 * 60 * 1000L);
+ long flexMillis = Math.max(JobInfo.getMinFlexMillis(), 2 * 60 * 1000L);
+ jib.setPeriodic(initervalMillis, flexMillis);
+ jib.setPersisted(true);
+
+ JobScheduler jobScheduler = c.getSystemService(JobScheduler.class);
+ jobScheduler.schedule(jib.build());
+ VpnStatus.logDebug("Scheduling VPN keep alive for VPN " + vp.mName);
+ }
+
+ public static void unscheduleKeepVPNAliveJobService(Context c) {
+ JobScheduler jobScheduler = c.getSystemService(JobScheduler.class);
+ jobScheduler.cancel(JOBID_KEEPVPNALIVE);
+ VpnStatus.logDebug("Unscheduling VPN keep alive");
+ }
+}