summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/insecure/assets/urls/cdev.bitmask.net.url3
-rw-r--r--app/src/insecure/assets/urls/dev.bitmask.net.url3
-rw-r--r--app/src/insecure/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java19
-rw-r--r--app/src/main/aidl/de/blinkt/openvpn/core/IOpenVPNServiceInternal.aidl2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/LaunchVPN.java203
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java62
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java17
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java43
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java10
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java9
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java26
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java249
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java23
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java20
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java1
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java27
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java39
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java194
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java91
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java30
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java35
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java3
-rw-r--r--app/src/main/res/values/strings.xml3
-rw-r--r--app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java43
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java2
28 files changed, 702 insertions, 470 deletions
diff --git a/app/src/insecure/assets/urls/cdev.bitmask.net.url b/app/src/insecure/assets/urls/cdev.bitmask.net.url
deleted file mode 100644
index 4ceca5ee..00000000
--- a/app/src/insecure/assets/urls/cdev.bitmask.net.url
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "main_url" : "https://cdev.bitmask.net/"
-} \ No newline at end of file
diff --git a/app/src/insecure/assets/urls/dev.bitmask.net.url b/app/src/insecure/assets/urls/dev.bitmask.net.url
deleted file mode 100644
index 5d4ae485..00000000
--- a/app/src/insecure/assets/urls/dev.bitmask.net.url
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "main_url" : "https://dev.bitmask.net/"
-} \ No newline at end of file
diff --git a/app/src/insecure/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java b/app/src/insecure/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java
index a13f056f..833d1e48 100644
--- a/app/src/insecure/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java
+++ b/app/src/insecure/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java
@@ -19,6 +19,7 @@ package se.leap.bitmaskclient.providersetup;
import android.content.SharedPreferences;
import android.content.res.Resources;
+import static se.leap.bitmaskclient.BuildConfig.DEBUG_MODE;
import android.os.Bundle;
import android.util.Pair;
@@ -44,6 +45,7 @@ import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
+import de.blinkt.openvpn.core.VpnStatus;
import okhttp3.OkHttpClient;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Provider;
@@ -104,6 +106,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {
if (isEmpty(provider.getMainUrlString()) || provider.getMainUrl().isDefault()) {
setErrorResult(currentDownload, malformed_url, null);
currentDownload.putParcelable(PROVIDER_KEY, provider);
+ VpnStatus.logWarning("[API] MainURL String is not set. Cannot setup provider.");
return currentDownload;
}
@@ -153,6 +156,10 @@ public class ProviderApiManager extends ProviderApiManagerBase {
return result;
}
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] PROVIDER JSON: " + providerDotJsonString);
+ }
+
try {
JSONObject providerJson = new JSONObject(providerDotJsonString);
@@ -183,6 +190,9 @@ public class ProviderApiManager extends ProviderApiManagerBase {
JSONObject providerDefinition = provider.getDefinition();
String eipServiceUrl = providerDefinition.getString(Provider.API_URL) + "/" + providerDefinition.getString(Provider.API_VERSION) + "/" + EIP.SERVICE_API_PATH;
eipServiceJsonString = downloadWithProviderCA(provider.getCaCert(), eipServiceUrl, lastDangerOn);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] EIP SERVICE JSON: " + eipServiceJsonString);
+ }
JSONObject eipServiceJson = new JSONObject(eipServiceJsonString);
@@ -213,6 +223,9 @@ public class ProviderApiManager extends ProviderApiManagerBase {
URL newCertStringUrl = new URL(provider.getApiUrlWithVersion() + "/" + PROVIDER_VPN_CERTIFICATE);
String certString = downloadWithProviderCA(provider.getCaCert(), newCertStringUrl.toString(), lastDangerOn);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] VPN CERT: " + certString);
+ }
if (ConfigHelper.checkErroneousDownload(certString)) {
if (certString == null || certString.isEmpty()) {
// probably 204
@@ -252,6 +265,9 @@ public class ProviderApiManager extends ProviderApiManagerBase {
URL geoIpUrl = provider.getGeoipUrl().getUrl();
String geoipJsonString = downloadFromUrlWithProviderCA(geoIpUrl.toString(), provider, lastDangerOn);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] MENSHEN JSON: " + geoipJsonString);
+ }
JSONObject geoipJson = new JSONObject(geoipJsonString);
if (geoipJson.has(ERRORS)) {
@@ -281,6 +297,9 @@ public class ProviderApiManager extends ProviderApiManagerBase {
if (validCertificate(provider, certString)) {
provider.setCaCert(certString);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] CA CERT: " + certString);
+ }
preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).apply();
result.putBoolean(BROADCAST_RESULT_KEY, true);
} else {
diff --git a/app/src/main/aidl/de/blinkt/openvpn/core/IOpenVPNServiceInternal.aidl b/app/src/main/aidl/de/blinkt/openvpn/core/IOpenVPNServiceInternal.aidl
index 293c2b6d..98cc3e42 100644
--- a/app/src/main/aidl/de/blinkt/openvpn/core/IOpenVPNServiceInternal.aidl
+++ b/app/src/main/aidl/de/blinkt/openvpn/core/IOpenVPNServiceInternal.aidl
@@ -23,4 +23,6 @@ interface IOpenVPNServiceInternal {
boolean isVpnRunning();
+ void startWithForegroundNotification();
+
}
diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
index b148d04d..b477f6d5 100644
--- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
@@ -5,29 +5,25 @@
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 androidx.annotation.StringRes;
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.eip.EipCommand;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
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.EIP.ERRORS;
import static se.leap.bitmaskclient.eip.EipResultBroadcast.tellToReceiverOrBroadcast;
/**
@@ -57,54 +53,53 @@ 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) {
+ showAlertInMainActivity(R.string.shortcut_profile_notfound);
+ 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) {
+ showAlertInMainActivity(R.string.vpn_error_establish);
+ 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
- showLogWindow();
- finish();
- } else {
- mSelectedProfile = profileToConnect;
- launchVPN();
+ 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
+ showAlertInMainActivity(R.string.no_vpn_support_image);
}
}
}
@@ -113,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();
- VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext());
+ if(requestCode==START_VPN_PROFILE && resultCode == Activity.RESULT_OK) {
+ EipCommand.launchVPNProfile(getApplicationContext(), selectedProfile, selectedGateway);
finish();
} else if (resultCode == Activity.RESULT_CANCELED) {
@@ -127,110 +117,29 @@ 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)
- VpnStatus.logError(R.string.nought_alwayson_warning);
-
- 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.N) {
+ showAlertInMainActivity(R.string.nought_alwayson_warning);
}
- });
- 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);
}
+ }
+ void showAlertInMainActivity(@StringRes int errorString) {
+ Bundle result = new Bundle();
+ setErrorResult(result, errorString);
+ tellToReceiverOrBroadcast(this.getApplicationContext(), EIP_ACTION_PREPARE_VPN, RESULT_CANCELED, result);
}
- 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);
- }
+ /**
+ * helper function to add error to result bundle
+ *
+ * @param result - result of an action
+ * @param errorMessageId - id of string resource describing the error
+ */
+ void setErrorResult(Bundle result, @StringRes int errorMessageId) {
+ VpnStatus.logError(errorMessageId);
+ result.putString(ERRORS, getResources().getString(errorMessageId));
+ result.putBoolean(BROADCAST_RESULT_KEY, false);
}
} \ No newline at end of file
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index 2c1a65dc..a734dd90 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -24,12 +24,13 @@ import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import androidx.annotation.RequiresApi;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
+import androidx.annotation.RequiresApi;
+
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.Inet6Address;
@@ -45,9 +46,10 @@ import de.blinkt.openvpn.core.VpnStatus.StateListener;
import de.blinkt.openvpn.core.connection.Connection;
import de.blinkt.openvpn.core.connection.Obfs4Connection;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.eip.EipStatus;
import se.leap.bitmaskclient.eip.VpnNotificationManager;
-import se.leap.bitmaskclient.pluggableTransports.Shapeshifter;
import se.leap.bitmaskclient.firewall.FirewallManager;
+import se.leap.bitmaskclient.pluggableTransports.Shapeshifter;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT;
@@ -61,7 +63,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
public static final String START_SERVICE_STICKY = "de.blinkt.openvpn.START_SERVICE_STICKY";
public static final String ALWAYS_SHOW_NOTIFICATION = "de.blinkt.openvpn.NOTIFICATION_ALWAYS_VISIBLE";
public static final String DISCONNECT_VPN = "de.blinkt.openvpn.DISCONNECT_VPN";
- public static final String NOTIFICATION_CHANNEL_BG_ID = "openvpn_bg";
public static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "openvpn_newstat";
public static final String VPNSERVICE_TUN = "vpnservice-tun";
public final static String ORBOT_PACKAGE_NAME = "org.torproject.android";
@@ -92,11 +93,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
private Shapeshifter shapeshifter;
private FirewallManager firewallManager;
- private static final int PRIORITY_MIN = -2;
- private static final int PRIORITY_DEFAULT = 0;
- private static final int PRIORITY_MAX = 2;
-
-
private final IBinder mBinder = new IOpenVPNServiceInternal.Stub() {
@Override
@@ -118,6 +114,11 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
public boolean isVpnRunning() throws RemoteException {
return OpenVPNService.this.isVpnRunning();
}
+
+ @Override
+ public void startWithForegroundNotification() throws RemoteException {
+ OpenVPNService.this.startWithForegroundNotification();
+ }
};
// From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
@@ -197,13 +198,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
firewallManager.stop();
}
- private boolean runningOnAndroidTV() {
- UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
- if (uiModeManager == null)
- return false;
- return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
- }
-
synchronized void registerDeviceStateReceiver(OpenVPNManagement magnagement) {
// Registers BroadcastReceiver to track network connection changes.
IntentFilter filter = new IntentFilter();
@@ -218,8 +212,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
registerReceiver(mDeviceStateReceiver, filter);
VpnStatus.addByteCountListener(mDeviceStateReceiver);
- /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
- addLollipopCMListener(); */
}
synchronized void unregisterDeviceStateReceiver() {
@@ -235,9 +227,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
mDeviceStateReceiver = null;
- /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
- removeLollipopCMListener();*/
-
}
@Override
@@ -281,6 +270,13 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
@Override
+ public void startWithForegroundNotification() {
+ // Always show notification here to avoid problem with startForeground timeout
+ notificationManager.createOpenVpnNotificationChannel();
+ notificationManager.buildForegroundServiceNotification(EipStatus.getInstance().getLevel(), this::onNotificationBuild);
+ }
+
+ @Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.getBooleanExtra(ALWAYS_SHOW_NOTIFICATION, false))
@@ -334,6 +330,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
} else {
/* The intent is null when we are set as always-on or the service has been restarted. */
Log.d(TAG, "Starting VPN due to isAlwaysOn system settings or app crash.");
+ startWithForegroundNotification();
+
mProfile = VpnStatus.getLastConnectedVpnProfile(this);
VpnStatus.logInfo(R.string.service_restarted);
@@ -375,9 +373,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
private void startOpenVPN() {
- /**
- * see change above (l. 292 ff)
- */
//TODO: investigate how connections[n] with n>0 get called during vpn setup (on connection refused?)
// Do we need to check if there's any obfs4 connection in mProfile.mConnections and start
// the dispatcher here? Can we start the dispatcher at a later point of execution, e.g. when
@@ -524,8 +519,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
@Override
public void onCreate() {
super.onCreate();
- notificationManager = new VpnNotificationManager(this, this);
- notificationManager.createOpenVpnNotificationChannel();
+ notificationManager = new VpnNotificationManager(this);
firewallManager = new FirewallManager(this, true);
}
@@ -543,9 +537,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
// Just in case unregister for state
VpnStatus.removeStateListener(this);
VpnStatus.flushLog();
- notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_BG_ID);
- notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_NEWSTATUS_ID);
firewallManager.onDestroy();
+ notificationManager.deleteOpenvpnNotificationChannel();
}
private String getTunConfigString() {
@@ -1013,14 +1006,10 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
if (mProcessThread == null && !mNotificationAlwaysVisible)
return;
- String channel = NOTIFICATION_CHANNEL_NEWSTATUS_ID;
// Display byte count only after being connected
-
if (level == LEVEL_CONNECTED) {
mDisplayBytecount = true;
mConnecttime = System.currentTimeMillis();
- if (!runningOnAndroidTV())
- channel = NOTIFICATION_CHANNEL_BG_ID;
firewallManager.start();
} else {
mDisplayBytecount = false;
@@ -1033,7 +1022,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
VpnStatus.getLastCleanLogMessage(this),
level,
0,
- channel);
+ NOTIFICATION_CHANNEL_NEWSTATUS_ID);
}
@@ -1064,7 +1053,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
null,
LEVEL_CONNECTED,
mConnecttime,
- NOTIFICATION_CHANNEL_BG_ID);
+ NOTIFICATION_CHANNEL_NEWSTATUS_ID);
}
}
@@ -1108,7 +1097,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
getString(resid),
LEVEL_WAITING_FOR_USER_INPUT,
0,
- NOTIFICATION_CHANNEL_BG_ID);
+ NOTIFICATION_CHANNEL_NEWSTATUS_ID);
}
@@ -1117,11 +1106,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
startForeground(notificationId, notification);
}
- @Override
- public void onNotificationStop() {
- stopForeground(true);
- }
-
public void trigger_url_open(String info) {
/*
String channel = NOTIFICATION_CHANNEL_USERREQ_ID;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
index 7c742746..540ca043 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
@@ -7,7 +7,6 @@ package de.blinkt.openvpn.core;
import android.annotation.TargetApi;
import android.content.Context;
-import android.content.Intent;
import android.os.Build;
import java.io.File;
@@ -18,7 +17,6 @@ import java.util.Arrays;
import java.util.Vector;
import se.leap.bitmaskclient.R;
-import de.blinkt.openvpn.VpnProfile;
public class VPNLaunchHelper {
private static final String MININONPIEVPN = "nopie_openvpn";
@@ -120,7 +118,6 @@ public class VPNLaunchHelper {
return false;
}
-
return true;
} catch (IOException e) {
VpnStatus.logException(e);
@@ -129,20 +126,6 @@ public class VPNLaunchHelper {
}
-
- public static void startOpenVpn(VpnProfile startprofile, Context context) {
- Intent startVPN = startprofile.prepareStartService(context);
- if (startVPN != null) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- //noinspection NewApi
- context.startForegroundService(startVPN);
- else
- context.startService(startVPN);
-
- }
- }
-
-
public static String getConfigFilePath(Context context) {
return context.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE;
}
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 676e6c82..126c4a98 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/MainActivity.java
@@ -20,13 +20,14 @@ package se.leap.bitmaskclient.base;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.util.Log;
+
import androidx.annotation.StringRes;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
-import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
@@ -35,24 +36,27 @@ import java.util.Observable;
import java.util.Observer;
import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.base.fragments.NavigationDrawerFragment;
-import se.leap.bitmaskclient.eip.EIP;
-import se.leap.bitmaskclient.eip.EipCommand;
-import se.leap.bitmaskclient.eip.EipSetupListener;
-import se.leap.bitmaskclient.eip.EipSetupObserver;
import se.leap.bitmaskclient.base.fragments.EipFragment;
import se.leap.bitmaskclient.base.fragments.ExcludeAppsFragment;
import se.leap.bitmaskclient.base.fragments.LogFragment;
+import se.leap.bitmaskclient.base.fragments.MainActivityErrorDialog;
+import se.leap.bitmaskclient.base.fragments.NavigationDrawerFragment;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
-import se.leap.bitmaskclient.providersetup.models.LeapSRPSession;
-import se.leap.bitmaskclient.providersetup.activities.LoginActivity;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
-import se.leap.bitmaskclient.base.fragments.MainActivityErrorDialog;
+import se.leap.bitmaskclient.eip.EIP;
+import se.leap.bitmaskclient.eip.EipCommand;
+import se.leap.bitmaskclient.eip.EipSetupListener;
+import se.leap.bitmaskclient.eip.EipSetupObserver;
+import se.leap.bitmaskclient.providersetup.activities.LoginActivity;
+import se.leap.bitmaskclient.providersetup.models.LeapSRPSession;
+import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed;
+import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message;
import static se.leap.bitmaskclient.base.models.Constants.ASK_TO_CANCEL_VPN;
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.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_REQUEST;
@@ -61,16 +65,14 @@ import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE
import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_LOG_IN;
import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_SWITCH_PROVIDER;
import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.storeProviderInPreferences;
+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.providersetup.ProviderAPI.ERRORID;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE;
-import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed;
-import static se.leap.bitmaskclient.R.string.vpn_certificate_user_message;
-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.base.utils.PreferenceHelper.storeProviderInPreferences;
public class MainActivity extends AppCompatActivity implements EipSetupListener, Observer, ExcludeAppsFragment.ExcludedAppsCallback {
@@ -256,7 +258,14 @@ 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:
+ if (resultCode == RESULT_CANCELED) {
+ String error = resultData.getString(ERRORS);
+ showMainActivityErrorDialog(error);
}
break;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java
index 4ff80ea6..b99182ae 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/EipFragment.java
@@ -52,6 +52,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
+import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.IOpenVPNServiceInternal;
import de.blinkt.openvpn.core.OpenVPNService;
import de.blinkt.openvpn.core.VpnStatus;
@@ -319,10 +320,7 @@ public class EipFragment extends Fragment implements Observer {
} else {
EipCommand.startVPN(context.getApplicationContext(), false);
}
- vpnStateImage.showProgress();
- routedText.setVisibility(GONE);
- vpnRoute.setVisibility(GONE);
- colorBackgroundALittle();
+ EipStatus.getInstance().updateState("UI_CONNECTING", "", 0, ConnectionStatus.LEVEL_START);
}
protected void stopEipIfPossible() {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java
index 4b307f23..f036b411 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/MainActivityErrorDialog.java
@@ -111,15 +111,15 @@ public class MainActivityErrorDialog extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
Context applicationContext = getContext().getApplicationContext();
- builder.setMessage(reasonToFail)
- .setNegativeButton(R.string.cancel, (dialog, id) -> {
- });
+ builder.setMessage(reasonToFail);
switch (downloadError) {
case ERROR_INVALID_VPN_CERTIFICATE:
builder.setPositiveButton(R.string.update_certificate, (dialog, which) ->
ProviderAPICommand.execute(getContext(), UPDATE_INVALID_VPN_CERTIFICATE, provider));
+ builder.setNegativeButton(R.string.cancel, (dialog, id) -> {});
break;
case NO_MORE_GATEWAYS:
+ builder.setNegativeButton(R.string.cancel, (dialog, id) -> {});
if (provider.supportsPluggableTransports()) {
if (getUsePluggableTransports(applicationContext)) {
builder.setPositiveButton(warning_option_try_ovpn, ((dialog, which) -> {
@@ -139,9 +139,7 @@ public class MainActivityErrorDialog extends DialogFragment {
}
break;
case ERROR_VPN_PREPARE:
- builder.setPositiveButton(R.string.retry, (dialog, which) -> {
- EipCommand.startVPN(applicationContext, false);
- });
+ builder.setPositiveButton(android.R.string.ok, (dialog, which) -> { });
break;
default:
break;
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
index f213b7f9..a0d295bd 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/models/Constants.java
@@ -80,7 +80,7 @@ public interface Constants {
String EIP_ACTION_START_BLOCKING_VPN = "se.leap.bitmaskclient.EIP_ACTION_START_BLOCKING_VPN";
String EIP_ACTION_STOP_BLOCKING_VPN = "se.leap.bitmaskclient.EIP_ACTION_STOP_BLOCKING_VPN";
String EIP_ACTION_PREPARE_VPN = "se.leap.bitmaskclient.EIP_ACTION_PREPARE_VPN";
- String EIP_ACTION_CONFIGURE_TETHERING = "se.leap.bitmaskclient.EIP_ACTION_CONFIGURE_TETHERING";
+ String EIP_ACTION_LAUNCH_VPN = "se.leap.bitmaskclient.EIP_ACTION_LAUNCH_VPN";
String EIP_RECEIVER = "EIP.RECEIVER";
String EIP_REQUEST = "EIP.REQUEST";
@@ -104,6 +104,13 @@ public interface Constants {
String PROVIDER_PROFILE_UUID = "Constants.PROVIDER_PROFILE_UUID";
String PROVIDER_PROFILE = "Constants.PROVIDER_PROFILE";
+ ////////////////////////////////////////////////
+ // PRESHIPPED PROVIDER CONFIG
+ ////////////////////////////////////////////////
+ String URLS = "urls";
+ String EXT_JSON = ".json";
+ String EXT_PEM = ".pem";
+
//////////////////////////////////////////////
// CREDENTIAL CONSTANTS
/////////////////////////////////////////////
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
index 77189dff..8a526499 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/InputStreamHelper.java
@@ -1,7 +1,11 @@
package se.leap.bitmaskclient.base.utils;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
/**
@@ -18,4 +22,26 @@ public class InputStreamHelper {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
+
+ public static String extractKeyFromInputStream(InputStream inputStream, String key) {
+ String value = "";
+
+ JSONObject fileContents = inputStreamToJson(inputStream);
+ if (fileContents != null)
+ value = fileContents.optString(key);
+ return value;
+ }
+
+ public static JSONObject inputStreamToJson(InputStream inputStream) {
+ JSONObject json = null;
+ try {
+ byte[] bytes = new byte[inputStream.available()];
+ if (inputStream.read(bytes) > 0)
+ json = new JSONObject(new String(bytes));
+ inputStream.reset();
+ } catch (IOException | JSONException e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
}
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 e5cf70be..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,16 +22,19 @@ 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;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.util.Log;
+
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.annotation.WorkerThread;
import androidx.core.app.JobIntentService;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
@@ -43,29 +46,35 @@ 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;
import se.leap.bitmaskclient.base.OnBootReceiver;
import se.leap.bitmaskclient.base.models.ProviderObservable;
-import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static de.blinkt.openvpn.core.connection.Connection.TransportType.OBFS4;
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_CONFIGURE_TETHERING;
-import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_IS_RUNNING;
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;
@@ -75,13 +84,13 @@ import static se.leap.bitmaskclient.base.models.Constants.EIP_RESTART_ON_BOOT;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
-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.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;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.ensureNotOnMainThread;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUsePluggableTransports;
/**
* EIP is the abstract base class for interacting with and managing the Encrypted
@@ -106,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.
@@ -116,7 +126,8 @@ public final class EIP extends JobIntentService implements Observer {
UNKNOWN,
ERROR_INVALID_VPN_CERTIFICATE,
NO_MORE_GATEWAYS,
- ERROR_VPN_PREPARE
+ ERROR_VPN_PREPARE,
+ ERROR_INVALID_PROFILE
}
/**
@@ -146,6 +157,10 @@ public final class EIP extends JobIntentService implements Observer {
openVpnServiceConnection.close();
openVpnServiceConnection = null;
}
+ if (voidVpnServiceConnection != null) {
+ voidVpnServiceConnection.close();
+ voidVpnServiceConnection = null;
+ }
}
/**
@@ -175,7 +190,7 @@ public final class EIP extends JobIntentService implements Observer {
int nClosestGateway;
switch (action) {
case EIP_ACTION_START:
- boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, true);
+ boolean earlyRoutes = intent.getBooleanExtra(EIP_EARLY_ROUTES, false);
nClosestGateway = intent.getIntExtra(EIP_N_CLOSEST_GATEWAY, 0);
startEIP(earlyRoutes, nClosestGateway);
break;
@@ -195,8 +210,9 @@ public final class EIP extends JobIntentService implements Observer {
disconnect();
earlyRoutes();
break;
- case EIP_ACTION_CONFIGURE_TETHERING:
- Log.d(TAG, "TODO: implement tethering configuration");
+ case EIP_ACTION_LAUNCH_VPN:
+ VpnProfile profile = (VpnProfile) intent.getSerializableExtra(PROVIDER_PROFILE);
+ launchProfile(profile);
break;
}
}
@@ -211,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();
}
@@ -234,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);
}
}
@@ -250,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);
}
}
@@ -260,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);
+ }
}
/**
@@ -271,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 = 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;
+ 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);
+
+ } 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
@@ -352,11 +432,14 @@ public final class EIP extends JobIntentService implements Observer {
void setErrorResult(Bundle result, String errorId, @StringRes int errorMessageId, Object... args) {
JSONObject errorJson = new JSONObject();
try {
+ String errorMessage;
if (args != null) {
- errorJson.put(ERRORS, getResources().getString(errorMessageId, args));
+ errorMessage = getResources().getString(errorMessageId, args);
} else {
- errorJson.put(ERRORS, getResources().getString(errorMessageId));
+ errorMessage = getResources().getString(errorMessageId);
}
+ VpnStatus.logWarning("[EIP] error: " + errorMessage);
+ errorJson.put(ERRORS, errorMessage);
errorJson.put(ERRORID, errorId);
} catch (JSONException e) {
e.printStackTrace();
@@ -406,6 +489,41 @@ public final class EIP extends JobIntentService implements Observer {
return false;
}
+ /**
+ * 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 launchProfile(VpnProfile vpnProfile, Bundle result) {
+ Intent startVPN = vpnProfile.prepareStartService(getApplicationContext());
+ if (startVPN != null) {
+ try {
+ initOpenVpnServiceConnection();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //noinspection NewApi
+ getApplicationContext().startForegroundService(startVPN);
+ openVpnServiceConnection.getService().startWithForegroundNotification();
+ } else {
+ getApplicationContext().startService(startVPN);
+ }
+ } catch (InterruptedException | IllegalStateException | RemoteException e) {
+ setErrorResult(result, R.string.vpn_error_establish, null);
+ }
+ } else {
+ setErrorResult(result, R.string.vpn_error_establish, null);
+ }
+ }
+
+ 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);
+ }
+ }
+
private @StringRes int getStringResourceForNoMoreGateways() {
if (ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports()) {
@@ -434,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 0650e8cd..46704419 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
@@ -9,14 +9,17 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import de.blinkt.openvpn.VpnProfile;
+
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;
-import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_CONFIGURE_TETHERING;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_LAUNCH_VPN;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_BLOCKING_VPN;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP;
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_RECEIVER;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE;
/**
* Use this class to send commands to EIP
@@ -73,6 +76,15 @@ public class EipCommand {
execute(context, EIP_ACTION_STOP);
}
+ 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);
@@ -87,13 +99,4 @@ public class EipCommand {
execute(context, EIP_ACTION_CHECK_CERT_VALIDITY, resultReceiver, null);
}
- public static void configureTethering(@NonNull Context context) {
- execute(context, EIP_ACTION_CONFIGURE_TETHERING);
- }
-
- @VisibleForTesting
- public static void configureTethering(@NonNull Context context, ResultReceiver resultReceiver) {
- execute(context, EIP_ACTION_CONFIGURE_TETHERING);
- }
-
}
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 f35e5e30..4706d550 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
@@ -54,10 +54,12 @@ import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETU
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_PROVIDER_API_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.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_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;
@@ -158,14 +160,14 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
ProviderObservable.getInstance().updateProvider(provider);
PreferenceHelper.storeProviderInPreferences(preferences, provider);
if (EipStatus.getInstance().isDisconnected()) {
- EipCommand.startVPN(context.getApplicationContext(), true);
+ EipCommand.startVPN(context.getApplicationContext(), false);
}
break;
case CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE:
provider = resultData.getParcelable(PROVIDER_KEY);
ProviderObservable.getInstance().updateProvider(provider);
PreferenceHelper.storeProviderInPreferences(preferences, provider);
- EipCommand.startVPN(context.getApplicationContext(), true);
+ EipCommand.startVPN(context.getApplicationContext(), false);
break;
case CORRECTLY_DOWNLOADED_GEOIP_JSON:
provider = resultData.getParcelable(PROVIDER_KEY);
@@ -230,6 +232,13 @@ public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.Sta
EipStatus.refresh();
}
break;
+ case EIP_ACTION_LAUNCH_VPN:
+ if (resultCode == RESULT_CANCELED) {
+ VpnStatus.logError("Error starting VpnService.");
+ finishGatewaySetup(false);
+ EipStatus.refresh();
+ }
+ break;
default:
break;
}
@@ -252,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/EipStatus.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
index ad84ec5a..bc123683 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
@@ -77,7 +77,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
currentStatus.setLocalizedResId(localizedResId);
currentStatus.setLevel(level);
currentStatus.setEipLevel(level);
- if (tmp != currentStatus.getLevel() || "RECONNECTING".equals(state)) {
+ if (tmp != currentStatus.getLevel() || "RECONNECTING".equals(state) || "UI_CONNECTING".equals(state)) {
refresh();
}
}
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..e2cd86b9 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
@@ -3,10 +3,10 @@ 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;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_PREPARE_VPN;
+import static se.leap.bitmaskclient.eip.EipResultBroadcast.tellToReceiverOrBroadcast;
public class VoidVpnLauncher extends Activity {
@@ -19,24 +19,25 @@ public class VoidVpnLauncher extends Activity {
}
public void setUp() {
- Intent blocking_intent = VpnService.prepare(getApplicationContext()); // stops the VPN connection created by another application.
- if (blocking_intent != null)
- startActivityForResult(blocking_intent, VPN_USER_PERMISSION);
+ Intent blockingIntent = null;
+ try {
+ blockingIntent = VpnService.prepare(getApplicationContext()); // stops the VPN connection created by another application.
+ } catch (NullPointerException npe) {
+ tellToReceiverOrBroadcast(this.getApplicationContext(), EIP_ACTION_PREPARE_VPN, RESULT_CANCELED);
+ finish();
+ }
+ if (blockingIntent != null) {
+ startActivityForResult(blockingIntent, 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 77038492..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,12 +55,27 @@ 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();
eipStatus = EipStatus.getInstance();
eipStatus.addObserver(this);
- notificationManager = new VpnNotificationManager(this, this);
+ notificationManager = new VpnNotificationManager(this);
notificationManager.createVoidVpnNotificationChannel();
}
@@ -99,14 +116,19 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat
closeFd();
}
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ notificationManager.deleteVoidVpnNotificationChannel();
+ }
+
private void stop() {
- notificationManager.stopNotifications(NOTIFICATION_CHANNEL_NEWSTATUS_ID);
- notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_NEWSTATUS_ID);
if (thread != null) {
thread.interrupt();
}
closeFd();
VpnStatus.updateStateString("NOPROCESS", "BLOCKING VPN STOPPED", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED);
+ stopForeground(true);
}
public static boolean isRunning() throws NullPointerException {
@@ -185,9 +207,11 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat
notificationManager.buildVoidVpnNotification(
blockingMessage,
blockingMessage,
- eipStatus.getLevel());
+ eipStatus.getLevel(),
+ this
+ );
} else {
- notificationManager.stopNotifications(NOTIFICATION_CHANNEL_NEWSTATUS_ID);
+ stopForeground(true);
}
}
@@ -196,9 +220,8 @@ public class VoidVpnService extends VpnService implements Observer, VpnNotificat
startForeground(notificationId, notification);
}
- @Override
- public void onNotificationStop() {
- stopForeground(true);
+ public void startWithForegroundNotification() {
+
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java
index b3ed5394..b007715b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java
@@ -27,32 +27,34 @@ import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.widget.RemoteViews;
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
import de.blinkt.openvpn.LaunchVPN;
import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.OpenVPNService;
-import se.leap.bitmaskclient.base.MainActivity;
+import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.MainActivity;
import se.leap.bitmaskclient.base.StartActivity;
import static android.os.Build.VERSION_CODES.O;
-import static androidx.core.app.NotificationCompat.PRIORITY_HIGH;
-import static androidx.core.app.NotificationCompat.PRIORITY_MAX;
-import static androidx.core.app.NotificationCompat.PRIORITY_MIN;
import static android.text.TextUtils.isEmpty;
+import static androidx.core.app.NotificationCompat.PRIORITY_DEFAULT;
+import static androidx.core.app.NotificationCompat.PRIORITY_MAX;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT;
+import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
import static se.leap.bitmaskclient.base.models.Constants.ASK_TO_CANCEL_VPN;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_STOP_BLOCKING_VPN;
-import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
/**
* Created by cyberta on 14.01.18.
@@ -60,29 +62,20 @@ import static se.leap.bitmaskclient.base.MainActivity.ACTION_SHOW_VPN_FRAGMENT;
public class VpnNotificationManager {
+ private static final String TAG = VpnNotificationManager.class.getSimpleName();
Context context;
- private VpnServiceCallback vpnServiceCallback;
- private NotificationManager notificationManager;
- private NotificationManagerCompat compatNotificationManager;
- private String[] notificationChannels = {
- OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
- OpenVPNService.NOTIFICATION_CHANNEL_BG_ID,
- VoidVpnService.NOTIFICATION_CHANNEL_NEWSTATUS_ID};
- private String lastNotificationChannel = "";
+ private final NotificationManagerCompat compatNotificationManager;
public interface VpnServiceCallback {
void onNotificationBuild(int notificationId, Notification notification);
- void onNotificationStop();
}
- public VpnNotificationManager(@NonNull Context context, @NonNull VpnServiceCallback vpnServiceCallback) {
+ public VpnNotificationManager(@NonNull Context context) {
this.context = context;
- notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
compatNotificationManager = NotificationManagerCompat.from(context);
- this.vpnServiceCallback = vpnServiceCallback;
}
- public void buildVoidVpnNotification(final String msg, String tickerText, ConnectionStatus status) {
+ public void buildVoidVpnNotification(final String msg, String tickerText, ConnectionStatus status, VpnServiceCallback callback) {
//TODO: implement extra Dashboard.ACTION_ASK_TO_CANCEL_BLOCKING_VPN
NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action.Builder(R.drawable.ic_menu_close_clear_cancel,
context.getString(R.string.vpn_button_turn_off_blocking), getStopVoidVpnIntent());
@@ -97,28 +90,45 @@ public class VpnNotificationManager {
PRIORITY_MAX,
0,
getMainActivityIntent(),
- actionBuilder.build());
+ actionBuilder.build(),
+ callback
+ );
}
- public void stopNotifications(String notificationChannelNewstatusId) {
- vpnServiceCallback.onNotificationStop();
- compatNotificationManager.cancel(notificationChannelNewstatusId.hashCode());
- }
- public void deleteNotificationChannel(String notificationChannel) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
- notificationManager.getNotificationChannel(notificationChannel) != null) {
- notificationManager.deleteNotificationChannel(notificationChannel);
+ public void buildForegroundServiceNotification(ConnectionStatus connectionStatus,
+ VpnServiceCallback callback) {
+ String message = "";
+ // if the app was killed by the system getLastCleanLogMessage returns an empty string
+ // because the state doesn't get persisted. We can use LEVEL_NOTCONNECTED as an indicator for
+ // that case because the openvpn service won't be connected then
+ if (connectionStatus == ConnectionStatus.LEVEL_NOTCONNECTED) {
+ message = context.getString(R.string.eip_state_not_connected);
+ } else {
+ message = VpnStatus.getLastCleanLogMessage(context);
}
+
+ NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action.Builder(0,
+ context.getString(R.string.vpn_button_turn_on), getStartOpenvpnIntent());
+
+ buildVpnNotification(
+ "",
+ message,
+ null,
+ "",
+ connectionStatus,
+ OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
+ PRIORITY_DEFAULT,
+ 0,
+ getMainActivityIntent(),
+ actionBuilder.build(),
+ callback
+ );
}
- /**
- * @param msg
- * @param tickerText
- * @param status
- * @param when
- */
- public void buildOpenVpnNotification(String profileName, boolean isObfuscated, String msg, String tickerText, ConnectionStatus status, long when, String notificationChannelNewstatusId) {
+ public void buildOpenVpnNotification(String profileName, boolean isObfuscated, String msg,
+ String tickerText, ConnectionStatus status, long when,
+ String notificationChannelNewstatusId, VpnServiceCallback vpnServiceCallback) {
String cancelString;
CharSequence bigmessage = null;
String ghostIcon = new String(Character.toChars(0x1f309));
@@ -158,7 +168,7 @@ public class VpnNotificationManager {
String appName = context.getString(R.string.app_name);
if (isEmpty(profileName)) {
title = appName;
- } else {
+ } else {
title = context.getString(R.string.notifcation_title_bitmask, appName, profileName);
}
@@ -167,15 +177,6 @@ public class VpnNotificationManager {
contentIntent = getUserInputIntent(msg);
else
contentIntent = getMainActivityIntent();
-
- int priority;
- if (OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID.equals(notificationChannelNewstatusId)) {
- priority = PRIORITY_HIGH;
- } else {
- // background channel
- priority = PRIORITY_MIN;
- }
-
buildVpnNotification(
title,
msg,
@@ -183,10 +184,25 @@ public class VpnNotificationManager {
tickerText,
status,
notificationChannelNewstatusId,
- priority,
+ PRIORITY_DEFAULT,
when,
contentIntent,
- actionBuilder.build());
+ actionBuilder.build(),
+ vpnServiceCallback);
+ }
+
+ public void buildOpenVpnNotification(String profileName, boolean isObfuscated, String msg, String tickerText, ConnectionStatus status, long when, String notificationChannelNewstatusId) {
+ buildOpenVpnNotification(profileName, isObfuscated, msg, tickerText, status, when, notificationChannelNewstatusId, null);
+ }
+
+ public void deleteOpenvpnNotificationChannel() {
+ compatNotificationManager.cancelAll();
+ compatNotificationManager.deleteNotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID);
+ }
+
+ public void deleteVoidVpnNotificationChannel() {
+ compatNotificationManager.cancelAll();
+ compatNotificationManager.deleteNotificationChannel(VoidVpnService.NOTIFICATION_CHANNEL_NEWSTATUS_ID);
}
@@ -197,16 +213,19 @@ public class VpnNotificationManager {
}
// Connection status change messages
- CharSequence name = context.getString(R.string.channel_name_status);
- NotificationChannel mChannel = new NotificationChannel(VoidVpnService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
- name, NotificationManager.IMPORTANCE_DEFAULT);
-
- mChannel.setDescription(context.getString(R.string.channel_description_status));
- mChannel.enableLights(true);
-
- mChannel.setLightColor(Color.BLUE);
- mChannel.setSound(null, null);
- notificationManager.createNotificationChannel(mChannel);
+ NotificationChannel channel = compatNotificationManager.getNotificationChannel(VoidVpnService.NOTIFICATION_CHANNEL_NEWSTATUS_ID);
+ if (channel == null) {
+ CharSequence name = context.getString(R.string.channel_name_status);
+ channel = new NotificationChannel(VoidVpnService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
+ name, NotificationManager.IMPORTANCE_DEFAULT);
+
+ channel.setDescription(context.getString(R.string.channel_description_status));
+ channel.enableLights(true);
+
+ channel.setLightColor(Color.BLUE);
+ channel.setSound(null, null);
+ compatNotificationManager.createNotificationChannel(channel);
+ }
}
@TargetApi(O)
@@ -215,29 +234,20 @@ public class VpnNotificationManager {
return;
}
- // Background message
- CharSequence name = context.getString(R.string.channel_name_background);
- NotificationChannel mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_BG_ID,
- name, NotificationManager.IMPORTANCE_MIN);
-
- mChannel.setDescription(context.getString(R.string.channel_description_background));
- mChannel.enableLights(false);
-
- mChannel.setLightColor(Color.DKGRAY);
- notificationManager.createNotificationChannel(mChannel);
-
// Connection status change messages
- name = context.getString(R.string.channel_name_status);
- mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
- name, NotificationManager.IMPORTANCE_DEFAULT);
-
-
- mChannel.setDescription(context.getString(R.string.channel_description_status));
- mChannel.enableLights(true);
-
- mChannel.setLightColor(Color.BLUE);
- mChannel.setSound(null, null);
- notificationManager.createNotificationChannel(mChannel);
+ NotificationChannel channel = compatNotificationManager.getNotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID);
+ if (channel == null) {
+ CharSequence name = context.getString(R.string.channel_name_status);
+ channel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
+ name, NotificationManager.IMPORTANCE_DEFAULT);
+
+ channel.setDescription(context.getString(R.string.channel_description_status));
+ channel.enableLights(true);
+
+ channel.setLightColor(Color.BLUE);
+ channel.setSound(null, null);
+ compatNotificationManager.createNotificationChannel(channel);
+ }
}
/**
@@ -253,7 +263,9 @@ public class VpnNotificationManager {
return remoteViews;
}
- private void buildVpnNotification(String title, String message, CharSequence bigMessage, String tickerText, ConnectionStatus status, String notificationChannelNewstatusId, int priority, long when, PendingIntent contentIntent, NotificationCompat.Action notificationAction) {
+ private void buildVpnNotification(String title, String message, CharSequence bigMessage, String tickerText,
+ ConnectionStatus status, String notificationChannelNewstatusId, int priority,
+ long when, PendingIntent contentIntent, NotificationCompat.Action notificationAction, VpnServiceCallback vpnServiceCallback) {
NotificationCompat.Builder nCompatBuilder = new NotificationCompat.Builder(context, notificationChannelNewstatusId);
int icon = getIconByConnectionStatus(status);
@@ -291,16 +303,10 @@ public class VpnNotificationManager {
Notification notification = nCompatBuilder.build();
int notificationId = notificationChannelNewstatusId.hashCode();
- if (!notificationChannelNewstatusId.equals(lastNotificationChannel)) {
- // Cancel old notification
- for (String channel : notificationChannels) {
- stopNotifications(channel);
- }
- }
-
compatNotificationManager.notify(notificationId, notification);
- vpnServiceCallback.onNotificationBuild(notificationId, notification);
- lastNotificationChannel = notificationChannelNewstatusId;
+ if (vpnServiceCallback != null) {
+ vpnServiceCallback.onNotificationBuild(notificationId, notification);
+ }
}
private PendingIntent getMainActivityIntent() {
@@ -308,6 +314,12 @@ public class VpnNotificationManager {
return PendingIntent.getActivity(context, 0, startActivity, PendingIntent.FLAG_CANCEL_CURRENT);
}
+ private PendingIntent getStartOpenvpnIntent() {
+ Intent startIntent = new Intent(context, EIP.class);
+ startIntent.setAction(EIP_ACTION_START);
+ return PendingIntent.getService(context, 0, startIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
private PendingIntent getStopVoidVpnIntent() {
Intent stopVoidVpnIntent = new Intent (context, VoidVpnService.class);
stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java
index ba902566..c863abd4 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiConnector.java
@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Scanner;
+import de.blinkt.openvpn.core.VpnStatus;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
@@ -68,6 +69,9 @@ public class ProviderApiConnector {
Request request = requestBuilder.build();
Response response = okHttpClient.newCall(request).execute();
+ if (!response.isSuccessful()) {
+ VpnStatus.logWarning("[API] API request failed canConnect(): " + url);
+ }
return response.isSuccessful();
}
@@ -88,6 +92,9 @@ public class ProviderApiConnector {
Request request = requestBuilder.build();
Response response = okHttpClient.newCall(request).execute();
+ if (!response.isSuccessful()) {
+ VpnStatus.logWarning("[API] API request failed: " + url);
+ }
InputStream inputStream = response.body().byteStream();
Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
if (scanner.hasNext()) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
index 8a0c8f02..c5dc6572 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
@@ -52,17 +52,29 @@ import java.util.NoSuchElementException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
+import de.blinkt.openvpn.core.VpnStatus;
import okhttp3.OkHttpClient;
+import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Constants.CREDENTIAL_ERRORS;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
-import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.utils.ConfigHelper;
import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
import se.leap.bitmaskclient.providersetup.models.LeapSRPSession;
import se.leap.bitmaskclient.providersetup.models.SrpCredentials;
import se.leap.bitmaskclient.providersetup.models.SrpRegistrationData;
-import se.leap.bitmaskclient.base.utils.ConfigHelper;
+import static se.leap.bitmaskclient.R.string.certificate_error;
+import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
+import static se.leap.bitmaskclient.R.string.error_json_exception_user_message;
+import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message;
+import static se.leap.bitmaskclient.R.string.malformed_url;
+import static se.leap.bitmaskclient.R.string.server_unreachable_message;
+import static se.leap.bitmaskclient.R.string.service_is_down_error;
+import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid;
+import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert;
+import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_details;
+import static se.leap.bitmaskclient.R.string.warning_expired_provider_cert;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_PROVIDER_API_EVENT;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
@@ -76,6 +88,11 @@ import static se.leap.bitmaskclient.base.models.Provider.CA_CERT;
import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.getFingerprintFromCertificate;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.parseRsaKeyFromString;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getFromPersistedProvider;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.BACKEND_ERROR_KEY;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.BACKEND_ERROR_MESSAGE;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE;
@@ -111,22 +128,6 @@ import static se.leap.bitmaskclient.providersetup.ProviderAPI.USER_MESSAGE;
import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING;
import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_INVALID_CERTIFICATE;
-import static se.leap.bitmaskclient.R.string.certificate_error;
-import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
-import static se.leap.bitmaskclient.R.string.error_json_exception_user_message;
-import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message;
-import static se.leap.bitmaskclient.R.string.malformed_url;
-import static se.leap.bitmaskclient.R.string.server_unreachable_message;
-import static se.leap.bitmaskclient.R.string.service_is_down_error;
-import static se.leap.bitmaskclient.R.string.vpn_certificate_is_invalid;
-import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert;
-import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_details;
-import static se.leap.bitmaskclient.R.string.warning_expired_provider_cert;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.getFingerprintFromCertificate;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.parseRsaKeyFromString;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.deleteProviderDetailsFromPreferences;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getFromPersistedProvider;
/**
* Implements the logic of the http api calls. The methods of this class needs to be called from
@@ -494,6 +495,7 @@ public abstract class ProviderApiManagerBase {
} else {
broadcastEvent(resultCode, resultData);
}
+ handleEventSummaryErrorLog(resultCode);
}
private void broadcastEvent(int resultCode , Bundle resultData) {
@@ -504,6 +506,40 @@ public abstract class ProviderApiManagerBase {
serviceCallback.broadcastEvent(intentUpdate);
}
+ private void handleEventSummaryErrorLog(int resultCode) {
+ String event = null;
+ switch (resultCode) {
+ case FAILED_LOGIN:
+ event = "login.";
+ break;
+ case FAILED_SIGNUP:
+ event = "signup.";
+ break;
+ case SUCCESSFUL_LOGOUT:
+ event = "logout.";
+ break;
+ case INCORRECTLY_DOWNLOADED_VPN_CERTIFICATE:
+ event = "download of vpn certificate.";
+ break;
+ case PROVIDER_NOK:
+ event = "setup or update provider details.";
+ break;
+ case INCORRECTLY_DOWNLOADED_EIP_SERVICE:
+ event = "update eip-service.json";
+ break;
+ case INCORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE:
+ event = "update invalid vpn certificate.";
+ break;
+ case INCORRECTLY_DOWNLOADED_GEOIP_JSON:
+ event = "download menshen service json.";
+ break;
+ default:
+ break;
+ }
+ if (event != null) {
+ VpnStatus.logWarning("[API] failed provider API event: " + event);
+ }
+ }
/**
* Validates parameters entered by the user to log in
@@ -598,6 +634,7 @@ public abstract class ProviderApiManagerBase {
} catch (NullPointerException | JSONException e) {
e.printStackTrace();
responseJson = getErrorMessageAsJson(error_json_exception_user_message);
+ VpnStatus.logWarning("[API] got null response for request: " + url);
}
return responseJson;
@@ -612,21 +649,29 @@ public abstract class ProviderApiManagerBase {
} catch (NullPointerException npe) {
plainResponseBody = formatErrorMessage(error_json_exception_user_message);
+ VpnStatus.logWarning("[API] Null response body for request " + url + ": " + npe.getLocalizedMessage());
} catch (UnknownHostException | SocketTimeoutException e) {
plainResponseBody = formatErrorMessage(server_unreachable_message);
+ VpnStatus.logWarning("[API] UnknownHostException or SocketTimeoutException for request " + url + ": " + e.getLocalizedMessage());
} catch (MalformedURLException e) {
plainResponseBody = formatErrorMessage(malformed_url);
+ VpnStatus.logWarning("[API] MalformedURLException for request " + url + ": " + e.getLocalizedMessage());
} catch (SSLHandshakeException | SSLPeerUnverifiedException e) {
plainResponseBody = formatErrorMessage(certificate_error);
+ VpnStatus.logWarning("[API] SSLHandshakeException or SSLPeerUnverifiedException for request " + url + ": " + e.getLocalizedMessage());
} catch (ConnectException e) {
plainResponseBody = formatErrorMessage(service_is_down_error);
+ VpnStatus.logWarning("[API] ConnectException for request " + url + ": " + e.getLocalizedMessage());
} catch (IllegalArgumentException e) {
plainResponseBody = formatErrorMessage(error_no_such_algorithm_exception_user_message);
+ VpnStatus.logWarning("[API] IllegalArgumentException for request " + url + ": " + e.getLocalizedMessage());
} catch (UnknownServiceException e) {
//unable to find acceptable protocols - tlsv1.2 not enabled?
plainResponseBody = formatErrorMessage(error_no_such_algorithm_exception_user_message);
+ VpnStatus.logWarning("[API] UnknownServiceException for request " + url + ": " + e.getLocalizedMessage());
} catch (IOException e) {
plainResponseBody = formatErrorMessage(error_io_exception_user_message);
+ VpnStatus.logWarning("[API] IOException for request " + url + ": " + e.getLocalizedMessage());
}
return plainResponseBody;
@@ -647,19 +692,26 @@ public abstract class ProviderApiManagerBase {
return ProviderApiConnector.canConnect(okHttpClient, providerUrl);
} catch (UnknownHostException | SocketTimeoutException e) {
+ VpnStatus.logWarning("[API] UnknownHostException or SocketTimeoutException during connection check: " + e.getLocalizedMessage());
setErrorResult(result, server_unreachable_message, null);
} catch (MalformedURLException e) {
+ VpnStatus.logWarning("[API] MalformedURLException during connection check: " + e.getLocalizedMessage());
setErrorResult(result, malformed_url, null);
} catch (SSLHandshakeException e) {
+ VpnStatus.logWarning("[API] SSLHandshakeException during connection check: " + e.getLocalizedMessage());
setErrorResult(result, warning_corrupted_provider_cert, ERROR_INVALID_CERTIFICATE.toString());
} catch (ConnectException e) {
+ VpnStatus.logWarning("[API] ConnectException during connection check: " + e.getLocalizedMessage());
setErrorResult(result, service_is_down_error, null);
} catch (IllegalArgumentException e) {
+ VpnStatus.logWarning("[API] IllegalArgumentException during connection check: " + e.getLocalizedMessage());
setErrorResult(result, error_no_such_algorithm_exception_user_message, null);
} catch (UnknownServiceException e) {
+ VpnStatus.logWarning("[API] UnknownServiceException during connection check: " + e.getLocalizedMessage());
//unable to find acceptable protocols - tlsv1.2 not enabled?
setErrorResult(result, error_no_such_algorithm_exception_user_message, null);
} catch (IOException e) {
+ VpnStatus.logWarning("[API] IOException during connection check: " + e.getLocalizedMessage());
setErrorResult(result, error_io_exception_user_message, null);
}
return false;
@@ -768,6 +820,7 @@ public abstract class ProviderApiManagerBase {
String caCert = provider.getCaCert();
if (ConfigHelper.checkErroneousDownload(caCert)) {
+ VpnStatus.logWarning("[API] No provider cert.");
return result;
}
@@ -802,6 +855,7 @@ public abstract class ProviderApiManagerBase {
protected Bundle setErrorResult(Bundle result, String stringJsonErrorMessage) {
String reasonToFail = pickErrorMessage(stringJsonErrorMessage);
+ VpnStatus.logWarning("[API] error: " + reasonToFail);
result.putString(ERRORS, reasonToFail);
result.putBoolean(BROADCAST_RESULT_KEY, false);
return result;
@@ -815,6 +869,7 @@ public abstract class ProviderApiManagerBase {
} else {
addErrorMessageToJson(errorJson, errorMessage);
}
+ VpnStatus.logWarning("[API] error: " + errorMessage);
result.putString(ERRORS, errorJson.toString());
result.putBoolean(BROADCAST_RESULT_KEY, false);
return result;
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
index d33a175f..654fb8e2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
@@ -22,12 +22,16 @@ import java.util.Set;
import se.leap.bitmaskclient.base.models.Provider;
+import static se.leap.bitmaskclient.base.models.Constants.EXT_JSON;
+import static se.leap.bitmaskclient.base.models.Constants.EXT_PEM;
+import static se.leap.bitmaskclient.base.models.Constants.URLS;
import static se.leap.bitmaskclient.base.models.Provider.GEOIP_URL;
import static se.leap.bitmaskclient.base.models.Provider.MAIN_URL;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_API_IP;
import static se.leap.bitmaskclient.base.models.Provider.PROVIDER_IP;
import static se.leap.bitmaskclient.base.utils.FileHelper.createFile;
import static se.leap.bitmaskclient.base.utils.FileHelper.persistFile;
+import static se.leap.bitmaskclient.base.utils.InputStreamHelper.extractKeyFromInputStream;
import static se.leap.bitmaskclient.base.utils.InputStreamHelper.getInputStreamFrom;
import static se.leap.bitmaskclient.base.utils.InputStreamHelper.loadInputStreamAsString;
@@ -45,10 +49,6 @@ public class ProviderManager implements AdapteeCollection<Provider> {
private static ProviderManager instance;
- final private static String URLS = "urls";
- final private static String EXT_JSON = ".json";
- final private static String EXT_PEM = ".pem";
-
public static ProviderManager getInstance(AssetManager assetsManager, File externalFilesDir) {
if (instance == null)
instance = new ProviderManager(assetsManager, externalFilesDir);
@@ -138,28 +138,6 @@ public class ProviderManager implements AdapteeCollection<Provider> {
return providers;
}
- private String extractKeyFromInputStream(InputStream inputStream, String key) {
- String value = "";
-
- JSONObject fileContents = inputStreamToJson(inputStream);
- if (fileContents != null)
- value = fileContents.optString(key);
- return value;
- }
-
- private JSONObject inputStreamToJson(InputStream inputStream) {
- JSONObject json = null;
- try {
- byte[] bytes = new byte[inputStream.available()];
- if (inputStream.read(bytes) > 0)
- json = new JSONObject(new String(bytes));
- inputStream.reset();
- } catch (IOException | JSONException e) {
- e.printStackTrace();
- }
- return json;
- }
-
public List<Provider> providers() {
List<Provider> allProviders = new ArrayList<>();
allProviders.addAll(defaultProviders);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java
index 161c53d3..b90d14f8 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/activities/CustomProviderSetupActivity.java
@@ -17,19 +17,33 @@
package se.leap.bitmaskclient.providersetup.activities;
import android.content.Intent;
+import android.content.res.AssetManager;
import android.os.Bundle;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+
import se.leap.bitmaskclient.BuildConfig;
+import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.providersetup.ProviderAPICommand;
-import se.leap.bitmaskclient.R;
+import static se.leap.bitmaskclient.BuildConfig.customProviderApiIp;
+import static se.leap.bitmaskclient.BuildConfig.customProviderIp;
+import static se.leap.bitmaskclient.BuildConfig.customProviderUrl;
+import static se.leap.bitmaskclient.BuildConfig.geoipUrl;
+import static se.leap.bitmaskclient.base.models.Constants.EXT_JSON;
+import static se.leap.bitmaskclient.base.models.Constants.EXT_PEM;
import static se.leap.bitmaskclient.base.models.Constants.REQUEST_CODE_CONFIGURE_LEAP;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.preferAnonymousUsage;
+import static se.leap.bitmaskclient.base.utils.InputStreamHelper.loadInputStreamAsString;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.SET_UP_PROVIDER;
import static se.leap.bitmaskclient.providersetup.ProviderSetupInterface.ProviderConfigState.SETTING_UP_PROVIDER;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.preferAnonymousUsage;
/**
* Created by cyberta on 17.08.18.
@@ -42,7 +56,7 @@ public class CustomProviderSetupActivity extends ProviderSetupBaseActivity {
super.onCreate(savedInstanceState);
setUpInitialUI();
restoreState(savedInstanceState);
- setProvider(new Provider(BuildConfig.customProviderUrl, BuildConfig.geoipUrl, BuildConfig.customProviderIp, BuildConfig.customProviderApiIp));
+ setDefaultProvider();
}
@Override
@@ -54,6 +68,21 @@ public class CustomProviderSetupActivity extends ProviderSetupBaseActivity {
}
}
+ private void setDefaultProvider() {
+ try {
+ AssetManager assetsManager = getAssets();
+ Provider customProvider = new Provider(customProviderUrl, geoipUrl, customProviderIp, customProviderApiIp);
+ String certificate = loadInputStreamAsString(assetsManager.open(customProvider.getDomain() + EXT_PEM));
+ String providerDefinition = loadInputStreamAsString(assetsManager.open(customProvider.getDomain() + EXT_JSON));
+ customProvider.setCaCert(certificate);
+ customProvider.define(new JSONObject(providerDefinition));
+ setProvider(customProvider);
+ } catch (IOException | JSONException e) {
+ e.printStackTrace();
+ setProvider(new Provider(customProviderUrl, geoipUrl, customProviderIp, customProviderApiIp));
+ }
+ }
+
private void setUpInitialUI() {
setContentView(R.layout.a_custom_provider_setup);
setProviderHeaderText(R.string.setup_provider);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
index c5100a67..5655e7b7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
@@ -7,6 +7,7 @@ import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
+import de.blinkt.openvpn.core.VpnStatus;
import okhttp3.Dns;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
@@ -28,10 +29,12 @@ class DnsResolver implements Dns {
}
String ip = currentProvider.getIpForHostname(hostname);
if (!ip.isEmpty()) {
+ VpnStatus.logWarning("[API] Normal DNS resolution for " + hostname + " seems to be blocked. Circumventing.");
ArrayList<InetAddress> addresses = new ArrayList<>();
addresses.add(InetAddress.getByAddress(hostname, IPAddress.asBytes(ip)));
return addresses;
} else {
+ VpnStatus.logWarning("[API] Could not resolve DNS for " + hostname);
throw new UnknownHostException("Hostname " + hostname + " not found");
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0f07ac8e..000effc7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -113,7 +113,7 @@
<string name="hide_experimental">Hide experimental features</string>
<string name="tethering_enabled_message">Please make sure to enable tethering in the <![CDATA[<b>system settings</b>]]> first.</string>
<string name="tethering_message">Share your VPN with other devices via:</string>
- <string name="tethering_wifi">Wifi hotspot</string>
+ <string name="tethering_wifi">Wi-Fi hotspot</string>
<string name="tethering_usb">USB tethering</string>
<string name="tethering_bluetooth">Bluetooth tethering</string>
<string name="do_not_show_again">Do not show again</string>
@@ -140,7 +140,6 @@
<string name="warning_option_try_ovpn">Try standard connection</string>
<string name="vpn_error_establish">Android failed to establish the VPN service.</string>
<string name="root_permission_error">%s cannot execute features like VPN Hotspot or IPv6 firewall without root permissions.</string>
-
<string name="qs_enable_vpn">Start %s</string>
<string name="version_update_found">Tap here to start the download.</string>
<string name="version_update_title">A new %s version has been found.</string>
diff --git a/app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java b/app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java
index 592db085..96d8ea69 100644
--- a/app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java
+++ b/app/src/production/java/se/leap/bitmaskclient/providersetup/ProviderApiManager.java
@@ -22,8 +22,6 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.util.Pair;
-import androidx.multidex.BuildConfig;
-
import org.json.JSONException;
import org.json.JSONObject;
@@ -34,25 +32,26 @@ import java.util.List;
import de.blinkt.openvpn.core.VpnStatus;
import okhttp3.OkHttpClient;
import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.eip.EIP;
import se.leap.bitmaskclient.base.models.Provider;
-import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
import se.leap.bitmaskclient.base.utils.ConfigHelper;
+import se.leap.bitmaskclient.eip.EIP;
+import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
import static android.text.TextUtils.isEmpty;
-import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
-import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
-import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING;
-import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON;
+import static se.leap.bitmaskclient.BuildConfig.DEBUG_MODE;
import static se.leap.bitmaskclient.R.string.downloading_vpn_certificate_failed;
import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
import static se.leap.bitmaskclient.R.string.malformed_url;
import static se.leap.bitmaskclient.R.string.setup_error_text;
import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_cert;
import static se.leap.bitmaskclient.R.string.warning_corrupted_provider_details;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
+import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CERTIFICATE_PINNING;
+import static se.leap.bitmaskclient.providersetup.ProviderSetupFailedDialog.DOWNLOAD_ERRORS.ERROR_CORRUPTED_PROVIDER_JSON;
/**
* Implements the logic of the provider api http requests. The methods of this class need to be called from
@@ -88,6 +87,7 @@ public class ProviderApiManager extends ProviderApiManagerBase {
if (isEmpty(provider.getMainUrlString()) || provider.getMainUrl().isDefault()) {
currentDownload.putBoolean(BROADCAST_RESULT_KEY, false);
setErrorResult(currentDownload, malformed_url, null);
+ VpnStatus.logWarning("[API] MainURL String is not set. Cannot setup provider.");
return currentDownload;
}
@@ -139,8 +139,8 @@ public class ProviderApiManager extends ProviderApiManagerBase {
return result;
}
- if (BuildConfig.DEBUG) {
- VpnStatus.logDebug("PROVIDER JSON: " + providerDotJsonString);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] PROVIDER JSON: " + providerDotJsonString);
}
try {
JSONObject providerJson = new JSONObject(providerDotJsonString);
@@ -168,10 +168,10 @@ public class ProviderApiManager extends ProviderApiManagerBase {
try {
String eipServiceUrl = provider.getApiUrlWithVersion() + "/" + EIP.SERVICE_API_PATH;
eipServiceJsonString = downloadWithProviderCA(provider.getCaCert(), eipServiceUrl);
- JSONObject eipServiceJson = new JSONObject(eipServiceJsonString);
- if (BuildConfig.DEBUG) {
- VpnStatus.logDebug("EIP SERVICE JSON: " + eipServiceJsonString);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] EIP SERVICE JSON: " + eipServiceJsonString);
}
+ JSONObject eipServiceJson = new JSONObject(eipServiceJsonString);
if (eipServiceJson.has(ERRORS)) {
setErrorResult(result, eipServiceJsonString);
} else {
@@ -197,8 +197,8 @@ public class ProviderApiManager extends ProviderApiManagerBase {
URL newCertStringUrl = new URL(provider.getApiUrlWithVersion() + "/" + PROVIDER_VPN_CERTIFICATE);
String certString = downloadWithProviderCA(provider.getCaCert(), newCertStringUrl.toString());
- if (BuildConfig.DEBUG) {
- VpnStatus.logDebug("VPN CERT: " + certString);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] VPN CERT: " + certString);
}
if (ConfigHelper.checkErroneousDownload(certString)) {
if (certString == null || certString.isEmpty()) {
@@ -240,6 +240,9 @@ public class ProviderApiManager extends ProviderApiManagerBase {
URL geoIpUrl = provider.getGeoipUrl().getUrl();
String geoipJsonString = downloadFromUrlWithProviderCA(geoIpUrl.toString(), provider);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] MENSHEN JSON: " + geoipJsonString);
+ }
JSONObject geoipJson = new JSONObject(geoipJsonString);
if (geoipJson.has(ERRORS)) {
@@ -267,10 +270,10 @@ public class ProviderApiManager extends ProviderApiManagerBase {
if (validCertificate(provider, certString)) {
provider.setCaCert(certString);
- preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).apply();
- if (BuildConfig.DEBUG) {
- VpnStatus.logDebug("CA CERT: " + certString);
+ if (DEBUG_MODE) {
+ VpnStatus.logDebug("[API] CA CERT: " + certString);
}
+ preferences.edit().putString(Provider.CA_CERT + "." + providerDomain, certString).apply();
result.putBoolean(BROADCAST_RESULT_KEY, true);
} else {
setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
index d4b7c5d1..5a341cd1 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
@@ -366,6 +366,8 @@ public class MockHelper {
return getClass().getClassLoader().getResourceAsStream(filename);
}
});
+ when(InputStreamHelper.extractKeyFromInputStream(any(InputStream.class), anyString())).thenCallRealMethod();
+ when(InputStreamHelper.inputStreamToJson(any(InputStream.class))).thenCallRealMethod();
}