diff options
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.java | 235 |
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); } } |