summaryrefslogtreecommitdiff
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
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.
-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");
+ }
+}