summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java235
1 files changed, 167 insertions, 68 deletions
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 f9cb9a86..2917bce1 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2014 Arne Schwabe
+ * Copyright (c) 2012-2016 Arne Schwabe
* Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
*/
@@ -10,15 +10,18 @@ import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.UiModeManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.net.ConnectivityManager;
import android.net.VpnService;
import android.os.Binder;
import android.os.Build;
+import android.os.Handler;
import android.os.Handler.Callback;
import android.os.IBinder;
import android.os.Message;
@@ -27,7 +30,9 @@ import android.preference.PreferenceManager;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
+import android.widget.Toast;
+import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Inet6Address;
@@ -81,6 +86,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
private String mLastTunCfg;
private String mRemoteGW;
private final Object mProcessLock = new Object();
+ private Handler guiHandler;
+ private Toast mlastToast;
+ private Runnable mOpenVPNThread;
// From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
public static String humanReadableByteCount(long bytes, boolean mbit) {
@@ -109,7 +117,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
@Override
public void onRevoke() {
- mManagement.stopVPN();
+ VpnStatus.logInfo(R.string.permission_revoked);
+ mManagement.stopVPN(false);
endVpnService();
}
@@ -125,6 +134,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
VpnStatus.removeByteCountListener(this);
unregisterDeviceStateReceiver();
ProfileManager.setConntectedVpnProfileDisconnected(this);
+ mOpenVPNThread = null;
if (!mStarting) {
stopForeground(!mNotificationAlwaysVisible);
@@ -135,7 +145,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
}
- private void showNotification(String msg, String tickerText, boolean lowpriority, long when, ConnectionStatus status) {
+ private void showNotification(final String msg, String tickerText, boolean lowpriority, long when, ConnectionStatus status) {
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
@@ -164,6 +174,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
jbNotificationExtras(lowpriority, nbuilder);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ lpNotificationExtras(nbuilder);
+
if (tickerText != null && !tickerText.equals(""))
nbuilder.setTicker(tickerText);
@@ -172,7 +185,34 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
mNotificationManager.notify(OPENVPN_STATUS, notification);
- //startForeground(OPENVPN_STATUS, notification);
+ // startForeground(OPENVPN_STATUS, notification);
+
+ // Check if running on a TV
+ if (runningOnAndroidTV() && !lowpriority)
+ guiHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+
+ if (mlastToast != null)
+ mlastToast.cancel();
+ String toastText = String.format(Locale.getDefault(), "%s - %s", mProfile.mName, msg);
+ mlastToast = Toast.makeText(getBaseContext(), toastText, Toast.LENGTH_SHORT);
+ mlastToast.show();
+ }
+ });
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private void lpNotificationExtras(Notification.Builder nbuilder) {
+ nbuilder.setCategory(Notification.CATEGORY_SERVICE);
+ nbuilder.setLocalOnly(true);
+
+ }
+
+ private boolean runningOnAndroidTV() {
+ UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
+ return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
}
private int getIconByConnectionStatus(ConnectionStatus level) {
@@ -215,20 +255,20 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
disconnectVPN.setAction(DISCONNECT_VPN);
PendingIntent disconnectPendingIntent = PendingIntent.getActivity(this, 0, disconnectVPN, 0);
- nbuilder.addAction(android.R.drawable.ic_menu_close_clear_cancel,
+ nbuilder.addAction(R.drawable.ic_menu_close_clear_cancel,
getString(R.string.cancel_connection), disconnectPendingIntent);
Intent pauseVPN = new Intent(this, OpenVPNService.class);
if (mDeviceStateReceiver == null || !mDeviceStateReceiver.isUserPaused()) {
pauseVPN.setAction(PAUSE_VPN);
PendingIntent pauseVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0);
- nbuilder.addAction(android.R.drawable.ic_media_pause,
+ nbuilder.addAction(R.drawable.ic_menu_pause,
getString(R.string.pauseVPN), pauseVPNPending);
} else {
pauseVPN.setAction(RESUME_VPN);
PendingIntent resumeVPNPending = PendingIntent.getService(this, 0, pauseVPN, 0);
- nbuilder.addAction(android.R.drawable.ic_media_play,
+ nbuilder.addAction(R.drawable.ic_menu_play,
getString(R.string.resumevpn), resumeVPNPending);
}
@@ -297,6 +337,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
VpnStatus.addStateListener(this);
VpnStatus.addByteCountListener(this);
+ guiHandler = new Handler(getMainLooper());
+
+
if (intent != null && PAUSE_VPN.equals(intent.getAction())) {
if (mDeviceStateReceiver != null)
mDeviceStateReceiver.userPause(true);
@@ -338,38 +381,50 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
mProfile = ProfileManager.get(this, profileUUID);
}
+ /* start the OpenVPN process itself in a background thread */
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ startOpenVPN();
+ }
+ }).start();
+
+
+ ProfileManager.setConnectedVpnProfile(this, mProfile);
+ /* TODO: At the moment we have no way to handle asynchronous PW input
+ * Fixing will also allow to handle challenge/response authentication */
+ if (mProfile.needUserPWInput(true) != 0)
+ return START_NOT_STICKY;
+
+ return START_STICKY;
+ }
+
+ private void startOpenVPN() {
+ VpnStatus.logInfo(R.string.building_configration);
+ VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, VpnStatus.ConnectionStatus.LEVEL_START);
+
+
+ try {
+ mProfile.writeConfigFile(this);
+ } catch (IOException e) {
+ VpnStatus.logException("Error writing config file", e);
+ endVpnService();
+ return;
+ }
// Extract information from the intent.
String prefix = getPackageName();
- String[] argv = intent.getStringArrayExtra(prefix + ".ARGV");
- String nativeLibraryDirectory = intent.getStringExtra(prefix + ".nativelib");
+ String nativeLibraryDirectory = getApplicationInfo().nativeLibraryDir;
+
+ // Also writes OpenVPN binary
+ String[] argv = VPNLaunchHelper.buildOpenvpnArgv(this);
- String startTitle = getString(R.string.start_vpn_title, mProfile.mName);
- String startTicker = getString(R.string.start_vpn_ticker, mProfile.mName);
- showNotification(startTitle, startTicker,
- false, 0, LEVEL_CONNECTING_NO_SERVER_REPLY_YET);
// Set a flag that we are starting a new VPN
mStarting = true;
// Stop the previous session by interrupting the thread.
- if (mManagement != null && mManagement.stopVPN())
- // an old was asked to exit, wait 1s
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- //ignore
- }
- synchronized (mProcessLock) {
- if (mProcessThread != null) {
- mProcessThread.interrupt();
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- //ignore
- }
- }
- }
+ stopOldOpenVPNProcess();
// An old running VPN should now be exited
mStarting = false;
@@ -380,10 +435,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
if (!"ovpn3".equals(BuildConfig.FLAVOR))
mOvpn3 = false;
-
// Open the Management Interface
if (!mOvpn3) {
-
// start a Thread that handles incoming messages of the managment socket
OpenVpnManagementThread ovpnManagementThread = new OpenVpnManagementThread(mProfile, this);
if (ovpnManagementThread.openManagementInterface(this)) {
@@ -393,13 +446,15 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
mManagement = ovpnManagementThread;
VpnStatus.logInfo("started Socket Thread");
} else {
- return START_NOT_STICKY;
+ endVpnService();
+ return;
}
}
-
Runnable processThread;
- if (mOvpn3) {
+ if (mOvpn3)
+
+ {
OpenVPNManagement mOpenVPN3 = instantiateOpenVPN3Core();
processThread = (Runnable) mOpenVPN3;
@@ -409,25 +464,53 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
} else {
HashMap<String, String> env = new HashMap<>();
processThread = new OpenVPNThread(this, argv, env, nativeLibraryDirectory);
+ mOpenVPNThread = processThread;
}
- synchronized (mProcessLock) {
+ synchronized (mProcessLock)
+
+ {
mProcessThread = new Thread(processThread, "OpenVPNProcessThread");
mProcessThread.start();
}
- if (mDeviceStateReceiver != null)
- unregisterDeviceStateReceiver();
- registerDeviceStateReceiver(mManagement);
+ new Handler(getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (mDeviceStateReceiver != null)
+ unregisterDeviceStateReceiver();
+ registerDeviceStateReceiver(mManagement);
+ }
+ }
- ProfileManager.setConnectedVpnProfile(this, mProfile);
- /* TODO: At the moment we have no way to handle asynchronous PW input
- * Fixing will also allow to handle challenge/response authentication */
- if (mProfile.needUserPWInput(true) != 0)
- return START_NOT_STICKY;
+ );
+ }
- return START_STICKY;
+ private void stopOldOpenVPNProcess() {
+ if (mManagement != null) {
+ if (mOpenVPNThread!=null)
+ ((OpenVPNThread) mOpenVPNThread).setReplaceConnection();
+ if (mManagement.stopVPN(true)) {
+ // an old was asked to exit, wait 1s
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ //ignore
+ }
+ }
+ }
+
+ synchronized (mProcessLock) {
+ if (mProcessThread != null) {
+ mProcessThread.interrupt();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ //ignore
+ }
+ }
+ }
}
private OpenVPNManagement instantiateOpenVPN3Core() {
@@ -435,7 +518,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
Class cl = Class.forName("de.blinkt.openvpn.core.OpenVPNThreadv3");
return (OpenVPNManagement) cl.getConstructor(OpenVPNService.class, VpnProfile.class).newInstance(this, mProfile);
} catch (IllegalArgumentException | InstantiationException | InvocationTargetException |
- NoSuchMethodException | ClassNotFoundException | IllegalAccessException e ) {
+ NoSuchMethodException | ClassNotFoundException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
@@ -445,7 +528,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
public void onDestroy() {
synchronized (mProcessLock) {
if (mProcessThread != null) {
- mManagement.stopVPN();
+ mManagement.stopVPN(true);
}
}
@@ -454,6 +537,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
// Just in case unregister for state
VpnStatus.removeStateListener(this);
+ VpnStatus.flushLog();
}
@@ -536,6 +620,26 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
Collection<ipAddress> positiveIPv4Routes = mRoutes.getPositiveIPList();
Collection<ipAddress> positiveIPv6Routes = mRoutesv6.getPositiveIPList();
+ if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mDnslist.size() >= 1) {
+ // Check if the first DNS Server is in the VPN range
+ try {
+ ipAddress dnsServer = new ipAddress(new CIDRIP(mDnslist.get(0), 32), true);
+ boolean dnsIncluded = false;
+ for (ipAddress net : positiveIPv4Routes) {
+ if (net.containsNet(dnsServer)) {
+ dnsIncluded = true;
+ }
+ }
+ if (!dnsIncluded) {
+ String samsungwarning = String.format("Warning Samsung Android 5.0+ devices ignore DNS servers outside the VPN range. To enable DNS resolution a route to your DNS Server (%s) has been added.", mDnslist.get(0));
+ VpnStatus.logWarning(samsungwarning);
+ positiveIPv4Routes.add(dnsServer);
+ }
+ } catch (Exception e) {
+ VpnStatus.logError("Error parsing DNS Server IP: " + mDnslist.get(0));
+ }
+ }
+
ipAddress multicastRange = new ipAddress(new CIDRIP("224.0.0.0", 3), true);
for (NetworkSpace.ipAddress route : positiveIPv4Routes) {
@@ -558,25 +662,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
}
- if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mDnslist.size() >= 1) {
- // Check if the first DNS Server is in the VPN range
- try {
- ipAddress dnsServer = new ipAddress(new CIDRIP(mDnslist.get(0), 32), true);
- boolean dnsIncluded=false;
- for (ipAddress net : positiveIPv4Routes) {
- if (net.containsNet(dnsServer)) {
- dnsIncluded = true;
- }
- }
- if (!dnsIncluded) {
- String samsungwarning = String.format("Warning Samsung Android 5.0+ devices ignore DNS servers outside the VPN range. To enable DNS add a custom route to your DNS Server (%s) or change to a DNS inside your VPN range", mDnslist.get(0));
- VpnStatus.logWarning(samsungwarning);
- }
- } catch (Exception e) {
- VpnStatus.logError("Error parsing DNS Server IP: " + mDnslist.get(0));
- }
- }
-
if (mDomain != null)
builder.addSearchDomain(mDomain);
@@ -672,12 +757,14 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setAllowedVpnPackages(Builder builder) {
+ boolean atLeastOneAllowedApp = false;
for (String pkg : mProfile.mAllowedAppsVpn) {
try {
if (mProfile.mAllowedAppsVpnAreDisallowed) {
builder.addDisallowedApplication(pkg);
} else {
builder.addAllowedApplication(pkg);
+ atLeastOneAllowedApp = true;
}
} catch (PackageManager.NameNotFoundException e) {
mProfile.mAllowedAppsVpn.remove(pkg);
@@ -685,6 +772,15 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
}
+ if (!mProfile.mAllowedAppsVpnAreDisallowed && !atLeastOneAllowedApp) {
+ VpnStatus.logDebug(R.string.no_allowed_app, getPackageName());
+ try {
+ builder.addAllowedApplication(getPackageName());
+ } catch (PackageManager.NameNotFoundException e) {
+ VpnStatus.logError("This should not happen: " + e.getLocalizedMessage());
+ }
+ }
+
if (mProfile.mAllowedAppsVpnAreDisallowed) {
VpnStatus.logDebug(R.string.disallowed_vpn_apps_info, TextUtils.join(", ", mProfile.mAllowedAppsVpn));
} else {
@@ -839,7 +935,9 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
} else if (level == LEVEL_CONNECTED) {
mDisplayBytecount = true;
mConnecttime = System.currentTimeMillis();
- lowpriority = true;
+ if (!runningOnAndroidTV())
+ lowpriority = true;
+
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
mNotificationManager.cancel(OPENVPN_STATUS);
@@ -852,7 +950,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
// CONNECTED
// Does not work :(
String msg = getString(resid);
- // showNotification(msg + " " + logmessage, msg, lowpriority, 0, level);
+ // showNotification(VpnStatus.getLastCleanLogMessage(this),
+ // msg, lowpriority, 0, level);
}
}