diff options
author | Arne Schwabe <arne@rfc2549.org> | 2023-06-01 00:28:41 +0200 |
---|---|---|
committer | Arne Schwabe <arne@rfc2549.org> | 2023-06-01 11:32:00 +0200 |
commit | 8fe897a3c7654ee0573bfb50333ebc3a319cf3f8 (patch) | |
tree | 155e15e9f706630307a03e20500f6ea5b62aebdd | |
parent | 3211fed0646db057c1a4bc8c3a8ac6583bbf7ca3 (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.xml | 5 | ||||
-rw-r--r-- | main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java | 9 | ||||
-rw-r--r-- | main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java | 106 |
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"); + } +} |