mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-05-08 21:12:26 +00:00
Add proper support for always-on VPN
The VPN can now be started by the system when the always-on VPN is enabled. In this case, the stop button is hidden and the previous configuration is used. Closes #165 Null intents (e.g. due to START_STICKY) are now handled, which prevents ForegroundServiceStartNotAllowedException in Android 12. Fixes #175
This commit is contained in:
@@ -92,6 +92,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
private static final int NOTIFY_ID_VPNSERVICE = 1;
|
||||
private static CaptureService INSTANCE;
|
||||
private ParcelFileDescriptor mParcelFileDescriptor;
|
||||
private boolean mIsAlwaysOnVPN;
|
||||
private CaptureSettings mSettings;
|
||||
private Billing mBilling;
|
||||
private Handler mHandler;
|
||||
@@ -171,32 +172,40 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
}
|
||||
|
||||
private int abortStart() {
|
||||
// NOTE: startForeground must be called before stopSelf, otherwise an exception will occur
|
||||
// NOTE: startForeground must be called before stopSelf, otherwise an exception will occur:
|
||||
// android.app.ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground()
|
||||
setupNotifications();
|
||||
|
||||
// Note: in Android 12, this may generate a ForegroundServiceStartNotAllowedException
|
||||
// if called when the app is in background.
|
||||
startForeground(NOTIFY_ID_VPNSERVICE, getStatusNotification());
|
||||
|
||||
stopSelf();
|
||||
sendServiceStatus(SERVICE_STATUS_STOPPED);
|
||||
return START_STICKY;
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
mBilling = Billing.newInstance(this);
|
||||
|
||||
if (intent == null) {
|
||||
Log.d(CaptureService.TAG, "NULL intent onStartCommand");
|
||||
return abortStart();
|
||||
}
|
||||
|
||||
Log.d(CaptureService.TAG, "onStartCommand");
|
||||
mSettings = (CaptureSettings) intent.getSerializableExtra("settings");
|
||||
|
||||
if (mSettings == null) {
|
||||
Log.e(CaptureService.TAG, "Missing capture settings");
|
||||
return abortStart();
|
||||
}
|
||||
// NOTE: a null intent may be delivered due to START_STICKY
|
||||
mSettings = (CaptureSettings) ((intent == null) ? null : intent.getSerializableExtra("settings"));
|
||||
if(mSettings == null) {
|
||||
// An Intent without extras is delivered in case of always on VPN
|
||||
// https://developer.android.com/guide/topics/connectivity/vpn#always-on
|
||||
mIsAlwaysOnVPN = (intent != null);
|
||||
|
||||
Log.d(CaptureService.TAG, "Missing capture settings, using previous ones");
|
||||
mSettings = new CaptureSettings(prefs);
|
||||
if(mIsAlwaysOnVPN)
|
||||
mSettings.root_capture = false;
|
||||
} else
|
||||
mIsAlwaysOnVPN = false;
|
||||
|
||||
// Retrieve DNS server
|
||||
dns_server = FALLBACK_DNS_SERVER;
|
||||
@@ -284,7 +293,6 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
} else
|
||||
app_filter_uid = -1;
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
mMalwareDetectionEnabled = Prefs.isMalwareDetectionEnabled(this, prefs);
|
||||
|
||||
if(!mSettings.root_capture) {
|
||||
@@ -357,6 +365,8 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
|
||||
setupNotifications();
|
||||
startForeground(NOTIFY_ID_VPNSERVICE, getStatusNotification());
|
||||
|
||||
// If the service is killed (e.g. due to low memory), then restart it with a NULL intent
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@@ -621,6 +631,10 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
(INSTANCE.mCaptureThread != null));
|
||||
}
|
||||
|
||||
public static boolean isAlwaysOnVPN() {
|
||||
return((INSTANCE != null) && INSTANCE.mIsAlwaysOnVPN);
|
||||
}
|
||||
|
||||
private void checkBlacklistsUpdates() {
|
||||
if(!mMalwareDetectionEnabled || (mBlacklistsUpdateThread != null))
|
||||
return;
|
||||
|
||||
@@ -338,6 +338,7 @@ private void refreshPcapDumpInfo() {
|
||||
ContextCompat.getDrawable(requireContext(), android.R.drawable.ic_media_play));
|
||||
mMenuItemStartBtn.setTitle(R.string.start_button);
|
||||
mMenuItemStartBtn.setEnabled(true);
|
||||
mMenuItemStartBtn.setVisible(true);
|
||||
mMenuSettings.setEnabled(true);
|
||||
}
|
||||
|
||||
@@ -359,6 +360,7 @@ private void refreshPcapDumpInfo() {
|
||||
ContextCompat.getDrawable(requireContext(), R.drawable.ic_media_stop));
|
||||
mMenuItemStartBtn.setTitle(R.string.stop_button);
|
||||
mMenuItemStartBtn.setEnabled(true);
|
||||
mMenuItemStartBtn.setVisible(!CaptureService.isAlwaysOnVPN());
|
||||
mMenuSettings.setEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,19 +7,19 @@ import android.os.Bundle;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class CaptureSettings implements Serializable {
|
||||
public final Prefs.DumpMode dump_mode;
|
||||
public final String app_filter;
|
||||
public final String collector_address;
|
||||
public final int collector_port;
|
||||
public final int http_server_port;
|
||||
public final boolean socks5_enabled;
|
||||
public final String socks5_proxy_address;
|
||||
public final int socks5_proxy_port;
|
||||
public final boolean ipv6_enabled;
|
||||
public final boolean root_capture;
|
||||
public final boolean pcapdroid_trailer;
|
||||
public final String capture_interface;
|
||||
public final String pcap_uri;
|
||||
public Prefs.DumpMode dump_mode;
|
||||
public String app_filter;
|
||||
public String collector_address;
|
||||
public int collector_port;
|
||||
public int http_server_port;
|
||||
public boolean socks5_enabled;
|
||||
public String socks5_proxy_address;
|
||||
public int socks5_proxy_port;
|
||||
public boolean ipv6_enabled;
|
||||
public boolean root_capture;
|
||||
public boolean pcapdroid_trailer;
|
||||
public String capture_interface;
|
||||
public String pcap_uri;
|
||||
|
||||
public CaptureSettings(SharedPreferences prefs) {
|
||||
dump_mode = Prefs.getDumpMode(prefs);
|
||||
|
||||
Reference in New Issue
Block a user