Support always-on-vpn
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
parent
f988306c17
commit
292df12175
@ -62,9 +62,6 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.net.VpnService" />
|
<action android:name="android.net.VpnService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<meta-data
|
|
||||||
android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
|
|
||||||
android:value="false" />
|
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
<service
|
||||||
|
@ -11,6 +11,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.wireguard.android.backend.WgQuickBackend;
|
||||||
import com.wireguard.android.model.TunnelManager;
|
import com.wireguard.android.model.TunnelManager;
|
||||||
import com.wireguard.android.util.ExceptionLoggers;
|
import com.wireguard.android.util.ExceptionLoggers;
|
||||||
|
|
||||||
@ -19,13 +20,16 @@ public class BootShutdownReceiver extends BroadcastReceiver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(final Context context, final Intent intent) {
|
public void onReceive(final Context context, final Intent intent) {
|
||||||
|
if (Application.getComponent().getBackendType() != WgQuickBackend.class) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
if (action == null)
|
if (action == null)
|
||||||
return;
|
return;
|
||||||
final TunnelManager tunnelManager = Application.getComponent().getTunnelManager();
|
final TunnelManager tunnelManager = Application.getComponent().getTunnelManager();
|
||||||
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
|
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
|
||||||
Log.i(TAG, "Broadcast receiver restoring state (boot)");
|
Log.i(TAG, "Broadcast receiver restoring state (boot)");
|
||||||
tunnelManager.restoreState().whenComplete(ExceptionLoggers.D);
|
tunnelManager.restoreState(false).whenComplete(ExceptionLoggers.D);
|
||||||
} else if (Intent.ACTION_SHUTDOWN.equals(action)) {
|
} else if (Intent.ACTION_SHUTDOWN.equals(action)) {
|
||||||
Log.i(TAG, "Broadcast receiver saving state (shutdown)");
|
Log.i(TAG, "Broadcast receiver saving state (shutdown)");
|
||||||
tunnelManager.saveState();
|
tunnelManager.saveState();
|
||||||
|
@ -94,9 +94,10 @@ public class SettingsActivity extends AppCompatActivity {
|
|||||||
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
|
public void onCreatePreferences(final Bundle savedInstanceState, final String key) {
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
if (Application.getComponent().getBackendType() != WgQuickBackend.class) {
|
if (Application.getComponent().getBackendType() != WgQuickBackend.class) {
|
||||||
final Preference toolsInstaller =
|
Preference pref = getPreferenceManager().findPreference("tools_installer");
|
||||||
getPreferenceManager().findPreference("tools_installer");
|
getPreferenceScreen().removePreference(pref);
|
||||||
getPreferenceScreen().removePreference(toolsInstaller);
|
pref = getPreferenceManager().findPreference("restore_on_boot");
|
||||||
|
getPreferenceScreen().removePreference(pref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
package com.wireguard.android.backend;
|
package com.wireguard.android.backend;
|
||||||
|
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
@ -13,9 +14,11 @@ import android.support.v4.util.ArraySet;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.wireguard.android.Application;
|
import com.wireguard.android.Application;
|
||||||
|
import com.wireguard.android.activity.MainActivity;
|
||||||
import com.wireguard.android.model.Tunnel;
|
import com.wireguard.android.model.Tunnel;
|
||||||
import com.wireguard.android.model.Tunnel.State;
|
import com.wireguard.android.model.Tunnel.State;
|
||||||
import com.wireguard.android.model.Tunnel.Statistics;
|
import com.wireguard.android.model.Tunnel.Statistics;
|
||||||
|
import com.wireguard.android.util.ExceptionLoggers;
|
||||||
import com.wireguard.config.Config;
|
import com.wireguard.config.Config;
|
||||||
import com.wireguard.config.IPCidr;
|
import com.wireguard.config.IPCidr;
|
||||||
import com.wireguard.config.Interface;
|
import com.wireguard.config.Interface;
|
||||||
@ -158,6 +161,10 @@ public final class GoBackend implements Backend {
|
|||||||
final VpnService.Builder builder = service.getBuilder();
|
final VpnService.Builder builder = service.getBuilder();
|
||||||
builder.setSession(tunnel.getName());
|
builder.setSession(tunnel.getName());
|
||||||
|
|
||||||
|
final Intent configureIntent = new Intent(context, MainActivity.class);
|
||||||
|
configureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
builder.setConfigureIntent(PendingIntent.getActivity(context, 0, configureIntent, 0));
|
||||||
|
|
||||||
for (final IPCidr addr : config.getInterface().getAddresses())
|
for (final IPCidr addr : config.getInterface().getAddresses())
|
||||||
builder.addAddress(addr.getAddress(), addr.getCidr());
|
builder.addAddress(addr.getAddress(), addr.getCidr());
|
||||||
|
|
||||||
@ -202,6 +209,7 @@ public final class GoBackend implements Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startVpnService() {
|
private void startVpnService() {
|
||||||
|
Log.d(TAG, "Requesting to start VpnService");
|
||||||
context.startService(new Intent(context, VpnService.class));
|
context.startService(new Intent(context, VpnService.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,5 +233,15 @@ public final class GoBackend implements Backend {
|
|||||||
vpnService = vpnService.newIncompleteFuture();
|
vpnService = vpnService.newIncompleteFuture();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
vpnService.complete(this);
|
||||||
|
if (intent == null || intent.getComponent() == null || !intent.getComponent().getPackageName().equals(getPackageName())) {
|
||||||
|
Log.d(TAG, "Service started by Always-on VPN feature");
|
||||||
|
Application.getComponent().getTunnelManager().restoreState(true).whenComplete(ExceptionLoggers.D);
|
||||||
|
}
|
||||||
|
return super.onStartCommand(intent, flags, startId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import com.wireguard.android.util.ObservableSortedKeyedArrayList;
|
|||||||
import com.wireguard.android.util.ObservableSortedKeyedList;
|
import com.wireguard.android.util.ObservableSortedKeyedList;
|
||||||
import com.wireguard.config.Config;
|
import com.wireguard.config.Config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -55,6 +56,8 @@ public final class TunnelManager extends BaseObservable {
|
|||||||
private final ObservableSortedKeyedList<String, Tunnel> tunnels =
|
private final ObservableSortedKeyedList<String, Tunnel> tunnels =
|
||||||
new ObservableSortedKeyedArrayList<>(COMPARATOR);
|
new ObservableSortedKeyedArrayList<>(COMPARATOR);
|
||||||
private Tunnel lastUsedTunnel;
|
private Tunnel lastUsedTunnel;
|
||||||
|
private boolean haveLoaded;
|
||||||
|
private ArrayList<CompletableFuture<Void>> delayedLoadRestoreTunnels = new ArrayList<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TunnelManager(final AsyncWorker asyncWorker, final Backend backend,
|
public TunnelManager(final AsyncWorker asyncWorker, final Backend backend,
|
||||||
@ -140,12 +143,27 @@ public final class TunnelManager extends BaseObservable {
|
|||||||
.whenComplete(ExceptionLoggers.E);
|
.whenComplete(ExceptionLoggers.E);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private void onTunnelsLoaded(final Iterable<String> present, final Collection<String> running) {
|
private void onTunnelsLoaded(final Iterable<String> present, final Collection<String> running) {
|
||||||
for (final String name : present)
|
for (final String name : present)
|
||||||
addToList(name, null, running.contains(name) ? State.UP : State.DOWN);
|
addToList(name, null, running.contains(name) ? State.UP : State.DOWN);
|
||||||
final String lastUsedName = preferences.getString(KEY_LAST_USED_TUNNEL, null);
|
final String lastUsedName = preferences.getString(KEY_LAST_USED_TUNNEL, null);
|
||||||
if (lastUsedName != null)
|
if (lastUsedName != null)
|
||||||
setLastUsedTunnel(tunnels.get(lastUsedName));
|
setLastUsedTunnel(tunnels.get(lastUsedName));
|
||||||
|
CompletableFuture<Void> toComplete[];
|
||||||
|
synchronized (delayedLoadRestoreTunnels) {
|
||||||
|
haveLoaded = true;
|
||||||
|
toComplete = delayedLoadRestoreTunnels.toArray(new CompletableFuture[delayedLoadRestoreTunnels.size()]);
|
||||||
|
delayedLoadRestoreTunnels.clear();
|
||||||
|
}
|
||||||
|
restoreState(true).whenComplete((v, t) -> {
|
||||||
|
for (final CompletableFuture<Void> f : toComplete) {
|
||||||
|
if (t == null)
|
||||||
|
f.complete(v);
|
||||||
|
else
|
||||||
|
f.completeExceptionally(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshTunnelStates() {
|
public void refreshTunnelStates() {
|
||||||
@ -157,9 +175,16 @@ public final class TunnelManager extends BaseObservable {
|
|||||||
.whenComplete(ExceptionLoggers.E);
|
.whenComplete(ExceptionLoggers.E);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletionStage<Void> restoreState() {
|
public CompletionStage<Void> restoreState(boolean force) {
|
||||||
if (!preferences.getBoolean(KEY_RESTORE_ON_BOOT, false))
|
if (!force && !preferences.getBoolean(KEY_RESTORE_ON_BOOT, false))
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
|
synchronized (delayedLoadRestoreTunnels) {
|
||||||
|
if (!haveLoaded) {
|
||||||
|
CompletableFuture<Void> f = new CompletableFuture<>();
|
||||||
|
delayedLoadRestoreTunnels.add(f);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
final Set<String> previouslyRunning = preferences.getStringSet(KEY_RUNNING_TUNNELS, null);
|
final Set<String> previouslyRunning = preferences.getStringSet(KEY_RUNNING_TUNNELS, null);
|
||||||
if (previouslyRunning == null)
|
if (previouslyRunning == null)
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
@ -236,6 +261,7 @@ public final class TunnelManager extends BaseObservable {
|
|||||||
tunnel.onStateChanged(e == null ? newState : tunnel.getState());
|
tunnel.onStateChanged(e == null ? newState : tunnel.getState());
|
||||||
if (e == null && newState == State.UP)
|
if (e == null && newState == State.UP)
|
||||||
setLastUsedTunnel(tunnel);
|
setLastUsedTunnel(tunnel);
|
||||||
|
saveState();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user