summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2017-12-12 15:05:30 +0100
committercyBerta <cyberta@riseup.net>2017-12-12 15:05:30 +0100
commita60fa8124fc8c8cfc80ced0a8faa62abb39075f5 (patch)
treecd00598c580c25c4b35262505d0a06ad82f13fb6
parent2852e3c1ada994b3ca2aff4a19e98c16ad540a25 (diff)
#8742 add notifications for blocking vpn
-rw-r--r--app/build.gradle5
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java47
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/VpnFragment.java23
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Constants.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java14
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java8
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java157
-rw-r--r--build.gradle3
9 files changed, 226 insertions, 38 deletions
diff --git a/app/build.gradle b/app/build.gradle
index cdeccb6c..718e9133 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -75,8 +75,9 @@ dependencies {
compile 'org.thoughtcrime.ssl.pinning:AndroidPinning:1.0.0'
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'mbanje.kurt:fabbutton:1.1.4'
- compile 'com.android.support:support-annotations:25.3.1'
- compile 'com.android.support:support-v4:26.0.0-alpha1'
+ compile "com.android.support:support-core-utils:26.1.0"
+ compile 'com.android.support:support-annotations:26.1.0'
+ compile 'com.android.support:support-v4:26.1.0'
}
def processFileInplace(file, Closure processText) {
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 86e1bb02..f52c30d9 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -243,8 +243,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
//noinspection NewApi
nbuilder.setChannelId(channel);
if (mProfile != null)
- //noinspection NewApi
- nbuilder.setShortcutId(mProfile.getUUIDString());
+ //noinspection NewApi
+ nbuilder.setShortcutId(mProfile.getUUIDString());
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
index 953a559d..d7f574b2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
+++ b/app/src/main/java/se/leap/bitmaskclient/BitmaskApp.java
@@ -1,6 +1,16 @@
package se.leap.bitmaskclient;
+import android.annotation.TargetApi;
import android.app.Application;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Build;
+
+import de.blinkt.openvpn.core.OpenVPNService;
+
+import static android.os.Build.VERSION_CODES.O;
/**
* Created by cyberta on 24.10.17.
@@ -13,5 +23,42 @@ public class BitmaskApp extends Application {
super.onCreate();
PRNGFixes.apply();
//TODO: add LeakCanary!
+ if (Build.VERSION.SDK_INT >= O)
+ createNotificationChannelsForOpenvpn();
}
+
+
+ @TargetApi(O)
+ private void createNotificationChannelsForOpenvpn() {
+ NotificationManager mNotificationManager =
+ (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // Background message
+ CharSequence name = getString(R.string.channel_name_background);
+ NotificationChannel mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_BG_ID,
+ name, NotificationManager.IMPORTANCE_MIN);
+
+ mChannel.setDescription(getString(R.string.channel_description_background));
+ mChannel.enableLights(false);
+
+ mChannel.setLightColor(Color.DKGRAY);
+ mNotificationManager.createNotificationChannel(mChannel);
+
+ // Connection status change messages
+
+ name = getString(R.string.channel_name_status);
+ mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID,
+ name, NotificationManager.IMPORTANCE_DEFAULT);
+
+
+ mChannel.setDescription(getString(R.string.channel_description_status));
+ mChannel.enableLights(true);
+
+ mChannel.setLightColor(Color.BLUE);
+ mNotificationManager.createNotificationChannel(mChannel);
+
+ }
+
+
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java
index 57c066aa..77aa17c1 100644
--- a/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/VpnFragment.java
@@ -35,15 +35,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
-import org.jetbrains.annotations.NotNull;
-
import java.util.Observable;
import java.util.Observer;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
-import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.IOpenVPNServiceInternal;
import de.blinkt.openvpn.core.OpenVPNService;
import de.blinkt.openvpn.core.ProfileManager;
@@ -55,11 +52,7 @@ import se.leap.bitmaskclient.eip.EipStatus;
import se.leap.bitmaskclient.eip.VoidVpnService;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_NONETWORK;
-import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.BLOCKING;
-import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.CONNECTED;
-import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.CONNECTING;
-import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.DISCONNECTED;
-import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.DISCONNECTING;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_STOP_BLOCKING_VPN;
public class VpnFragment extends Fragment implements Observer {
@@ -160,7 +153,7 @@ public class VpnFragment extends Fragment implements Observer {
handleSwitchOff();
else
handleSwitchOn();
-
+ //FIXME ONBOOT IS BROKEN!
saveStatus(eip_status.isConnected() || eip_status.isConnecting());
}
@@ -197,6 +190,7 @@ public class VpnFragment extends Fragment implements Observer {
} else if (eip_status.isConnected()) {
askToStopEIP();
} else if (eip_status.isBlocking()) {
+ //FIXME DEAD CODE
stop();
} else {
updateIcon();
@@ -230,14 +224,19 @@ public class VpnFragment extends Fragment implements Observer {
}
private void stop() {
-
if (eip_status.isBlockingVpnEstablished()) {
- Log.d(TAG, "stop VoidVpn!");
- VoidVpnService.stop();
+ stopBlockingVpn();
}
disconnect();
}
+ private void stopBlockingVpn() {
+ Log.d(TAG, "stop VoidVpn!");
+ Intent stopVoidVpnIntent = new Intent(dashboard, VoidVpnService.class);
+ stopVoidVpnIntent.setAction(ACTION_STOP_BLOCKING_VPN);
+ dashboard.startService(stopVoidVpnIntent);
+ }
+
private void disconnect() {
ProfileManager.setConntectedVpnProfileDisconnected(dashboard);
if (mService != null) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java b/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
index ed4ebcbc..449c111d 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
@@ -31,6 +31,8 @@ public interface Constants {
public final static String ACTION_STOP_EIP = TAG + ".STOP_EIP";
public final static String ACTION_UPDATE_EIP_SERVICE = TAG + ".UPDATE_EIP_SERVICE";
public final static String ACTION_IS_EIP_RUNNING = TAG + ".IS_RUNNING";
+ public final static String ACTION_START_BLOCKING_VPN = TAG + ".ACTION_START_BLOCKING_VPN";
+ public final static String ACTION_STOP_BLOCKING_VPN = TAG + ".ACTION_STOP_BLOCKING_VPN";
public final static String EIP_NOTIFICATION = TAG + ".EIP_NOTIFICATION";
public final static String ALLOWED_ANON = "allow_anonymous";
public final static String ALLOWED_REGISTERED = "allow_registration";
@@ -39,7 +41,6 @@ public interface Constants {
public final static String KEY = TAG + ".KEY";
public final static String RECEIVER_TAG = TAG + ".RECEIVER_TAG";
public final static String REQUEST_TAG = TAG + ".REQUEST_TAG";
- public final static String START_BLOCKING_VPN_PROFILE = TAG + ".START_BLOCKING_VPN_PROFILE";
public final static String PROVIDER_CONFIGURED = TAG + ".PROVIDER_CONFIGURED";
public final static String IS_ALWAYS_ON = TAG + ".IS_ALWAYS_ON";
public final static String RESTART_ON_BOOT = TAG + ".RESTART_ON_BOOT";
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 dc2e81f5..b1d7c994 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
@@ -66,12 +66,17 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
@Override
public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
+ 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
@@ -80,7 +85,6 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
private void setEipLevel(ConnectionStatus level) {
- EipLevel tmp = current_eip_level;
switch (level) {
case LEVEL_CONNECTED:
current_eip_level = EipLevel.CONNECTED;
@@ -105,10 +109,6 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
current_eip_level = EipLevel.UNKNOWN; //??
break;
}
- if (tmp != current_eip_level) {
- current_status.setChanged();
- current_status.notifyObservers();
- }
}
@VisibleForTesting
@@ -117,8 +117,8 @@ public class EipStatus extends Observable implements VpnStatus.StateListener {
}
/**
- * This method intends to ignore states that are valid for less than a second.
- * This way flickering UI changes can be avoided
+ * 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
*/
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 5c9263b3..27e2d95e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
@@ -28,8 +28,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.START_BLOCKING_VPN_PROFILE);
- startService(void_vpn_service);
+ void_vpn_service.setAction(Constants.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 46c010ca..e5e50f6e 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
@@ -1,35 +1,61 @@
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.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.IOException;
+import java.util.Observable;
+import java.util.Observer;
import de.blinkt.openvpn.core.ConnectionStatus;
import de.blinkt.openvpn.core.VpnStatus;
+import se.leap.bitmaskclient.Dashboard;
import se.leap.bitmaskclient.R;
+import static android.os.Build.VERSION_CODES.O;
import static se.leap.bitmaskclient.Dashboard.SHARED_PREFERENCES;
import static se.leap.bitmaskclient.eip.Constants.ACTION_START_ALWAYS_ON_EIP;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_STOP_BLOCKING_VPN;
-public class VoidVpnService extends VpnService {
+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";
- private static final String STATE_STOP = "STOPVOIDVPN";
+ public static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "bitmask_void_vpn_news";
+ private EipStatus eipStatus;
+ NotificationManager notificationManager;
+ NotificationManagerCompat compatNotificationManager;
@Override
- public int onStartCommand(final Intent intent, int flags, int startId) {
+ 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.equals(Constants.START_BLOCKING_VPN_PROFILE)) {
+ if (action.equals(Constants.ACTION_START_BLOCKING_VPN)) {
thread = new Thread(new Runnable() {
public void run() {
establishBlockingVpn();
@@ -51,6 +77,8 @@ public class VoidVpnService extends VpnService {
}
});
thread.run();
+ } else if (action.equals(ACTION_STOP_BLOCKING_VPN)) {
+ stop();
}
return START_STICKY;
}
@@ -61,12 +89,28 @@ 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();
- VpnStatus.updateStateString(STATE_STOP, "",
- R.string.void_vpn_stopped, ConnectionStatus.LEVEL_NOTCONNECTED);
}
public static boolean isRunning() throws NullPointerException {
@@ -114,12 +158,101 @@ public class VoidVpnService extends VpnService {
private void requestVpnWithLastSelectedProfile() {
Intent startEIP = new Intent(getApplicationContext(), EIP.class);
startEIP.setAction(ACTION_START_ALWAYS_ON_EIP);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- //noinspection NewApi
- getApplicationContext().startForegroundService(startEIP);
+ 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 {
- getApplicationContext().startService(startEIP);
+ 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(Constants.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;
+ }
+ }
}
diff --git a/build.gradle b/build.gradle
index dc2396d7..7917b9d6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,5 +16,8 @@ allprojects {
repositories {
mavenCentral()
jcenter()
+ maven {
+ url "https://maven.google.com"
+ }
}
}