mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-19 21:05:25 +08:00
Add toggle for auto-reconnection on VPN termination
This allows the user to run other VPN apps, disconnecting PCAPdroid, and restart it when they terminate. Closes #411
This commit is contained in:
parent
85ded375e9
commit
630e911172
@ -128,6 +128,14 @@
|
||||
<action android:name="android.net.VpnService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name=".VpnReconnectService"
|
||||
android:foregroundServiceType="specialUse"
|
||||
android:exported="false">
|
||||
|
||||
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
|
||||
android:value="waits for the termination of the active VpnService"/>
|
||||
</service>
|
||||
|
||||
<receiver android:name="com.emanuelef.remote_capture.ActionReceiver" />
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ package com.emanuelef.remote_capture;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.VpnService;
|
||||
import android.os.Handler;
|
||||
@ -33,6 +34,7 @@ import androidx.activity.ComponentActivity;
|
||||
import androidx.activity.result.ActivityResult;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
@ -41,33 +43,40 @@ import java.net.UnknownHostException;
|
||||
|
||||
public class CaptureHelper {
|
||||
private static final String TAG = "CaptureHelper";
|
||||
private final ComponentActivity mActivity;
|
||||
private final ActivityResultLauncher<Intent> mLauncher;
|
||||
private final Context mContext;
|
||||
private final @Nullable ActivityResultLauncher<Intent> mLauncher;
|
||||
private final boolean mResolveHosts;
|
||||
private CaptureSettings mSettings;
|
||||
private CaptureStartListener mListener;
|
||||
|
||||
public CaptureHelper(ComponentActivity activity, boolean resolve_hosts) {
|
||||
mActivity = activity;
|
||||
mContext = activity;
|
||||
mResolveHosts = resolve_hosts;
|
||||
mLauncher = activity.registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(), this::captureServiceResult);
|
||||
}
|
||||
|
||||
/** Note: This constructor does not handle the first-time VPN prepare */
|
||||
public CaptureHelper(Context context) {
|
||||
mContext = context;
|
||||
mResolveHosts = true;
|
||||
mLauncher = null;
|
||||
}
|
||||
|
||||
private void captureServiceResult(final ActivityResult result) {
|
||||
if(result.getResultCode() == Activity.RESULT_OK)
|
||||
resolveHosts();
|
||||
else if(mListener != null) {
|
||||
Utils.showToastLong(mActivity, R.string.vpn_setup_failed);
|
||||
Utils.showToastLong(mContext, R.string.vpn_setup_failed);
|
||||
mListener.onCaptureStartResult(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void startCaptureOk() {
|
||||
final Intent intent = new Intent(mActivity, CaptureService.class);
|
||||
final Intent intent = new Intent(mContext, CaptureService.class);
|
||||
intent.putExtra("settings", mSettings);
|
||||
|
||||
ContextCompat.startForegroundService(mActivity, intent);
|
||||
ContextCompat.startForegroundService(mContext, intent);
|
||||
if(mListener != null)
|
||||
mListener.onCaptureStartResult(true);
|
||||
}
|
||||
@ -121,7 +130,7 @@ public class CaptureHelper {
|
||||
if(failed_host == null)
|
||||
startCaptureOk();
|
||||
else {
|
||||
Utils.showToastLong(mActivity, R.string.host_resolution_failed, failed_host);
|
||||
Utils.showToastLong(mContext, R.string.host_resolution_failed, failed_host);
|
||||
mListener.onCaptureStartResult(false);
|
||||
}
|
||||
});
|
||||
@ -139,23 +148,26 @@ public class CaptureHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent vpnPrepareIntent = VpnService.prepare(mActivity);
|
||||
Intent vpnPrepareIntent = VpnService.prepare(mContext);
|
||||
if(vpnPrepareIntent != null) {
|
||||
new AlertDialog.Builder(mActivity)
|
||||
.setMessage(R.string.vpn_setup_msg)
|
||||
.setPositiveButton(R.string.ok, (dialog, whichButton) -> {
|
||||
try {
|
||||
mLauncher.launch(vpnPrepareIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Utils.showToastLong(mActivity, R.string.no_intent_handler_found);
|
||||
if (mLauncher != null)
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setMessage(R.string.vpn_setup_msg)
|
||||
.setPositiveButton(R.string.ok, (dialog, whichButton) -> {
|
||||
try {
|
||||
mLauncher.launch(vpnPrepareIntent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Utils.showToastLong(mContext, R.string.no_intent_handler_found);
|
||||
mListener.onCaptureStartResult(false);
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(dialog -> {
|
||||
Utils.showToastLong(mContext, R.string.vpn_setup_failed);
|
||||
mListener.onCaptureStartResult(false);
|
||||
}
|
||||
})
|
||||
.setOnCancelListener(dialog -> {
|
||||
Utils.showToastLong(mActivity, R.string.vpn_setup_failed);
|
||||
mListener.onCaptureStartResult(false);
|
||||
})
|
||||
.show();
|
||||
})
|
||||
.show();
|
||||
else if (mListener != null)
|
||||
mListener.onCaptureStartResult(false);
|
||||
} else
|
||||
resolveHosts();
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with PCAPdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2020-21 - Emanuele Faranda
|
||||
* Copyright 2020-24 - Emanuele Faranda
|
||||
*/
|
||||
|
||||
package com.emanuelef.remote_capture;
|
||||
@ -112,6 +112,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
final Condition mCaptureStopped = mLock.newCondition();
|
||||
private ParcelFileDescriptor mParcelFileDescriptor;
|
||||
private boolean mIsAlwaysOnVPN;
|
||||
private boolean mRevoked;
|
||||
private SharedPreferences mPrefs;
|
||||
private CaptureSettings mSettings;
|
||||
private Billing mBilling;
|
||||
@ -236,6 +237,9 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
return abortStart();
|
||||
}
|
||||
|
||||
if (VpnReconnectService.isAvailable())
|
||||
VpnReconnectService.stopService();
|
||||
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mBilling = Billing.newInstance(this);
|
||||
|
||||
@ -605,6 +609,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
@Override
|
||||
public void onRevoke() {
|
||||
Log.d(CaptureService.TAG, "onRevoke");
|
||||
mRevoked = true;
|
||||
stopService();
|
||||
super.onRevoke();
|
||||
}
|
||||
@ -1404,6 +1409,15 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
reloadDecryptionList();
|
||||
reloadBlocklist();
|
||||
reloadFirewallWhitelist();
|
||||
} else if (cur_status == ServiceStatus.STOPPED) {
|
||||
if (mRevoked && Prefs.restartOnDisconnect(mPrefs) && !mIsAlwaysOnVPN && (isVpnCapture() == 1)) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
Log.i(TAG, "VPN disconnected, starting reconnect service");
|
||||
|
||||
final Intent intent = new Intent(this, VpnReconnectService.class);
|
||||
ContextCompat.startForegroundService(this, intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -646,7 +646,7 @@ public class Utils {
|
||||
// Using the deprecated API instead to keep things simple.
|
||||
// https://developer.android.com/reference/android/net/ConnectivityManager#getAllNetworks()
|
||||
@SuppressWarnings("deprecation")
|
||||
public static boolean hasVPNRunning(Context context) {
|
||||
public static Network getRunningVpn(Context context) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if(cm != null) {
|
||||
try {
|
||||
@ -657,7 +657,7 @@ public class Utils {
|
||||
|
||||
if ((cap != null) && cap.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
|
||||
Log.d("hasVPNRunning", "detected VPN connection: " + net.toString());
|
||||
return true;
|
||||
return net;
|
||||
}
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
@ -666,7 +666,7 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void showToast(Context context, int id, Object... args) {
|
||||
|
||||
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* This file is part of PCAPdroid.
|
||||
*
|
||||
* PCAPdroid is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* PCAPdroid is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with PCAPdroid. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright 2020-24 - Emanuele Faranda
|
||||
*/
|
||||
|
||||
package com.emanuelef.remote_capture;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.emanuelef.remote_capture.activities.MainActivity;
|
||||
import com.emanuelef.remote_capture.model.CaptureSettings;
|
||||
|
||||
/**
|
||||
* Service which waits for other apps VPNService to terminate before
|
||||
* restarting the capture.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public class VpnReconnectService extends Service {
|
||||
private static final String TAG = "VpnReconnectService";
|
||||
private static final String NOTIFY_CHAN_VPNRECONNECT = "VPN Reconnection";
|
||||
public static final int NOTIFY_ID_VPNRECONNECT = 10;
|
||||
private static final String STOP_ACTION = "stop";
|
||||
|
||||
private static VpnReconnectService INSTANCE;
|
||||
private Handler mHandler;
|
||||
private ConnectivityManager.NetworkCallback mNetworkCallback;
|
||||
private Network mActiveVpnNetwork;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.d(TAG, "onCreate");
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
INSTANCE = this;
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy");
|
||||
|
||||
unregisterNetworkCallback();
|
||||
INSTANCE = null;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
|
||||
Log.d(TAG, "onStartCommand");
|
||||
|
||||
if ((intent != null) && (intent.getAction() != null) && (intent.getAction().equals(STOP_ACTION))) {
|
||||
Utils.showToastLong(this, R.string.vpn_reconnection_aborted);
|
||||
stopService();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
startForeground(NOTIFY_ID_VPNRECONNECT, buildNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
|
||||
else
|
||||
startForeground(NOTIFY_ID_VPNRECONNECT, buildNotification());
|
||||
|
||||
mHandler.postDelayed(() -> {
|
||||
Log.i(TAG, "Could not detect a VPN within the timeout, automatic reconnection aborted");
|
||||
stopService();
|
||||
}, 10000);
|
||||
|
||||
if (!registerNetworkCallbacks()) {
|
||||
stopService();
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
private Notification buildNotification() {
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
NotificationChannel chan = new NotificationChannel(NOTIFY_CHAN_VPNRECONNECT,
|
||||
NOTIFY_CHAN_VPNRECONNECT, NotificationManager.IMPORTANCE_LOW); // low: no sound
|
||||
chan.setShowBadge(false);
|
||||
nm.createNotificationChannel(chan);
|
||||
}
|
||||
|
||||
// Status notification builder
|
||||
PendingIntent startMainApp = PendingIntent.getActivity(this, 0,
|
||||
new Intent(this, MainActivity.class), Utils.getIntentFlags(PendingIntent.FLAG_UPDATE_CURRENT));
|
||||
|
||||
Intent abortReconnectIntent = new Intent(this, VpnReconnectService.class);
|
||||
abortReconnectIntent.setAction(STOP_ACTION);
|
||||
PendingIntent abortReconnect = PendingIntent.getService(this, 0, abortReconnectIntent, Utils.getIntentFlags(0));
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFY_CHAN_VPNRECONNECT)
|
||||
.setSmallIcon(R.drawable.ic_logo)
|
||||
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
|
||||
.setContentIntent(startMainApp)
|
||||
.setDeleteIntent(abortReconnect)
|
||||
.setOngoing(true)
|
||||
.setAutoCancel(false)
|
||||
.setContentTitle(getString(R.string.vpn_reconnection))
|
||||
.setContentText(getString(R.string.waiting_for_vpn_disconnect))
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setCategory(NotificationCompat.CATEGORY_STATUS)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW); // see IMPORTANCE_LOW
|
||||
|
||||
Log.d(TAG, "running");
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void checkAvailableNetwork(ConnectivityManager cm, Network network) {
|
||||
if (network.equals(mActiveVpnNetwork))
|
||||
return;
|
||||
|
||||
NetworkCapabilities cap = cm.getNetworkCapabilities(network);
|
||||
if ((cap != null) && cap.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
|
||||
mActiveVpnNetwork = network;
|
||||
Log.d(TAG, "Detected active VPN network: " + mActiveVpnNetwork);
|
||||
|
||||
// cancel the deadline timer / onLost timer
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean registerNetworkCallbacks() {
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(Service.CONNECTIVITY_SERVICE);
|
||||
|
||||
mNetworkCallback = new ConnectivityManager.NetworkCallback() {
|
||||
@Override
|
||||
public void onAvailable(@NonNull Network network) {
|
||||
Log.d(TAG, "onAvailable: " + network);
|
||||
|
||||
checkAvailableNetwork(cm, network);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLost(@NonNull Network network) {
|
||||
Log.d(TAG, "onLost: " + network);
|
||||
|
||||
// NOTE: when onLost is called, the TRANSPORT_VPN capability may already have been removed
|
||||
if (network.equals(mActiveVpnNetwork)) {
|
||||
// NOTE: onAvailable and onLost may be called multiple times before the actual VPN is started.
|
||||
// Use a debounce delay to prevent mis-detection
|
||||
mHandler.postDelayed(() -> {
|
||||
Log.i(TAG, "Active VPN disconnected, starting the capture");
|
||||
unregisterNetworkCallback();
|
||||
|
||||
Context ctx = VpnReconnectService.this;
|
||||
CaptureSettings settings = new CaptureSettings(ctx, PreferenceManager.getDefaultSharedPreferences(ctx));
|
||||
|
||||
CaptureHelper helper = new CaptureHelper(ctx);
|
||||
helper.setListener(success -> stopService());
|
||||
helper.startCapture(settings);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
Log.d(TAG, "registerNetworkCallback");
|
||||
|
||||
NetworkRequest.Builder builder = new NetworkRequest.Builder()
|
||||
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
|
||||
|
||||
// necessary to see other apps network events on Android 12+
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
builder.setIncludeOtherUidNetworks(true);
|
||||
|
||||
cm.registerNetworkCallback(builder.build(), mNetworkCallback);
|
||||
} catch (SecurityException e) {
|
||||
// this is a bug in Android 11 - https://issuetracker.google.com/issues/175055271?pli=1
|
||||
e.printStackTrace();
|
||||
|
||||
Log.e(TAG, "registerNetworkCallback failed");
|
||||
mNetworkCallback = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The VPN may already be active
|
||||
Network net = Utils.getRunningVpn(this);
|
||||
if (net != null)
|
||||
checkAvailableNetwork(cm, net);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void unregisterNetworkCallback() {
|
||||
if(mNetworkCallback != null) {
|
||||
ConnectivityManager cm = (ConnectivityManager) getSystemService(Service.CONNECTIVITY_SERVICE);
|
||||
|
||||
try {
|
||||
Log.d(TAG, "unregisterNetworkCallback");
|
||||
cm.unregisterNetworkCallback(mNetworkCallback);
|
||||
} catch(IllegalArgumentException e) {
|
||||
Log.w(TAG, "unregisterNetworkCallback failed: " + e);
|
||||
}
|
||||
|
||||
mNetworkCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
@RequiresApi(api = Build.VERSION_CODES.BASE)
|
||||
public static boolean isAvailable() {
|
||||
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void stopService() {
|
||||
Log.d(TAG, "stopService called");
|
||||
VpnReconnectService service = INSTANCE;
|
||||
if (service == null)
|
||||
return;
|
||||
|
||||
service.unregisterNetworkCallback();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
service.stopForeground(STOP_FOREGROUND_REMOVE);
|
||||
else
|
||||
service.stopForeground(true);
|
||||
|
||||
service.stopSelf();
|
||||
}
|
||||
}
|
||||
@ -63,6 +63,7 @@ import com.emanuelef.remote_capture.ConnectionsRegister;
|
||||
import com.emanuelef.remote_capture.Log;
|
||||
import com.emanuelef.remote_capture.MitmReceiver;
|
||||
import com.emanuelef.remote_capture.PCAPdroid;
|
||||
import com.emanuelef.remote_capture.VpnReconnectService;
|
||||
import com.emanuelef.remote_capture.activities.prefs.SettingsActivity;
|
||||
import com.emanuelef.remote_capture.fragments.ConnectionsFragment;
|
||||
import com.emanuelef.remote_capture.fragments.StatusFragment;
|
||||
@ -85,7 +86,6 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashSet;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@ -720,6 +720,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
}
|
||||
|
||||
public void startCapture() {
|
||||
if (VpnReconnectService.isAvailable())
|
||||
VpnReconnectService.stopService();
|
||||
|
||||
if(showRemoteServerAlert())
|
||||
return;
|
||||
|
||||
@ -729,7 +732,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Prefs.isRootCaptureEnabled(mPrefs) && Utils.hasVPNRunning(this)) {
|
||||
if(!Prefs.isRootCaptureEnabled(mPrefs) && (Utils.getRunningVpn(this) != null)) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.active_vpn_detected)
|
||||
.setMessage(R.string.disconnect_vpn_confirm)
|
||||
|
||||
@ -44,6 +44,7 @@ import com.emanuelef.remote_capture.Log;
|
||||
import com.emanuelef.remote_capture.PCAPdroid;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
import com.emanuelef.remote_capture.MitmAddon;
|
||||
import com.emanuelef.remote_capture.VpnReconnectService;
|
||||
import com.emanuelef.remote_capture.activities.BaseActivity;
|
||||
import com.emanuelef.remote_capture.activities.MainActivity;
|
||||
import com.emanuelef.remote_capture.activities.MitmSetupWizard;
|
||||
@ -147,6 +148,7 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment
|
||||
private SwitchPreference mMalwareDetectionEnabled;
|
||||
private SwitchPreference mTrailerEnabled;
|
||||
private SwitchPreference mPcapngEnabled;
|
||||
private SwitchPreference mRestartOnDisconnect;
|
||||
private Billing mIab;
|
||||
private boolean mHasStartedMitmWizard;
|
||||
private boolean mRootDecryptionNoticeShown = false;
|
||||
@ -266,6 +268,9 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment
|
||||
} else
|
||||
mRootCaptureEnabled.setVisible(false);
|
||||
|
||||
mRestartOnDisconnect = requirePreference(Prefs.PREF_RESTART_ON_DISCONNECT);
|
||||
mRestartOnDisconnect.setVisible(VpnReconnectService.isAvailable());
|
||||
|
||||
mDnsSettings = requirePreference("dns_settings");;
|
||||
mVpnExceptions = requirePreference(Prefs.PREF_VPN_EXCEPTIONS);
|
||||
mVpnExceptions.setOnPreferenceClickListener(preference -> {
|
||||
@ -409,6 +414,9 @@ public class SettingsActivity extends BaseActivity implements PreferenceFragment
|
||||
socks5ProxyHideShow(mTlsDecryption.isChecked(), false);
|
||||
}
|
||||
|
||||
if (VpnReconnectService.isAvailable())
|
||||
mRestartOnDisconnect.setVisible(!enabled);
|
||||
|
||||
mIpMode.setVisible(!enabled);
|
||||
mCapInterface.setVisible(enabled);
|
||||
mVpnExceptions.setVisible(!enabled);
|
||||
|
||||
@ -109,6 +109,7 @@ public class Prefs {
|
||||
public static final String PREF_DNS_SERVER_V6 = "dns_v6";
|
||||
public static final String PREF_USE_SYSTEM_DNS = "system_dns";
|
||||
public static final String PREF_PCAPNG_ENABLED = "pcapng_format";
|
||||
public static final String PREF_RESTART_ON_DISCONNECT = "restart_on_disconnect";
|
||||
|
||||
public enum DumpMode {
|
||||
NONE,
|
||||
@ -225,6 +226,7 @@ public class Prefs {
|
||||
&& p.getBoolean(PREF_PCAPNG_ENABLED, true));
|
||||
}
|
||||
public static boolean startAtBoot(SharedPreferences p) { return(p.getBoolean(PREF_START_AT_BOOT, false)); }
|
||||
public static boolean restartOnDisconnect(SharedPreferences p) { return(p.getBoolean(PREF_RESTART_ON_DISCONNECT, false)); }
|
||||
public static boolean isTLSDecryptionSetupDone(SharedPreferences p) { return(p.getBoolean(PREF_TLS_DECRYPTION_SETUP_DONE, false)); }
|
||||
public static boolean getFullPayloadMode(SharedPreferences p) { return(p.getBoolean(PREF_FULL_PAYLOAD, false)); }
|
||||
public static boolean isPrivateDnsBlockingEnabled(SharedPreferences p) { return(p.getBoolean(PREF_AUTO_BLOCK_PRIVATE_DNS, true)); }
|
||||
|
||||
@ -505,4 +505,9 @@
|
||||
<string name="both">Both</string>
|
||||
<string name="whats_new">What\'s new</string>
|
||||
<string name="raw_bytes">Raw bytes</string>
|
||||
<string name="restart_on_disconnection">Restart on disconnection</string>
|
||||
<string name="restart_on_disconnection_summary">Automatically restart the capture after being stopped by other VPN apps</string>
|
||||
<string name="vpn_reconnection">VPN reconnection</string>
|
||||
<string name="vpn_reconnection_aborted">VPN reconnection aborted</string>
|
||||
<string name="waiting_for_vpn_disconnect">Waiting for the active VPN to disconnect…</string>
|
||||
</resources>
|
||||
|
||||
@ -153,6 +153,13 @@
|
||||
app:summary="@string/start_at_boot_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
app:key="restart_on_disconnect"
|
||||
android:title="@string/restart_on_disconnection"
|
||||
app:iconSpaceReserved="false"
|
||||
app:summary="@string/restart_on_disconnection_summary"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<SwitchPreference
|
||||
app:key="pcapdroid_trailer"
|
||||
app:title="@string/pcapdroid_trailer"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user