summaryrefslogtreecommitdiff
path: root/app/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/de/blinkt/openvpn/LaunchVPN.java11
-rw-r--r--app/src/main/java/de/blinkt/openvpn/VpnProfile.java148
-rw-r--r--app/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java27
-rw-r--r--app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java4
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java54
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/Connection.java7
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java83
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java4
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java140
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java25
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java32
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java10
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java108
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java251
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java7
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java2
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java29
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java161
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/X509Utils.java71
-rw-r--r--app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java505
-rw-r--r--app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Dashboard.java18
27 files changed, 1106 insertions, 608 deletions
diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
index 90216a70..721e8991 100644
--- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.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
*/
@@ -20,6 +20,7 @@ import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
+import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
@@ -63,6 +64,8 @@ public class LaunchVPN extends Activity {
public static final String EXTRA_KEY = "de.blinkt.openvpn.shortcutProfileUUID";
public static final String EXTRA_NAME = "de.blinkt.openvpn.shortcutProfileName";
public static final String EXTRA_HIDELOG = "de.blinkt.openvpn.showNoLogWindow";
+ public static final String CLEARLOG = "clearlogconnect";
+
private static final int START_VPN_PROFILE= 70;
@@ -93,6 +96,10 @@ public class LaunchVPN extends Activity {
if(Intent.ACTION_MAIN.equals(action)) {
+ // Check if we need to clear the log
+ if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(CLEARLOG, true))
+ VpnStatus.clearLog();
+
// we got called to be the starting point, most likely a shortcut
String shortcutUUID = intent.getStringExtra( EXTRA_KEY);
String shortcutName = intent.getStringExtra( EXTRA_NAME);
@@ -115,7 +122,7 @@ public class LaunchVPN extends Activity {
}
}
-
+
@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
index 43e1b57c..dbe4b440 100644
--- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.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
*/
@@ -18,6 +18,7 @@ import android.os.Build;
import android.preference.PreferenceManager;
import android.security.KeyChain;
import android.security.KeyChainException;
+import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Base64;
@@ -71,7 +72,7 @@ public class VpnProfile implements Serializable, Cloneable {
private static final long serialVersionUID = 7085688938959334563L;
public static final int MAXLOGLEVEL = 4;
- public static final int CURRENT_PROFILE_VERSION = 5;
+ public static final int CURRENT_PROFILE_VERSION = 6;
public static final int DEFAULT_MSSFIX_SIZE = 1450;
public static String DEFAULT_DNS1 = "8.8.8.8";
public static String DEFAULT_DNS2 = "8.8.4.4";
@@ -152,7 +153,7 @@ public class VpnProfile implements Serializable, Cloneable {
public int mMssFix =0; // -1 is default,
public Connection[] mConnections = new Connection[0];
public boolean mRemoteRandom=false;
- public HashSet<String> mAllowedAppsVpn = new HashSet<String>();
+ public HashSet<String> mAllowedAppsVpn = new HashSet<>();
public boolean mAllowedAppsVpnAreDisallowed = true;
public String mProfileCreator;
@@ -160,8 +161,7 @@ public class VpnProfile implements Serializable, Cloneable {
public String mServerName = "openvpn.blinkt.de";
public String mServerPort = "1194";
public boolean mUseUdp = true;
-
-
+ public boolean mPushPeerInfo=false;
public VpnProfile(String name) {
mUuid = UUID.randomUUID();
@@ -197,6 +197,7 @@ public class VpnProfile implements Serializable, Cloneable {
mCheckRemoteCN = false;
mPersistTun = false;
mAllowLocalLAN = true;
+ mPushPeerInfo =false;
mMssFix = 0;
}
@@ -222,10 +223,16 @@ public class VpnProfile implements Serializable, Cloneable {
mAllowedAppsVpnAreDisallowed=true;
}
if (mAllowedAppsVpn==null)
- mAllowedAppsVpn = new HashSet<String>();
+ mAllowedAppsVpn = new HashSet<>();
if (mConnections ==null)
mConnections = new Connection[0];
+ if (mProfileVersion < 6) {
+ if (TextUtils.isEmpty(mProfileCreator))
+ mUserEditable=true;
+ }
+
+
mProfileVersion= CURRENT_PROFILE_VERSION;
}
@@ -260,8 +267,12 @@ public class VpnProfile implements Serializable, Cloneable {
cfg += "management-query-passwords\n";
cfg += "management-hold\n\n";
- if (!configForOvpn3)
+ if (!configForOvpn3) {
cfg += String.format("setenv IV_GUI_VER %s \n", openVpnEscape(getVersionEnvString(context)));
+ String versionString = String.format("%d %s %s %s %s %s", Build.VERSION.SDK_INT, Build.VERSION.RELEASE,
+ NativeUtils.getNativeAPI(), Build.BRAND, Build.BOARD, Build.MODEL);
+ cfg += String.format("setenv IV_PLAT_VER %s\n", openVpnEscape(versionString)) ;
+ }
cfg += "machine-readable-output\n";
@@ -432,9 +443,9 @@ public class VpnProfile implements Serializable, Cloneable {
}
if (mMssFix !=0){
- if (mMssFix!=1450)
- cfg+=String.format("mssfix %d\n", mMssFix, Locale.US);
- else
+ if (mMssFix!=1450) {
+ cfg += String.format("mssfix %d\n", mMssFix, Locale.US);
+ } else
cfg+="mssfix\n";
}
@@ -495,6 +506,9 @@ public class VpnProfile implements Serializable, Cloneable {
cfg += "preresolve\n";
}
+ if (mPushPeerInfo)
+ cfg+="push-peer-info\n";
+
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean usesystemproxy = prefs.getBoolean("usesystemproxy", true);
if (usesystemproxy) {
@@ -541,7 +555,7 @@ public class VpnProfile implements Serializable, Cloneable {
}
//! Put inline data inline and other data as normal escaped filename
- private String insertFileData(String cfgentry, String filedata) {
+ public static String insertFileData(String cfgentry, String filedata) {
if (filedata == null) {
// TODO: generate good error
return String.format("%s %s\n", cfgentry, "missing");
@@ -553,8 +567,9 @@ public class VpnProfile implements Serializable, Cloneable {
}
}
+ @NonNull
private Collection<String> getCustomRoutes(String routes) {
- Vector<String> cidrRoutes = new Vector<String>();
+ Vector<String> cidrRoutes = new Vector<>();
if (routes == null) {
// No routes set, return empty vector
return cidrRoutes;
@@ -563,7 +578,7 @@ public class VpnProfile implements Serializable, Cloneable {
if (!route.equals("")) {
String cidrroute = cidrToIPAndNetmask(route);
if (cidrroute == null)
- return null;
+ return cidrRoutes;
cidrRoutes.add(cidrroute);
}
@@ -573,7 +588,7 @@ public class VpnProfile implements Serializable, Cloneable {
}
private Collection<String> getCustomRoutesv6(String routes) {
- Vector<String> cidrRoutes = new Vector<String>();
+ Vector<String> cidrRoutes = new Vector<>();
if (routes == null) {
// No routes set, return empty vector
return cidrRoutes;
@@ -606,8 +621,8 @@ public class VpnProfile implements Serializable, Cloneable {
return null;
- long nm = 0xffffffffl;
- nm = (nm << (32 - len)) & 0xffffffffl;
+ long nm = 0xffffffffL;
+ nm = (nm << (32 - len)) & 0xffffffffL;
String netmask = String.format(Locale.ENGLISH, "%d.%d.%d.%d", (nm & 0xff000000) >> 24, (nm & 0xff0000) >> 16, (nm & 0xff00) >> 8, nm & 0xff);
return parts[0] + " " + netmask;
@@ -708,7 +723,7 @@ public class VpnProfile implements Serializable, Cloneable {
public VpnProfile copy(String name) {
try {
- VpnProfile copy = (VpnProfile) clone();
+ VpnProfile copy = clone();
copy.mName = name;
return copy;
@@ -726,17 +741,14 @@ public class VpnProfile implements Serializable, Cloneable {
}
synchronized String[] getKeyStoreCertificates(Context context,int tries) {
- PrivateKey privateKey = null;
- X509Certificate[] caChain;
- Exception exp;
try {
- privateKey = KeyChain.getPrivateKey(context, mAlias);
+ PrivateKey privateKey = KeyChain.getPrivateKey(context, mAlias);
mPrivateKey = privateKey;
String keystoreChain = null;
- caChain = KeyChain.getCertificateChain(context, mAlias);
+ X509Certificate[] caChain = KeyChain.getCertificateChain(context, mAlias);
if(caChain == null)
throw new NoCertReturnedException("No certificate returned from Keystore");
@@ -758,11 +770,12 @@ public class VpnProfile implements Serializable, Cloneable {
String caout = null;
if (!TextUtils.isEmpty(mCaFilename)) {
try {
- Certificate cacert = X509Utils.getCertificateFromFile(mCaFilename);
+ Certificate[] cacerts = X509Utils.getCertificatesFromFile(mCaFilename);
StringWriter caoutWriter = new StringWriter();
PemWriter pw = new PemWriter(caoutWriter);
- pw.writeObject(new PemObject("CERTIFICATE", cacert.getEncoded()));
+ for (Certificate cert: cacerts)
+ pw.writeObject(new PemObject("CERTIFICATE", cert.getEncoded()));
pw.close();
caout= caoutWriter.toString();
@@ -796,20 +809,19 @@ public class VpnProfile implements Serializable, Cloneable {
}
return new String[]{ca, extra, user};
- } catch (InterruptedException e) {
- exp=e;
- } catch (FileNotFoundException e) {
- exp=e;
- } catch (CertificateException e) {
- exp=e;
- } catch (IOException e) {
- exp=e;
- } catch (KeyChainException e) {
- exp=e;
- } catch (NoCertReturnedException e) {
- exp =e;
- } catch (IllegalArgumentException e) {
- exp =e;
+ } catch (InterruptedException | IOException | KeyChainException | NoCertReturnedException | IllegalArgumentException
+ | CertificateException e) {
+ e.printStackTrace();
+ VpnStatus.logError(R.string.keyChainAccessError, e.getLocalizedMessage());
+
+ VpnStatus.logError(R.string.keychain_access);
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
+ if (!mAlias.matches("^[a-zA-Z0-9]$")) {
+ VpnStatus.logError(R.string.jelly_keystore_alphanumeric_bug);
+ }
+ }
+ return null;
+
} catch (AssertionError e) {
if (tries ==0)
return null;
@@ -822,19 +834,9 @@ public class VpnProfile implements Serializable, Cloneable {
return getKeyStoreCertificates(context, tries-1);
}
- exp.printStackTrace();
- VpnStatus.logError(R.string.keyChainAccessError, exp.getLocalizedMessage());
-
- VpnStatus.logError(R.string.keychain_access);
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
- if (!mAlias.matches("^[a-zA-Z0-9]$")) {
- VpnStatus.logError(R.string.jelly_keystore_alphanumeric_bug);
- }
- }
- return null;
}
- //! Return an error if somethign is wrong
+ //! Return an error if something is wrong
public int checkProfile(Context context) {
if (mAuthenticationType == TYPE_KEYSTORE || mAuthenticationType == TYPE_USERPASS_KEYSTORE) {
if (mAlias == null)
@@ -845,8 +847,14 @@ public class VpnProfile implements Serializable, Cloneable {
if (mIPv4Address == null || cidrToIPAndNetmask(mIPv4Address) == null)
return R.string.ipv4_format_error;
}
- if (!mUseDefaultRoute && (getCustomRoutes(mCustomRoutes) == null || getCustomRoutes(mExcludedRoutes) ==null))
- return R.string.custom_route_format_error;
+ if (!mUseDefaultRoute) {
+ if (!TextUtils.isEmpty(mCustomRoutes) && getCustomRoutes(mCustomRoutes).size() == 0 )
+ return R.string.custom_route_format_error;
+
+ if (!TextUtils.isEmpty(mExcludedRoutes) && getCustomRoutes(mExcludedRoutes).size() == 0 )
+ return R.string.custom_route_format_error;
+
+ }
boolean noRemoteEnabled = true;
for (Connection c : mConnections)
@@ -980,7 +988,6 @@ public class VpnProfile implements Serializable, Cloneable {
public String getSignedData(String b64data) {
PrivateKey privkey = getKeystoreKey();
- Exception err;
byte[] data = Base64.decode(b64data, Base64.DEFAULT);
@@ -1004,26 +1011,14 @@ public class VpnProfile implements Serializable, Cloneable {
byte[] signed_bytes = rsaSigner.doFinal(data);
return Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
- } catch (NoSuchAlgorithmException e) {
- err = e;
- } catch (InvalidKeyException e) {
- err = e;
- } catch (NoSuchPaddingException e) {
- err = e;
- } catch (IllegalBlockSizeException e) {
- err = e;
- } catch (BadPaddingException e) {
- err = e;
+ } catch (NoSuchAlgorithmException | InvalidKeyException | IllegalBlockSizeException
+ | BadPaddingException | NoSuchPaddingException e) {
+ VpnStatus.logError(R.string.error_rsa_sign, e.getClass().toString(), e.getLocalizedMessage());
+ return null;
}
-
- VpnStatus.logError(R.string.error_rsa_sign, err.getClass().toString(), err.getLocalizedMessage());
-
- return null;
-
}
private String processSignJellyBeans(PrivateKey privkey, byte[] data) {
- Exception err;
try {
Method getKey = privkey.getClass().getSuperclass().getDeclaredMethod("getOpenSSLKey");
getKey.setAccessible(true);
@@ -1044,21 +1039,10 @@ public class VpnProfile implements Serializable, Cloneable {
byte[] signed_bytes = NativeUtils.rsasign(data, pkey);
return Base64.encodeToString(signed_bytes, Base64.NO_WRAP);
- } catch (NoSuchMethodException e) {
- err = e;
- } catch (IllegalArgumentException e) {
- err = e;
- } catch (IllegalAccessException e) {
- err = e;
- } catch (InvocationTargetException e) {
- err = e;
- } catch (InvalidKeyException e) {
- err = e;
+ } catch (NoSuchMethodException | InvalidKeyException | InvocationTargetException | IllegalAccessException | IllegalArgumentException e) {
+ VpnStatus.logError(R.string.error_rsa_sign, e.getClass().toString(), e.getLocalizedMessage());
+ return null;
}
- VpnStatus.logError(R.string.error_rsa_sign, err.getClass().toString(), err.getLocalizedMessage());
-
- return null;
-
}
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java b/app/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java
new file mode 100644
index 00000000..8cdc1e90
--- /dev/null
+++ b/app/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012-2015 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.activities;
+
+import android.app.Activity;
+import android.app.UiModeManager;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.view.Window;
+
+public class BaseActivity extends Activity {
+ private boolean isAndroidTV() {
+ final UiModeManager uiModeManager = (UiModeManager) getSystemService(Activity.UI_MODE_SERVICE);
+ return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ if (isAndroidTV()) {
+ requestWindowFeature(Window.FEATURE_OPTIONS_PANEL);
+ }
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
index dfd815e4..f55de486 100644
--- a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.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
*/
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java b/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java
index 45f09c8e..130084f5 100644
--- a/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.java
+++ b/app/src/main/java/de/blinkt/openvpn/activities/LogWindow.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
*/
@@ -15,7 +15,7 @@ import de.blinkt.openvpn.fragments.LogFragment;
/**
* Created by arne on 13.10.13.
*/
-public class LogWindow extends Activity {
+public class LogWindow extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java b/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java
index 94ed8a0b..07f2152f 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/CIDRIP.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
*/
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
index 232c454b..80a15c54 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ConfigParser.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
*/
@@ -37,6 +37,8 @@ public class ConfigParser {
public void parseConfig(Reader reader) throws IOException, ConfigParseError {
+ HashMap<String, String> optionAliases = new HashMap<>();
+ optionAliases.put("server-poll-timeout", "timeout-connect");
BufferedReader br = new BufferedReader(reader);
@@ -48,9 +50,15 @@ public class ConfigParser {
if (line == null)
break;
- if (lineno == 1 && (line.startsWith("PK\003\004")
- || (line.startsWith("PK\007\008"))))
- throw new ConfigParseError("Input looks like a ZIP Archive. Import is only possible for OpenVPN config files (.ovpn/.conf)");
+ if (lineno == 1) {
+ if ((line.startsWith("PK\003\004")
+ || (line.startsWith("PK\007\008")))) {
+ throw new ConfigParseError("Input looks like a ZIP Archive. Import is only possible for OpenVPN config files (.ovpn/.conf)");
+ }
+ if (line.startsWith("\uFEFF")) {
+ line = line.substring(1);
+ }
+ }
// Check for OpenVPN Access Server Meta information
if (line.startsWith("# OVPN_ACCESS_SERVER_")) {
@@ -70,6 +78,9 @@ public class ConfigParser {
checkinlinefile(args, br);
String optionname = args.get(0);
+ if (optionAliases.get(optionname)!=null)
+ optionname = optionAliases.get(optionname);
+
if (!options.containsKey(optionname)) {
options.put(optionname, new Vector<Vector<String>>());
}
@@ -137,7 +148,7 @@ public class ConfigParser {
}
- public class ConfigParseError extends Exception {
+ public static class ConfigParseError extends Exception {
private static final long serialVersionUID = -60L;
public ConfigParseError(String msg) {
@@ -388,6 +399,10 @@ public class ConfigParser {
np.mCustomRoutesv6 = customIPv6Routes;
}
+ Vector<String> routeNoPull = getOption("route-nopull", 1, 1);
+ if (routeNoPull!=null)
+ np.mRoutenopull=true;
+
// Also recognize tls-auth [inline] direction ...
Vector<Vector<String>> tlsauthoptions = getAllOption("tls-auth", 1, 2);
if (tlsauthoptions != null) {
@@ -567,6 +582,9 @@ public class ConfigParser {
if (getOption("persist-tun", 0, 0) != null)
np.mPersistTun = true;
+ if (getOption("push-peer-info", 0, 0) != null)
+ np.mPushPeerInfo = true;
+
Vector<String> connectretry = getOption("connect-retry", 1, 1);
if (connectretry != null)
np.mConnectRetry = connectretry.get(1);
@@ -709,8 +727,18 @@ public class ConfigParser {
conn.mUseUdp = isUdpProto(proto.get(1));
}
+ Vector<String> connectTimeout = getOption("connect-timeout", 1, 1);
+ if (connectTimeout != null) {
+ try {
+ conn.mConnectTimeout = Integer.parseInt(connectTimeout.get(1));
+ } catch (NumberFormatException nfe) {
+ throw new ConfigParseError(String.format("Argument to connect-timeout (%s) must to be an integer: %s",
+ connectTimeout.get(1), nfe.getLocalizedMessage()));
+
+ }
+ }
- // Parse remote config
+ // Parse remote config
Vector<Vector<String>> remotes = getAllOption("remote", 1, 3);
@@ -838,13 +866,21 @@ public class ConfigParser {
return false;
}
+ //! Generate options for custom options
private String getOptionStrings(Vector<Vector<String>> option) {
String custom = "";
for (Vector<String> optionsline : option) {
if (!ignoreThisOption(optionsline)) {
- for (String arg : optionsline)
- custom += VpnProfile.openVpnEscape(arg) + " ";
- custom += "\n";
+ // Check if option had been inlined and inline again
+ if (optionsline.size() == 2 && "extra-certs".equals(optionsline.get(0)) ) {
+ custom += VpnProfile.insertFileData(optionsline.get(0), optionsline.get(1));
+
+
+ } else {
+ for (String arg : optionsline)
+ custom += VpnProfile.openVpnEscape(arg) + " ";
+ custom += "\n";
+ }
}
}
return custom;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/Connection.java b/app/src/main/java/de/blinkt/openvpn/core/Connection.java
index b10664ce..3455450b 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/Connection.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/Connection.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
*/
@@ -16,6 +16,7 @@ public class Connection implements Serializable, Cloneable {
public String mCustomConfiguration="";
public boolean mUseCustomConfig=false;
public boolean mEnabled=true;
+ public int mConnectTimeout = 0;
private static final long serialVersionUID = 92031902903829089L;
@@ -33,6 +34,10 @@ public class Connection implements Serializable, Cloneable {
else
cfg += " tcp-client\n";
+ if (mConnectTimeout!=0)
+ cfg += String.format(" connect-timeout %d\n" , mConnectTimeout);
+
+
if (!TextUtils.isEmpty(mCustomConfiguration) && mUseCustomConfig) {
cfg += mCustomConfiguration;
cfg += "\n";
diff --git a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
index 4ccf5472..40684af3 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.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
*/
@@ -12,15 +12,19 @@ import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.State;
+import android.os.Handler;
import android.preference.PreferenceManager;
+
import se.leap.bitmaskclient.R;
import de.blinkt.openvpn.core.VpnStatus.ByteCountListener;
import java.util.LinkedList;
+import java.util.Objects;
import static de.blinkt.openvpn.core.OpenVPNManagement.pauseReason;
-public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountListener {
+public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountListener, OpenVPNManagement.PausedStateCallback {
+ private final Handler mDisconnectHandler;
private int lastNetwork = -1;
private OpenVPNManagement mManagement;
@@ -29,12 +33,36 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
// Data traffic limit in bytes
private final long TRAFFIC_LIMIT = 64 * 1024;
+ // Time to wait after network disconnect to pause the VPN
+ private final int DISCONNECT_WAIT = 20;
+
connectState network = connectState.DISCONNECTED;
connectState screen = connectState.SHOULDBECONNECTED;
connectState userpause = connectState.SHOULDBECONNECTED;
private String lastStateMsg = null;
+ private java.lang.Runnable mDelayDisconnectRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!(network == connectState.PENDINGDISCONNECT))
+ return;
+
+ network = connectState.DISCONNECTED;
+
+ // Set screen state to be disconnected if disconnect pending
+ if (screen == connectState.PENDINGDISCONNECT)
+ screen = connectState.DISCONNECTED;
+
+ mManagement.pause(getPauseReason());
+ }
+ };
+ private NetworkInfo lastConnectedNetwork;
+
+ @Override
+ public boolean shouldBeRunning() {
+ return shouldBeConnected();
+ }
enum connectState {
SHOULDBECONNECTED,
@@ -54,6 +82,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
LinkedList<Datapoint> trafficdata = new LinkedList<DeviceStateReceiver.Datapoint>();
+
@Override
public void updateByteCount(long in, long out, long diffIn, long diffOut) {
if (screen != connectState.PENDINGDISCONNECT)
@@ -99,6 +128,8 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
public DeviceStateReceiver(OpenVPNManagement magnagement) {
super();
mManagement = magnagement;
+ mManagement.setPauseCallback(this);
+ mDisconnectHandler = new Handler();
}
@@ -113,7 +144,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
boolean screenOffPause = prefs.getBoolean("screenoff", false);
if (screenOffPause) {
- if (ProfileManager.getLastConnectedVpn()!=null && !ProfileManager.getLastConnectedVpn().mPersistTun)
+ if (ProfileManager.getLastConnectedVpn() != null && !ProfileManager.getLastConnectedVpn().mPersistTun)
VpnStatus.logError(R.string.screen_nopersistenttun);
screen = connectState.PENDINGDISCONNECT;
@@ -126,6 +157,8 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
boolean connected = shouldBeConnected();
screen = connectState.SHOULDBECONNECTED;
+ /* We should connect now, cancel any outstanding disconnect timer */
+ mDisconnectHandler.removeCallbacks(mDelayDisconnectRunnable);
/* should be connected has changed because the screen is on now, connect the VPN */
if (shouldBeConnected() != connected)
mManagement.resume();
@@ -140,6 +173,10 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
private void fillTrafficData() {
trafficdata.add(new Datapoint(System.currentTimeMillis(), TRAFFIC_LIMIT));
}
+ public static boolean equalsObj(Object a, Object b) {
+ return (a == null) ? (b == null) : a.equals(b);
+ }
+
public void networkStateChange(Context context) {
@@ -175,34 +212,49 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
if (networkInfo != null && networkInfo.getState() == State.CONNECTED) {
int newnet = networkInfo.getType();
+
+ boolean pendingDisconnect = (network == connectState.PENDINGDISCONNECT);
network = connectState.SHOULDBECONNECTED;
- if (lastNetwork != newnet) {
+ boolean sameNetwork;
+ if (lastConnectedNetwork == null
+ || lastConnectedNetwork.getType() != networkInfo.getType()
+ || !equalsObj(lastConnectedNetwork.getExtraInfo(), networkInfo.getExtraInfo())
+ )
+ sameNetwork = false;
+ else
+ sameNetwork = true;
+
+ /* Same network, connection still 'established' */
+ if (pendingDisconnect && sameNetwork) {
+ mDisconnectHandler.removeCallbacks(mDelayDisconnectRunnable);
+ // Reprotect the sockets just be sure
+ mManagement.networkChange(true);
+ } else {
+ /* Different network or connection not established anymore */
+
if (screen == connectState.PENDINGDISCONNECT)
screen = connectState.DISCONNECTED;
if (shouldBeConnected()) {
- if (lastNetwork == -1) {
- mManagement.resume();
- } else {
- mManagement.networkChange();
+ mDisconnectHandler.removeCallbacks(mDelayDisconnectRunnable);
- }
+ if (pendingDisconnect || !sameNetwork)
+ mManagement.networkChange(sameNetwork);
+ else
+ mManagement.resume();
}
lastNetwork = newnet;
+ lastConnectedNetwork = networkInfo;
}
} else if (networkInfo == null) {
// Not connected, stop openvpn, set last connected network to no network
lastNetwork = -1;
if (sendusr1) {
- network = connectState.DISCONNECTED;
-
- // Set screen state to be disconnected if disconnect pending
- if (screen == connectState.PENDINGDISCONNECT)
- screen = connectState.DISCONNECTED;
+ network = connectState.PENDINGDISCONNECT;
+ mDisconnectHandler.postDelayed(mDelayDisconnectRunnable, DISCONNECT_WAIT * 1000);
- mManagement.pause(getPauseReason());
}
}
@@ -213,6 +265,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL
}
+
public boolean isUserPaused() {
return userpause == connectState.DISCONNECTED;
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
index 6e9e63c5..db3ae751 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.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
*/
@@ -37,5 +37,7 @@ public class ICSOpenVPNApplication extends Application {
if (BuildConfig.DEBUG) {
//ACRA.init(this);
}
+
+ VpnStatus.initLogCache(getApplicationContext().getCacheDir());
}
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java b/app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java
new file mode 100644
index 00000000..5c1741d9
--- /dev/null
+++ b/app/src/main/java/de/blinkt/openvpn/core/LogFileHandler.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2012-2015 Arne Schwabe
+ * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt
+ */
+
+package de.blinkt.openvpn.core;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Created by arne on 23.01.16.
+ */
+class LogFileHandler extends Handler {
+ static final int TRIM_LOG_FILE = 100;
+ static final int FLUSH_TO_DISK = 101;
+ static final int LOG_INIT = 102;
+ public static final int LOG_MESSAGE = 103;
+ private static FileOutputStream mLogFile;
+ private static BufferedOutputStream mBufLogfile;
+
+ public static final String LOGFILE_NAME = "logcache.dat";
+
+
+ public LogFileHandler(Looper looper) {
+ super(looper);
+ }
+
+
+ @Override
+ public void handleMessage(Message msg) {
+ try {
+ if (msg.what == LOG_INIT) {
+ readLogCache((File) msg.obj);
+ openLogFile((File) msg.obj);
+ } else if (msg.what == LOG_MESSAGE && msg.obj instanceof VpnStatus.LogItem) {
+ // Ignore log messages if not yet initialized
+ if (mLogFile == null)
+ return;
+ writeLogItemToDisk((VpnStatus.LogItem) msg.obj);
+ } else if (msg.what == TRIM_LOG_FILE) {
+ trimLogFile();
+ for (VpnStatus.LogItem li : VpnStatus.getlogbuffer())
+ writeLogItemToDisk(li);
+ } else if (msg.what == FLUSH_TO_DISK) {
+ flushToDisk();
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ VpnStatus.logError("Error during log cache: " + msg.what);
+ VpnStatus.logException(e);
+ }
+
+ }
+
+ private void flushToDisk() throws IOException {
+ mLogFile.flush();
+ }
+
+ private static void trimLogFile() {
+ try {
+ mBufLogfile.flush();
+ mLogFile.getChannel().truncate(0);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ private void writeLogItemToDisk(VpnStatus.LogItem li) throws IOException {
+ Parcel p = Parcel.obtain();
+ li.writeToParcel(p, 0);
+ // We do not really care if the log cache breaks between Android upgrades,
+ // write binary format to disc
+ byte[] liBytes = p.marshall();
+
+ mLogFile.write(liBytes.length & 0xff);
+ mLogFile.write(liBytes.length >> 8);
+ mLogFile.write(liBytes);
+ p.recycle();
+ }
+
+ private void openLogFile (File cacheDir) throws FileNotFoundException {
+ File logfile = new File(cacheDir, LOGFILE_NAME);
+ mLogFile = new FileOutputStream(logfile);
+ mBufLogfile = new BufferedOutputStream(mLogFile);
+ }
+
+ private void readLogCache(File cacheDir) {
+ File logfile = new File(cacheDir, LOGFILE_NAME);
+
+ if (!logfile.exists() || !logfile.canRead())
+ return;
+
+ VpnStatus.logDebug("Reread log items from cache file");
+
+ try {
+ BufferedInputStream logFile = new BufferedInputStream(new FileInputStream(logfile));
+
+ byte[] buf = new byte[8192];
+ int read = logFile.read(buf, 0, 2);
+
+ while (read > 0) {
+ // Marshalled LogItem
+ int len = (0xff & buf[0]) | buf[1] << 8;
+
+ read = logFile.read(buf, 0, len);
+
+ Parcel p = Parcel.obtain();
+ p.unmarshall(buf, 0, read);
+ p.setDataPosition(0);
+ VpnStatus.LogItem li = VpnStatus.LogItem.CREATOR.createFromParcel(p);
+ VpnStatus.newLogItem(li, true);
+ p.recycle();
+
+ //Next item
+ read = logFile.read(buf, 0, 2);
+ }
+
+ } catch (java.io.IOException | java.lang.RuntimeException e) {
+ VpnStatus.logError("Reading cached logfile failed");
+ VpnStatus.logException(e);
+ e.printStackTrace();
+ // ignore reading file error
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java b/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java
index 440458e4..04e4e8b4 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/LollipopDeviceStateListener.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
*/
diff --git a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
index f67b7730..ea003d41 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/NativeUtils.java
@@ -1,19 +1,26 @@
/*
- * 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
*/
package de.blinkt.openvpn.core;
+import android.os.Build;
+
import java.security.InvalidKeyException;
public class NativeUtils {
- public static native byte[] rsasign(byte[] input,int pkey) throws InvalidKeyException;
- public static native String[] getIfconfig() throws IllegalArgumentException;
- static native void jniclose(int fdint);
-
- static {
- System.loadLibrary("stlport_shared");
- System.loadLibrary("opvpnutil");
- }
+ public static native byte[] rsasign(byte[] input, int pkey) throws InvalidKeyException;
+
+ public static native String[] getIfconfig() throws IllegalArgumentException;
+
+ static native void jniclose(int fdint);
+
+ public static native String getNativeAPI();
+
+ static {
+ System.loadLibrary("opvpnutil");
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN)
+ System.loadLibrary("jbcrypto");
+ }
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
index c86f9e44..eb6d4d42 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/NetworkSpace.java
@@ -1,28 +1,28 @@
/*
- * 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
*/
package de.blinkt.openvpn.core;
import android.os.Build;
+import android.support.annotation.NonNull;
import android.text.TextUtils;
import junit.framework.Assert;
-import org.jetbrains.annotations.NotNull;
-
import java.math.BigInteger;
import java.net.Inet6Address;
-import java.util.*;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.PriorityQueue;
+import java.util.TreeSet;
+import java.util.Vector;
import se.leap.bitmaskclient.BuildConfig;
public class NetworkSpace {
-
-
-
static class ipAddress implements Comparable<ipAddress> {
private BigInteger netAddress;
public int networkMask;
@@ -38,7 +38,7 @@ public class NetworkSpace {
* 2. smaller networks are returned as smaller
*/
@Override
- public int compareTo(@NotNull ipAddress another) {
+ public int compareTo(@NonNull ipAddress another) {
int comp = getFirstAddress().compareTo(another.getFirstAddress());
if (comp != 0)
return comp;
@@ -159,16 +159,20 @@ public class NetworkSpace {
String getIPv6Address() {
if (BuildConfig.DEBUG) Assert.assertTrue (!isV4);
BigInteger r = netAddress;
- if (r.compareTo(BigInteger.ZERO)==0 && networkMask==0)
- return "::";
Vector<String> parts = new Vector<String>();
- while (r.compareTo(BigInteger.ZERO) == 1) {
- parts.add(0, String.format(Locale.US, "%x", r.mod(BigInteger.valueOf(0x10000)).longValue()));
+ while (r.compareTo(BigInteger.ZERO) == 1 || parts.size() <3) {
+ long part = r.mod(BigInteger.valueOf(0x10000)).longValue();
+ if (part!=0)
+ parts.add(0, String.format(Locale.US, "%x", part));
+ else
+ parts.add(0, "");
r = r.shiftRight(16);
}
-
- return TextUtils.join(":", parts);
+ String ipv6str = TextUtils.join(":", parts);
+ while (ipv6str.contains(":::"))
+ ipv6str = ipv6str.replace(":::", "::");
+ return ipv6str;
}
public boolean containsNet(ipAddress network) {
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
index 1f28c77d..2771fa6a 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNManagement.java
@@ -1,11 +1,15 @@
/*
- * 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
*/
package de.blinkt.openvpn.core;
public interface OpenVPNManagement {
+ interface PausedStateCallback {
+ boolean shouldBeRunning();
+ }
+
enum pauseReason {
noNetwork,
userPause,
@@ -25,5 +29,7 @@ public interface OpenVPNManagement {
/*
* Rebind the interface
*/
- void networkChange();
+ void networkChange(boolean sameNetwork);
+
+ void setPauseCallback(PausedStateCallback callback);
}
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..17be29b0 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,6 +30,7 @@ import android.preference.PreferenceManager;
import android.system.OsConstants;
import android.text.TextUtils;
import android.util.Log;
+import android.widget.Toast;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -81,6 +85,8 @@ 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;
// 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,6 +115,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
@Override
public void onRevoke() {
+ VpnStatus.logInfo(R.string.permission_revoked);
mManagement.stopVPN();
endVpnService();
}
@@ -135,7 +142,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 +171,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);
@@ -173,6 +183,33 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
mNotificationManager.notify(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 +252,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 +334,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);
@@ -454,6 +494,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
// Just in case unregister for state
VpnStatus.removeStateListener(this);
+ VpnStatus.flushLog();
}
@@ -536,6 +577,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,24 +619,7 @@ 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)
@@ -672,12 +716,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 +731,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 +894,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 +909,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);
}
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
index d856feb7..10bc9e87 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.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
*/
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
index 1c3b3362..4c550f47 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
@@ -1,23 +1,20 @@
/*
- * 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
*/
package de.blinkt.openvpn.core;
import android.content.Context;
-import android.content.SharedPreferences;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.ParcelFileDescriptor;
-import android.preference.PreferenceManager;
+import android.support.annotation.NonNull;
import android.util.Log;
import junit.framework.Assert;
-import org.jetbrains.annotations.NotNull;
-
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
@@ -42,30 +39,23 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
private LocalSocket mSocket;
private VpnProfile mProfile;
private OpenVPNService mOpenVPNService;
- private LinkedList<FileDescriptor> mFDList = new LinkedList<FileDescriptor>();
+ private LinkedList<FileDescriptor> mFDList = new LinkedList<>();
private LocalServerSocket mServerSocket;
- private boolean mReleaseHold = true;
private boolean mWaitingForRelease = false;
private long mLastHoldRelease = 0;
- private static final Vector<OpenVpnManagementThread> active = new Vector<OpenVpnManagementThread>();
+ private static final Vector<OpenVpnManagementThread> active = new Vector<>();
private LocalSocket mServerSocketLocal;
private pauseReason lastPauseReason = pauseReason.noNetwork;
+ private PausedStateCallback mPauseCallback;
public OpenVpnManagementThread(VpnProfile profile, OpenVPNService openVpnService) {
mProfile = profile;
mOpenVPNService = openVpnService;
-
-
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(openVpnService);
- boolean managemeNetworkState = prefs.getBoolean("netchangereconnect", true);
- if (managemeNetworkState)
- mReleaseHold = false;
-
}
- public boolean openManagementInterface(@NotNull Context c) {
+ public boolean openManagementInterface(@NonNull Context c) {
// Could take a while to open connection
int tries = 8;
@@ -74,7 +64,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
mServerSocketLocal = new LocalSocket();
- while (tries > 0 && !mServerSocketLocal.isConnected()) {
+ while (tries > 0 && !mServerSocketLocal.isBound()) {
try {
mServerSocketLocal.bind(new LocalSocketAddress(socketName,
LocalSocketAddress.Namespace.FILESYSTEM));
@@ -82,7 +72,7 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
// wait 300 ms before retrying
try {
Thread.sleep(300);
- } catch (InterruptedException e1) {
+ } catch (InterruptedException ignored) {
}
}
@@ -167,7 +157,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
//! Hack O Rama 2000!
private void protectFileDescriptor(FileDescriptor fd) {
- Exception exp;
try {
Method getInt = FileDescriptor.class.getDeclaredMethod("getInt$");
int fdint = (Integer) getInt.invoke(fd);
@@ -183,20 +172,12 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
//pfd.close();
NativeUtils.jniclose(fdint);
return;
- } catch (NoSuchMethodException e) {
- exp = e;
- } catch (IllegalArgumentException e) {
- exp = e;
- } catch (IllegalAccessException e) {
- exp = e;
- } catch (InvocationTargetException e) {
- exp = e;
- } catch (NullPointerException e) {
- exp = e;
+ } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException | IllegalAccessException | NullPointerException e) {
+ VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")", e);
}
Log.d("Openvpn", "Failed to retrieve fd from socket: " + fd);
- VpnStatus.logException("Failed to retrieve fd from socket (" + fd + ")", exp);
+
}
private String processInput(String pendingInput) {
@@ -225,31 +206,41 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
String argument = parts[1];
- if (cmd.equals("INFO")) {
+ switch (cmd) {
+ case "INFO":
/* Ignore greeting from management */
- return;
- } else if (cmd.equals("PASSWORD")) {
- processPWCommand(argument);
- } else if (cmd.equals("HOLD")) {
- handleHold();
- } else if (cmd.equals("NEED-OK")) {
- processNeedCommand(argument);
- } else if (cmd.equals("BYTECOUNT")) {
- processByteCount(argument);
- } else if (cmd.equals("STATE")) {
- processState(argument);
- } else if (cmd.equals("PROXY")) {
- processProxyCMD(argument);
- } else if (cmd.equals("LOG")) {
- processLogMessage(argument);
- } else if (cmd.equals("RSA_SIGN")) {
- processSignCommand(argument);
- } else {
- VpnStatus.logWarning("MGMT: Got unrecognized command" + command);
- Log.i(TAG, "Got unrecognized command" + command);
+ return;
+ case "PASSWORD":
+ processPWCommand(argument);
+ break;
+ case "HOLD":
+ handleHold();
+ break;
+ case "NEED-OK":
+ processNeedCommand(argument);
+ break;
+ case "BYTECOUNT":
+ processByteCount(argument);
+ break;
+ case "STATE":
+ processState(argument);
+ break;
+ case "PROXY":
+ processProxyCMD(argument);
+ break;
+ case "LOG":
+ processLogMessage(argument);
+ break;
+ case "RSA_SIGN":
+ processSignCommand(argument);
+ break;
+ default:
+ VpnStatus.logWarning("MGMT: Got unrecognized command" + command);
+ Log.i(TAG, "Got unrecognized command" + command);
+ break;
}
} else if (command.startsWith("SUCCESS:")) {
- /* Ignore this kind of message too */
+ /* Ignore this kind of message too */
return;
} else if (command.startsWith("PROTECTFD: ")) {
FileDescriptor fdtoprotect = mFDList.pollFirst();
@@ -278,16 +269,22 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
Log.d("OpenVPN", argument);
VpnStatus.LogLevel level;
- if (args[1].equals("I")) {
- level = VpnStatus.LogLevel.INFO;
- } else if (args[1].equals("W")) {
- level = VpnStatus.LogLevel.WARNING;
- } else if (args[1].equals("D")) {
- level = VpnStatus.LogLevel.VERBOSE;
- } else if (args[1].equals("F")) {
- level = VpnStatus.LogLevel.ERROR;
- } else {
- level = VpnStatus.LogLevel.INFO;
+ switch (args[1]) {
+ case "I":
+ level = VpnStatus.LogLevel.INFO;
+ break;
+ case "W":
+ level = VpnStatus.LogLevel.WARNING;
+ break;
+ case "D":
+ level = VpnStatus.LogLevel.VERBOSE;
+ break;
+ case "F":
+ level = VpnStatus.LogLevel.ERROR;
+ break;
+ default:
+ level = VpnStatus.LogLevel.INFO;
+ break;
}
int ovpnlevel = Integer.parseInt(args[2]) & 0x0F;
@@ -299,8 +296,15 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
VpnStatus.logMessageOpenVPN(level, ovpnlevel, msg);
}
+ boolean shouldBeRunning() {
+ if (mPauseCallback == null)
+ return false;
+ else
+ return mPauseCallback.shouldBeRunning();
+ }
+
private void handleHold() {
- if (mReleaseHold) {
+ if (shouldBeRunning()) {
releaseHoldCmd();
} else {
mWaitingForRelease = true;
@@ -327,11 +331,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
//managmentCommand("log on all\n");
}
+
public void releaseHold() {
- mReleaseHold = true;
if (mWaitingForRelease)
releaseHoldCmd();
-
}
private void processProxyCMD(String argument) {
@@ -391,15 +394,19 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
String status = "ok";
- if (needed.equals("PROTECTFD")) {
- FileDescriptor fdtoprotect = mFDList.pollFirst();
- protectFileDescriptor(fdtoprotect);
- } else if (needed.equals("DNSSERVER")) {
- mOpenVPNService.addDNS(extra);
- } else if (needed.equals("DNSDOMAIN")) {
- mOpenVPNService.setDomain(extra);
- } else if (needed.equals("ROUTE")) {
- String[] routeparts = extra.split(" ");
+ switch (needed) {
+ case "PROTECTFD":
+ FileDescriptor fdtoprotect = mFDList.pollFirst();
+ protectFileDescriptor(fdtoprotect);
+ break;
+ case "DNSSERVER":
+ mOpenVPNService.addDNS(extra);
+ break;
+ case "DNSDOMAIN":
+ mOpenVPNService.setDomain(extra);
+ break;
+ case "ROUTE": {
+ String[] routeparts = extra.split(" ");
/*
buf_printf (&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface);
@@ -407,38 +414,46 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
buf_printf (&out, "%s %s %s", network, netmask, gateway);
*/
- if (routeparts.length == 5) {
- if (BuildConfig.DEBUG) Assert.assertEquals("dev", routeparts[3]);
- mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]);
- } else if (routeparts.length >= 3) {
- mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null);
- } else {
- VpnStatus.logError("Unrecognized ROUTE cmd:" + Arrays.toString(routeparts) + " | " + argument);
+ if (routeparts.length == 5) {
+ if (BuildConfig.DEBUG) Assert.assertEquals("dev", routeparts[3]);
+ mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]);
+ } else if (routeparts.length >= 3) {
+ mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null);
+ } else {
+ VpnStatus.logError("Unrecognized ROUTE cmd:" + Arrays.toString(routeparts) + " | " + argument);
+ }
+
+ break;
}
+ case "ROUTE6": {
+ String[] routeparts = extra.split(" ");
+ mOpenVPNService.addRoutev6(routeparts[0], routeparts[1]);
+ break;
+ }
+ case "IFCONFIG":
+ String[] ifconfigparts = extra.split(" ");
+ int mtu = Integer.parseInt(ifconfigparts[2]);
+ mOpenVPNService.setLocalIP(ifconfigparts[0], ifconfigparts[1], mtu, ifconfigparts[3]);
+ break;
+ case "IFCONFIG6":
+ mOpenVPNService.setLocalIPv6(extra);
+
+ break;
+ case "PERSIST_TUN_ACTION":
+ // check if tun cfg stayed the same
+ status = mOpenVPNService.getTunReopenStatus();
+ break;
+ case "OPENTUN":
+ if (sendTunFD(needed, extra))
+ return;
+ else
+ status = "cancel";
+ // This not nice or anything but setFileDescriptors accepts only FilDescriptor class :(
- } else if (needed.equals("ROUTE6")) {
- String[] routeparts = extra.split(" ");
- mOpenVPNService.addRoutev6(routeparts[0], routeparts[1]);
- } else if (needed.equals("IFCONFIG")) {
- String[] ifconfigparts = extra.split(" ");
- int mtu = Integer.parseInt(ifconfigparts[2]);
- mOpenVPNService.setLocalIP(ifconfigparts[0], ifconfigparts[1], mtu, ifconfigparts[3]);
- } else if (needed.equals("IFCONFIG6")) {
- mOpenVPNService.setLocalIPv6(extra);
-
- } else if (needed.equals("PERSIST_TUN_ACTION")) {
- // check if tun cfg stayed the same
- status = mOpenVPNService.getTunReopenStatus();
- } else if (needed.equals("OPENTUN")) {
- if (sendTunFD(needed, extra))
+ break;
+ default:
+ Log.e(TAG, "Unkown needok command " + argument);
return;
- else
- status = "cancel";
- // This not nice or anything but setFileDescriptors accepts only FilDescriptor class :(
-
- } else {
- Log.e(TAG, "Unkown needok command " + argument);
- return;
}
String cmd = String.format("needok '%s' %s\n", needed, status);
@@ -446,7 +461,6 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
}
private boolean sendTunFD(String needed, String extra) {
- Exception exp;
if (!extra.equals("tun")) {
// We only support tun
VpnStatus.logError(String.format("Device type %s requested, but only tun is possible with the Android API, sorry!", extra));
@@ -480,18 +494,10 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
pfd.close();
return true;
- } catch (NoSuchMethodException e) {
- exp = e;
- } catch (IllegalArgumentException e) {
- exp = e;
- } catch (IllegalAccessException e) {
- exp = e;
- } catch (InvocationTargetException e) {
- exp = e;
- } catch (IOException e) {
- exp = e;
+ } catch (NoSuchMethodException | IllegalArgumentException | InvocationTargetException |
+ IOException | IllegalAccessException exp) {
+ VpnStatus.logException("Could not send fd over socket", exp);
}
- VpnStatus.logException("Could not send fd over socket", exp);
return false;
}
@@ -559,14 +565,21 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
}
@Override
- public void networkChange() {
- if (!mWaitingForRelease)
+ public void networkChange(boolean samenetwork) {
+ if (mWaitingForRelease)
+ releaseHold();
+ else if (samenetwork)
+ managmentCommand("network-change samenetwork\n");
+ else
managmentCommand("network-change\n");
}
- public void signalusr1() {
- mReleaseHold = false;
+ @Override
+ public void setPauseCallback(PausedStateCallback callback) {
+ mPauseCallback = callback;
+ }
+ public void signalusr1() {
if (!mWaitingForRelease)
managmentCommand("signal SIGUSR1\n");
else
diff --git a/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java b/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java
index a788426a..49a7eaa9 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/PRNGFixes.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
*/
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
index 086cdb44..4f9c219b 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.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
*/
@@ -128,6 +128,11 @@ public class ProfileManager {
ProfileManager.tmpprofile = tmp;
}
+ public static boolean isTempProfile()
+ {
+ return mLastConnectedVpn == tmpprofile;
+ }
+
public void saveProfile(Context context, VpnProfile profile) {
ObjectOutputStream vpnfile;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java b/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java
index 6e2abb13..34fb69f8 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/ProxyDetection.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
*/
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
index 47cb633c..f2cf8cec 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.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
*/
@@ -14,14 +14,15 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Arrays;
import java.util.Vector;
import se.leap.bitmaskclient.R;
import de.blinkt.openvpn.VpnProfile;
public class VPNLaunchHelper {
- private static final String MININONPIEVPN = "nopievpn";
- private static final String MINIPIEVPN = "pievpn";
+ private static final String MININONPIEVPN = "nopie_openvpn";
+ private static final String MINIPIEVPN = "pie_openvpn";
private static final String OVPNCONFIGFILE = "android.conf";
@@ -29,15 +30,22 @@ public class VPNLaunchHelper {
static private String writeMiniVPN(Context context) {
String[] abis;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
- abis = getSupportedAbisLollipop();
+ abis = getSupportedABIsLollipop();
else
+ //noinspection deprecation
abis = new String[]{Build.CPU_ABI, Build.CPU_ABI2};
+ String nativeAPI = NativeUtils.getNativeAPI();
+ if (!nativeAPI.equals(abis[0])) {
+ VpnStatus.logWarning(R.string.abi_mismatch, Arrays.toString(abis), nativeAPI);
+ abis = new String[] {nativeAPI};
+ }
+
for (String abi: abis) {
- File mvpnout = new File(context.getCacheDir(), getMiniVPNExecutableName() + "." + abi);
- if ((mvpnout.exists() && mvpnout.canExecute()) || writeMiniVPNBinary(context, abi, mvpnout)) {
- return mvpnout.getPath();
+ File vpnExecutable = new File(context.getCacheDir(), getMiniVPNExecutableName() + "." + abi);
+ if ((vpnExecutable.exists() && vpnExecutable.canExecute()) || writeMiniVPNBinary(context, abi, vpnExecutable)) {
+ return vpnExecutable.getPath();
}
}
@@ -45,7 +53,7 @@ public class VPNLaunchHelper {
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private static String[] getSupportedAbisLollipop() {
+ private static String[] getSupportedABIsLollipop() {
return Build.SUPPORTED_ABIS;
}
@@ -118,12 +126,13 @@ public class VPNLaunchHelper {
public static void startOpenVpn(VpnProfile startprofile, Context context) {
- if(writeMiniVPN(context)==null) {
+ VpnStatus.logInfo(R.string.building_configration);
+ VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, VpnStatus.ConnectionStatus.LEVEL_START);
+ if(writeMiniVPN(context)==null) {
VpnStatus.logError("Error writing minivpn binary");
return;
}
- VpnStatus.logInfo(R.string.building_configration);
Intent startVPN = startprofile.prepareStartService(context);
if(startVPN!=null)
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
index 62a60643..3ac1595c 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java
@@ -1,18 +1,19 @@
/*
- * 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
*/
package de.blinkt.openvpn.core;
import android.annotation.SuppressLint;
-import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.os.Build;
+import android.os.HandlerThread;
+import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -74,6 +75,58 @@ public class VpnStatus {
private static final int MAXLOGENTRIES = 1000;
+
+ public static String getLastCleanLogMessage(Context c) {
+ String message = mLaststatemsg;
+ switch (mLastLevel) {
+ case LEVEL_CONNECTED:
+ String[] parts = mLaststatemsg.split(",");
+ /*
+ (a) the integer unix date/time,
+ (b) the state name,
+ 0 (c) optional descriptive string (used mostly on RECONNECTING
+ and EXITING to show the reason for the disconnect),
+
+ 1 (d) optional TUN/TAP local IPv4 address
+ 2 (e) optional address of remote server,
+ 3 (f) optional port of remote server,
+ 4 (g) optional local address,
+ 5 (h) optional local port, and
+ 6 (i) optional TUN/TAP local IPv6 address.
+*/
+ // Return only the assigned IP addresses in the UI
+ if (parts.length >= 7)
+ message = String.format(Locale.US, "%s %s", parts[1], parts[6]);
+ break;
+ }
+
+ while (message.endsWith(","))
+ message = message.substring(0, message.length() - 1);
+
+ String status = mLaststate;
+ if (status.equals("NOPROCESS"))
+ return message;
+
+ String prefix = c.getString(mLastStateresid);
+ if (mLastStateresid == R.string.unknown_state)
+ message = status + message;
+ if (message.length() > 0)
+ prefix += ": ";
+
+ return prefix + message;
+
+ }
+
+ public static void initLogCache(File cacheDir) {
+ Message m = mLogFileHandler.obtainMessage(LogFileHandler.LOG_INIT, cacheDir);
+ mLogFileHandler.sendMessage(m);
+
+ }
+
+ public static void flushLog() {
+ mLogFileHandler.sendEmptyMessage(LogFileHandler.FLUSH_TO_DISK);
+ }
+
public enum ConnectionStatus {
LEVEL_CONNECTED,
LEVEL_VPNPAUSED,
@@ -81,6 +134,7 @@ public class VpnStatus {
LEVEL_CONNECTING_NO_SERVER_REPLY_YET,
LEVEL_NONETWORK,
LEVEL_NOTCONNECTED,
+ LEVEL_START,
LEVEL_AUTH_FAILED,
LEVEL_WAITING_FOR_USER_INPUT,
UNKNOWN_LEVEL
@@ -128,18 +182,24 @@ public class VpnStatus {
private static ConnectionStatus mLastLevel = ConnectionStatus.LEVEL_NOTCONNECTED;
+ private static final LogFileHandler mLogFileHandler;
+
static {
logbuffer = new LinkedList<>();
logListener = new Vector<>();
stateListener = new Vector<>();
byteCountListener = new Vector<>();
+
+ HandlerThread mHandlerThread = new HandlerThread("LogFileWriter", Thread.MIN_PRIORITY);
+ mHandlerThread.start();
+ mLogFileHandler = new LogFileHandler(mHandlerThread.getLooper());
+
logInformation();
+
}
public static class LogItem implements Parcelable {
-
-
private Object[] mArgs = null;
private String mMessage = null;
private int mRessourceId;
@@ -261,6 +321,7 @@ public class VpnStatus {
String version = "error getting version";
try {
+ @SuppressLint("PackageManagerGetSignatures")
Signature raw = c.getPackageManager().getPackageInfo(c.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0];
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));
@@ -287,11 +348,11 @@ public class VpnStatus {
NoSuchAlgorithmException ignored) {
}
- Object[] argsext = Arrays.copyOf(mArgs, mArgs.length + 2);
+ Object[] argsext = Arrays.copyOf(mArgs, mArgs.length);
argsext[argsext.length - 1] = apksign;
argsext[argsext.length - 2] = version;
- return c.getString(R.string.mobile_info_extended, argsext);
+ return c.getString(R.string.mobile_info, argsext);
}
@@ -310,12 +371,6 @@ public class VpnStatus {
}
}
- public void saveLogToDisk(Context c) {
-
- File logOut = new File(c.getCacheDir(), "log.xml");
-
- }
-
public interface LogListener {
void newLog(LogItem logItem);
}
@@ -336,10 +391,12 @@ public class VpnStatus {
public synchronized static void clearLog() {
logbuffer.clear();
logInformation();
+ mLogFileHandler.sendEmptyMessage(LogFileHandler.TRIM_LOG_FILE);
}
private static void logInformation() {
- logInfo(R.string.mobile_info, Build.MODEL, Build.BOARD, Build.BRAND, Build.VERSION.SDK_INT);
+ logInfo(R.string.mobile_info, Build.MODEL, Build.BOARD, Build.BRAND, Build.VERSION.SDK_INT,
+ NativeUtils.getNativeAPI(), Build.VERSION.RELEASE, Build.ID, Build.FINGERPRINT, "", "");
}
public synchronized static void addLogListener(LogListener ll) {
@@ -369,32 +426,34 @@ public class VpnStatus {
}
private static int getLocalizedState(String state) {
- if (state.equals("CONNECTING"))
- return R.string.state_connecting;
- else if (state.equals("WAIT"))
- return R.string.state_wait;
- else if (state.equals("AUTH"))
- return R.string.state_auth;
- else if (state.equals("GET_CONFIG"))
- return R.string.state_get_config;
- else if (state.equals("ASSIGN_IP"))
- return R.string.state_assign_ip;
- else if (state.equals("ADD_ROUTES"))
- return R.string.state_add_routes;
- else if (state.equals("CONNECTED"))
- return R.string.state_connected;
- else if (state.equals("DISCONNECTED"))
- return R.string.state_disconnected;
- else if (state.equals("RECONNECTING"))
- return R.string.state_reconnecting;
- else if (state.equals("EXITING"))
- return R.string.state_exiting;
- else if (state.equals("RESOLVE"))
- return R.string.state_resolve;
- else if (state.equals("TCP_CONNECT"))
- return R.string.state_tcp_connect;
- else
- return R.string.unknown_state;
+ switch (state) {
+ case "CONNECTING":
+ return R.string.state_connecting;
+ case "WAIT":
+ return R.string.state_wait;
+ case "AUTH":
+ return R.string.state_auth;
+ case "GET_CONFIG":
+ return R.string.state_get_config;
+ case "ASSIGN_IP":
+ return R.string.state_assign_ip;
+ case "ADD_ROUTES":
+ return R.string.state_add_routes;
+ case "CONNECTED":
+ return R.string.state_connected;
+ case "DISCONNECTED":
+ return R.string.state_disconnected;
+ case "RECONNECTING":
+ return R.string.state_reconnecting;
+ case "EXITING":
+ return R.string.state_exiting;
+ case "RESOLVE":
+ return R.string.state_resolve;
+ case "TCP_CONNECT":
+ return R.string.state_tcp_connect;
+ default:
+ return R.string.unknown_state;
+ }
}
@@ -496,17 +555,33 @@ public class VpnStatus {
newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args));
}
+ private static void newLogItem(LogItem logItem) {
+ newLogItem(logItem, false);
+ }
+
+
+ synchronized static void newLogItem(LogItem logItem, boolean cachedLine) {
+ if (cachedLine) {
+ logbuffer.addFirst(logItem);
+ } else {
+ logbuffer.addLast(logItem);
+ Message m = mLogFileHandler.obtainMessage(LogFileHandler.LOG_MESSAGE, logItem);
+ mLogFileHandler.sendMessage(m);
+ }
+
+ if (logbuffer.size() > MAXLOGENTRIES + MAXLOGENTRIES / 2) {
+ while (logbuffer.size() > MAXLOGENTRIES)
+ logbuffer.removeFirst();
+ mLogFileHandler.sendMessage(mLogFileHandler.obtainMessage(LogFileHandler.TRIM_LOG_FILE));
+ }
- private synchronized static void newLogItem(LogItem logItem) {
- logbuffer.addLast(logItem);
- if (logbuffer.size() > MAXLOGENTRIES)
- logbuffer.removeFirst();
for (LogListener ll : logListener) {
ll.newLog(logItem);
}
}
+
public static void logError(String msg) {
newLogItem(new LogItem(LogLevel.ERROR, msg));
diff --git a/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java b/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
index 0786967b..4048f0e0 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/X509Utils.java
@@ -1,11 +1,12 @@
/*
- * 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
*/
package de.blinkt.openvpn.core;
import android.content.Context;
+import android.content.res.Resources;
import android.text.TextUtils;
import se.leap.bitmaskclient.R;
@@ -20,30 +21,39 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateFactory;
+import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.Hashtable;
+import java.util.Vector;
public class X509Utils {
- public static Certificate getCertificateFromFile(String certfilename) throws FileNotFoundException, CertificateException {
+ public static Certificate[] getCertificatesFromFile(String certfilename) throws FileNotFoundException, CertificateException {
CertificateFactory certFact = CertificateFactory.getInstance("X.509");
- InputStream inStream;
-
+ Vector<Certificate> certificates = new Vector<>();
if(VpnProfile.isEmbedded(certfilename)) {
- // The java certifcate reader is ... kind of stupid
- // It does NOT ignore chars before the --BEGIN ...
int subIndex = certfilename.indexOf("-----BEGIN CERTIFICATE-----");
- subIndex = Math.max(0,subIndex);
- inStream = new ByteArrayInputStream(certfilename.substring(subIndex).getBytes());
+ do {
+ // The java certifcate reader is ... kind of stupid
+ // It does NOT ignore chars before the --BEGIN ...
+ subIndex = Math.max(0, subIndex);
+ InputStream inStream = new ByteArrayInputStream(certfilename.substring(subIndex).getBytes());
+ certificates.add(certFact.generateCertificate(inStream));
+ subIndex = certfilename.indexOf("-----BEGIN CERTIFICATE-----", subIndex+1);
+ } while (subIndex > 0);
+ return certificates.toArray(new Certificate[certificates.size()]);
} else {
- inStream = new FileInputStream(certfilename);
+ InputStream inStream = new FileInputStream(certfilename);
+ return new Certificate[] {certFact.generateCertificate(inStream)};
}
- return certFact.generateCertificate(inStream);
}
public static PemObject readPemObjectFromFile (String keyfilename) throws IOException {
@@ -67,9 +77,10 @@ public class X509Utils {
public static String getCertificateFriendlyName (Context c, String filename) {
if(!TextUtils.isEmpty(filename)) {
try {
- X509Certificate cert = (X509Certificate) getCertificateFromFile(filename);
-
- return getCertificateFriendlyName(cert);
+ X509Certificate cert = (X509Certificate) getCertificatesFromFile(filename)[0];
+ String friendlycn = getCertificateFriendlyName(cert);
+ friendlycn = getCertificateValidityString(cert, c.getResources()) + friendlycn;
+ return friendlycn;
} catch (Exception e) {
VpnStatus.logError("Could not read certificate" + e.getLocalizedMessage());
@@ -78,6 +89,40 @@ public class X509Utils {
return c.getString(R.string.cannotparsecert);
}
+ public static String getCertificateValidityString(X509Certificate cert, Resources res) {
+ try {
+ cert.checkValidity();
+ } catch (CertificateExpiredException ce) {
+ return "EXPIRED: ";
+ } catch (CertificateNotYetValidException cny) {
+ return "NOT YET VALID: ";
+ }
+
+ Date certNotAfter = cert.getNotAfter();
+ Date now = new Date();
+ long timeLeft = certNotAfter.getTime() - now.getTime(); // Time left in ms
+
+ // More than 72h left, display days
+ // More than 3 months display months
+ if (timeLeft > 90l* 24 * 3600 * 1000) {
+ long months = getMonthsDifference(now, certNotAfter);
+ return res.getString(R.string.months_left, months);
+ } else if (timeLeft > 72 * 3600 * 1000) {
+ long days = timeLeft / (24 * 3600 * 1000);
+ return res.getString(R.string.days_left, days);
+ } else {
+ long hours = timeLeft / (3600 * 1000);
+
+ return res.getString(R.string.hours_left, hours);
+ }
+ }
+
+ public static int getMonthsDifference(Date date1, Date date2) {
+ int m1 = date1.getYear() * 12 + date1.getMonth();
+ int m2 = date2.getYear() * 12 + date2.getMonth();
+ return m2 - m1 + 1;
+ }
+
public static String getCertificateFriendlyName(X509Certificate cert) {
X500Principal principal = cert.getSubjectX500Principal();
byte[] encodedSubject = principal.getEncoded();
diff --git a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
index 2a75c15e..f75e459e 100644
--- a/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.java
+++ b/app/src/main/java/de/blinkt/openvpn/fragments/LogFragment.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
*/
@@ -22,6 +22,9 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
import android.text.SpannableString;
import android.text.format.DateFormat;
import android.text.style.ImageSpan;
@@ -33,6 +36,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
@@ -41,8 +46,6 @@ import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
-import org.jetbrains.annotations.Nullable;
-
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
@@ -67,11 +70,12 @@ import static de.blinkt.openvpn.core.OpenVPNService.humanReadableByteCount;
import se.leap.bitmaskclient.Dashboard;
public class LogFragment extends ListFragment implements StateListener, SeekBar.OnSeekBarChangeListener, RadioGroup.OnCheckedChangeListener, VpnStatus.ByteCountListener {
- private static final String LOGTIMEFORMAT = "logtimeformat";
- private static final int START_VPN_CONFIG = 0;
+ private static final String LOGTIMEFORMAT = "logtimeformat";
+ private static final int START_VPN_CONFIG = 0;
private static final String VERBOSITYLEVEL = "verbositylevel";
+
private SeekBar mLogLevelSlider;
private LinearLayout mOptionsLayout;
private RadioGroup mTimeRadioGroup;
@@ -79,10 +83,11 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
private TextView mDownStatus;
private TextView mConnectStatus;
private boolean mShowOptionsLayout;
+ private CheckBox mClearLogCheckBox;
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- ladapter.setLogLevel(progress+1);
+ ladapter.setLogLevel(progress + 1);
}
@Override
@@ -134,7 +139,7 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
private static final int MESSAGE_NEWLOG = 0;
- private static final int MESSAGE_CLEARLOG = 1;
+ private static final int MESSAGE_CLEARLOG = 1;
private static final int MESSAGE_NEWTS = 2;
private static final int MESSAGE_NEWLOGLEVEL = 3;
@@ -144,110 +149,109 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
public static final int TIME_FORMAT_ISO = 2;
private static final int MAX_STORED_LOG_ENTRIES = 1000;
- private Vector<LogItem> allEntries=new Vector<LogItem>();
+ private Vector<LogItem> allEntries = new Vector<>();
- private Vector<LogItem> currentLevelEntries=new Vector<LogItem>();
+ private Vector<LogItem> currentLevelEntries = new Vector<LogItem>();
- private Handler mHandler;
+ private Handler mHandler;
- private Vector<DataSetObserver> observers=new Vector<DataSetObserver>();
+ private Vector<DataSetObserver> observers = new Vector<DataSetObserver>();
- private int mTimeFormat=0;
- private int mLogLevel=3;
+ private int mTimeFormat = 0;
+ private int mLogLevel = 3;
public LogWindowListAdapter() {
- initLogBuffer();
- if (mHandler == null) {
- mHandler = new Handler(this);
- }
-
- VpnStatus.addLogListener(this);
- }
+ initLogBuffer();
+ if (mHandler == null) {
+ mHandler = new Handler(this);
+ }
+ VpnStatus.addLogListener(this);
+ }
- private void initLogBuffer() {
- allEntries.clear();
+ private void initLogBuffer() {
+ allEntries.clear();
Collections.addAll(allEntries, VpnStatus.getlogbuffer());
initCurrentMessages();
- }
-
- String getLogStr() {
- String str = "";
- for(LogItem entry:allEntries) {
- str+=getTime(entry, TIME_FORMAT_ISO) + entry.getString(getActivity()) + '\n';
- }
- return str;
- }
-
-
- private void shareLog() {
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_TEXT, getLogStr());
- shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.ics_openvpn_log_file));
- shareIntent.setType("text/plain");
- startActivity(Intent.createChooser(shareIntent, "Send Logfile"));
- }
-
- @Override
- public void registerDataSetObserver(DataSetObserver observer) {
- observers.add(observer);
-
- }
-
- @Override
- public void unregisterDataSetObserver(DataSetObserver observer) {
- observers.remove(observer);
- }
-
- @Override
- public int getCount() {
- return currentLevelEntries.size();
- }
-
- @Override
- public Object getItem(int position) {
- return currentLevelEntries.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return ((Object)currentLevelEntries.get(position)).hashCode();
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- TextView v;
- if(convertView==null)
- v = new TextView(getActivity());
- else
- v = (TextView) convertView;
-
- LogItem le = currentLevelEntries.get(position);
- String msg = le.getString(getActivity());
+ }
+
+ String getLogStr() {
+ String str = "";
+ for (LogItem entry : allEntries) {
+ str += getTime(entry, TIME_FORMAT_ISO) + entry.getString(getActivity()) + '\n';
+ }
+ return str;
+ }
+
+
+ private void shareLog() {
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_TEXT, getLogStr());
+ shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.ics_openvpn_log_file));
+ shareIntent.setType("text/plain");
+ startActivity(Intent.createChooser(shareIntent, "Send Logfile"));
+ }
+
+ @Override
+ public void registerDataSetObserver(DataSetObserver observer) {
+ observers.add(observer);
+
+ }
+
+ @Override
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ observers.remove(observer);
+ }
+
+ @Override
+ public int getCount() {
+ return currentLevelEntries.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return currentLevelEntries.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return ((Object) currentLevelEntries.get(position)).hashCode();
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ TextView v;
+ if (convertView == null)
+ v = new TextView(getActivity());
+ else
+ v = (TextView) convertView;
+
+ LogItem le = currentLevelEntries.get(position);
+ String msg = le.getString(getActivity());
String time = getTime(le, mTimeFormat);
- msg = time + msg;
+ msg = time + msg;
int spanStart = time.length();
SpannableString t = new SpannableString(msg);
//t.setSpan(getSpanImage(le,(int)v.getTextSize()),spanStart,spanStart+1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
- v.setText(t);
- return v;
- }
+ v.setText(t);
+ return v;
+ }
private String getTime(LogItem le, int time) {
if (time != TIME_FORMAT_NONE) {
Date d = new Date(le.getLogtime());
java.text.DateFormat timeformat;
- if (time== TIME_FORMAT_ISO)
+ if (time == TIME_FORMAT_ISO)
timeformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
else
timeformat = DateFormat.getTimeFormat(getActivity());
@@ -289,49 +293,49 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
}
@Override
- public int getItemViewType(int position) {
- return 0;
- }
-
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- @Override
- public boolean isEmpty() {
- return currentLevelEntries.isEmpty();
-
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return true;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return true;
- }
-
- @Override
- public void newLog(LogItem logMessage) {
- Message msg = Message.obtain();
- assert (msg!=null);
- msg.what=MESSAGE_NEWLOG;
- Bundle bundle=new Bundle();
- bundle.putParcelable("logmessage", logMessage);
- msg.setData(bundle);
- mHandler.sendMessage(msg);
- }
-
- @Override
- public boolean handleMessage(Message msg) {
- // We have been called
- if(msg.what==MESSAGE_NEWLOG) {
-
- LogItem logMessage = msg.getData().getParcelable("logmessage");
- if(addLogMessage(logMessage))
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return currentLevelEntries.isEmpty();
+
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ @Override
+ public void newLog(LogItem logMessage) {
+ Message msg = Message.obtain();
+ assert (msg != null);
+ msg.what = MESSAGE_NEWLOG;
+ Bundle bundle = new Bundle();
+ bundle.putParcelable("logmessage", logMessage);
+ msg.setData(bundle);
+ mHandler.sendMessage(msg);
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ // We have been called
+ if (msg.what == MESSAGE_NEWLOG) {
+
+ LogItem logMessage = msg.getData().getParcelable("logmessage");
+ if (addLogMessage(logMessage))
for (DataSetObserver observer : observers) {
observer.onChanged();
}
@@ -340,25 +344,25 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
observer.onInvalidated();
}
initLogBuffer();
- } else if (msg.what == MESSAGE_NEWTS) {
- for (DataSetObserver observer : observers) {
- observer.onInvalidated();
- }
- } else if (msg.what == MESSAGE_NEWLOGLEVEL) {
+ } else if (msg.what == MESSAGE_NEWTS) {
+ for (DataSetObserver observer : observers) {
+ observer.onInvalidated();
+ }
+ } else if (msg.what == MESSAGE_NEWLOGLEVEL) {
initCurrentMessages();
- for (DataSetObserver observer: observers) {
+ for (DataSetObserver observer : observers) {
observer.onChanged();
}
}
- return true;
- }
+ return true;
+ }
private void initCurrentMessages() {
currentLevelEntries.clear();
- for(LogItem li: allEntries) {
+ for (LogItem li : allEntries) {
if (li.getVerbosityLevel() <= mLogLevel ||
mLogLevel == VpnProfile.MAXLOGLEVEL)
currentLevelEntries.add(li);
@@ -366,7 +370,6 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
}
/**
- *
* @param logmessage
* @return True if the current entries have changed
*/
@@ -376,7 +379,7 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
if (allEntries.size() > MAX_STORED_LOG_ENTRIES) {
Vector<LogItem> oldAllEntries = allEntries;
allEntries = new Vector<LogItem>(allEntries.size());
- for (int i=50;i<oldAllEntries.size();i++) {
+ for (int i = 50; i < oldAllEntries.size(); i++) {
allEntries.add(oldAllEntries.elementAt(i));
}
initCurrentMessages();
@@ -392,85 +395,81 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
}
void clearLog() {
- // Actually is probably called from GUI Thread as result of the user
- // pressing a button. But better safe than sorry
- VpnStatus.clearLog();
- VpnStatus.logInfo(R.string.logCleared);
- mHandler.sendEmptyMessage(MESSAGE_CLEARLOG);
- }
-
+ // Actually is probably called from GUI Thread as result of the user
+ // pressing a button. But better safe than sorry
+ VpnStatus.clearLog();
+ VpnStatus.logInfo(R.string.logCleared);
+ mHandler.sendEmptyMessage(MESSAGE_CLEARLOG);
+ }
- public void setTimeFormat(int newTimeFormat) {
- mTimeFormat= newTimeFormat;
- mHandler.sendEmptyMessage(MESSAGE_NEWTS);
- }
+ public void setTimeFormat(int newTimeFormat) {
+ mTimeFormat = newTimeFormat;
+ mHandler.sendEmptyMessage(MESSAGE_NEWTS);
+ }
public void setLogLevel(int logLevel) {
mLogLevel = logLevel;
mHandler.sendEmptyMessage(MESSAGE_NEWLOGLEVEL);
}
-
- }
-
-
- private LogWindowListAdapter ladapter;
- private TextView mSpeedView;
+ }
+ private LogWindowListAdapter ladapter;
+ private TextView mSpeedView;
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if(item.getItemId()==R.id.clearlog) {
- ladapter.clearLog();
- return true;
- } else if(item.getItemId()==R.id.cancel){
- Intent intent = new Intent(getActivity(),DisconnectVPN.class);
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.clearlog) {
+ ladapter.clearLog();
+ return true;
+ } else if (item.getItemId() == R.id.cancel) {
+ Intent intent = new Intent(getActivity(), DisconnectVPN.class);
startActivity(intent);
return true;
- } else if(item.getItemId()==R.id.send) {
- ladapter.shareLog();
- } else if(item.getItemId()==R.id.edit_vpn) {
- VpnProfile lastConnectedprofile = ProfileManager.getLastConnectedVpn();
-
- if(lastConnectedprofile!=null) {
- Intent vprefintent = new Intent(getActivity(),Dashboard.class)
- .putExtra(VpnProfile.EXTRA_PROFILEUUID,lastConnectedprofile.getUUIDString());
- startActivityForResult(vprefintent,START_VPN_CONFIG);
- } else {
- Toast.makeText(getActivity(), R.string.log_no_last_vpn, Toast.LENGTH_LONG).show();
- }
- } else if(item.getItemId() == R.id.toggle_time) {
- showHideOptionsPanel();
- } else if(item.getItemId() == android.R.id.home) {
- // This is called when the Home (Up) button is pressed
- // in the Action Bar.
- Intent parentActivityIntent = new Intent(getActivity(), Dashboard.class);
- parentActivityIntent.addFlags(
- Intent.FLAG_ACTIVITY_CLEAR_TOP |
- Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(parentActivityIntent);
- getActivity().finish();
- return true;
-
- }
- return super.onOptionsItemSelected(item);
-
- }
+ } else if (item.getItemId() == R.id.send) {
+ ladapter.shareLog();
+ } else if (item.getItemId() == R.id.edit_vpn) {
+ VpnProfile lastConnectedprofile = ProfileManager.getLastConnectedVpn();
+
+ if (lastConnectedprofile != null) {
+ Intent vprefintent = new Intent(getActivity(), Dashboard.class)
+ .putExtra(VpnProfile.EXTRA_PROFILEUUID, lastConnectedprofile.getUUIDString());
+ startActivityForResult(vprefintent, START_VPN_CONFIG);
+ } else {
+ Toast.makeText(getActivity(), R.string.log_no_last_vpn, Toast.LENGTH_LONG).show();
+ }
+ } else if (item.getItemId() == R.id.toggle_time) {
+ showHideOptionsPanel();
+ } else if (item.getItemId() == android.R.id.home) {
+ // This is called when the Home (Up) button is pressed
+ // in the Action Bar.
+ Intent parentActivityIntent = new Intent(getActivity(), Dashboard.class);
+ parentActivityIntent.addFlags(
+ Intent.FLAG_ACTIVITY_CLEAR_TOP |
+ Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(parentActivityIntent);
+ getActivity().finish();
+ return true;
+
+ }
+ return super.onOptionsItemSelected(item);
+
+ }
private void showHideOptionsPanel() {
boolean optionsVisible = (mOptionsLayout.getVisibility() != View.GONE);
ObjectAnimator anim;
if (optionsVisible) {
- anim = ObjectAnimator.ofFloat(mOptionsLayout,"alpha",1.0f, 0f);
+ anim = ObjectAnimator.ofFloat(mOptionsLayout, "alpha", 1.0f, 0f);
anim.addListener(collapseListener);
} else {
mOptionsLayout.setVisibility(View.VISIBLE);
- anim = ObjectAnimator.ofFloat(mOptionsLayout,"alpha", 0f, 1.0f);
+ anim = ObjectAnimator.ofFloat(mOptionsLayout, "alpha", 0f, 1.0f);
//anim = new TranslateAnimation(0.0f, 0.0f, mOptionsLayout.getHeight(), 0.0f);
}
@@ -493,16 +492,16 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.logmenu, menu);
+ inflater.inflate(R.menu.logmenu, menu);
if (getResources().getBoolean(R.bool.logSildersAlwaysVisible))
menu.removeItem(R.id.toggle_time);
- }
+ }
- @Override
+ @Override
public void onResume() {
- super.onResume();
- VpnStatus.addStateListener(this);
+ super.onResume();
+ VpnStatus.addStateListener(this);
VpnStatus.addByteCountListener(this);
Intent intent = new Intent(getActivity(), OpenVPNService.class);
intent.setAction(OpenVPNService.START_SERVICE);
@@ -512,45 +511,45 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == START_VPN_CONFIG && resultCode== Activity.RESULT_OK) {
- String configuredVPN = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID);
+ if (requestCode == START_VPN_CONFIG && resultCode == Activity.RESULT_OK) {
+ String configuredVPN = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID);
- final VpnProfile profile = ProfileManager.get(getActivity(),configuredVPN);
- ProfileManager.getInstance(getActivity()).saveProfile(getActivity(), profile);
- // Name could be modified, reset List adapter
+ final VpnProfile profile = ProfileManager.get(getActivity(), configuredVPN);
+ ProfileManager.getInstance(getActivity()).saveProfile(getActivity(), profile);
+ // Name could be modified, reset List adapter
- AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
- dialog.setTitle(R.string.configuration_changed);
- dialog.setMessage(R.string.restart_vpn_after_change);
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity());
+ dialog.setTitle(R.string.configuration_changed);
+ dialog.setMessage(R.string.restart_vpn_after_change);
- dialog.setPositiveButton(R.string.restart,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Intent intent = new Intent(getActivity(), LaunchVPN.class);
- intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUIDString());
- intent.setAction(Intent.ACTION_MAIN);
- startActivity(intent);
- }
+ dialog.setPositiveButton(R.string.restart,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(getActivity(), LaunchVPN.class);
+ intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUIDString());
+ intent.setAction(Intent.ACTION_MAIN);
+ startActivity(intent);
+ }
- });
- dialog.setNegativeButton(R.string.ignore, null);
- dialog.create().show();
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
+ });
+ dialog.setNegativeButton(R.string.ignore, null);
+ dialog.create().show();
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
@Override
public void onStop() {
- super.onStop();
- VpnStatus.removeStateListener(this);
+ super.onStop();
+ VpnStatus.removeStateListener(this);
VpnStatus.removeByteCountListener(this);
getActivity().getPreferences(0).edit().putInt(LOGTIMEFORMAT, ladapter.mTimeFormat)
- .putInt(VERBOSITYLEVEL, ladapter.mLogLevel).apply();
+ .putInt(VERBOSITYLEVEL, ladapter.mLogLevel).apply();
}
@@ -567,7 +566,7 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
int position, long id) {
ClipboardManager clipboard = (ClipboardManager)
getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
- ClipData clip = ClipData.newPlainText("Log Entry",((TextView) view).getText());
+ ClipData clip = ClipData.newPlainText("Log Entry", ((TextView) view).getText());
clipboard.setPrimaryClip(clip);
Toast.makeText(getActivity(), R.string.copied_entry, Toast.LENGTH_SHORT).show();
return true;
@@ -583,8 +582,8 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
setHasOptionsMenu(true);
ladapter = new LogWindowListAdapter();
- ladapter.mTimeFormat = getActivity().getPreferences(0).getInt(LOGTIMEFORMAT, 0);
- int logLevel = getActivity().getPreferences(0).getInt(VERBOSITYLEVEL, 0);
+ ladapter.mTimeFormat = getActivity().getPreferences(0).getInt(LOGTIMEFORMAT, 1);
+ int logLevel = getActivity().getPreferences(0).getInt(VERBOSITYLEVEL, 1);
ladapter.setLogLevel(logLevel);
setListAdapter(ladapter);
@@ -592,7 +591,7 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
mTimeRadioGroup = (RadioGroup) v.findViewById(R.id.timeFormatRadioGroup);
mTimeRadioGroup.setOnCheckedChangeListener(this);
- if(ladapter.mTimeFormat== LogWindowListAdapter.TIME_FORMAT_ISO) {
+ if (ladapter.mTimeFormat == LogWindowListAdapter.TIME_FORMAT_ISO) {
mTimeRadioGroup.check(R.id.radioISO);
} else if (ladapter.mTimeFormat == LogWindowListAdapter.TIME_FORMAT_NONE) {
mTimeRadioGroup.check(R.id.radioNone);
@@ -600,16 +599,25 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
mTimeRadioGroup.check(R.id.radioShort);
}
+ mClearLogCheckBox = (CheckBox) v.findViewById(R.id.clearlogconnect);
+ mClearLogCheckBox.setChecked(PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean(LaunchVPN.CLEARLOG, true));
+ mClearLogCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ PreferenceManager.getDefaultSharedPreferences(getActivity()).edit().putBoolean(LaunchVPN.CLEARLOG, isChecked).apply();
+ }
+ });
+
mSpeedView = (TextView) v.findViewById(R.id.speed);
mOptionsLayout = (LinearLayout) v.findViewById(R.id.logOptionsLayout);
mLogLevelSlider = (SeekBar) v.findViewById(R.id.LogLevelSlider);
- mLogLevelSlider.setMax(VpnProfile.MAXLOGLEVEL-1);
- mLogLevelSlider.setProgress(logLevel-1);
+ mLogLevelSlider.setMax(VpnProfile.MAXLOGLEVEL - 1);
+ mLogLevelSlider.setProgress(logLevel - 1);
mLogLevelSlider.setOnSeekBarChangeListener(this);
- if(getResources().getBoolean(R.bool.logSildersAlwaysVisible))
+ if (getResources().getBoolean(R.bool.logSildersAlwaysVisible))
mOptionsLayout.setVisibility(View.VISIBLE);
mUpStatus = (TextView) v.findViewById(R.id.speedUp);
@@ -623,16 +631,16 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- if(getResources().getBoolean(R.bool.logSildersAlwaysVisible)) {
- mShowOptionsLayout=true;
- if (mOptionsLayout!= null)
+ if (getResources().getBoolean(R.bool.logSildersAlwaysVisible)) {
+ mShowOptionsLayout = true;
+ if (mOptionsLayout != null)
mOptionsLayout.setVisibility(View.VISIBLE);
}
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
//getActionBar().setDisplayHomeAsUpEnabled(true);
@@ -642,19 +650,16 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
@Override
public void updateState(final String status, final String logMessage, final int resId, final ConnectionStatus level) {
if (isAdded()) {
+ final String cleanLogMessage = VpnStatus.getLastCleanLogMessage(getActivity());
+
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isAdded()) {
- String prefix = getString(resId) + ":";
- if (status.equals("BYTECOUNT") || status.equals("NOPROCESS"))
- prefix = "";
- if (resId == R.string.unknown_state)
- prefix += status;
- if (mSpeedView != null)
- mSpeedView.setText(prefix + logMessage);
-
+ if (mSpeedView != null) {
+ mSpeedView.setText(cleanLogMessage);
+ }
if (mConnectStatus != null)
mConnectStatus.setText(getString(resId));
}
@@ -664,10 +669,10 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar.
}
- @Override
+ @Override
public void onDestroy() {
- VpnStatus.removeLogListener(ladapter);
- super.onDestroy();
- }
+ VpnStatus.removeLogListener(ladapter);
+ super.onDestroy();
+ }
}
diff --git a/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java b/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java
index 82378b00..347ce708 100644
--- a/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.java
+++ b/app/src/main/java/de/blinkt/openvpn/views/SeekBarTicks.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
*/
@@ -42,10 +42,9 @@ public class SeekBarTicks extends SeekBar {
TypedArray a = context.obtainStyledAttributes(attrs,
new int[] { android.R.attr.secondaryProgress }, defStyle, 0);
-
- int tickColor = a.getColor(0, android.R.color.black);
mTickPaint = new Paint();
- mTickPaint.setColor( context.getResources().getColor(tickColor));
+ //noinspection deprecation
+ mTickPaint.setColor( context.getResources().getColor(android.R.color.black));
a.recycle();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index bdc36e89..218b22a7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -14,6 +14,22 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+/**
+ * Copyright (c) 2013 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;
import android.annotation.*;
@@ -31,6 +47,7 @@ import org.json.*;
import java.net.*;
import butterknife.*;
+import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.eip.*;
import se.leap.bitmaskclient.userstatus.*;
@@ -75,6 +92,7 @@ public class Dashboard extends Activity implements ProviderAPIResultReceiver.Rec
app = this;
PRNGFixes.apply();
+ VpnStatus.initLogCache(getApplicationContext().getCacheDir());
preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
fragment_manager = new FragmentManagerEnhanced(getFragmentManager());