summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/eip
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/eip')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java44
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java16
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipResultBroadcast.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipSetupListener.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java374
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java20
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java12
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java15
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java22
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java355
13 files changed, 812 insertions, 72 deletions
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 e0c96ebb..e5cf70be 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -49,39 +49,39 @@ import de.blinkt.openvpn.core.IOpenVPNServiceInternal;
import de.blinkt.openvpn.core.OpenVPNService;
import de.blinkt.openvpn.core.VpnStatus;
import de.blinkt.openvpn.core.connection.Connection;
-import se.leap.bitmaskclient.OnBootReceiver;
-import se.leap.bitmaskclient.ProviderObservable;
+import se.leap.bitmaskclient.base.OnBootReceiver;
+import se.leap.bitmaskclient.base.models.ProviderObservable;
import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.utils.PreferenceHelper;
+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.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT;
-import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_CONFIGURE_TETHERING;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_IS_RUNNING;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_BLOCKING_VPN;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN;
-import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES;
-import static se.leap.bitmaskclient.Constants.EIP_N_CLOSEST_GATEWAY;
-import static se.leap.bitmaskclient.Constants.EIP_RECEIVER;
-import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT;
-import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE;
-import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
-import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
+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_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;
+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.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.eip.EIP.EIPErrors.ERROR_INVALID_VPN_CERTIFICATE;
import static se.leap.bitmaskclient.eip.EIP.EIPErrors.NO_MORE_GATEWAYS;
import static se.leap.bitmaskclient.eip.EipResultBroadcast.tellToReceiverOrBroadcast;
-import static se.leap.bitmaskclient.utils.ConfigHelper.ensureNotOnMainThread;
-import static se.leap.bitmaskclient.utils.PreferenceHelper.getUsePluggableTransports;
+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
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 25450f56..39d4e33e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipCommand.java
@@ -11,14 +11,14 @@ import androidx.annotation.VisibleForTesting;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_CONFIGURE_TETHERING;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_START;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_BLOCKING_VPN;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP;
-import static se.leap.bitmaskclient.Constants.EIP_EARLY_ROUTES;
-import static se.leap.bitmaskclient.Constants.EIP_N_CLOSEST_GATEWAY;
-import static se.leap.bitmaskclient.Constants.EIP_RECEIVER;
+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_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;
/**
* Use this class to send commands to EIP
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipResultBroadcast.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipResultBroadcast.java
index 92d1338c..68d9c8ad 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipResultBroadcast.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipResultBroadcast.java
@@ -8,10 +8,10 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.util.Log;
import static android.content.Intent.CATEGORY_DEFAULT;
-import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT;
-import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE;
-import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_KEY;
-import static se.leap.bitmaskclient.Constants.EIP_REQUEST;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_EIP_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_REQUEST;
public class EipResultBroadcast {
private static final String TAG = EipResultBroadcast.class.getSimpleName();
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupListener.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupListener.java
new file mode 100644
index 00000000..13d9bdec
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupListener.java
@@ -0,0 +1,12 @@
+package se.leap.bitmaskclient.eip;
+
+import android.content.Intent;
+
+/**
+ * Created by cyberta on 05.12.18.
+ */
+public interface EipSetupListener {
+ void handleEipEvent(Intent intent);
+
+ void handleProviderApiEvent(Intent intent);
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
new file mode 100644
index 00000000..1c101e2d
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipSetupObserver.java
@@ -0,0 +1,374 @@
+/**
+ * Copyright (c) 2020 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package se.leap.bitmaskclient.eip;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import org.json.JSONObject;
+
+import java.util.Vector;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import de.blinkt.openvpn.LaunchVPN;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ConnectionStatus;
+import de.blinkt.openvpn.core.LogItem;
+import de.blinkt.openvpn.core.VpnStatus;
+import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.providersetup.ProviderAPI;
+import se.leap.bitmaskclient.providersetup.ProviderAPICommand;
+import se.leap.bitmaskclient.base.models.ProviderObservable;
+import se.leap.bitmaskclient.appUpdate.DownloadServiceCommand;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+
+import static android.app.Activity.RESULT_CANCELED;
+import static android.content.Intent.CATEGORY_DEFAULT;
+import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET;
+import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NOTCONNECTED;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_EIP_EVENT;
+import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT;
+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_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_REQUEST;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PROFILE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON;
+import static se.leap.bitmaskclient.appUpdate.DownloadServiceCommand.CHECK_VERSION_FILE;
+
+/**
+ * Created by cyberta on 05.12.18.
+ */
+public class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListener, VpnStatus.LogListener {
+
+ private static final String TAG = EipSetupObserver.class.getName();
+
+ //The real timout is 4*2s + 1*4s + 1*8s + 1*16s + 1*32s + 1*64s = 132 s;
+ private static final String TIMEOUT = "4";
+ private static final int UPDATE_CHECK_TIMEOUT = 1000*60*60*24*7;
+ private Context context;
+ private VpnProfile setupVpnProfile;
+ private String observedProfileFromVpnStatus;
+ AtomicBoolean changingGateway = new AtomicBoolean(false);
+ AtomicInteger setupNClosestGateway = new AtomicInteger();
+ AtomicInteger reconnectTry = new AtomicInteger();
+ private Vector<EipSetupListener> listeners = new Vector<>();
+ private SharedPreferences preferences;
+ private static EipSetupObserver instance;
+
+ private EipSetupObserver(Context context, SharedPreferences preferences) {
+ this.context = context;
+ this.preferences = preferences;
+ IntentFilter updateIntentFilter = new IntentFilter(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT);
+ updateIntentFilter.addAction(BROADCAST_EIP_EVENT);
+ updateIntentFilter.addAction(BROADCAST_PROVIDER_API_EVENT);
+ updateIntentFilter.addCategory(CATEGORY_DEFAULT);
+ LocalBroadcastManager.getInstance(context.getApplicationContext()).registerReceiver(this, updateIntentFilter);
+ instance = this;
+ VpnStatus.addLogListener(this);
+ }
+
+ public static void init(Context context, SharedPreferences preferences) {
+ if (instance == null) {
+ instance = new EipSetupObserver(context, preferences);
+ }
+ }
+
+ public static boolean reconnectingWithDifferentGateway() {
+ return instance.setupNClosestGateway.get() > 0;
+ }
+
+ public static int connectionRetry() {
+ return instance.reconnectTry.get();
+ }
+
+ public static int gatewayOrder() {
+ return instance.setupNClosestGateway.get();
+ }
+
+ public static synchronized void addListener(EipSetupListener listener) {
+ if (instance.listeners.contains(listener)) {
+ return;
+ }
+ instance.listeners.add(listener);
+ }
+
+ public static synchronized void removeListener(EipSetupListener listener) {
+ instance.listeners.remove(listener);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
+ switch (action) {
+ case BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT:
+ handleGatewaySetupObserverEvent(intent);
+ break;
+ case BROADCAST_EIP_EVENT:
+ handleEipEvent(intent);
+ break;
+ case BROADCAST_PROVIDER_API_EVENT:
+ handleProviderApiEvent(intent);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void handleProviderApiEvent(Intent intent) {
+ int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
+ Bundle resultData = intent.getParcelableExtra(BROADCAST_RESULT_KEY);
+ if (resultData == null) {
+ resultData = Bundle.EMPTY;
+ }
+
+ Provider provider;
+ switch (resultCode) {
+ case CORRECTLY_DOWNLOADED_EIP_SERVICE:
+ Log.d(TAG, "correctly updated service json");
+ provider = resultData.getParcelable(PROVIDER_KEY);
+ ProviderObservable.getInstance().updateProvider(provider);
+ PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ if (EipStatus.getInstance().isDisconnected()) {
+ EipCommand.startVPN(context.getApplicationContext(), true);
+ }
+ 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);
+ break;
+ case CORRECTLY_DOWNLOADED_GEOIP_JSON:
+ provider = resultData.getParcelable(PROVIDER_KEY);
+ ProviderObservable.getInstance().updateProvider(provider);
+ PreferenceHelper.storeProviderInPreferences(preferences, provider);
+ maybeStartEipService(resultData);
+ break;
+ case INCORRECTLY_DOWNLOADED_GEOIP_JSON:
+ maybeStartEipService(resultData);
+ break;
+ default:
+ break;
+ }
+
+ for (EipSetupListener listener : listeners) {
+ listener.handleProviderApiEvent(intent);
+ }
+ }
+
+ private void maybeStartEipService(Bundle resultData) {
+ if (resultData.getBoolean(EIP_ACTION_START)) {
+ boolean earlyRoutes = resultData.getBoolean(EIP_EARLY_ROUTES);
+ EipCommand.startVPN(context.getApplicationContext(), earlyRoutes);
+ }
+ }
+
+
+ private void handleEipEvent(Intent intent) {
+ int resultCode = intent.getIntExtra(BROADCAST_RESULT_CODE, RESULT_CANCELED);
+ Bundle result = intent.getBundleExtra(BROADCAST_RESULT_KEY);
+ String eipRequest = result.getString(EIP_REQUEST);
+ EIP.EIPErrors error = EIP.EIPErrors.UNKNOWN;
+ try {
+ JSONObject jsonObject = new JSONObject(result.getString(EIP.ERRORS));
+ error = EIP.EIPErrors.valueOf(jsonObject.getString(EIP.ERRORID));
+ } catch (Exception e) {
+ //ignore
+ }
+ if (eipRequest == null) {
+ return;
+ }
+ switch (eipRequest) {
+ case EIP_ACTION_START:
+ case EIP_ACTION_START_ALWAYS_ON_VPN:
+ if (resultCode == RESULT_CANCELED) {
+ //setup failed
+ if (error == EIP.EIPErrors.NO_MORE_GATEWAYS) {
+ finishGatewaySetup(false);
+ EipCommand.startBlockingVPN(context.getApplicationContext());
+ } else {
+ //FIXME:
+ finishGatewaySetup(false);
+ EipCommand.stopVPN(context);
+ EipStatus.refresh();
+ }
+ }
+ break;
+ case EIP_ACTION_PREPARE_VPN:
+ if (resultCode == RESULT_CANCELED) {
+ VpnStatus.logError("Error preparing VpnService.");
+ finishGatewaySetup(false);
+ EipStatus.refresh();
+ }
+ break;
+ default:
+ break;
+ }
+
+ for (EipSetupListener listener : listeners) {
+ listener.handleEipEvent(intent);
+ }
+ }
+
+ private void handleGatewaySetupObserverEvent(Intent event) {
+ if (observedProfileFromVpnStatus != null || setupVpnProfile != null) {
+ //finish last setup observation
+ Log.d(TAG, "finish last gateway setup");
+ finishGatewaySetup(true);
+ }
+
+ VpnProfile vpnProfile = (VpnProfile) event.getSerializableExtra(PROVIDER_PROFILE);
+ if (vpnProfile == null) {
+ Log.e(TAG, "Tried to setup non existing vpn profile.");
+ return;
+ }
+ setupVpnProfile = vpnProfile;
+ setupNClosestGateway.set(event.getIntExtra(Gateway.KEY_N_CLOSEST_GATEWAY, 0));
+ Log.d(TAG, "bitmaskapp add state listener");
+ VpnStatus.addStateListener(this);
+
+ launchVPN(setupVpnProfile);
+ }
+
+ private void launchVPN(VpnProfile vpnProfile) {
+ 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());
+ context.startActivity(intent);
+ }
+
+ @Override
+ public void updateState(String state, String logmessage, int localizedResId, ConnectionStatus level) {
+ // VpnStatus.updateStateString("NOPROCESS", "No process running.", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED);
+
+ Log.d(TAG, "vpn status: " + state + " - " + logmessage + " - " + level);
+ if (observedProfileFromVpnStatus == null ||
+ setupVpnProfile == null) {
+ return;
+ }
+ if (!observedProfileFromVpnStatus.equals(setupVpnProfile.getUUIDString())) {
+ Log.d(TAG, "vpn profile to setup and observed profile currently is used differ: " + setupVpnProfile.getUUIDString() + " vs. " + observedProfileFromVpnStatus);
+ return;
+ }
+
+ if (ConnectionStatus.LEVEL_STOPPING == level) {
+ finishGatewaySetup(false);
+ } else if ("CONNECTRETRY".equals(state) && LEVEL_CONNECTING_NO_SERVER_REPLY_YET.equals(level)) {
+ Log.d(TAG, "trying gateway: " + setupVpnProfile.getName());
+ if (TIMEOUT.equals(logmessage)) {
+ Log.e(TAG, "Timeout reached! Try next gateway!");
+ VpnStatus.logError("Timeout reached! Try next gateway!");
+ selectNextGateway();
+ return;
+ }
+ int current = reconnectTry.get();
+ reconnectTry.set(current + 1);
+ } else if ("NOPROCESS".equals(state) && LEVEL_NOTCONNECTED == level) {
+ //??
+ } else if ("CONNECTED".equals(state)) {
+ //saveLastProfile(context.getApplicationContext(), setupVpnProfile.getUUIDString());
+ Provider provider = ProviderObservable.getInstance().getCurrentProvider();
+ if (setupNClosestGateway.get() > 0 || provider.shouldUpdateEipServiceJson()) {
+ //setupNClostestGateway > 0: at least one failed gateway -> did the provider change it's gateways?
+ ProviderAPICommand.execute(context, ProviderAPI.DOWNLOAD_SERVICE_JSON, provider);
+ }
+
+ if (shouldCheckAppUpdate()) {
+ DownloadServiceCommand.execute(context, CHECK_VERSION_FILE);
+ }
+ finishGatewaySetup(false);
+ } else if ("TCP_CONNECT".equals(state)) {
+ changingGateway.set(false);
+ }
+ }
+
+ private boolean shouldCheckAppUpdate() {
+ return System.currentTimeMillis() - PreferenceHelper.getLastAppUpdateCheck(context) >= UPDATE_CHECK_TIMEOUT;
+ }
+
+ private void selectNextGateway() {
+ changingGateway.set(true);
+ reconnectTry.set(0);
+ EipCommand.startVPN(context.getApplicationContext(), false, setupNClosestGateway.get() + 1);
+ }
+
+ private void finishGatewaySetup(boolean changingGateway) {
+ VpnStatus.removeStateListener(this);
+ setupVpnProfile = null;
+ setupNClosestGateway.set(0);
+ observedProfileFromVpnStatus = null;
+ this.changingGateway.set(changingGateway);
+ this.reconnectTry.set(0);
+ }
+
+ /**
+ * gets called as soon as a new VPN is about to launch
+ *
+ * @param uuid
+ */
+ @Override
+ public void setConnectedVPN(String uuid) {
+ observedProfileFromVpnStatus = uuid;
+ }
+
+ @Override
+ public void newLog(LogItem logItem) {
+ if (logItem.getLogLevel() == VpnStatus.LogLevel.ERROR) {
+ switch (logItem.getErrorType()) {
+ case SHAPESHIFTER:
+ VpnProfile profile = VpnStatus.getLastConnectedVpnProfile();
+ if (profile == null) {
+ EipCommand.startVPN(context.getApplicationContext(), false, 0);
+ } else {
+ GatewaysManager gatewaysManager = new GatewaysManager(context.getApplicationContext());
+ int position = gatewaysManager.getPosition(profile);
+ setupNClosestGateway.set(position >= 0 ? position : 0);
+ selectNextGateway();
+ }
+ break;
+ default:
+ break;
+
+ }
+ }
+ }
+}
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 f3eea415..1df54e6e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -32,16 +32,16 @@ import java.util.Set;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ConfigParser;
import de.blinkt.openvpn.core.connection.Connection;
-import se.leap.bitmaskclient.utils.PreferenceHelper;
-
-import static se.leap.bitmaskclient.Constants.HOST;
-import static se.leap.bitmaskclient.Constants.IP_ADDRESS;
-import static se.leap.bitmaskclient.Constants.LOCATION;
-import static se.leap.bitmaskclient.Constants.LOCATIONS;
-import static se.leap.bitmaskclient.Constants.NAME;
-import static se.leap.bitmaskclient.Constants.OPENVPN_CONFIGURATION;
-import static se.leap.bitmaskclient.Constants.TIMEZONE;
-import static se.leap.bitmaskclient.Constants.VERSION;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+
+import static se.leap.bitmaskclient.base.models.Constants.HOST;
+import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS;
+import static se.leap.bitmaskclient.base.models.Constants.LOCATION;
+import static se.leap.bitmaskclient.base.models.Constants.LOCATIONS;
+import static se.leap.bitmaskclient.base.models.Constants.NAME;
+import static se.leap.bitmaskclient.base.models.Constants.OPENVPN_CONFIGURATION;
+import static se.leap.bitmaskclient.base.models.Constants.TIMEZONE;
+import static se.leap.bitmaskclient.base.models.Constants.VERSION;
/**
* Gateway provides objects defining gateways and their metadata.
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
index 0ba0f207..33fd3c21 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
@@ -8,7 +8,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
-import static se.leap.bitmaskclient.utils.ConfigHelper.getCurrentTimezone;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.getCurrentTimezone;
public class GatewaySelector {
private final static String TAG = GatewaySelector.class.getSimpleName();
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
index 354fd9a3..a5d4c416 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -35,15 +35,15 @@ import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ConfigParser;
import de.blinkt.openvpn.core.VpnStatus;
import de.blinkt.openvpn.core.connection.Connection;
-import se.leap.bitmaskclient.Provider;
-import se.leap.bitmaskclient.ProviderObservable;
+import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.base.models.ProviderObservable;
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.Constants.GATEWAYS;
-import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY;
-import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
-import static se.leap.bitmaskclient.utils.PreferenceHelper.getUsePluggableTransports;
+import static se.leap.bitmaskclient.base.models.Constants.GATEWAYS;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUsePluggableTransports;
/**
* @author parmegv
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 9a3c8f85..e6905448 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
@@ -6,7 +6,7 @@ import android.net.VpnService;
import android.os.Build;
import android.os.Bundle;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_BLOCKING_VPN;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START_BLOCKING_VPN;
public class VoidVpnLauncher extends Activity {
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 78deea0b..77038492 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
@@ -33,14 +33,13 @@ import java.util.Observer;
import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.VpnNotificationManager;
-
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_BLOCKING_VPN;
-import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP_BLOCKING_VPN;
-import static se.leap.bitmaskclient.Constants.EIP_IS_ALWAYS_ON;
-import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES;
-import static se.leap.bitmaskclient.utils.ConfigHelper.getProviderFormattedString;
+
+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_STOP_BLOCKING_VPN;
+import static se.leap.bitmaskclient.base.models.Constants.EIP_IS_ALWAYS_ON;
+import static se.leap.bitmaskclient.base.models.Constants.SHARED_PREFERENCES;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
public class VoidVpnService extends VpnService implements Observer, VpnNotificationManager.VpnServiceCallback {
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
index 83904729..c747b731 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
@@ -22,7 +22,7 @@ import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Date;
-import se.leap.bitmaskclient.utils.ConfigHelper;
+import se.leap.bitmaskclient.base.utils.ConfigHelper;
public class VpnCertificateValidator {
public final static String TAG = VpnCertificateValidator.class.getSimpleName();
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
index 08e219c7..51069d6d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -30,21 +30,21 @@ import java.util.Iterator;
import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.ConfigParser;
import de.blinkt.openvpn.core.connection.Connection;
-import se.leap.bitmaskclient.Provider;
+import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.pluggableTransports.Obfs4Options;
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.Constants.CAPABILITIES;
-import static se.leap.bitmaskclient.Constants.IP_ADDRESS;
-import static se.leap.bitmaskclient.Constants.OPTIONS;
-import static se.leap.bitmaskclient.Constants.PORTS;
-import static se.leap.bitmaskclient.Constants.PROTOCOLS;
-import static se.leap.bitmaskclient.Constants.PROVIDER_PRIVATE_KEY;
-import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE;
-import static se.leap.bitmaskclient.Constants.REMOTE;
-import static se.leap.bitmaskclient.Constants.TRANSPORT;
-import static se.leap.bitmaskclient.Constants.TYPE;
+import static se.leap.bitmaskclient.base.models.Constants.CAPABILITIES;
+import static se.leap.bitmaskclient.base.models.Constants.IP_ADDRESS;
+import static se.leap.bitmaskclient.base.models.Constants.OPTIONS;
+import static se.leap.bitmaskclient.base.models.Constants.PORTS;
+import static se.leap.bitmaskclient.base.models.Constants.PROTOCOLS;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_PRIVATE_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_VPN_CERTIFICATE;
+import static se.leap.bitmaskclient.base.models.Constants.REMOTE;
+import static se.leap.bitmaskclient.base.models.Constants.TRANSPORT;
+import static se.leap.bitmaskclient.base.models.Constants.TYPE;
import static se.leap.bitmaskclient.pluggableTransports.Dispatcher.DISPATCHER_IP;
import static se.leap.bitmaskclient.pluggableTransports.Dispatcher.DISPATCHER_PORT;
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java
new file mode 100644
index 00000000..b3ed5394
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnNotificationManager.java
@@ -0,0 +1,355 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.annotation.TargetApi;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+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 de.blinkt.openvpn.LaunchVPN;
+import de.blinkt.openvpn.core.ConnectionStatus;
+import de.blinkt.openvpn.core.OpenVPNService;
+import se.leap.bitmaskclient.base.MainActivity;
+import se.leap.bitmaskclient.R;
+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 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.models.Constants.ASK_TO_CANCEL_VPN;
+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.
+ */
+
+public class VpnNotificationManager {
+
+ 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 = "";
+
+ public interface VpnServiceCallback {
+ void onNotificationBuild(int notificationId, Notification notification);
+ void onNotificationStop();
+ }
+
+ public VpnNotificationManager(@NonNull Context context, @NonNull VpnServiceCallback vpnServiceCallback) {
+ 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) {
+ //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());
+
+ buildVpnNotification(
+ context.getString(R.string.void_vpn_title),
+ msg,
+ null,
+ tickerText,
+ status,
+ VoidVpnService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
+ PRIORITY_MAX,
+ 0,
+ getMainActivityIntent(),
+ actionBuilder.build());
+ }
+
+ 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);
+ }
+ }
+
+ /**
+ * @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) {
+ String cancelString;
+ CharSequence bigmessage = null;
+ String ghostIcon = new String(Character.toChars(0x1f309));
+
+ switch (status) {
+ // show cancel if no connection
+ case LEVEL_START:
+ case LEVEL_NONETWORK:
+ case LEVEL_CONNECTING_SERVER_REPLIED:
+ case LEVEL_CONNECTING_NO_SERVER_REPLY_YET:
+ cancelString = context.getString(R.string.cancel);
+ if (isObfuscated && Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
+ Spannable spannable = new SpannableString(context.getString(R.string.obfuscated_connection_try));
+ spannable.setSpan(new StyleSpan(Typeface.ITALIC), 0, spannable.length() -1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ bigmessage = TextUtils.concat(spannable, " " + ghostIcon + "\n" + msg);
+ }
+ break;
+
+ // show disconnect if connection exists
+ case LEVEL_CONNECTED:
+ if (isObfuscated && Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
+ Spannable spannable = new SpannableString(context.getString(R.string.obfuscated_connection));
+ spannable.setSpan(new StyleSpan(Typeface.ITALIC), 0, spannable.length() -1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ bigmessage = TextUtils.concat(spannable, " " + ghostIcon + "\n" + msg);
+ }
+ default:
+ cancelString = context.getString(R.string.cancel_connection);
+ }
+
+ if (isObfuscated) {
+ msg = ghostIcon + " " + msg;
+ }
+
+ NotificationCompat.Action.Builder actionBuilder = new NotificationCompat.Action.
+ Builder(R.drawable.ic_menu_close_clear_cancel, cancelString, getDisconnectIntent());
+ String title;
+ String appName = context.getString(R.string.app_name);
+ if (isEmpty(profileName)) {
+ title = appName;
+ } else {
+ title = context.getString(R.string.notifcation_title_bitmask, appName, profileName);
+ }
+
+ PendingIntent contentIntent;
+ if (status == LEVEL_WAITING_FOR_USER_INPUT)
+ 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,
+ bigmessage,
+ tickerText,
+ status,
+ notificationChannelNewstatusId,
+ priority,
+ when,
+ contentIntent,
+ actionBuilder.build());
+ }
+
+
+ @TargetApi(O)
+ public void createVoidVpnNotificationChannel() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ return;
+ }
+
+ // 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);
+ }
+
+ @TargetApi(O)
+ public void createOpenVpnNotificationChannel() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ 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);
+ }
+
+ /**
+ * @return a custom remote view for notifications for API 16 - 19
+ */
+ private RemoteViews getKitkatCustomRemoteView(ConnectionStatus status, String title, String message) {
+ int iconResource = getIconByConnectionStatus(status);
+ RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.v_custom_notification);
+ remoteViews.setImageViewResource(R.id.image_icon, iconResource);
+ remoteViews.setTextViewText(R.id.message, message);
+ remoteViews.setTextViewText(R.id.title, title);
+
+ 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) {
+ NotificationCompat.Builder nCompatBuilder = new NotificationCompat.Builder(context, notificationChannelNewstatusId);
+ int icon = getIconByConnectionStatus(status);
+
+ // this is a workaround to avoid confusion between the Android's system vpn notification
+ // showing a filled out key icon and the bitmask icon indicating a different state.
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT &&
+ notificationChannelNewstatusId.equals(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID)) {
+ if (status != LEVEL_NONETWORK) {
+ // removes the icon from the system status bar
+ icon = android.R.color.transparent;
+ // adds the icon to the notification in the notification drawer
+ nCompatBuilder.setContent(getKitkatCustomRemoteView(status, title, message));
+ }
+ } else {
+ nCompatBuilder.setStyle(new NotificationCompat.BigTextStyle().
+ setBigContentTitle(title).
+ bigText(bigMessage));
+ }
+ nCompatBuilder.addAction(notificationAction);
+ nCompatBuilder.setContentTitle(title);
+ nCompatBuilder.setCategory(NotificationCompat.CATEGORY_SERVICE);
+ nCompatBuilder.setLocalOnly(true);
+ nCompatBuilder.setContentText(message);
+ nCompatBuilder.setOnlyAlertOnce(true);
+ nCompatBuilder.setSmallIcon(icon);
+ nCompatBuilder.setPriority(priority);
+ nCompatBuilder.setOngoing(true);
+ nCompatBuilder.setUsesChronometer(true);
+ nCompatBuilder.setWhen(when);
+ nCompatBuilder.setContentIntent(contentIntent);
+ if (!isEmpty(tickerText)) {
+ nCompatBuilder.setTicker(tickerText);
+ }
+
+ 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;
+ }
+
+ private PendingIntent getMainActivityIntent() {
+ Intent startActivity = new Intent(context, StartActivity.class);
+ return PendingIntent.getActivity(context, 0, startActivity, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
+ private PendingIntent getStopVoidVpnIntent() {
+ Intent stopVoidVpnIntent = new Intent (context, VoidVpnService.class);
+ stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN);
+ return PendingIntent.getService(context, 0, stopVoidVpnIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
+ private PendingIntent getDisconnectIntent() {
+ Intent disconnectVPN = new Intent(context, MainActivity.class);
+ disconnectVPN.setAction(ACTION_SHOW_VPN_FRAGMENT);
+ disconnectVPN.putExtra(ASK_TO_CANCEL_VPN, true);
+ disconnectVPN.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ return PendingIntent.getActivity(context, 0, disconnectVPN, PendingIntent.FLAG_CANCEL_CURRENT);
+ }
+
+ private PendingIntent getUserInputIntent(String needed) {
+ Intent intent = new Intent(context, LaunchVPN.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ intent.putExtra("need", needed);
+ Bundle b = new Bundle();
+ b.putString("need", needed);
+ PendingIntent pIntent = PendingIntent.getActivity(context, 12, intent, 0);
+ return pIntent;
+ }
+
+ private int getIconByConnectionStatus(ConnectionStatus level) {
+ switch (level) {
+ case LEVEL_CONNECTED:
+ return R.drawable.ic_stat_vpn;
+ case LEVEL_AUTH_FAILED:
+ case LEVEL_NONETWORK:
+ case LEVEL_NOTCONNECTED:
+ return R.drawable.ic_stat_vpn_offline;
+ case LEVEL_CONNECTING_NO_SERVER_REPLY_YET:
+ case LEVEL_WAITING_FOR_USER_INPUT:
+ case LEVEL_CONNECTING_SERVER_REPLIED:
+ return R.drawable.ic_stat_vpn_outline;
+ case LEVEL_BLOCKING:
+ return R.drawable.ic_stat_vpn_blocking;
+ case UNKNOWN_LEVEL:
+ default:
+ return R.drawable.ic_stat_vpn_offline;
+ }
+ }
+
+}