diff options
8 files changed, 251 insertions, 191 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java index 3f45f391..0c9cbddf 100644 --- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java +++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java @@ -5,29 +5,21 @@ package de.blinkt.openvpn; -import android.annotation.TargetApi; import android.app.Activity; -import android.app.AlertDialog; import android.content.ActivityNotFoundException; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.content.Intent; -import android.content.SharedPreferences; import android.net.VpnService; import android.os.Build; import android.os.Bundle; -import java.io.IOException; - import de.blinkt.openvpn.core.ConnectionStatus; -import de.blinkt.openvpn.core.Preferences; -import de.blinkt.openvpn.core.VPNLaunchHelper; import de.blinkt.openvpn.core.VpnStatus; -import se.leap.bitmaskclient.base.MainActivity; import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.base.MainActivity; import se.leap.bitmaskclient.eip.EipCommand; import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_PREPARE_VPN; +import static se.leap.bitmaskclient.base.models.Constants.EIP_N_CLOSEST_GATEWAY; import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE; import static se.leap.bitmaskclient.eip.EipResultBroadcast.tellToReceiverOrBroadcast; @@ -58,54 +50,56 @@ import static se.leap.bitmaskclient.eip.EipResultBroadcast.tellToReceiverOrBroad */ public class LaunchVPN extends Activity { - public static final String EXTRA_KEY = "de.blinkt.openvpn.shortcutProfileUUID"; - public static final String EXTRA_NAME = "de.blinkt.openvpn.shortcutProfileName"; public static final String EXTRA_HIDELOG = "de.blinkt.openvpn.showNoLogWindow"; - public static final String CLEARLOG = "clearlogconnect"; - - private static final int START_VPN_PROFILE = 70; private static final String TAG = LaunchVPN.class.getName(); + private VpnProfile selectedProfile; + private int selectedGateway; - private VpnProfile mSelectedProfile; - private boolean mhideLog = false; - - private boolean mCmfixed = false; - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - startVpnFromIntent(); - } - - protected void startVpnFromIntent() { - // Resolve the intent - final Intent intent = getIntent(); final String action = intent.getAction(); + if (!Intent.ACTION_MAIN.equals(action)) { + finish(); + } - // If the intent is a request to create a shortcut, we'll do that and exit - + VpnProfile profileToConnect = (VpnProfile) intent.getExtras().getSerializable(PROVIDER_PROFILE); + selectedGateway = intent.getExtras().getInt(EIP_N_CLOSEST_GATEWAY, 0); + if (profileToConnect == null) { + VpnStatus.logError(R.string.shortcut_profile_notfound); + // show Log window to display error + showLogWindow(); + finish(); + } else { + selectedProfile = profileToConnect; + } - if (Intent.ACTION_MAIN.equals(action)) { - // Check if we need to clear the log - if (Preferences.getDefaultSharedPreferences(this).getBoolean(CLEARLOG, true)) - VpnStatus.clearLog(); + Intent vpnIntent; + try { + vpnIntent = VpnService.prepare(this.getApplicationContext()); + } catch (NullPointerException npe) { + tellToReceiverOrBroadcast(this.getApplicationContext(), EIP_ACTION_PREPARE_VPN, RESULT_CANCELED); + finish(); + return; + } - // we got called to be the starting point, most likely a shortcut - mhideLog = intent.getBooleanExtra(EXTRA_HIDELOG, false); - VpnProfile profileToConnect = (VpnProfile) intent.getExtras().getSerializable(PROVIDER_PROFILE); + if (vpnIntent != null) { + // we don't have the permission yet to start the VPN - if (profileToConnect == null) { - VpnStatus.logError(R.string.shortcut_profile_notfound); - // show Log window to display error + VpnStatus.updateStateString("USER_VPN_PERMISSION", "", R.string.state_user_vpn_permission, + ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT); + // Start the query + try { + startActivityForResult(vpnIntent, START_VPN_PROFILE); + } catch (ActivityNotFoundException ane) { + // Shame on you Sony! At least one user reported that + // an official Sony Xperia Arc S image triggers this exception + VpnStatus.logError(R.string.no_vpn_support_image); showLogWindow(); - finish(); - } else { - mSelectedProfile = profileToConnect; - launchVPN(); } } } @@ -114,13 +108,8 @@ public class LaunchVPN extends Activity { protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if(requestCode==START_VPN_PROFILE) { - SharedPreferences prefs = Preferences.getDefaultSharedPreferences(this); - boolean showLogWindow = prefs.getBoolean("showlogwindow", true); - - if(!mhideLog && showLogWindow) - showLogWindow(); - EipCommand.launchVPNProfile(getApplicationContext(), mSelectedProfile); + if(requestCode==START_VPN_PROFILE && resultCode == Activity.RESULT_OK) { + EipCommand.launchVPNProfile(getApplicationContext(), selectedProfile, selectedGateway); finish(); } else if (resultCode == Activity.RESULT_CANCELED) { @@ -128,110 +117,20 @@ public class LaunchVPN extends Activity { VpnStatus.updateStateString("USER_VPN_PERMISSION_CANCELLED", "", R.string.state_user_vpn_permission_cancelled, ConnectionStatus.LEVEL_NOTCONNECTED); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { VpnStatus.logError(R.string.nought_alwayson_warning); + // show Log window to display error + showLogWindow(); + } finish(); } } void showLogWindow() { - Intent startLW = new Intent(getBaseContext(), MainActivity.class); startLW.putExtra(MainActivity.ACTION_SHOW_LOG_FRAGMENT, true); startLW.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity(startLW); - - } - - void showConfigErrorDialog(int vpnok) { - AlertDialog.Builder d = new AlertDialog.Builder(this); - d.setTitle(R.string.config_error_found); - d.setMessage(vpnok); - d.setPositiveButton(android.R.string.ok, new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - - } - }); - d.setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - finish(); - } - }); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) - setOnDismissListener(d); - d.show(); - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - private void setOnDismissListener(AlertDialog.Builder d) { - d.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - finish(); - } - }); - } - - void launchVPN() { - int vpnok = mSelectedProfile.checkProfile(this); - if (vpnok != R.string.no_error_found) { - showConfigErrorDialog(vpnok); - return; - } - - Intent intent = null; - try { - intent = VpnService.prepare(this.getApplicationContext()); - } catch (NullPointerException npe) { - tellToReceiverOrBroadcast(this.getApplicationContext(), EIP_ACTION_PREPARE_VPN, RESULT_CANCELED); - finish(); - return; - } - - // Check if we want to fix /dev/tun - SharedPreferences prefs = Preferences.getDefaultSharedPreferences(this); - boolean usecm9fix = prefs.getBoolean("useCM9Fix", false); - boolean loadTunModule = prefs.getBoolean("loadTunModule", false); - - if (loadTunModule) - execeuteSUcmd("insmod /system/lib/modules/tun.ko"); - - if (usecm9fix && !mCmfixed) { - execeuteSUcmd("chown system /dev/tun"); - } - - if (intent != null) { - VpnStatus.updateStateString("USER_VPN_PERMISSION", "", R.string.state_user_vpn_permission, - ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT); - // Start the query - try { - startActivityForResult(intent, START_VPN_PROFILE); - } catch (ActivityNotFoundException ane) { - // Shame on you Sony! At least one user reported that - // an official Sony Xperia Arc S image triggers this exception - VpnStatus.logError(R.string.no_vpn_support_image); - showLogWindow(); - } - } else { - onActivityResult(START_VPN_PROFILE, Activity.RESULT_OK, null); - } - - } - - private void execeuteSUcmd(String command) { - try { - ProcessBuilder pb = new ProcessBuilder("su", "-c", command); - Process p = pb.start(); - int ret = p.waitFor(); - if (ret == 0) - mCmfixed = true; - } catch (InterruptedException | IOException e) { - VpnStatus.logException("SU command", e); - } } }
\ No newline at end of file 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<ResultReceiver> 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); } } @@ -469,6 +552,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<VoidVpnService> 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. * Pattern stolen from android.security.KeyChain.java 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() { + + } + } |