diff options
29 files changed, 294 insertions, 900 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 86c9a74b..a00582cc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -34,14 +34,14 @@ android:logo="@mipmap/ic_launcher" android:theme="@style/BitmaskTheme"> <service - android:name=".eip.VoidVpnService" + android:name="de.blinkt.openvpn.core.OpenVPNService" android:permission="android.permission.BIND_VPN_SERVICE"> <intent-filter> <action android:name="android.net.VpnService" /> </intent-filter> </service> <service - android:name="de.blinkt.openvpn.core.OpenVPNService" + android:name=".eip.VoidVpnService" android:permission="android.permission.BIND_VPN_SERVICE"> <intent-filter> <action android:name="android.net.VpnService" /> diff --git a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java index 5bf178ae..60e4abb6 100644 --- a/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java +++ b/app/src/main/java/de/blinkt/openvpn/LaunchVPN.java @@ -5,10 +5,6 @@ package de.blinkt.openvpn; -import se.leap.bitmaskclient.R; - -import se.leap.bitmaskclient.R; - import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; @@ -25,10 +21,12 @@ import java.io.IOException; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.Preferences; -import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.core.VPNLaunchHelper; import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.MainActivity; +import se.leap.bitmaskclient.R; + +import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; /** * This Activity actually handles two stages of a launcher shortcut's life cycle. @@ -61,7 +59,6 @@ public class LaunchVPN extends Activity { 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"; - public static final String EXTRA_TEMP_VPN_PROFILE = "se.leap.bitmask.tempVpnProfile"; private static final int START_VPN_PROFILE = 70; @@ -94,15 +91,8 @@ public class LaunchVPN extends Activity { 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); mhideLog = intent.getBooleanExtra(EXTRA_HIDELOG, false); - VpnProfile profileToConnect = (VpnProfile) intent.getExtras().getSerializable(EXTRA_TEMP_VPN_PROFILE); - if (profileToConnect == null) - profileToConnect = ProfileManager.get(this, shortcutUUID); - - if (shortcutName != null && profileToConnect == null) - profileToConnect = ProfileManager.getInstance(this).getProfileByName(shortcutName); + VpnProfile profileToConnect = (VpnProfile) intent.getExtras().getSerializable(PROVIDER_PROFILE); if (profileToConnect == null) { VpnStatus.logError(R.string.shortcut_profile_notfound); @@ -126,7 +116,6 @@ public class LaunchVPN extends Activity { if(!mhideLog && showLogWindow) showLogWindow(); - ProfileManager.updateLRU(this, mSelectedProfile); VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext()); finish(); diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java index bd28ae16..7b9003aa 100644 --- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -5,9 +5,6 @@ package de.blinkt.openvpn; -import se.leap.bitmaskclient.R; -import se.leap.bitmaskclient.BuildConfig; - import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; @@ -15,7 +12,6 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; -import android.os.RemoteException; import android.preference.PreferenceManager; import android.security.KeyChain; import android.security.KeyChainException; @@ -24,7 +20,8 @@ import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Base64; -import de.blinkt.openvpn.core.*; +import com.google.gson.Gson; + import org.spongycastle.util.io.pem.PemObject; import org.spongycastle.util.io.pem.PemWriter; @@ -37,7 +34,11 @@ import java.io.Serializable; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.security.*; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -52,6 +53,21 @@ import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; +import de.blinkt.openvpn.core.Connection; +import de.blinkt.openvpn.core.ExtAuthHelper; +import de.blinkt.openvpn.core.NativeUtils; +import de.blinkt.openvpn.core.OpenVPNService; +import de.blinkt.openvpn.core.OrbotHelper; +import de.blinkt.openvpn.core.PasswordCache; +import de.blinkt.openvpn.core.Preferences; +import de.blinkt.openvpn.core.VPNLaunchHelper; +import de.blinkt.openvpn.core.VpnStatus; +import de.blinkt.openvpn.core.X509Utils; +import se.leap.bitmaskclient.BuildConfig; +import se.leap.bitmaskclient.R; + +import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; + public class VpnProfile implements Serializable, Cloneable { // Note that this class cannot be moved to core where it belongs since // the profile loading depends on it being here @@ -755,11 +771,8 @@ public class VpnProfile implements Serializable, Cloneable { } public Intent getStartServiceIntent(Context context) { - String prefix = context.getPackageName(); - Intent intent = new Intent(context, OpenVPNService.class); - intent.putExtra(prefix + ".profileUUID", mUuid.toString()); - intent.putExtra(prefix + ".profileVersion", mVersion); + intent.putExtra(PROVIDER_PROFILE, this); return intent; } @@ -1113,6 +1126,26 @@ public class VpnProfile implements Serializable, Cloneable { return mName; } + public String toJson() { + Gson gson = new Gson(); + try { + return gson.toJson(this); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static VpnProfile fromJson(String json) { + try { + Gson gson = new Gson(); + return gson.fromJson(json, VpnProfile.class); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + public String getUUIDString() { return mUuid.toString().toLowerCase(Locale.ENGLISH); } diff --git a/app/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java b/app/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java deleted file mode 100644 index 8cdc1e90..00000000 --- a/app/src/main/java/de/blinkt/openvpn/activities/BaseActivity.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 deleted file mode 100644 index 068821f5..00000000 --- a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.activities; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.RemoteException; - -import de.blinkt.openvpn.LaunchVPN; -import se.leap.bitmaskclient.R; -import de.blinkt.openvpn.core.IOpenVPNServiceInternal; -import de.blinkt.openvpn.core.OpenVPNService; -import de.blinkt.openvpn.core.ProfileManager; -import de.blinkt.openvpn.core.VpnStatus; - -/** - * Created by arne on 13.10.13. - */ -public class DisconnectVPN extends Activity implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener { - private IOpenVPNServiceInternal mService; - private ServiceConnection mConnection = new ServiceConnection() { - - - - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - - mService = IOpenVPNServiceInternal.Stub.asInterface(service); - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - mService = null; - } - - }; - - @Override - protected void onResume() { - super.onResume(); - Intent intent = new Intent(this, OpenVPNService.class); - intent.setAction(OpenVPNService.START_SERVICE); - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - showDisconnectDialog(); - } - - @Override - protected void onPause() { - super.onPause(); - unbindService(mConnection); - } - - private void showDisconnectDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.title_cancel); - builder.setMessage(R.string.cancel_connection_query); - builder.setNegativeButton(android.R.string.cancel, this); - builder.setPositiveButton(R.string.cancel_connection, this); - builder.setNeutralButton(R.string.reconnect, this); - builder.setOnCancelListener(this); - - builder.show(); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == DialogInterface.BUTTON_POSITIVE) { - ProfileManager.setConntectedVpnProfileDisconnected(this); - if (mService != null) { - try { - mService.stopVPN(false); - } catch (RemoteException e) { - VpnStatus.logException(e); - } - } - } else if (which == DialogInterface.BUTTON_NEUTRAL) { - Intent intent = new Intent(this, LaunchVPN.class); - intent.putExtra(LaunchVPN.EXTRA_KEY, VpnStatus.getLastConnectedVPNProfile()); - intent.setAction(Intent.ACTION_MAIN); - startActivity(intent); - } - finish(); - } - - @Override - public void onCancel(DialogInterface dialog) { - finish(); - } -} 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 c396f181..eeed29bc 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java +++ b/app/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java @@ -145,7 +145,7 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL boolean screenOffPause = prefs.getBoolean("screenoff", false); if (screenOffPause) { - if (ProfileManager.getLastConnectedVpn() != null && !ProfileManager.getLastConnectedVpn().mPersistTun) + if (VpnStatus.getLastConnectedVpnProfile() != null && !VpnStatus.getLastConnectedVpnProfile().mPersistTun) VpnStatus.logError(R.string.screen_nopersistenttun); screen = connectState.PENDINGDISCONNECT; diff --git a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java deleted file mode 100644 index 5b1307b7..00000000 --- a/app/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.TargetApi; -import android.app.Application; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.content.Context; -import android.graphics.Color; -import android.os.Build; - -/* -import org.acra.ACRA; -import org.acra.ReportingInteractionMode; -import org.acra.annotation.ReportsCrashes; -*/ - -import se.leap.bitmaskclient.R; - -public class ICSOpenVPNApplication extends Application { - private StatusListener mStatus; - - @Override - public void onCreate() { - super.onCreate(); - PRNGFixes.apply(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - - createNotificationChannels(); - mStatus = new StatusListener(); - mStatus.init(getApplicationContext()); - - } - - @TargetApi(Build.VERSION_CODES.O) - private void createNotificationChannels() { - NotificationManager mNotificationManager = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - // Background message - CharSequence name = getString(R.string.channel_name_background); - NotificationChannel mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_BG_ID, - name, NotificationManager.IMPORTANCE_MIN); - - mChannel.setDescription(getString(R.string.channel_description_background)); - mChannel.enableLights(false); - - mChannel.setLightColor(Color.DKGRAY); - mNotificationManager.createNotificationChannel(mChannel); - - // Connection status change messages - - name = getString(R.string.channel_name_status); - mChannel = new NotificationChannel(OpenVPNService.NOTIFICATION_CHANNEL_NEWSTATUS_ID, - name, NotificationManager.IMPORTANCE_LOW); - - mChannel.setDescription(getString(R.string.channel_description_status)); - mChannel.enableLights(true); - - mChannel.setLightColor(Color.BLUE); - mNotificationManager.createNotificationChannel(mChannel); - } -}
\ No newline at end of file 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 af31e977..82c4e1df 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -48,6 +48,7 @@ import se.leap.bitmaskclient.VpnNotificationManager; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; import static de.blinkt.openvpn.core.NetworkSpace.IpAddress; +import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; public class OpenVPNService extends VpnService implements StateListener, Callback, ByteCountListener, IOpenVPNServiceInternal, VpnNotificationManager.VpnServiceCallback { @@ -61,6 +62,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac public final static String ORBOT_PACKAGE_NAME = "org.torproject.android"; private static final String PAUSE_VPN = "de.blinkt.openvpn.PAUSE_VPN"; private static final String RESUME_VPN = "se.leap.bitmaskclient.RESUME_VPN"; + private static final String TAG = OpenVPNService.class.getSimpleName(); private static boolean mNotificationAlwaysVisible = false; private final Vector<String> mDnslist = new Vector<>(); private final NetworkSpace mRoutes = new NetworkSpace(); @@ -177,7 +179,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } VpnStatus.removeByteCountListener(this); unregisterDeviceStateReceiver(); - ProfileManager.setConntectedVpnProfileDisconnected(this); mOpenVPNThread = null; if (!mStarting) { stopForeground(!mNotificationAlwaysVisible); @@ -312,51 +313,38 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac 0, NOTIFICATION_CHANNEL_NEWSTATUS_ID); - if (intent != null && intent.hasExtra(getPackageName() + ".profileUUID")) { - String profileUUID = intent.getStringExtra(getPackageName() + ".profileUUID"); - int profileVersion = intent.getIntExtra(getPackageName() + ".profileVersion", 0); - // Try for 10s to get current version of the profile - mProfile = ProfileManager.get(this, profileUUID, profileVersion, 100); + if (intent != null && intent.hasExtra(PROVIDER_PROFILE)) { + mProfile = (VpnProfile) intent.getSerializableExtra(PROVIDER_PROFILE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { updateShortCutUsage(mProfile); } + VpnStatus.setAlwaysOn(false); } else { /* The intent is null when we are set as always-on or the service has been restarted. */ - mProfile = ProfileManager.getLastConnectedProfile(this); + mProfile = VpnStatus.getLastConnectedVpnProfile(this); VpnStatus.logInfo(R.string.service_restarted); - /* Got no profile, just stop */ - if (mProfile == null) { - Log.d("OpenVPN", "Got no last connected profile on null intent. Assuming always on."); - mProfile = ProfileManager.getAlwaysOnVPN(this); - - if (mProfile == null) { - stopSelf(startId); - return START_NOT_STICKY; - } + if (mProfile != null) { + VpnStatus.setAlwaysOn(true); } - /* Do the asynchronous keychain certificate stuff */ - mProfile.checkForRestart(this); } if (mProfile == null) { - stopSelf(startId); + stopService(startId); + Log.d(TAG, "Stopping service, no profile found"); return START_NOT_STICKY; + } else { + Log.d(TAG, "Found profile: " + mProfile.mName); + /* Do the asynchronous keychain certificate stuff */ + mProfile.checkForRestart(this); } /* start the OpenVPN process itself in a background thread */ - new Thread(new Runnable() { - @Override - public void run() { - startOpenVPN(); - } - }).start(); + new Thread(this::startOpenVPN).start(); - - ProfileManager.setConnectedVpnProfile(this, mProfile); - VpnStatus.setConnectedVPNProfile(mProfile.getUUIDString()); + VpnStatus.setLastConnectedVpnProfile(getApplicationContext(), mProfile); return START_STICKY; } @@ -369,6 +357,11 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac shortcutManager.reportShortcutUsed(profile.getUUIDString()); } + private void stopService(int startId) { + VpnStatus.updateStateString("NOPROCESS", "VPN STOPPED", R.string.state_noprocess, ConnectionStatus.LEVEL_NOTCONNECTED); + stopSelf(startId); + } + private void startOpenVPN() { /** * see change above (l. 292 ff) @@ -428,21 +421,17 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } synchronized (mProcessLock) - { mProcessThread = new Thread(processThread, "OpenVPNProcessThread"); mProcessThread.start(); } - new Handler(getMainLooper()).post(new Runnable() { - @Override - public void run() { - if (mDeviceStateReceiver != null) - unregisterDeviceStateReceiver(); - - registerDeviceStateReceiver(mManagement); - } - } + new Handler(getMainLooper()).post(() -> { + if (mDeviceStateReceiver != null) { + unregisterDeviceStateReceiver(); + } + registerDeviceStateReceiver(mManagement); + } ); } diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java index 6df1379a..b52e39a2 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java +++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNStatusService.java @@ -114,7 +114,7 @@ public class OpenVPNStatusService extends Service implements VpnStatus.LogListen @Override public String getLastConnectedVPN() throws RemoteException { - return VpnStatus.getLastConnectedVPNProfile(); + return VpnStatus.getLastConnectedVPNProfileId(); } @Override diff --git a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java b/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java deleted file mode 100644 index b9edc4b2..00000000 --- a/app/src/main/java/de/blinkt/openvpn/core/ProfileManager.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; -import java.util.Set; -import java.util.UUID; - -import de.blinkt.openvpn.VpnProfile; - -public class ProfileManager { - private static final String PREFS_NAME = "VPNList"; - - private static final String LAST_CONNECTED_PROFILE = "lastConnectedProfile"; - private static final String TEMPORARY_PROFILE_FILENAME = "temporary-vpn-profile"; - private static ProfileManager instance; - - private static VpnProfile mLastConnectedVpn = null; - private HashMap<String, VpnProfile> profiles = new HashMap<>(); - private static VpnProfile tmpprofile = null; - - - private static VpnProfile get(String key) { - if (tmpprofile != null && tmpprofile.getUUIDString().equals(key)) - return tmpprofile; - - if (instance == null) - return null; - return instance.profiles.get(key); - - } - - - private ProfileManager() { - } - - private static void checkInstance(Context context) { - if (instance == null) { - instance = new ProfileManager(); - instance.loadVPNList(context); - } - } - - synchronized public static ProfileManager getInstance(Context context) { - checkInstance(context); - return instance; - } - - public static void setConntectedVpnProfileDisconnected(Context c) { - SharedPreferences prefs = Preferences.getDefaultSharedPreferences(c); - Editor prefsedit = prefs.edit(); - prefsedit.putString(LAST_CONNECTED_PROFILE, null); - prefsedit.apply(); - - } - - /** - * Sets the profile that is connected (to connect if the service restarts) - */ - public static void setConnectedVpnProfile(Context c, VpnProfile connectedProfile) { - SharedPreferences prefs = Preferences.getDefaultSharedPreferences(c); - Editor prefsedit = prefs.edit(); - - prefsedit.putString(LAST_CONNECTED_PROFILE, connectedProfile.getUUIDString()); - prefsedit.apply(); - mLastConnectedVpn = connectedProfile; - - } - - /** - * Returns the profile that was last connected (to connect if the service restarts) - */ - public static VpnProfile getLastConnectedProfile(Context c) { - SharedPreferences prefs = Preferences.getDefaultSharedPreferences(c); - - String lastConnectedProfile = prefs.getString(LAST_CONNECTED_PROFILE, null); - if (lastConnectedProfile != null) - return get(c, lastConnectedProfile); - else - return null; - } - - - public Collection<VpnProfile> getProfiles() { - return profiles.values(); - } - - public VpnProfile getProfileByName(String name) { - for (VpnProfile vpnp : profiles.values()) { - if (vpnp.getName().equals(name)) { - return vpnp; - } - } - return null; - } - - public void saveProfileList(Context context) { - SharedPreferences sharedprefs = Preferences.getSharedPreferencesMulti(PREFS_NAME, context); - Editor editor = sharedprefs.edit(); - editor.putStringSet("vpnlist", profiles.keySet()); - - // For reasing I do not understand at all - // Android saves my prefs file only one time - // if I remove the debug code below :( - int counter = sharedprefs.getInt("counter", 0); - editor.putInt("counter", counter + 1); - editor.apply(); - - } - - public void addProfile(VpnProfile profile) { - profiles.put(profile.getUUID().toString(), profile); - - } - - public static void setTemporaryProfile(Context c, VpnProfile tmp) { - tmp.mTemporaryProfile = true; - ProfileManager.tmpprofile = tmp; - saveProfile(c, tmp, true, true); - } - - public static boolean isTempProfile() { - return mLastConnectedVpn != null && mLastConnectedVpn == tmpprofile; - } - - public void saveProfile(Context context, VpnProfile profile) { - saveProfile(context, profile, true, false); - } - - private static void saveProfile(Context context, VpnProfile profile, boolean updateVersion, boolean isTemporary) { - - if (updateVersion) - profile.mVersion += 1; - ObjectOutputStream vpnFile; - - String filename = profile.getUUID().toString() + ".vp"; - if (isTemporary) - filename = TEMPORARY_PROFILE_FILENAME + ".vp"; - - try { - vpnFile = new ObjectOutputStream(context.openFileOutput(filename, Activity.MODE_PRIVATE)); - - vpnFile.writeObject(profile); - vpnFile.flush(); - vpnFile.close(); - } catch (IOException e) { - VpnStatus.logException("saving VPN profile", e); - throw new RuntimeException(e); - } - } - - - private void loadVPNList(Context context) { - profiles = new HashMap<>(); - SharedPreferences listpref = Preferences.getSharedPreferencesMulti(PREFS_NAME, context); - Set<String> vlist = listpref.getStringSet("vpnlist", null); - if (vlist == null) { - vlist = new HashSet<>(); - } - // Always try to load the temporary profile - vlist.add(TEMPORARY_PROFILE_FILENAME); - - for (String vpnentry : vlist) { - ObjectInputStream vpnfile=null; - try { - vpnfile = new ObjectInputStream(context.openFileInput(vpnentry + ".vp")); - VpnProfile vp = ((VpnProfile) vpnfile.readObject()); - - // Sanity check - if (vp == null || vp.mName == null || vp.getUUID() == null) - continue; - - vp.upgradeProfile(); - if (vpnentry.equals(TEMPORARY_PROFILE_FILENAME)) { - tmpprofile = vp; - } else { - profiles.put(vp.getUUID().toString(), vp); - } - - - } catch (IOException | ClassNotFoundException e) { - if (!vpnentry.equals(TEMPORARY_PROFILE_FILENAME)) - VpnStatus.logException("Loading VPN List", e); - } finally { - if (vpnfile!=null) { - try { - vpnfile.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - } - - - public void removeProfile(Context context, VpnProfile profile) { - String vpnentry = profile.getUUID().toString(); - profiles.remove(vpnentry); - saveProfileList(context); - context.deleteFile(vpnentry + ".vp"); - if (mLastConnectedVpn == profile) - mLastConnectedVpn = null; - - } - - public static VpnProfile get(Context context, String profileUUID) { - return get(context, profileUUID, 0, 10); - } - - public static VpnProfile get(Context context, String profileUUID, int version, int tries) { - checkInstance(context); - VpnProfile profile = get(profileUUID); - int tried = 0; - while ((profile == null || profile.mVersion < version) && (tried++ < tries)) { - try { - Thread.sleep(100); - } catch (InterruptedException ignored) { - } - instance.loadVPNList(context); - profile = get(profileUUID); - int ver = profile == null ? -1 : profile.mVersion; - } - - if (tried > 5) - - { - int ver = profile == null ? -1 : profile.mVersion; - VpnStatus.logError(String.format(Locale.US, "Used x %d tries to get current version (%d/%d) of the profile", tried, ver, version)); - } - return profile; - } - - public static VpnProfile getLastConnectedVpn() { - return mLastConnectedVpn; - } - - public static VpnProfile getAlwaysOnVPN(Context context) { - checkInstance(context); - SharedPreferences prefs = Preferences.getDefaultSharedPreferences(context); - - String uuid = prefs.getString("alwaysOnVpn", null); - return get(uuid); - - } - - public static void updateLRU(Context c, VpnProfile profile) { - profile.mLastUsed = System.currentTimeMillis(); - // LRU does not change the profile, no need for the service to refresh - if (profile!=tmpprofile) - saveProfile(c, profile, false, false); - } -} diff --git a/app/src/main/java/de/blinkt/openvpn/core/StatusListener.java b/app/src/main/java/de/blinkt/openvpn/core/StatusListener.java deleted file mode 100644 index 5d0b7037..00000000 --- a/app/src/main/java/de/blinkt/openvpn/core/StatusListener.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; - -import java.io.DataInputStream; -import java.io.File; -import java.io.IOException; - -/** - * Created by arne on 09.11.16. - */ - -public class StatusListener { - private File mCacheDir; - private ServiceConnection mConnection = new ServiceConnection() { - - - @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - // We've bound to LocalService, cast the IBinder and get LocalService instance - IServiceStatus serviceStatus = IServiceStatus.Stub.asInterface(service); - try { - /* Check if this a local service ... */ - if (service.queryLocalInterface("de.blinkt.openvpn.core.IServiceStatus") == null) { - // Not a local service - VpnStatus.setConnectedVPNProfile(serviceStatus.getLastConnectedVPN()); - VpnStatus.setTrafficHistory(serviceStatus.getTrafficHistory()); - ParcelFileDescriptor pfd = serviceStatus.registerStatusCallback(mCallback); - DataInputStream fd = new DataInputStream(new ParcelFileDescriptor.AutoCloseInputStream(pfd)); - - short len = fd.readShort(); - byte[] buf = new byte[65336]; - while (len != 0x7fff) { - fd.readFully(buf, 0, len); - LogItem logitem = new LogItem(buf, len); - VpnStatus.newLogItem(logitem, false); - len = fd.readShort(); - } - fd.close(); - - - - } else { - VpnStatus.initLogCache(mCacheDir); - } - - } catch (RemoteException | IOException e) { - e.printStackTrace(); - VpnStatus.logException(e); - } - } - - @Override - public void onServiceDisconnected(ComponentName arg0) { - - } - - }; - - void init(Context c) { - - Intent intent = new Intent(c, OpenVPNStatusService.class); - intent.setAction(OpenVPNService.START_SERVICE); - mCacheDir = c.getCacheDir(); - - c.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - - - } - - - private IStatusCallbacks mCallback = new IStatusCallbacks.Stub() - - { - @Override - public void newLogItem(LogItem item) throws RemoteException { - VpnStatus.newLogItem(item); - } - - @Override - public void updateStateString(String state, String msg, int resid, ConnectionStatus - level) throws RemoteException { - VpnStatus.updateStateString(state, msg, resid, level); - } - - @Override - public void updateByteCount(long inBytes, long outBytes) throws RemoteException { - VpnStatus.updateByteCount(inBytes, outBytes); - } - - @Override - public void connectedVPN(String uuid) throws RemoteException { - VpnStatus.setConnectedVPNProfile(uuid); - } - }; - -} 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 fa28a875..c362e299 100644 --- a/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/app/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -17,8 +17,11 @@ import java.util.LinkedList; import java.util.Locale; import java.util.Vector; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; +import de.blinkt.openvpn.VpnProfile; import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.utils.PreferenceHelper; import static se.leap.bitmaskclient.utils.ConfigHelper.getProviderFormattedString; @@ -31,6 +34,8 @@ public class VpnStatus { private static CopyOnWriteArrayList<StateListener> stateListener; private static Vector<ByteCountListener> byteCountListener; + private static AtomicBoolean isAlwaysOnBooting = new AtomicBoolean(false); + private static String mLaststatemsg = ""; private static String mLaststate = "NOPROCESS"; @@ -39,10 +44,10 @@ public class VpnStatus { private static HandlerThread mHandlerThread; - private static String mLastConnectedVPNUUID; static boolean readFileLog =false; final static java.lang.Object readFileLock = new Object(); + private static VpnProfile lastConnectedProfile; public static TrafficHistory trafficHistory; @@ -136,17 +141,40 @@ public class VpnStatus { } public static void setConnectedVPNProfile(String uuid) { - mLastConnectedVPNUUID = uuid; for (StateListener sl: stateListener) sl.setConnectedVPN(uuid); } - public static String getLastConnectedVPNProfile() + public static String getLastConnectedVPNProfileId() { - return mLastConnectedVPNUUID; + return lastConnectedProfile != null ? lastConnectedProfile.getUUIDString() : null; + } + + public static VpnProfile getLastConnectedVpnProfile() { + return lastConnectedProfile; + } + + public static VpnProfile getLastConnectedVpnProfile(Context context) { + return PreferenceHelper.getLastConnectedVpnProfile(context); } + + /** + * Sets the profile that is connected (to connect if the service restarts) + */ + public static void setLastConnectedVpnProfile(Context context, VpnProfile connectedProfile) { + PreferenceHelper.setLastUsedVpnProfile(context, connectedProfile); + lastConnectedProfile = connectedProfile; + setConnectedVPNProfile(lastConnectedProfile.getUUIDString()); + } + + + public static String getLastConnectedVpnName() { + return lastConnectedProfile != null ? lastConnectedProfile.mName : null; + } + + public static void setTrafficHistory(TrafficHistory trafficHistory) { VpnStatus.trafficHistory = trafficHistory; } @@ -479,7 +507,6 @@ public class VpnStatus { } - public static synchronized void updateByteCount(long in, long out) { TrafficHistory.LastDiff diff = trafficHistory.add(in, out); @@ -487,4 +514,12 @@ public class VpnStatus { bcl.updateByteCount(in, out, diff.getDiffIn(), diff.getDiffOut()); } } + + public static void setAlwaysOn(boolean alwaysOn) { + isAlwaysOnBooting.set(alwaysOn); + } + + public static boolean isAlwaysOn() { + return isAlwaysOnBooting.get(); + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/Constants.java b/app/src/main/java/se/leap/bitmaskclient/Constants.java index f9ee8fcf..42df6d1d 100644 --- a/app/src/main/java/se/leap/bitmaskclient/Constants.java +++ b/app/src/main/java/se/leap/bitmaskclient/Constants.java @@ -11,6 +11,8 @@ public interface Constants { String SHARED_PREFERENCES = "LEAPPreferences"; String PREFERENCES_APP_VERSION = "bitmask version"; String ALWAYS_ON_SHOW_DIALOG = "DIALOG.ALWAYS_ON_SHOW_DIALOG"; + String CLEARLOG = "clearlogconnect"; + String LAST_USED_PROFILE = "last_used_profile"; ////////////////////////////////////////////// @@ -68,6 +70,7 @@ public interface Constants { String PROVIDER_CONFIGURED = "Constants.PROVIDER_CONFIGURED"; String PROVIDER_EIP_DEFINITION = "Constants.EIP_DEFINITION"; String PROVIDER_PROFILE_UUID = "Constants.PROVIDER_PROFILE_UUID"; + String PROVIDER_PROFILE = "Constants.PROVIDER_PROFILE"; ////////////////////////////////////////////// // CREDENTIAL CONSTANTS diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java index 11ad8da3..269bf61f 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java @@ -50,10 +50,9 @@ import java.util.Observer; import butterknife.ButterKnife; import butterknife.InjectView; import butterknife.OnClick; -import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.IOpenVPNServiceInternal; import de.blinkt.openvpn.core.OpenVPNService; -import de.blinkt.openvpn.core.ProfileManager; +import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.eip.EipCommand; import se.leap.bitmaskclient.eip.EipStatus; import se.leap.bitmaskclient.fragments.DonationReminderDialog; @@ -537,9 +536,9 @@ public class EipFragment extends Fragment implements Observer { private void setVpnRouteText() { String vpnRouteString = provider.getName(); - VpnProfile vpnProfile = ProfileManager.getLastConnectedVpn(); - if (vpnProfile != null && !TextUtils.isEmpty(vpnProfile.mName)) { - vpnRouteString += " (" + vpnProfile.mName + ")"; + String profileName = VpnStatus.getLastConnectedVpnName(); + if (!TextUtils.isEmpty(profileName)) { + vpnRouteString += " (" + profileName + ")"; } vpnRoute.setText(vpnRouteString); } diff --git a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java index ff94fe82..a8aa2dfb 100644 --- a/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java +++ b/app/src/main/java/se/leap/bitmaskclient/EipSetupObserver.java @@ -35,6 +35,7 @@ import static se.leap.bitmaskclient.Constants.EIP_ACTION_START; import static se.leap.bitmaskclient.Constants.EIP_ACTION_START_ALWAYS_ON_VPN; import static se.leap.bitmaskclient.Constants.EIP_REQUEST; import static se.leap.bitmaskclient.Constants.PROVIDER_KEY; +import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE; import static se.leap.bitmaskclient.ProviderAPI.CORRECTLY_UPDATED_INVALID_VPN_CERTIFICATE; @@ -186,7 +187,7 @@ class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListe finishGatewaySetup(true); } - VpnProfile vpnProfile = (VpnProfile) event.getSerializableExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE); + VpnProfile vpnProfile = (VpnProfile) event.getSerializableExtra(PROVIDER_PROFILE); if (vpnProfile == null) { Log.e(TAG, "Tried to setup non existing vpn profile."); return; @@ -204,7 +205,7 @@ class EipSetupObserver extends BroadcastReceiver implements VpnStatus.StateListe intent.setAction(Intent.ACTION_MAIN); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true); - intent.putExtra(LaunchVPN.EXTRA_TEMP_VPN_PROFILE, vpnProfile); + intent.putExtra(PROVIDER_PROFILE, vpnProfile); intent.putExtra(Gateway.KEY_N_CLOSEST_GATEWAY, setupNClosestGateway.get()); context.startActivity(intent); } diff --git a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java index f33bc27e..2efce9e4 100644 --- a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java +++ b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java @@ -6,6 +6,9 @@ import android.content.Intent; import android.content.SharedPreferences; import android.util.Log; +import de.blinkt.openvpn.core.VpnStatus; +import se.leap.bitmaskclient.utils.PreferenceHelper; + import static android.content.Intent.ACTION_BOOT_COMPLETED; import static se.leap.bitmaskclient.Constants.APP_ACTION_CONFIGURE_ALWAYS_ON_PROFILE; import static se.leap.bitmaskclient.Constants.EIP_IS_ALWAYS_ON; @@ -27,7 +30,7 @@ public class OnBootReceiver extends BroadcastReceiver { preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); boolean providerConfigured = !preferences.getString(PROVIDER_VPN_CERTIFICATE, "").isEmpty(); boolean startOnBoot = preferences.getBoolean(EIP_RESTART_ON_BOOT, false); - boolean isAlwaysOnConfigured = preferences.getBoolean(EIP_IS_ALWAYS_ON, false); + boolean isAlwaysOnConfigured = VpnStatus.isAlwaysOn(); Log.d("OpenVPN", "OpenVPN onBoot intent received. Provider configured? " + providerConfigured + " Start on boot? " + startOnBoot + " isAlwaysOn feature configured: " + isAlwaysOnConfigured); if (providerConfigured) { if (isAlwaysOnConfigured) { diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java index 29e2199f..a5434871 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java @@ -46,7 +46,6 @@ import java.util.concurrent.LinkedBlockingQueue; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.IOpenVPNServiceInternal; import de.blinkt.openvpn.core.OpenVPNService; -import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.OnBootReceiver; import se.leap.bitmaskclient.R; @@ -54,7 +53,6 @@ import se.leap.bitmaskclient.R; import static android.app.Activity.RESULT_CANCELED; import static android.app.Activity.RESULT_OK; import static android.content.Intent.CATEGORY_DEFAULT; -import static de.blinkt.openvpn.LaunchVPN.EXTRA_TEMP_VPN_PROFILE; import static se.leap.bitmaskclient.Constants.BROADCAST_EIP_EVENT; import static se.leap.bitmaskclient.Constants.BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT; import static se.leap.bitmaskclient.Constants.BROADCAST_RESULT_CODE; @@ -70,6 +68,7 @@ import static se.leap.bitmaskclient.Constants.EIP_N_CLOSEST_GATEWAY; import static se.leap.bitmaskclient.Constants.EIP_RECEIVER; import static se.leap.bitmaskclient.Constants.EIP_REQUEST; import static se.leap.bitmaskclient.Constants.EIP_RESTART_ON_BOOT; +import static se.leap.bitmaskclient.Constants.PROVIDER_PROFILE; import static se.leap.bitmaskclient.Constants.PROVIDER_VPN_CERTIFICATE; import static se.leap.bitmaskclient.Constants.SHARED_PREFERENCES; import static se.leap.bitmaskclient.MainActivityErrorDialog.DOWNLOAD_ERRORS.ERROR_INVALID_VPN_CERTIFICATE; @@ -243,7 +242,7 @@ public final class EIP extends JobIntentService implements Observer { */ private void launchActiveGateway(@NonNull Gateway gateway, int nClosestGateway) { Intent intent = new Intent(BROADCAST_GATEWAY_SETUP_OBSERVER_EVENT); - intent.putExtra(EXTRA_TEMP_VPN_PROFILE, gateway.getProfile()); + intent.putExtra(PROVIDER_PROFILE, gateway.getProfile()); intent.putExtra(Gateway.KEY_N_CLOSEST_GATEWAY, nClosestGateway); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); @@ -401,7 +400,6 @@ public final class EIP extends JobIntentService implements Observer { return false; } - ProfileManager.setConntectedVpnProfileDisconnected(this); try { return openVpnServiceConnection.getService().stopVPN(false); } catch (RemoteException e) { diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java index fc07c521..64904816 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java @@ -25,9 +25,7 @@ import java.util.Observable; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.LogItem; -import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.core.VpnStatus; -import se.leap.bitmaskclient.Provider; /** * EipStatus is a Singleton that represents a reduced set of a vpn's ConnectionStatus. @@ -37,6 +35,7 @@ import se.leap.bitmaskclient.Provider; public class EipStatus extends Observable implements VpnStatus.StateListener { public static String TAG = EipStatus.class.getSimpleName(); private static EipStatus currentStatus; + public enum EipLevel { CONNECTING, DISCONNECTING, @@ -99,7 +98,7 @@ public class EipStatus extends Observable implements VpnStatus.StateListener { currentEipLevel = EipLevel.CONNECTED; break; case LEVEL_VPNPAUSED: - if (ProfileManager.getLastConnectedVpn() != null && ProfileManager.getLastConnectedVpn().mPersistTun) { + if (VpnStatus.getLastConnectedVpnProfile() != null && VpnStatus.getLastConnectedVpnProfile().mPersistTun) { //if persistTun is enabled, treat EipLevel as connecting as it *shouldn't* allow passing traffic in the clear... currentEipLevel = EipLevel.CONNECTING; } else { diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java index 317a91bd..55ade1ae 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java @@ -132,4 +132,18 @@ public class Gateway { public String toString() { return new Gson().toJson(this, Gateway.class); } + + @Override + public boolean equals(Object obj) { + return obj instanceof Gateway && + (this.mVpnProfile != null && + ((Gateway) obj).mVpnProfile != null && + this.mVpnProfile.mConnections != null && + ((Gateway) obj).mVpnProfile != null && + this.mVpnProfile.mConnections.length > 0 && + ((Gateway) obj).mVpnProfile.mConnections.length > 0 && + this.mVpnProfile.mConnections[0].mServerName != null && + this.mVpnProfile.mConnections[0].mServerName.equals(((Gateway) obj).mVpnProfile.mConnections[0].mServerName)) || + this.mVpnProfile == null && ((Gateway) obj).mVpnProfile == null; + } } diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java index 003cef7d..060843fd 100644 --- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java +++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013, 2014, 2015 LEAP Encryption Access Project and contributers + * Copyright (c) 2013 - 2019 LEAP Encryption Access Project and contributors * * 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 @@ -18,7 +18,6 @@ package se.leap.bitmaskclient.eip; import android.content.Context; import android.content.SharedPreferences; -import android.util.Log; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -29,13 +28,8 @@ import org.json.JSONObject; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; import java.util.List; -import de.blinkt.openvpn.VpnProfile; -import de.blinkt.openvpn.core.Connection; -import de.blinkt.openvpn.core.ProfileManager; import se.leap.bitmaskclient.Provider; import se.leap.bitmaskclient.utils.PreferenceHelper; @@ -52,13 +46,11 @@ public class GatewaysManager { private Context context; private SharedPreferences preferences; private List<Gateway> gateways = new ArrayList<>(); - private ProfileManager profileManager; private Type listType = new TypeToken<ArrayList<Gateway>>() {}.getType(); GatewaysManager(Context context, SharedPreferences preferences) { this.context = context; this.preferences = preferences; - profileManager = ProfileManager.getInstance(context); } /** @@ -102,7 +94,7 @@ public class GatewaysManager { if (isOpenVpnGateway(gw)) { JSONObject secrets = secretsConfiguration(); Gateway aux = new Gateway(eipDefinition, secrets, gw); - if (!containsProfileWithSecrets(aux.getProfile())) { + if (!gateways.contains(aux)) { addGateway(aux); } } @@ -139,85 +131,19 @@ public class GatewaysManager { return result; } - private boolean containsProfileWithSecrets(VpnProfile profile) { - boolean result = false; - Collection<VpnProfile> profiles = profileManager.getProfiles(); - for (VpnProfile aux : profiles) { - result = result || sameConnections(profile.mConnections, aux.mConnections) - && profile.mClientCertFilename.equalsIgnoreCase(aux.mClientCertFilename) - && profile.mClientKeyFilename.equalsIgnoreCase(aux.mClientKeyFilename); - } - return result; - } - - void clearGatewaysAndProfiles() { + void clearGateways() { gateways.clear(); - ArrayList<VpnProfile> profiles = new ArrayList<>(profileManager.getProfiles()); - for (VpnProfile profile : profiles) { - profileManager.removeProfile(context, profile); - } } private void addGateway(Gateway gateway) { - removeDuplicatedGateway(gateway); gateways.add(gateway); - - VpnProfile profile = gateway.getProfile(); - profileManager.addProfile(profile); - } - - private void removeDuplicatedGateway(Gateway gateway) { - Iterator<Gateway> it = gateways.iterator(); - List<Gateway> gatewaysToRemove = new ArrayList<>(); - while (it.hasNext()) { - Gateway aux = it.next(); - if (sameConnections(aux.getProfile().mConnections, gateway.getProfile().mConnections)) { - gatewaysToRemove.add(aux); - } - } - gateways.removeAll(gatewaysToRemove); - removeDuplicatedProfiles(gateway.getProfile()); - } - - private void removeDuplicatedProfiles(VpnProfile original) { - Collection<VpnProfile> profiles = profileManager.getProfiles(); - List<VpnProfile> removeList = new ArrayList<>(); - for (VpnProfile aux : profiles) { - if (sameConnections(original.mConnections, aux.mConnections)) { - removeList.add(aux); - } - } - for (VpnProfile profile : removeList) { - profileManager.removeProfile(context, profile); - } - } - - /** - * check if all connections in c1 are also in c2 - * @param c1 array of connections - * @param c2 array of connections - * @return true if all connections of c1 exist in c2 and vice versa - */ - private boolean sameConnections(Connection[] c1, Connection[] c2) { - int sameConnections = 0; - for (Connection c1_aux : c1) { - for (Connection c2_aux : c2) - if (c2_aux.mServerName.equals(c1_aux.mServerName)) { - sameConnections++; - break; - } - } - return c1.length == c2.length && c1.length == sameConnections; } /** * read EipServiceJson from preferences and set gateways */ void configureFromPreferences() { - //TODO: THIS IS A QUICK FIX - it deletes all profiles in ProfileManager, thus it's possible - // to add all gateways from prefs without duplicates, but this should be refactored. - clearGatewaysAndProfiles(); fromEipServiceJson( PreferenceHelper.getEipDefinitionFromPreferences(preferences) ); diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java b/app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java index e3d004f5..09817b78 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/AlwaysOnDialog.java @@ -1,13 +1,14 @@ package se.leap.bitmaskclient.fragments; import android.app.Dialog; -import android.content.DialogInterface; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatDialogFragment; +import android.support.v7.widget.AppCompatTextView; import android.view.LayoutInflater; import android.view.View; import android.widget.CheckBox; @@ -36,6 +37,10 @@ public class AlwaysOnDialog extends AppCompatDialogFragment { @InjectView(R.id.user_message) IconTextView userMessage; + @InjectView(R.id.block_vpn_user_message) + AppCompatTextView blockVpnUserMessage; + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -51,22 +56,21 @@ public class AlwaysOnDialog extends AppCompatDialogFragment { userMessage.setIcon(R.drawable.ic_settings); userMessage.setText(getString(R.string.always_on_vpn_user_message)); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + blockVpnUserMessage.setVisibility(View.VISIBLE); + } + builder.setView(view) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - if (doNotShowAgainCheckBox.isChecked()) { - saveShowAlwaysOnDialog(getContext(), false); - } - Intent intent = new Intent("android.net.vpn.SETTINGS"); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); + .setPositiveButton(android.R.string.ok, (dialog, id) -> { + if (doNotShowAgainCheckBox.isChecked()) { + saveShowAlwaysOnDialog(getContext(), false); } + Intent intent = new Intent("android.net.vpn.SETTINGS"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); }) - .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); + .setNegativeButton(R.string.cancel, (dialog, id) -> dialog.cancel()); return builder.create(); } } diff --git a/app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java b/app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java index 268d326d..64d199dc 100644 --- a/app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java +++ b/app/src/main/java/se/leap/bitmaskclient/fragments/LogFragment.java @@ -8,16 +8,12 @@ package se.leap.bitmaskclient.fragments; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; -import android.app.Activity; -import android.app.AlertDialog; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.database.DataSetObserver; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.Handler.Callback; @@ -27,7 +23,6 @@ import android.support.annotation.Nullable; import android.support.v4.app.ListFragment; import android.text.SpannableString; import android.text.format.DateFormat; -import android.text.style.ImageSpan; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -37,7 +32,6 @@ 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; @@ -52,17 +46,16 @@ import java.util.Date; import java.util.Locale; import java.util.Vector; -import de.blinkt.openvpn.LaunchVPN; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConnectionStatus; import de.blinkt.openvpn.core.LogItem; import de.blinkt.openvpn.core.OpenVPNManagement; import de.blinkt.openvpn.core.OpenVPNService; import de.blinkt.openvpn.core.Preferences; -import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.core.VpnStatus; import de.blinkt.openvpn.core.VpnStatus.LogListener; import de.blinkt.openvpn.core.VpnStatus.StateListener; +import se.leap.bitmaskclient.Constants; import se.leap.bitmaskclient.R; import static de.blinkt.openvpn.core.OpenVPNService.humanReadableByteCount; @@ -70,7 +63,6 @@ import static de.blinkt.openvpn.core.OpenVPNService.humanReadableByteCount; public class LogFragment extends ListFragment implements StateListener, SeekBar.OnSeekBarChangeListener, RadioGroup.OnCheckedChangeListener, VpnStatus.ByteCountListener { public static final String TAG = LogFragment.class.getSimpleName(); private static final String LOGTIMEFORMAT = "logtimeformat"; - private static final int START_VPN_CONFIG = 0; private static final String VERBOSITYLEVEL = "verbositylevel"; @@ -242,7 +234,6 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. SpannableString t = new SpannableString(msg); - //t.setSpan(getSpanImage(le,(int)v.getTextSize()),spanStart,spanStart+1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); v.setText(t); return v; } @@ -264,34 +255,6 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. } - private ImageSpan getSpanImage(LogItem li, int imageSize) { - int imageRes = android.R.drawable.ic_menu_call; - - switch (li.getLogLevel()) { - case ERROR: - imageRes = android.R.drawable.ic_notification_clear_all; - break; - case INFO: - imageRes = android.R.drawable.ic_menu_compass; - break; - case VERBOSE: - imageRes = android.R.drawable.ic_menu_info_details; - break; - case WARNING: - imageRes = android.R.drawable.ic_menu_camera; - break; - } - - Drawable d = getResources().getDrawable(imageRes); - - - //d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); - d.setBounds(0, 0, imageSize, imageSize); - ImageSpan span = new ImageSpan(d, ImageSpan.ALIGN_BOTTOM); - - return span; - } - @Override public int getItemViewType(int position) { return 0; @@ -488,39 +451,6 @@ 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); - - 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); - - - 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); - } - - - @Override public void onStop() { super.onStop(); VpnStatus.removeStateListener(this); @@ -566,7 +496,7 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. setListAdapter(ladapter); - mTimeRadioGroup = (RadioGroup) v.findViewById(R.id.timeFormatRadioGroup); + mTimeRadioGroup = v.findViewById(R.id.timeFormatRadioGroup); mTimeRadioGroup.setOnCheckedChangeListener(this); if (ladapter.mTimeFormat == LogWindowListAdapter.TIME_FORMAT_ISO) { @@ -577,19 +507,15 @@ 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) { - Preferences.getDefaultSharedPreferences(getActivity()).edit().putBoolean(LaunchVPN.CLEARLOG, isChecked).apply(); - } - }); + mClearLogCheckBox = v.findViewById(R.id.clearlogconnect); + mClearLogCheckBox.setChecked(PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean(Constants.CLEARLOG, true)); + mClearLogCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> + Preferences.getDefaultSharedPreferences(getActivity()).edit().putBoolean(Constants.CLEARLOG, isChecked).apply()); - mSpeedView = (TextView) v.findViewById(R.id.speed); + mSpeedView = v.findViewById(R.id.speed); - mOptionsLayout = (LinearLayout) v.findViewById(R.id.logOptionsLayout); - mLogLevelSlider = (SeekBar) v.findViewById(R.id.LogLevelSlider); + mOptionsLayout = v.findViewById(R.id.logOptionsLayout); + mLogLevelSlider = v.findViewById(R.id.LogLevelSlider); mLogLevelSlider.setMax(VpnProfile.MAXLOGLEVEL - 1); mLogLevelSlider.setProgress(logLevel - 1); @@ -598,9 +524,9 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. if (getResources().getBoolean(R.bool.logSildersAlwaysVisible)) mOptionsLayout.setVisibility(View.VISIBLE); - mUpStatus = (TextView) v.findViewById(R.id.speedUp); - mDownStatus = (TextView) v.findViewById(R.id.speedDown); - mConnectStatus = (TextView) v.findViewById(R.id.speedStatus); + mUpStatus = v.findViewById(R.id.speedUp); + mDownStatus = v.findViewById(R.id.speedDown); + mConnectStatus = v.findViewById(R.id.speedStatus); if (mShowOptionsLayout) mOptionsLayout.setVisibility(View.VISIBLE); return v; @@ -635,17 +561,13 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. if (isAdded()) { final String cleanLogMessage = VpnStatus.getLastCleanLogMessage(getActivity()); - getActivity().runOnUiThread(new Runnable() { - - @Override - public void run() { - if (isAdded()) { - if (mSpeedView != null) { - mSpeedView.setText(cleanLogMessage); - } - if (mConnectStatus != null) - mConnectStatus.setText(cleanLogMessage); + getActivity().runOnUiThread(() -> { + if (isAdded()) { + if (mSpeedView != null) { + mSpeedView.setText(cleanLogMessage); } + if (mConnectStatus != null) + mConnectStatus.setText(cleanLogMessage); } }); } diff --git a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java index 44831049..f5cf590b 100644 --- a/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java +++ b/app/src/main/java/se/leap/bitmaskclient/utils/PreferenceHelper.java @@ -16,10 +16,13 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import de.blinkt.openvpn.VpnProfile; import se.leap.bitmaskclient.Provider; +import static android.content.Context.MODE_PRIVATE; import static se.leap.bitmaskclient.Constants.ALWAYS_ON_SHOW_DIALOG; import static se.leap.bitmaskclient.Constants.DEFAULT_SHARED_PREFS_BATTERY_SAVER; +import static se.leap.bitmaskclient.Constants.LAST_USED_PROFILE; import static se.leap.bitmaskclient.Constants.PREFERENCES_APP_VERSION; import static se.leap.bitmaskclient.Constants.PROVIDER_CONFIGURED; import static se.leap.bitmaskclient.Constants.PROVIDER_EIP_DEFINITION; @@ -140,6 +143,27 @@ public class PreferenceHelper { apply(); } + /** + * Sets the profile that is connected (to connect if the service restarts) + */ + public static void setLastUsedVpnProfile(Context context, VpnProfile connectedProfile) { + SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + SharedPreferences.Editor prefsedit = prefs.edit(); + prefsedit.putString(LAST_USED_PROFILE, connectedProfile.toJson()); + prefsedit.apply(); + } + + /** + * Returns the profile that was last connected (to connect if the service restarts) + */ + public static VpnProfile getLastConnectedVpnProfile(Context context) { + SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); + String lastConnectedProfileJson = preferences.getString(LAST_USED_PROFILE, null); + return VpnProfile.fromJson(lastConnectedProfileJson); + } + + + public static void clearDataOfLastProvider(SharedPreferences preferences) { clearDataOfLastProvider(preferences, false); @@ -206,7 +230,7 @@ public class PreferenceHelper { if (context == null) { return; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); preferences.edit().putBoolean(ALWAYS_ON_SHOW_DIALOG, showAlwaysOnDialog).apply(); } @@ -214,7 +238,7 @@ public class PreferenceHelper { if (context == null) { return true; } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); return preferences.getBoolean(ALWAYS_ON_SHOW_DIALOG, true); } @@ -232,36 +256,14 @@ public class PreferenceHelper { return result; } - /*public static void saveLastProfile(Context context, String uuid) { - if (context == null) { - return; - } - putString(context, PROVIDER_PROFILE_UUID, uuid); - } - - public static void clearLastProfile(Context context) { - if (context == null) { - return; - } - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); - preferences.edit().remove(PROVIDER_PROFILE_UUID).apply(); - } - - public static String getLastProfile(Context context){ - return getString(context, PROVIDER_PROFILE_UUID, null); - } - - public static void saveLastGatewayNumber(Context context, int number) { - - } -*/ public static String getString(Context context, String key, String defValue) { - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); return preferences.getString(key, defValue); } public static void putString(Context context, String key, String value){ - SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE); + SharedPreferences preferences = context.getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE); preferences.edit().putString(key, value).apply(); } + } diff --git a/app/src/main/res/drawable/cust_button_secondary.xml b/app/src/main/res/drawable/cust_button_secondary.xml index 553adca8..baed7b44 100644 --- a/app/src/main/res/drawable/cust_button_secondary.xml +++ b/app/src/main/res/drawable/cust_button_secondary.xml @@ -4,16 +4,16 @@ <shape android:shape="rectangle" > <corners android:radius="50dp" /> <padding android:left="8dp" android:right="8dp"/> - <solid android:color="@android:color/transparent"/> - <stroke android:width="2dp" android:color="@color/colorPrimary"/> + <solid android:color="#20000000"/> + <stroke android:width="2dp" android:color="@color/colorPrimaryDark"/> </shape> </item> <item android:state_focused="true"> <shape android:shape="rectangle" > <corners android:radius="50dp" /> <padding android:left="8dp" android:right="8dp"/> - <solid android:color="@android:color/transparent"/> - <stroke android:width="2dp" android:color="@color/colorPrimary"/> + <solid android:color="#20000000"/> + <stroke android:width="2dp" android:color="@color/colorPrimaryDark"/> </shape> </item> <item > diff --git a/app/src/main/res/layout/d_checkbox_confirm.xml b/app/src/main/res/layout/d_checkbox_confirm.xml index 6dd22417..a9a84c0e 100644 --- a/app/src/main/res/layout/d_checkbox_confirm.xml +++ b/app/src/main/res/layout/d_checkbox_confirm.xml @@ -1,24 +1,63 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical"> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> - <se.leap.bitmaskclient.views.IconTextView - android:id="@+id/user_message" + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="@dimen/activity_horizontal_margin" - android:layout_marginBottom="0dp" - android:autoLink="web" - /> + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical"> + <android.support.v7.widget.AppCompatTextView + android:id="@+id/tvTitle" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/standard_margin" + android:layout_marginTop="@dimen/add_button_margin" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:text="@string/always_on_vpn" + android:textAllCaps="true" + android:textAppearance="@style/TextAppearance.AppCompat.Title" + android:textStyle="bold" + /> - <CheckBox - android:id="@+id/do_not_show_again" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="@dimen/stdpadding" - android:text="@string/do_not_show_again" /> + <se.leap.bitmaskclient.views.IconTextView + android:id="@+id/user_message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_marginBottom="0dp" + android:autoLink="web" + tools:text="@string/always_on_vpn_user_message" + android:textSize="17sp" + /> + + <android.support.v7.widget.AppCompatTextView + android:id="@+id/block_vpn_user_message" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/activity_horizontal_margin" + android:layout_marginRight="@dimen/activity_horizontal_margin" + android:layout_marginTop="@dimen/standard_margin" + android:layout_marginBottom="@dimen/standard_margin" + android:text="@string/always_on_blocking_vpn_user_message" + android:visibility="gone" + tools:visibility="visible" + android:textSize="17sp" + /> + + + <CheckBox + android:id="@+id/do_not_show_again" + android:textAppearance="@style/TextAppearance.AppCompat" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="@dimen/stdpadding" + android:text="@string/do_not_show_again" /> -</LinearLayout>
\ No newline at end of file + </LinearLayout> +</ScrollView>
\ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0fba388e..0b25a2a2 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -113,6 +113,7 @@ <string name="always_on_vpn">VPN immer anlassen</string> <string name="do_not_show_again">Nicht erneut anzeigen</string> <string name="always_on_vpn_user_message">Um durchgehend aktives VPN in den Android VPN Einstellungen einzuschalten, klicke auf das Zahnrad [img src] und aktiviere den Schalter.</string> + <string name="always_on_blocking_vpn_user_message">Aktiviere zusätzlich die Option \"Verbindungen nur über VPN zulassen\", um deine Privatsphäre optimal zu schützen.</string> <string name="donate_title">Spenden</string> <string name="donate_default_message">Spende noch heute, wenn du sichere Kommunikation wertschätzt, die sowohl für Endnutzer*innen als auch Menschen die den Service bereit stellen leicht nutzbar ist.</string> <string name="donate_message">LEAP benötigt Spenden und Stipendien. Spende noch heute wenn du sichere Kommunikation wertschätzt, die sowohl für Enandwender*innen als auch Menschen die den Dienst anbieten leicht nutzbar ist.</string> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 551b7cb1..1b22592a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,6 +104,7 @@ <string name="always_on_vpn">Always-on VPN</string> <string name="do_not_show_again">Do not show again</string> <string name="always_on_vpn_user_message">To enable always-on VPN in Android VPN Settings click on the configure icon [img src] and turn the switch on.</string> + <string name="always_on_blocking_vpn_user_message">To protect your privacy optimally, you should also activate the option \"Block connections without VPN\".</string> <string name="donate_title">Donate</string> <string name="donate_default_message">Please donate today if you value secure communication that is easy for both the end-user and the service provider.</string> <string name="donate_message">LEAP depends on donations and grants. Please donate today if you value secure communication that is easy for both the end-user and the service provider.</string> diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/EipStatusTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/EipStatusTest.java index f332b094..8495f962 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/EipStatusTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/EipStatusTest.java @@ -3,14 +3,17 @@ package se.leap.bitmaskclient.eip; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConnectionStatus; -import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.core.VpnStatus; import se.leap.bitmaskclient.R; +import se.leap.bitmaskclient.testutils.MockHelper; +import se.leap.bitmaskclient.testutils.TestSetupHelper; +import se.leap.bitmaskclient.utils.PreferenceHelper; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_AUTH_FAILED; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED; @@ -21,6 +24,8 @@ import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_VPNPAUSED; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; import static de.blinkt.openvpn.core.ConnectionStatus.UNKNOWN_LEVEL; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.powermock.api.mockito.PowerMockito.doNothing; import static org.powermock.api.mockito.PowerMockito.mockStatic; import static org.powermock.api.mockito.PowerMockito.when; import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.CONNECTING; @@ -32,7 +37,7 @@ import static se.leap.bitmaskclient.eip.EipStatus.EipLevel.UNKNOWN; * TODO: Mock AsyncTask */ @RunWith(PowerMockRunner.class) -@PrepareForTest({ProfileManager.class}) +@PrepareForTest({PreferenceHelper.class}) public class EipStatusTest { EipStatus eipStatus; @@ -55,10 +60,11 @@ public class EipStatusTest { @Test public void testUpdateState_LEVEL_VPNPAUSED_hasPersistentTun() throws Exception { - mockStatic(ProfileManager.class); + mockStatic(PreferenceHelper.class); VpnProfile mockVpnProfile = new VpnProfile("mockProfile"); mockVpnProfile.mPersistTun = true; - when(ProfileManager.getLastConnectedVpn()).thenReturn(mockVpnProfile); + doNothing().when(PreferenceHelper.class); + VpnStatus.setLastConnectedVpnProfile(null, mockVpnProfile); VpnStatus.updateStateString("SCREENOFF", "", R.string.state_screenoff, LEVEL_VPNPAUSED); assertTrue("LEVEL_VPN_PAUSED eipLevel", eipStatus.getEipLevel() == CONNECTING); assertTrue("LEVEL_VPN_PAUSED level", eipStatus.getLevel() == LEVEL_VPNPAUSED); @@ -67,10 +73,11 @@ public class EipStatusTest { @Test public void testUpdateState_LEVEL_VPNPAUSED_hasNotPersistentTun() throws Exception { - mockStatic(ProfileManager.class); + mockStatic(PreferenceHelper.class); VpnProfile mockVpnProfile = new VpnProfile("mockProfile"); mockVpnProfile.mPersistTun = false; - when(ProfileManager.getLastConnectedVpn()).thenReturn(mockVpnProfile); + doNothing().when(PreferenceHelper.class); + VpnStatus.setLastConnectedVpnProfile(null, mockVpnProfile); VpnStatus.updateStateString("SCREENOFF", "", R.string.state_screenoff, LEVEL_VPNPAUSED); assertTrue("LEVEL_VPN_PAUSED eipLevel", eipStatus.getEipLevel() == DISCONNECTED); assertTrue("LEVEL_VPN_PAUSED level", eipStatus.getLevel() == LEVEL_VPNPAUSED); diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java index 4726cab7..160e5ddd 100644 --- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java +++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java @@ -46,7 +46,6 @@ public class GatewaysManagerTest { when(sharedPreferences.getString(eq(Constants.PROVIDER_PRIVATE_KEY), anyString())).thenReturn(secrets.getString(Constants.PROVIDER_PRIVATE_KEY)); when(sharedPreferences.getString(eq(Provider.CA_CERT), anyString())).thenReturn(secrets.getString(Provider.CA_CERT)); when(sharedPreferences.getString(eq(Constants.PROVIDER_VPN_CERTIFICATE), anyString())).thenReturn(secrets.getString(Constants.PROVIDER_VPN_CERTIFICATE)); - when(mockContext.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPreferences); gatewaysManager = new GatewaysManager(mockContext, sharedPreferences); @@ -74,7 +73,7 @@ public class GatewaysManagerTest { String eipServiceJson = TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("eip-service-two-gateways.json")); gatewaysManager.fromEipServiceJson(new JSONObject(eipServiceJson)); assertEquals(2, gatewaysManager.size()); - gatewaysManager.clearGatewaysAndProfiles(); + gatewaysManager.clearGateways(); assertEquals(0, gatewaysManager.size()); } |