From 704bdf92e6265ee4bdb7e177c7d09284ebc29868 Mon Sep 17 00:00:00 2001 From: cyBerta Date: Fri, 26 Feb 2021 13:13:29 +0100 Subject: Bigger refactoring: * always use a bound service connection to start a vpn service as foreground service to fix remote excptions. These appeared if the system wasn't able to set the service as forground shortly after it was started * move vpn start logic from LaunchVPN activity to EIP service. LaunchVPN/VoidVPNLauncher is only used in case we need to ask the user for a permission. It reduces visual glitches when the transparent LaunchVPN activity appears and disappears --- .../se/leap/bitmaskclient/base/MainActivity.java | 3 +- .../main/java/se/leap/bitmaskclient/eip/EIP.java | 206 ++++++++++++++++++--- .../java/se/leap/bitmaskclient/eip/EipCommand.java | 5 +- .../leap/bitmaskclient/eip/EipSetupObserver.java | 8 +- .../java/se/leap/bitmaskclient/eip/Gateway.java | 1 - .../se/leap/bitmaskclient/eip/VoidVpnLauncher.java | 13 +- .../se/leap/bitmaskclient/eip/VoidVpnService.java | 21 +++ 7 files changed, 209 insertions(+), 48 deletions(-) (limited to 'app/src/main/java/se/leap') diff --git a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java index a7a14ed0..126c4a98 100644 --- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java +++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java @@ -258,7 +258,8 @@ public class MainActivity extends AppCompatActivity implements EipSetupListener, break; case EIP_ACTION_PREPARE_VPN: if (resultCode == RESULT_CANCELED) { - showMainActivityErrorDialog(getString(R.string.vpn_error_establish), ERROR_VPN_PREPARE); + String error = resultData.getString(ERRORS); + showMainActivityErrorDialog(error, ERROR_VPN_PREPARE); } break; case EIP_ACTION_LAUNCH_VPN: diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java index eed2d8d9..604a80fd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.net.VpnService; import android.os.Build; import android.os.Bundle; import android.os.IBinder; @@ -45,10 +46,12 @@ import java.util.Observer; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import de.blinkt.openvpn.LaunchVPN; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.IOpenVPNServiceInternal; import de.blinkt.openvpn.core.OpenVPNService; +import de.blinkt.openvpn.core.Preferences; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.connection.Connection; import se.leap.bitmaskclient.R; @@ -63,14 +66,15 @@ import static de.blinkt.openvpn.core.connection.Connection.TransportType.OPENVPN import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid; import static se.leap.bitmaskclient.R.string.warning_client_parsing_error_gateways; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT; +import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE; import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY; +import static se.leap.bitmaskclient.base.models.Constants.CLEARLOG; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_IS_RUNNING; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_PREPARE_VPN; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_BLOCKING_VPN; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_IS_RUNNING; +import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP_BLOCKING_VPN; import static se.leap.bitmaskclient.base.models.Constants.EIP_EARLY_ROUTES; @@ -82,7 +86,9 @@ import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICA import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.base.utils.ConfigHelper.ensureNotOnMainThread; import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUsePluggableTransports; +import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_PROFILE; import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_INVALID_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.eip.EIP.EIPErrors.ERROR_VPN_PREPARE; import static se.leap.bitmaskclient.eip.EIP.EIPErrors.NO_MORE_GATEWAYS; import static se.leap.bitmaskclient.eip.EipResultBroadcast.tellToReceiverOrBroadcast; @@ -109,6 +115,7 @@ public final class EIP extends JobIntentService implements Observer { // Service connection to OpenVpnService, shared between threads private volatile OpenVpnServiceConnection openVpnServiceConnection; private WeakReference mResultRef = new WeakReference<>(null); + private volatile VoidVpnServiceConnection voidVpnServiceConnection; /** * Unique job ID for this service. @@ -150,6 +157,10 @@ public final class EIP extends JobIntentService implements Observer { openVpnServiceConnection.close(); openVpnServiceConnection = null; } + if (voidVpnServiceConnection != null) { + voidVpnServiceConnection.close(); + voidVpnServiceConnection = null; + } } /** @@ -201,7 +212,7 @@ public final class EIP extends JobIntentService implements Observer { break; case EIP_ACTION_LAUNCH_VPN: VpnProfile profile = (VpnProfile) intent.getSerializableExtra(PROVIDER_PROFILE); - launchVpn(profile); + launchProfile(profile); break; } } @@ -216,11 +227,11 @@ public final class EIP extends JobIntentService implements Observer { @SuppressLint("ApplySharedPref") private void startEIP(boolean earlyRoutes, int nClosestGateway) { Log.d(TAG, "start EIP with early routes: " + earlyRoutes + " and nClosest Gateway: " + nClosestGateway); + Bundle result = new Bundle(); if (!eipStatus.isBlockingVpnEstablished() && earlyRoutes) { - earlyRoutes(); + earlyRoutes(result); } - Bundle result = new Bundle(); if (!preferences.getBoolean(EIP_RESTART_ON_BOOT, false)) { preferences.edit().putBoolean(EIP_RESTART_ON_BOOT, true).commit(); } @@ -239,12 +250,11 @@ public final class EIP extends JobIntentService implements Observer { } Gateway gateway = gatewaysManager.select(nClosestGateway); - - if (launchActiveGateway(gateway, nClosestGateway)) { - tellToReceiverOrBroadcast(this, EIP_ACTION_START, RESULT_OK); - } else { - setErrorResult(result, NO_MORE_GATEWAYS.toString(), getStringResourceForNoMoreGateways(), getString(R.string.app_name)); + launchActiveGateway(gateway, nClosestGateway, result); + if (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)) { tellToReceiverOrBroadcast(this, EIP_ACTION_START, RESULT_CANCELED, result); + } else { + tellToReceiverOrBroadcast(this, EIP_ACTION_START, RESULT_OK); } } @@ -255,9 +265,19 @@ public final class EIP extends JobIntentService implements Observer { private void startEIPAlwaysOnVpn() { GatewaysManager gatewaysManager = new GatewaysManager(getApplicationContext()); Gateway gateway = gatewaysManager.select(0); + Bundle result = new Bundle(); - if (!launchActiveGateway(gateway, 0)) { - Log.d(TAG, "startEIPAlwaysOnVpn no active profile available!"); + launchActiveGateway(gateway, 0, result); + if (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)){ + VpnStatus.logWarning("ALWAYS-ON VPN: " + getString(R.string.no_vpn_profiles_defined)); + } + } + + private void earlyRoutes() { + Bundle result = new Bundle(); + earlyRoutes(result); + if (result.containsKey(BROADCAST_RESULT_KEY) && !result.getBoolean(BROADCAST_RESULT_KEY)){ + tellToReceiverOrBroadcast(this, EIP_ACTION_START_BLOCKING_VPN, RESULT_CANCELED, result); } } @@ -265,10 +285,27 @@ public final class EIP extends JobIntentService implements Observer { * Early routes are routes that block traffic until a new * VpnService is started properly. */ - private void earlyRoutes() { - Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class); - voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(voidVpnLauncher); + private void earlyRoutes(Bundle result) { + Intent blockingIntent = VpnService.prepare(getApplicationContext()); // stops the VPN connection created by another application. + if (blockingIntent == null) { + try { + initVoidVpnServiceConnection(); + Intent voidVpnService = new Intent(getApplicationContext(), VoidVpnService.class); + voidVpnService.setAction(EIP_ACTION_START_BLOCKING_VPN); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + getApplicationContext().startForegroundService(voidVpnService); + voidVpnServiceConnection.getService().startWithForegroundNotification(); + } else { + getApplicationContext().startService(voidVpnService); + } + } catch (InterruptedException | IllegalStateException e) { + setErrorResult(result, R.string.vpn_error_establish, null); + } + } else { + Intent voidVpnLauncher = new Intent(getApplicationContext(), VoidVpnLauncher.class); + voidVpnLauncher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(voidVpnLauncher); + } } /** @@ -276,21 +313,59 @@ public final class EIP extends JobIntentService implements Observer { * * @param gateway to connect to */ - private boolean launchActiveGateway(Gateway gateway, int nClosestGateway) { + private void launchActiveGateway(Gateway gateway, int nClosestGateway, Bundle result) { VpnProfile profile; Connection.TransportType transportType = getUsePluggableTransports(this) ? OBFS4 : OPENVPN; if (gateway == null || (profile = gateway.getProfile(transportType)) == null) { - return false; + setErrorResult(result, NO_MORE_GATEWAYS.toString(), getStringResourceForNoMoreGateways(), getString(R.string.app_name)); + return; + } + + Intent intent; + try { + intent = VpnService.prepare(getApplicationContext()); + } catch (NullPointerException npe) { + setErrorResult(result, ERROR_VPN_PREPARE.toString(), R.string.vpn_error_establish); + return; } + if (intent == null) { + // vpn has been successfully prepared + + //inform EipSetupObserver about vpn connecting attempt + Intent setupObserverIntent = new Intent(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT); + setupObserverIntent.putExtra(PROVIDER_PROFILE, profile); + setupObserverIntent.putExtra(EIP_N_CLOSEST_GATEWAY, nClosestGateway); + LocalBroadcastManager.getInstance(this).sendBroadcast(setupObserverIntent); + + // Check if we need to clear the log + if (Preferences.getDefaultSharedPreferences(this).getBoolean(CLEARLOG, true)) + VpnStatus.clearLog(); + + // check profile configuration + int vpnok = profile.checkProfile(this); + if (vpnok != R.string.no_error_found) { + VpnStatus.logError(R.string.config_error_found); + VpnStatus.logError(vpnok); + setErrorResult(result, ERROR_INVALID_PROFILE.toString(), R.string.config_error_found); + return; + } + + //launch profile + launchProfile(profile, result); - Intent intent = new Intent(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT); - intent.putExtra(PROVIDER_PROFILE, profile); - intent.putExtra(Gateway.KEY_N_CLOSEST_GATEWAY, nClosestGateway); - LocalBroadcastManager.getInstance(this).sendBroadcast(intent); - return true; + } else { + // vpn permission is missing + Intent permissionIntent = new Intent(getApplicationContext(), LaunchVPN.class); + permissionIntent.setAction(Intent.ACTION_MAIN); + permissionIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + permissionIntent.putExtra(PROVIDER_PROFILE, profile); + permissionIntent.putExtra(EIP_N_CLOSEST_GATEWAY, nClosestGateway); + startActivity(permissionIntent); + } } + /** * Stop VPN * First checks if the OpenVpnConnection is open then @@ -415,11 +490,11 @@ public final class EIP extends JobIntentService implements Observer { } /** - * creates an OpenVpnServiceConnection if necessary and starts OpenVPN as foreground service + * binds OpenVPNService to this service, starts it as a foreground service with a profile + * @param vpnProfile OpenVPN profile used to create a VPN connection + * @param result Bundle containing information about possible errors */ - private void launchVpn(VpnProfile vpnProfile) { - Bundle result = new Bundle(); - + private void launchProfile(VpnProfile vpnProfile, Bundle result) { Intent startVPN = vpnProfile.prepareStartService(getApplicationContext()); if (startVPN != null) { try { @@ -433,11 +508,19 @@ public final class EIP extends JobIntentService implements Observer { } } catch (InterruptedException | IllegalStateException | RemoteException e) { setErrorResult(result, R.string.vpn_error_establish, null); - tellToReceiverOrBroadcast(this.getApplicationContext(), EIP_ACTION_PREPARE_VPN, RESULT_CANCELED); } } else { setErrorResult(result, R.string.vpn_error_establish, null); - tellToReceiverOrBroadcast(this.getApplicationContext(), EIP_ACTION_PREPARE_VPN, RESULT_CANCELED); + } + } + + private void launchProfile(VpnProfile vpnProfile) { + Bundle bundle = new Bundle(); + launchProfile(vpnProfile, bundle); + if (bundle.containsKey(BROADCAST_RESULT_KEY) && !bundle.getBoolean(BROADCAST_RESULT_KEY)) { + tellToReceiverOrBroadcast(this.getApplicationContext(), EIP_ACTION_LAUNCH_VPN, RESULT_CANCELED, bundle); + } else { + tellToReceiverOrBroadcast(this.getApplicationContext(), EIP_ACTION_LAUNCH_VPN, RESULT_OK); } } @@ -468,6 +551,69 @@ public final class EIP extends JobIntentService implements Observer { } } + /** + * Assigns a new VoidVpnServiceConnection to EIP's member variable voidVpnServiceConnection. + * Only one thread at a time can create the service connection, that will be shared between threads + * + * @throws InterruptedException thrown if thread gets interrupted + * @throws IllegalStateException thrown if this method was not called from a background thread + */ + private void initVoidVpnServiceConnection() throws InterruptedException, IllegalStateException { + if (voidVpnServiceConnection == null) { + Log.d(TAG, "serviceConnection is still null"); + voidVpnServiceConnection = new VoidVpnServiceConnection(this); + } + } + + public static class VoidVpnServiceConnection implements Closeable { + private final Context context; + private ServiceConnection serviceConnection; + private VoidVpnService voidVpnService; + + VoidVpnServiceConnection(Context context) throws InterruptedException, IllegalStateException { + this.context = context; + ensureNotOnMainThread(context); + Log.d(TAG, "initSynchronizedServiceConnection!"); + initSynchronizedServiceConnection(context); + } + + @Override + public void close() { + context.unbindService(serviceConnection); + } + + private void initSynchronizedServiceConnection(final Context context) throws InterruptedException { + final BlockingQueue blockingQueue = new LinkedBlockingQueue<>(1); + this.serviceConnection = new ServiceConnection() { + volatile boolean mConnectedAtLeastOnce = false; + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (!mConnectedAtLeastOnce) { + mConnectedAtLeastOnce = true; + try { + VoidVpnService.VoidVpnServiceBinder binder = (VoidVpnService.VoidVpnServiceBinder) service; + blockingQueue.put(binder.getService()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + Intent intent = new Intent(context, VoidVpnService.class); + context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); + voidVpnService = blockingQueue.take(); + } + + public VoidVpnService getService() { + return voidVpnService; + } + } + /** * Creates a service connection to OpenVpnService. * The constructor blocks until the service is bound to the given Context. diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java index 7f330f0d..46704419 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java @@ -76,12 +76,15 @@ public class EipCommand { execute(context, EIP_ACTION_STOP); } - public static void launchVPNProfile(@NonNull Context context, VpnProfile vpnProfile) { + public static void launchVPNProfile(@NonNull Context context, VpnProfile vpnProfile, Integer closestGateway) { Intent baseIntent = new Intent(); baseIntent.putExtra(PROVIDER_PROFILE, vpnProfile); + baseIntent.putExtra(EIP_N_CLOSEST_GATEWAY, closestGateway); execute(context, EIP_ACTION_LAUNCH_VPN, null, baseIntent); } + public static void launchVoidVPN(@NonNull Context context) { execute(context, EIP_ACTION_START_BLOCKING_VPN);} + @VisibleForTesting public static void stopVPN(@NonNull Context context, ResultReceiver resultReceiver) { execute(context, EIP_ACTION_STOP, resultReceiver, null); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java index 4b7498e2..4706d550 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java @@ -59,6 +59,7 @@ import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_PREPARE_VPN import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; import static se.leap.bitmaskclient.base.models.Constants.EIP_EARLY_ROUTES; +import static se.leap.bitmaskclient.base.models.Constants.EIP_N_CLOSEST_GATEWAY; import static se.leap.bitmaskclient.base.models.Constants.EIP_REQUEST; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE; @@ -260,20 +261,19 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta return; } setupVpnProfile = vpnProfile; - setupNClosestGateway.set(event.getIntExtra(Gateway.KEY_N_CLOSEST_GATEWAY, 0)); + setupNClosestGateway.set(event.getIntExtra(EIP_N_CLOSEST_GATEWAY, 0)); Log.d(TAG, "bitmaskapp add state listener"); VpnStatus.addStateListener(this); - - launchVPN(setupVpnProfile); } private void launchVPN(VpnProfile vpnProfile) { + EipCommand.launchVPNProfile(context, vpnProfile, setupNClosestGateway.get()); Intent intent = new Intent(context.getApplicationContext(), LaunchVPN.class); intent.setAction(Intent.ACTION_MAIN); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true); intent.putExtra(PROVIDER_PROFILE, vpnProfile); - intent.putExtra(Gateway.KEY_N_CLOSEST_GATEWAY, setupNClosestGateway.get()); + intent.putExtra(EIP_N_CLOSEST_GATEWAY, setupNClosestGateway.get()); context.startActivity(intent); } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java index 1df54e6e..6b44856e 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -55,7 +55,6 @@ import static se.leap.bitmaskclient.base.models.Constants.VERSION; public class Gateway { public final static String TAG = Gateway.class.getSimpleName(); - public final static String KEY_N_CLOSEST_GATEWAY = "N_CLOSEST_GATEWAY"; private JSONObject generalConfiguration; private JSONObject secrets; diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java index e6905448..6d6b24c8 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java @@ -3,11 +3,8 @@ package se.leap.bitmaskclient.eip; import android.app.Activity; import android.content.Intent; import android.net.VpnService; -import android.os.Build; import android.os.Bundle; -import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_BLOCKING_VPN; - public class VoidVpnLauncher extends Activity { private static final int VPN_USER_PERMISSION = 71; @@ -23,20 +20,14 @@ public class VoidVpnLauncher extends Activity { if (blocking_intent != null) startActivityForResult(blocking_intent, VPN_USER_PERMISSION); else { - onActivityResult(VPN_USER_PERMISSION, RESULT_OK, null); + EipCommand.startBlockingVPN(getApplicationContext()); } } protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == VPN_USER_PERMISSION) { if (resultCode == RESULT_OK) { - Intent void_vpn_service = new Intent(getApplicationContext(), VoidVpnService.class); - void_vpn_service.setAction(EIP_ACTION_START_BLOCKING_VPN); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - startForegroundService(void_vpn_service); - } else { - startService(void_vpn_service); - } + EipCommand.launchVoidVPN(getApplicationContext()); } } finish(); diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java index 476dda77..68ad78e4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java @@ -21,7 +21,9 @@ import android.app.Notification; import android.content.Intent; import android.content.SharedPreferences; import android.net.VpnService; +import android.os.Binder; import android.os.Build; +import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.system.OsConstants; import android.util.Log; @@ -53,6 +55,21 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat private EipStatus eipStatus; private VpnNotificationManager notificationManager; + private final IBinder binder = new VoidVpnServiceBinder(); + public class VoidVpnServiceBinder extends Binder { + VoidVpnService getService() { + // Return this instance of LocalService so clients can call public methods + return VoidVpnService.this; + } + } + + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + + @Override public void onCreate() { super.onCreate(); @@ -203,4 +220,8 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat startForeground(notificationId, notification); } + public void startWithForegroundNotification() { + + } + } -- cgit v1.2.3