summaryrefslogtreecommitdiff
path: root/main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java
blob: 5bff9eb29616a4998e5a711cc3bd8b272ec63bd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * 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.Build;
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(getMinPeriodMillis(), 5 * 60 * 1000L);
        long flexMillis = Math.max(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);
    }

    private static long getMinPeriodMillis() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return JobInfo.getMinPeriodMillis();
        } else {
            return 15 * 60 * 1000L;   // 15 minutes
        }
    }

    private static long getMinFlexMillis() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return JobInfo.getMinFlexMillis();
        }
        else
        {
           return 5 * 60 * 1000L; // 5 minutes
        }
    }

    public static void unscheduleKeepVPNAliveJobService(Context c) {
        JobScheduler jobScheduler = c.getSystemService(JobScheduler.class);
        jobScheduler.cancel(JOBID_KEEPVPNALIVE);
        VpnStatus.logDebug("Unscheduling VPN keep alive");
    }
}