diff options
author | cyberta <cyberta@riseup.net> | 2017-12-14 10:19:03 -0800 |
---|---|---|
committer | cyberta <cyberta@riseup.net> | 2017-12-14 10:19:03 -0800 |
commit | 67ff3447f10c43770dc9ee4dccf358321063d131 (patch) | |
tree | cb29df26bec196f8628947897a5fe9977b00a229 /app/src/main/java/se/leap/bitmaskclient/eip | |
parent | 18e24819eed388d349dbf6d7cd21534d7074bf5d (diff) | |
parent | 25d215400d500bdb7537e604ed91ced586821ef2 (diff) |
Merge branch '8742_always-on_VPN' into '0.9.8'
8742 always on vpn
See merge request leap/bitmask_android!20
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/eip')
4 files changed, 416 insertions, 90 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 0b35dc3d..a84ab941 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -19,12 +19,25 @@ package se.leap.bitmaskclient.eip; import android.app.*; import android.content.*; import android.os.*; +import android.util.Log; import org.json.*; import de.blinkt.openvpn.*; import se.leap.bitmaskclient.*; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_CHECK_CERT_VALIDITY; +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_EIP; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_STOP; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_UPDATE; +import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; +import static se.leap.bitmaskclient.Constants.EIP_REQUEST; +import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; +import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; + /** * EIP is the abstract base class for interacting with and managing the Encrypted * Internet Proxy connection. Connections are started, stopped, and queried through @@ -55,9 +68,8 @@ public final class EIP extends IntentService { @Override public void onCreate() { super.onCreate(); - context = getApplicationContext(); - preferences = getSharedPreferences(Constants.SHARED_PREFERENCES, MODE_PRIVATE); + preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); eip_definition = eipDefinitionFromPreferences(); if (gateways_manager.isEmpty()) gatewaysFromPreferences(); @@ -66,17 +78,19 @@ public final class EIP extends IntentService { @Override protected void onHandleIntent(Intent intent) { String action = intent.getAction(); - mReceiver = intent.getParcelableExtra(Constants.EIP_RECEIVER); + mReceiver = intent.getParcelableExtra(EIP_RECEIVER); - if (action.equals(Constants.EIP_ACTION_START)) + if (action.equals(EIP_ACTION_START)) startEIP(); - else if (action.equals(Constants.EIP_ACTION_STOP)) + else if (action.equals(EIP_ACTION_START_ALWAYS_ON_EIP)) + startAlwaysOnEIP(); + else if (action.equals(EIP_ACTION_STOP)) stopEIP(); - else if (action.equals(Constants.EIP_ACTION_IS_RUNNING)) + else if (action.equals(EIP_ACTION_IS_RUNNING)) isRunning(); - else if (action.equals(Constants.EIP_ACTION_UPDATE)) + else if (action.equals(EIP_ACTION_UPDATE)) updateEIPService(); - else if (action.equals(Constants.EIP_ACTION_CHECK_CERT_VALIDITY)) + else if (action.equals(EIP_ACTION_CHECK_CERT_VALIDITY)) checkCertValidity(); } @@ -88,15 +102,38 @@ public final class EIP extends IntentService { private void startEIP() { if (gateways_manager.isEmpty()) updateEIPService(); - earlyRoutes(); + if (!EipStatus.getInstance().isBlockingVpnEstablished()) { + earlyRoutes(); + } gateway = gateways_manager.select(); if (gateway != null && gateway.getProfile() != null) { mReceiver = VpnFragment.getReceiver(); launchActiveGateway(); - tellToReceiver(Constants.EIP_ACTION_START, Activity.RESULT_OK); + tellToReceiver(EIP_ACTION_START, Activity.RESULT_OK); } else - tellToReceiver(Constants.EIP_ACTION_START, Activity.RESULT_CANCELED); + tellToReceiver(EIP_ACTION_START, Activity.RESULT_CANCELED); + } + + /** + * Tries to start the last used vpn profile when the OS was rebooted and always-on-VPN is enabled. + * The {@link OnBootReceiver} will care if there is no profile. + */ + private void startAlwaysOnEIP() { + Log.d(TAG, "startAlwaysOnEIP vpn"); + + if (gateways_manager.isEmpty()) + updateEIPService(); + + gateway = gateways_manager.select(); + + if (gateway != null && gateway.getProfile() != null) { + //mReceiver = VpnFragment.getReceiver(); + Log.d(TAG, "startAlwaysOnEIP eip launch avtive gateway vpn"); + launchActiveGateway(); + } else { + Log.d(TAG, "startAlwaysOnEIP no active profile available!"); + } } /** @@ -124,7 +161,7 @@ public final class EIP extends IntentService { if (eip_status.isConnected() || eip_status.isConnecting()) result_code = Activity.RESULT_OK; - tellToReceiver(Constants.EIP_ACTION_STOP, result_code); + tellToReceiver(EIP_ACTION_STOP, result_code); } /** @@ -137,7 +174,7 @@ public final class EIP extends IntentService { int resultCode = (eip_status.isConnected()) ? Activity.RESULT_OK : Activity.RESULT_CANCELED; - tellToReceiver(Constants.EIP_ACTION_IS_RUNNING, resultCode); + tellToReceiver(EIP_ACTION_IS_RUNNING, resultCode); } /** @@ -148,13 +185,13 @@ public final class EIP extends IntentService { eip_definition = eipDefinitionFromPreferences(); if (eip_definition.length() > 0) updateGateways(); - tellToReceiver(Constants.EIP_ACTION_UPDATE, Activity.RESULT_OK); + tellToReceiver(EIP_ACTION_UPDATE, Activity.RESULT_OK); } private JSONObject eipDefinitionFromPreferences() { JSONObject result = new JSONObject(); try { - String eip_definition_string = preferences.getString(Constants.PROVIDER_KEY, ""); + String eip_definition_string = preferences.getString(PROVIDER_KEY, ""); if (!eip_definition_string.isEmpty()) { result = new JSONObject(eip_definition_string); } @@ -184,17 +221,17 @@ public final class EIP extends IntentService { } private void checkCertValidity() { - VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(Constants.PROVIDER_VPN_CERTIFICATE, "")); + VpnCertificateValidator validator = new VpnCertificateValidator(preferences.getString(PROVIDER_VPN_CERTIFICATE, "")); int resultCode = validator.isValid() ? Activity.RESULT_OK : Activity.RESULT_CANCELED; - tellToReceiver(Constants.EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); + tellToReceiver(EIP_ACTION_CHECK_CERT_VALIDITY, resultCode); } private void tellToReceiver(String action, int resultCode) { if (mReceiver != null) { Bundle resultData = new Bundle(); - resultData.putString(Constants.EIP_REQUEST, action); + resultData.putString(EIP_REQUEST, action); mReceiver.send(resultCode, resultData); } } 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 501543b8..ddf152d2 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java @@ -17,19 +17,37 @@ package se.leap.bitmaskclient.eip; import android.content.*; +import android.os.AsyncTask; +import android.support.annotation.VisibleForTesting; import java.util.*; import de.blinkt.openvpn.core.*; +/** + * EipStatus is a Singleton that represents a reduced set of a vpn's ConnectionStatus. + * EipStatus changes it's state (EipLevel) when ConnectionStatus gets updated by OpenVpnService or + * by VoidVpnService. + */ public class EipStatus extends Observable implements VpnStatus.StateListener { public static String TAG = EipStatus.class.getSimpleName(); private static EipStatus current_status; + public enum EipLevel { + CONNECTING, + DISCONNECTING, + CONNECTED, + DISCONNECTED, + BLOCKING, + UNKNOWN + } - private static ConnectionStatus level = ConnectionStatus.LEVEL_NOTCONNECTED; - private static boolean - wants_to_disconnect = false, - is_connecting = false; + /** + * vpn_level holds the connection status of the openvpn vpn and the traffic blocking + * void vpn. LEVEL_BLOCKING is set when the latter vpn is up. All other states are set by + * openvpn. + */ + private ConnectionStatus vpn_level = ConnectionStatus.LEVEL_NOTCONNECTED; + private static EipLevel current_eip_level = EipLevel.DISCONNECTED; int last_error_line = 0; private String state, log_message; @@ -48,64 +66,137 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { @Override public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) { - updateStatus(state, logmessage, localizedResId, level); - if (isConnected() || isDisconnected() || wantsToDisconnect()) { - setConnectedOrDisconnected(); - } else - setConnecting(); + ConnectionStatus tmp = current_status.getLevel(); + current_status = getInstance(); + current_status.setState(state); + current_status.setLogMessage(logmessage); + current_status.setLocalizedResId(localizedResId); + current_status.setLevel(level); + current_status.setEipLevel(level); + if (tmp != current_status.getLevel()) { + current_status.setChanged(); + current_status.notifyObservers(); + } } @Override public void setConnectedVPN(String uuid) { } - private void updateStatus(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) { - current_status = getInstance(); - current_status.setState(state); - current_status.setLogMessage(logmessage); - current_status.setLocalizedResId(localizedResId); - current_status.setLevel(level); - current_status.setChanged(); + + private void setEipLevel(ConnectionStatus level) { + switch (level) { + case LEVEL_CONNECTED: + current_eip_level = EipLevel.CONNECTED; + break; + case LEVEL_VPNPAUSED: + throw new IllegalStateException("Ics-Openvpn's VPNPAUSED state is not supported by Bitmask"); + case LEVEL_CONNECTING_SERVER_REPLIED: + case LEVEL_CONNECTING_NO_SERVER_REPLY_YET: + case LEVEL_WAITING_FOR_USER_INPUT: + case LEVEL_START: + current_eip_level = EipLevel.CONNECTING; + break; + case LEVEL_AUTH_FAILED: + case LEVEL_NOTCONNECTED: + current_eip_level = EipLevel.DISCONNECTED; + break; + case LEVEL_NONETWORK: + case LEVEL_BLOCKING: + setEipLevelWithDelay(level); + break; + case UNKNOWN_LEVEL: + current_eip_level = EipLevel.UNKNOWN; //?? + break; + } } - public boolean wantsToDisconnect() { - return wants_to_disconnect; + @VisibleForTesting + EipLevel getEipLevel() { + return current_eip_level; } - public boolean isConnecting() { - return is_connecting; + /** + * This is a debouncing method ignoring states that are valid for less than a second. + * This way flickering UI changes can be avoided. + * + * @param futureLevel + */ + private void setEipLevelWithDelay(ConnectionStatus futureLevel) { + new DelayTask(current_status.getLevel(), futureLevel).execute(); } - public boolean isConnected() { - return level == ConnectionStatus.LEVEL_CONNECTED; + private static class DelayTask extends AsyncTask<Void, Void, Void> { + + private final ConnectionStatus currentLevel; + private final ConnectionStatus futureLevel; + + public DelayTask(ConnectionStatus currentLevel, ConnectionStatus futureLevel) { + this.currentLevel = currentLevel; + this.futureLevel = futureLevel; + } + protected Void doInBackground(Void... levels) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.interrupted(); + } + return null; + } + + protected void onPostExecute(Void result) {; + if (currentLevel == current_status.getLevel()) { + switch (futureLevel) { + case LEVEL_NONETWORK: + current_eip_level = EipLevel.DISCONNECTED; + break; + case LEVEL_BLOCKING: + current_eip_level = EipLevel.BLOCKING; + break; + default: + break; + } + current_status.setChanged(); + current_status.notifyObservers(); + } + } } - public boolean isDisconnected() { - return level == ConnectionStatus.LEVEL_NOTCONNECTED; + public boolean isConnecting() { + return current_eip_level == EipLevel.CONNECTING; } - public boolean isPaused() { - return level == ConnectionStatus.LEVEL_VPNPAUSED; + public boolean isConnected() { + return current_eip_level == EipLevel.CONNECTED; } - public void setConnecting() { - is_connecting = true; + /** + * @return true if current_eip_level is for at least a second {@link EipLevel#BLOCKING}. + * See {@link #setEipLevelWithDelay(ConnectionStatus)}. + */ + public boolean isBlocking() { + return current_eip_level == EipLevel.BLOCKING; + } - wants_to_disconnect = false; - current_status.setChanged(); - current_status.notifyObservers(); + /** + * + * @return true immediately after traffic blocking VoidVpn was established. + */ + public boolean isBlockingVpnEstablished() { + return vpn_level == ConnectionStatus.LEVEL_BLOCKING; } - public void setConnectedOrDisconnected() { - is_connecting = false; - wants_to_disconnect = false; - current_status.setChanged(); - current_status.notifyObservers(); + public boolean isDisconnected() { + return current_eip_level == EipLevel.DISCONNECTED; } - public void setDisconnecting() { - wants_to_disconnect = true; - is_connecting = false; + /** + * ics-openvpn's paused state is not implemented yet + * @return + */ + @Deprecated + public boolean isPaused() { + return vpn_level == ConnectionStatus.LEVEL_VPNPAUSED; } public String getState() { @@ -121,7 +212,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { } public ConnectionStatus getLevel() { - return level; + return vpn_level; } private void setState(String state) { @@ -137,7 +228,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { } private void setLevel(ConnectionStatus level) { - EipStatus.level = level; + this.vpn_level = level; } public boolean errorInLast(int lines, Context context) { @@ -169,7 +260,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { @Override public String toString() { - return "State: " + state + " Level: " + level.toString(); + return "State: " + state + " Level: " + vpn_level.toString(); } } 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 b1aab79c..9a3c8f85 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java @@ -1,11 +1,12 @@ package se.leap.bitmaskclient.eip; -import android.app.*; -import android.content.*; -import android.net.*; -import android.os.*; +import android.app.Activity; +import android.content.Intent; +import android.net.VpnService; +import android.os.Build; +import android.os.Bundle; -import se.leap.bitmaskclient.Constants; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_BLOCKING_VPN; public class VoidVpnLauncher extends Activity { @@ -30,8 +31,12 @@ public class VoidVpnLauncher extends Activity { if (requestCode == VPN_USER_PERMISSION) { if (resultCode == RESULT_OK) { Intent void_vpn_service = new Intent(getApplicationContext(), VoidVpnService.class); - void_vpn_service.setAction(Constants.EIP_ACTION_BLOCK_VPN_PROFILE); - startService(void_vpn_service); + 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); + } } } 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 ff375553..792de2cb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java @@ -1,43 +1,88 @@ package se.leap.bitmaskclient.eip; -import android.content.*; -import android.net.*; -import android.os.*; +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.content.SharedPreferences; +import android.graphics.Color; +import android.net.VpnService; +import android.os.Build; +import android.os.ParcelFileDescriptor; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; +import android.util.Log; -import java.io.*; +import java.io.IOException; +import java.util.Observable; +import java.util.Observer; -import se.leap.bitmaskclient.Constants; +import de.blinkt.openvpn.core.ConnectionStatus; +import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.Dashboard; +import se.leap.bitmaskclient.R; -public class VoidVpnService extends VpnService { +import static android.os.Build.VERSION_CODES.O; +import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_EIP; +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; + + +public class VoidVpnService extends VpnService implements Observer { static final String TAG = VoidVpnService.class.getSimpleName(); static ParcelFileDescriptor fd; - static Thread thread; + private final int ALWAYS_ON_MIN_API_LEVEL = Build.VERSION_CODES.N; + private static final String STATE_ESTABLISH = "ESTABLISHVOIDVPN"; + public static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "bitmask_void_vpn_news"; + private EipStatus eipStatus; + NotificationManager notificationManager; + NotificationManagerCompat compatNotificationManager; + + @Override + public void onCreate() { + super.onCreate(); + notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + compatNotificationManager = NotificationManagerCompat.from(this); + eipStatus = EipStatus.getInstance(); + eipStatus.addObserver(this); + } @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent != null ? intent.getAction() : ""; - if (action == Constants.EIP_ACTION_BLOCK_VPN_PROFILE) { + if (action.equals(EIP_ACTION_START_BLOCKING_VPN)) { + thread = new Thread(new Runnable() { + public void run() { + establishBlockingVpn(); + SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + preferences.edit().putBoolean(EIP_IS_ALWAYS_ON, false).commit(); + Log.d(TAG, "start blocking vpn profile - always on = false"); + } + }); + thread.run(); + } else if (action.equals("android.net.VpnService") && Build.VERSION.SDK_INT >= ALWAYS_ON_MIN_API_LEVEL) { + //only always-on feature triggers this thread = new Thread(new Runnable() { public void run() { - Builder builder = new Builder(); - builder.setSession("Blocking until running"); - builder.addAddress("10.42.0.8", 16); - builder.addRoute("0.0.0.0", 1); - builder.addRoute("192.168.1.0", 24); - builder.addDnsServer("10.42.0.1"); - try { - fd = builder.establish(); - - } catch (Exception e) { - e.printStackTrace(); - } + establishBlockingVpn(); + SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + preferences.edit().putBoolean(EIP_IS_ALWAYS_ON, true).commit(); + requestVpnWithLastSelectedProfile(); + Log.d(TAG, "start blocking vpn profile - always on = true"); } }); thread.run(); + } else if (action.equals(EIP_ACTION_STOP_BLOCKING_VPN)) { + stop(); } - return 0; + return START_STICKY; } @Override @@ -46,9 +91,27 @@ public class VoidVpnService extends VpnService { closeFd(); } - public static void stop() { - if (thread != null) + @TargetApi(O) + private void createNotificationChannel() { + + // Connection status change messages + CharSequence name = getString(R.string.channel_name_status); + NotificationChannel mChannel = new NotificationChannel(VoidVpnService.NOTIFICATION_CHANNEL_NEWSTATUS_ID, + name, NotificationManagerCompat.IMPORTANCE_DEFAULT); + + mChannel.setDescription(getString(R.string.channel_description_status)); + mChannel.enableLights(true); + + mChannel.setLightColor(Color.BLUE); + notificationManager.createNotificationChannel(mChannel); + } + + + private void stop() { + stopNotifications(); + if (thread != null) { thread.interrupt(); + } closeFd(); } @@ -64,4 +127,134 @@ public class VoidVpnService extends VpnService { e.printStackTrace(); } } + + private Builder prepareBlockingVpnProfile() { + Builder builder = new Builder(); + builder.setSession("Blocking until running"); + builder.addRoute("0.0.0.0", 1); + builder.addRoute("192.168.1.0", 24); + builder.addDnsServer("10.42.0.1"); + builder.addAddress("10.42.0.8", 16); + return builder; + + } + + private void establishBlockingVpn() { + try { + VpnStatus.logInfo(getString(R.string.void_vpn_establish)); + VpnStatus.updateStateString(STATE_ESTABLISH, "", + R.string.void_vpn_establish, ConnectionStatus.LEVEL_BLOCKING); + Builder builder = prepareBlockingVpnProfile(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder.addDisallowedApplication(getPackageName()); + } + + fd = builder.establish(); + } catch (Exception e) { + // Catch any exception + e.printStackTrace(); + VpnStatus.logError(R.string.void_vpn_error_establish); + } + } + + private void requestVpnWithLastSelectedProfile() { + Intent startEIP = new Intent(getApplicationContext(), EIP.class); + startEIP.setAction(EIP_ACTION_START_ALWAYS_ON_EIP); + getApplicationContext().startService(startEIP); + } + + @Override + public void update(Observable observable, Object arg) { + if (observable instanceof EipStatus) { + eipStatus = (EipStatus) observable; + } + + if (thread == null) { + return; + } + + if (eipStatus.isBlockingVpnEstablished()) { + showNotification(getString(eipStatus.getLocalizedResId()), + getString(eipStatus.getLocalizedResId()), eipStatus.getLevel()); + } else { + stopNotifications(); + } + } + + private void stopNotifications() { + stopForeground(true); + compatNotificationManager.cancel(NOTIFICATION_CHANNEL_NEWSTATUS_ID.hashCode()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + notificationManager.getNotificationChannel(NOTIFICATION_CHANNEL_NEWSTATUS_ID) != null) { + notificationManager.deleteNotificationChannel(NOTIFICATION_CHANNEL_NEWSTATUS_ID); + } + } + + /** + * @param msg + * @param tickerText + * @param status + */ + private void showNotification(final String msg, String tickerText, ConnectionStatus status) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(); + } + + int icon = getIconByConnectionStatus(status); + NotificationCompat.Builder nCompatBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_NEWSTATUS_ID); + + nCompatBuilder.setContentTitle(getString(R.string.notifcation_title_bitmask, getString(R.string.void_vpn_title))); + nCompatBuilder.setCategory(NotificationCompat.CATEGORY_SERVICE); + nCompatBuilder.setLocalOnly(true); + nCompatBuilder.setContentText(msg); + nCompatBuilder.setOnlyAlertOnce(true); + nCompatBuilder.setSmallIcon(icon); + if (tickerText != null && !tickerText.equals("")) { + nCompatBuilder.setTicker(tickerText); + } + + nCompatBuilder.setContentIntent(getDashboardIntent()); + //TODO: implement extra Dashboard.ACTION_ASK_TO_CANCEL_BLOCKING_VPN + NotificationCompat.Action.Builder builder = new NotificationCompat.Action.Builder(R.drawable.ic_menu_close_clear_cancel, getString(R.string.vpn_button_turn_off_blocking), getStopVoidVpnIntent()); + nCompatBuilder.addAction(builder.build()); + + Notification notification = nCompatBuilder.build(); + int notificationId = NOTIFICATION_CHANNEL_NEWSTATUS_ID.hashCode(); + compatNotificationManager.notify(notificationId, notification); + startForeground(notificationId, notification); + } + + private PendingIntent getDashboardIntent() { + Intent startDashboard = new Intent(this, Dashboard.class); + startDashboard.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | + Intent.FLAG_ACTIVITY_SINGLE_TOP); + return PendingIntent.getActivity(this, 0, startDashboard, PendingIntent.FLAG_CANCEL_CURRENT); + } + + private PendingIntent getStopVoidVpnIntent() { + Intent stopVoidVpnIntent = new Intent (this, VoidVpnService.class); + stopVoidVpnIntent.setAction(EIP_ACTION_STOP_BLOCKING_VPN); + return PendingIntent.getService(this, 0, stopVoidVpnIntent, PendingIntent.FLAG_CANCEL_CURRENT); + } + + //TODO: replace with getIconByEipLevel(EipLevel level) + 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; + } + } } |