diff --git a/app/src/custom/assets/riseup.net.json b/app/src/custom/assets/riseup.net.json
index 5e14abc38413e5dc23b368e9b25c339a6fbc82a7..407e2e22dd404d0541e6f8601afc7904cb739867 100644
--- a/app/src/custom/assets/riseup.net.json
+++ b/app/src/custom/assets/riseup.net.json
@@ -1,37 +1,37 @@
 {
-  "api_uri":"https://api.black.riseup.net:4430",
-  "api_version":"3",
-  "ca_cert_fingerprint":"SHA256: dd919b7513b4a1368faa20e38cd3314156805677f48b787cdd9b4a92dec64eb0",
-  "ca_cert_uri":"https://black.riseup.net/ca.crt",
-  "default_language":"en",
-  "description":{
-    "en": "Riseup is a non-profit collective in Seattle that provides online communication tools for people and groups working toward liberatory social change."
+  "api_uri": "https://api.black.riseup.net:4430",
+  "api_version": "3",
+  "ca_cert_fingerprint": "SHA256: dd919b7513b4a1368faa20e38cd3314156805677f48b787cdd9b4a92dec64eb0",
+  "ca_cert_uri": "https://black.riseup.net/ca.crt",
+  "default_language": "en",
+  "description": {
+    "en": "Riseup Networks"
   },
-  "domain":"riseup.net",
-  "enrollment_policy":"open",
-  "languages":[
+  "domain": "riseup.net",
+  "enrollment_policy": "open",
+  "languages": [
     "en"
   ],
-  "name":{
-    "en":"Riseup Networks"
+  "name": {
+    "en": "Riseup Networks"
   },
-  "service":{
-    "allow_anonymous":true,
-    "allow_free":true,
-    "allow_limited_bandwidth":false,
-    "allow_paid":false,
-    "allow_registration":false,
-    "allow_unlimited_bandwidth":true,
-    "bandwidth_limit":102400,
-    "default_service_level":1,
-    "levels":{
-      "1":{
-        "description":"Please donate.",
-        "name":"free"
+  "service": {
+    "allow_anonymous": true,
+    "allow_free": true,
+    "allow_limited_bandwidth": false,
+    "allow_paid": false,
+    "allow_registration": false,
+    "allow_unlimited_bandwidth": true,
+    "bandwidth_limit": 102400,
+    "default_service_level": 1,
+    "levels": {
+      "1": {
+        "description": "Please donate.",
+        "name": "free"
       }
     }
   },
-  "services":[
+  "services": [
     "openvpn"
   ]
 }
\ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java
index 37f9eb18eac1517c4ef1b3c3c5c473c9c9f9a2df..e8789b32c7cda0526a3f4b60db700b037015138b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/CensorshipCircumventionFragment.java
@@ -5,6 +5,7 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4Kcp;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUsePortHopping;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.resetSnowflakeSettings;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUsePortHopping;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUseTunnel;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useBridges;
@@ -29,11 +30,11 @@ import se.leap.bitmaskclient.databinding.FCensorshipCircumventionBinding;
 import se.leap.bitmaskclient.eip.EipCommand;
 
 public class CensorshipCircumventionFragment extends Fragment {
-    public static int DISCOVERY_NONE = 100200000;
+    public static int DISCOVERY_AUTOMATICALLY = 100200000;
     public static int DISCOVERY_SNOWFLAKE = 100200001;
     public static int DISCOVERY_INVITE_PROXY = 100200002;
 
-    public static int TUNNELING_NONE = 100300000;
+    public static int TUNNELING_AUTOMATICALLY = 100300000;
     public static int TUNNELING_OBFS4 = 100300001;
     public static int TUNNELING_OBFS4_KCP = 100300002;
 
@@ -68,20 +69,20 @@ public class CensorshipCircumventionFragment extends Fragment {
 
 
     private void initDiscovery() {
-
-        RadioButton noneRadioButton = new RadioButton(binding.getRoot().getContext());
-        noneRadioButton.setText(getText(R.string.automatically_select));
-        noneRadioButton.setId(DISCOVERY_NONE);
-        noneRadioButton.setChecked(!(hasSnowflakePrefs() && getUseSnowflake()) && !ProviderObservable.getInstance().getCurrentProvider().hasIntroducer());
-        binding.discoveryRadioGroup.addView(noneRadioButton);
+        boolean hasIntroducer = ProviderObservable.getInstance().getCurrentProvider().hasIntroducer();
+        RadioButton automaticallyRadioButton = new RadioButton(binding.getRoot().getContext());
+        automaticallyRadioButton.setText(getText(R.string.automatically_select));
+        automaticallyRadioButton.setId(DISCOVERY_AUTOMATICALLY);
+        automaticallyRadioButton.setChecked(!hasSnowflakePrefs() && !hasIntroducer);
+        binding.discoveryRadioGroup.addView(automaticallyRadioButton);
 
         RadioButton snowflakeRadioButton = new RadioButton(binding.getRoot().getContext());
         snowflakeRadioButton.setText(getText(R.string.snowflake));
         snowflakeRadioButton.setId(DISCOVERY_SNOWFLAKE);
-        snowflakeRadioButton.setChecked(hasSnowflakePrefs() && getUseSnowflake());
+        snowflakeRadioButton.setChecked(!hasIntroducer && hasSnowflakePrefs() && getUseSnowflake());
         binding.discoveryRadioGroup.addView(snowflakeRadioButton);
 
-        if (ProviderObservable.getInstance().getCurrentProvider().hasIntroducer()) {
+        if (hasIntroducer) {
             RadioButton inviteProxyRadioButton = new RadioButton(binding.getRoot().getContext());
             inviteProxyRadioButton.setText(getText(R.string.invite_proxy));
             inviteProxyRadioButton.setId(DISCOVERY_INVITE_PROXY);
@@ -91,15 +92,13 @@ public class CensorshipCircumventionFragment extends Fragment {
 
         binding.discoveryRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
             useBridges(true);
-            if (checkedId == DISCOVERY_NONE) {
-                useSnowflake(false);
+            if (checkedId == DISCOVERY_AUTOMATICALLY) {
+                resetSnowflakeSettings();
             } else if (checkedId == DISCOVERY_SNOWFLAKE) {
                 useSnowflake(true);
             } else if (checkedId == DISCOVERY_INVITE_PROXY) {
                 useSnowflake(false);
             }
-
-            tryReconnectVpn();
         });
     }
 
@@ -115,7 +114,7 @@ public class CensorshipCircumventionFragment extends Fragment {
         RadioButton noneRadioButton = new RadioButton(binding.getRoot().getContext());
         noneRadioButton.setText(getText(R.string.automatically_select));
         noneRadioButton.setChecked(!getUseObfs4() && !getUseObfs4Kcp());
-        noneRadioButton.setId(TUNNELING_NONE);
+        noneRadioButton.setId(TUNNELING_AUTOMATICALLY);
         binding.tunnelingRadioGroup.addView(noneRadioButton);
 
         if (ProviderObservable.getInstance().getCurrentProvider().supportsObfs4()) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
index eb777abc52384ec3d5352119ff42530158f922d7..4567bf9744cd50fb389a5d9199f3316e34cf30e7 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
@@ -3,7 +3,7 @@ package se.leap.bitmaskclient.base.fragments;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 import static se.leap.bitmaskclient.R.string.advanced_settings;
-import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_NONE;
+import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_AUTOMATICALLY;
 import static se.leap.bitmaskclient.base.models.Constants.GATEWAY_PINNING;
 import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP;
 import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
@@ -16,6 +16,7 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getPreferUDP;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getShowAlwaysOnDialog;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.preferUDP;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.resetSnowflakeSettings;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setAllowExperimentalTransports;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUseObfuscationPinning;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUsePortHopping;
@@ -23,7 +24,7 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUseTunnel;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useBridges;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useObfuscationPinning;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useSnowflake;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.usesManualBridges;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useManualBridgeSettings;
 import static se.leap.bitmaskclient.base.utils.ViewHelper.setActionBarSubtitle;
 
 import android.app.AlertDialog;
@@ -37,10 +38,12 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.EditText;
+import android.widget.LinearLayout;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatTextView;
 import androidx.appcompat.widget.SwitchCompat;
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.Fragment;
@@ -94,43 +97,42 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh
 
     private void initAutomaticCircumventionEntry(View rootView) {
         IconSwitchEntry automaticCircumvention = rootView.findViewById(R.id.bridge_automatic_switch);
-        if (ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports()) {
-            automaticCircumvention.setVisibility(VISIBLE);
-
-            automaticCircumvention.setChecked(getUseBridges() && !usesManualBridges());
-            automaticCircumvention.setOnCheckedChangeListener((buttonView, isChecked) -> {
-                if (!buttonView.isPressed()) {
-                    return;
-                }
+        automaticCircumvention.setChecked(getUseBridges() && !useManualBridgeSettings());
+        automaticCircumvention.setOnCheckedChangeListener((buttonView, isChecked) -> {
+            if (!buttonView.isPressed()) {
+                return;
+            }
 
-                if (isChecked) {
-                    useSnowflake(false);
-                    setUseTunnel(TUNNELING_NONE);
-                    setUsePortHopping(false);
-                }
+            if (isChecked) {
+                resetSnowflakeSettings();
+                setUseTunnel(TUNNELING_AUTOMATICALLY);
+                setUsePortHopping(false);
+            } else {
+                useSnowflake(false);
+            }
+            if (ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports()) {
                 useBridges(isChecked);
                 if (VpnStatus.isVPNActive()) {
                     EipCommand.startVPN(getContext(), false);
                     Toast.makeText(getContext(), R.string.reconnecting, Toast.LENGTH_LONG).show();
                 }
-            });
+            }
+        });
 
-            //We check the UI state of the useUdpEntry here as well, in order to avoid a situation
-            //where both entries are disabled, because both preferences are enabled.
-            //bridges can be enabled not only from here but also from error handling
-            boolean useUDP = getPreferUDP() && useUdpEntry.isEnabled();
-            automaticCircumvention.setEnabled(!useUDP);
-            automaticCircumvention.setSubtitle(getString(useUDP?R.string.disabled_while_udp_on:R.string.automatic_bridge_description));
-        } else {
-            automaticCircumvention.setVisibility(GONE);
-        }
+        //We check the UI state of the useUdpEntry here as well, in order to avoid a situation
+        //where both entries are disabled, because both preferences are enabled.
+        //bridges can be enabled not only from here but also from error handling
+        boolean useUDP = getPreferUDP() && useUdpEntry.isEnabled();
+        automaticCircumvention.setEnabled(!useUDP);
+        automaticCircumvention.setSubtitle(getString(useUDP ? R.string.disabled_while_udp_on : R.string.automatic_bridge_description));
     }
 
     private void initManualCircumventionEntry(View rootView) {
+        LinearLayout manualConfigRoot = rootView.findViewById(R.id.bridge_manual_switch_entry);
+        manualConfigRoot.setVisibility(ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports() ? VISIBLE : GONE);
         IconTextEntry manualConfiguration = rootView.findViewById(R.id.bridge_manual_switch);
-        manualConfiguration.setVisibility(ProviderObservable.getInstance().getCurrentProvider().supportsPluggableTransports() ? VISIBLE : GONE);
         SwitchCompat manualConfigurationSwitch = rootView.findViewById(R.id.bridge_manual_switch_control);
-        boolean usesManualBridge = usesManualBridges();
+        boolean usesManualBridge = useManualBridgeSettings();
         manualConfigurationSwitch.setChecked(usesManualBridge);
         manualConfigurationSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
             if (!buttonView.isPressed()) {
@@ -148,12 +150,12 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh
         //bridges can be enabled not only from here but also from error handling
         boolean useUDP = getPreferUDP() && useUdpEntry.isEnabled();
         manualConfiguration.setEnabled(!useUDP);
-        manualConfiguration.setSubtitle(getString(useUDP? R.string.disabled_while_udp_on:R.string.manual_bridge_description));
+        manualConfiguration.setSubtitle(getString(useUDP ? R.string.disabled_while_udp_on : R.string.manual_bridge_description));
     }
 
     private void resetManualConfig() {
         useSnowflake(false);
-        setUseTunnel(TUNNELING_NONE);
+        setUseTunnel(TUNNELING_AUTOMATICALLY);
         setUsePortHopping(false);
         useBridges(false);
         if (VpnStatus.isVPNActive()) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
index 25a9a3fbbea4df61355bb67c24e903f101e0c9ef..b7c6db5d73991884e0d21bc0adf08e6adff21a13 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/PreferenceHelper.java
@@ -1,7 +1,7 @@
 package se.leap.bitmaskclient.base.utils;
 
 import static android.content.Context.MODE_PRIVATE;
-import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_NONE;
+import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_AUTOMATICALLY;
 import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_OBFS4;
 import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_OBFS4_KCP;
 import static se.leap.bitmaskclient.base.models.Constants.ALLOW_EXPERIMENTAL_TRANSPORTS;
@@ -65,15 +65,13 @@ import androidx.annotation.WorkerThread;
 import androidx.security.crypto.EncryptedSharedPreferences;
 import androidx.security.crypto.MasterKey;
 
-import com.google.gson.Gson;
-
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.IOException;
-import java.net.MalformedURLException;
 import java.net.URL;
 import java.security.GeneralSecurityException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -81,9 +79,6 @@ import java.util.Set;
 
 import de.blinkt.openvpn.VpnProfile;
 import de.blinkt.openvpn.core.NativeUtils;
-import io.swagger.client.JSON;
-import mobile.BitmaskMobile;
-import mobilemodels.BitmaskMobileCore;
 import se.leap.bitmaskclient.BuildConfig;
 import se.leap.bitmaskclient.base.models.Introducer;
 import se.leap.bitmaskclient.base.models.Provider;
@@ -218,20 +213,17 @@ public class PreferenceHelper {
     public static HashMap<String, Provider> getCustomProviders() {
         Set<String> providerDomains = getCustomProviderDomains();
         HashMap<String, Provider> customProviders = new HashMap<>();
-        if (providerDomains.size() > 0) {
-            for (String domain : providerDomains) {
-                String mainURL = preferences.getString(Provider.MAIN_URL + "." + domain, null);
-                if (mainURL != null) {
-                    Introducer introducer = null;
-                    try {
-                       introducer = Introducer.fromUrl(BitmaskCoreProvider.getBitmaskMobile().getIntroducerURLByDomain(domain));
-                    } catch (Exception e) {
-                        e.printStackTrace();
-                    }
-                    customProviders.put(mainURL, Provider.createCustomProvider(mainURL, domain, introducer));
+        for (String domain : providerDomains) {
+            String mainURL = preferences.getString(Provider.MAIN_URL + "." + domain, null);
+            if (mainURL != null) {
+                Introducer introducer = null;
+                try {
+                   introducer = Introducer.fromUrl(BitmaskCoreProvider.getBitmaskMobile().getIntroducerURLByDomain(domain));
+                } catch (Exception e) {
+                    e.printStackTrace();
                 }
+                customProviders.put(mainURL, Provider.createCustomProvider(mainURL, domain, introducer));
             }
-
         }
 
         return customProviders;
@@ -491,6 +483,10 @@ public class PreferenceHelper {
         return hasKey(USE_SNOWFLAKE);
     }
 
+    public static void resetSnowflakeSettings() {
+        removeKey(USE_SNOWFLAKE);
+    }
+
     public static Boolean getUseSnowflake() {
         return getBoolean(USE_SNOWFLAKE, true);
     }
@@ -619,12 +615,8 @@ public class PreferenceHelper {
         return getUseTunnel() == TUNNELING_OBFS4_KCP;
     }
 
-    public static boolean usesManualBridges(){
-        return getUseSnowflake() || usesSpecificTunnel() || getUsePortHopping();
-    }
-
-    public static boolean usesSpecificTunnel() {
-        return getUseObfs4() || getUseObfs4Kcp();
+    public static boolean useManualBridgeSettings(){
+        return (hasSnowflakePrefs() && getUseSnowflake()) || getUseObfs4() || getUseObfs4Kcp() || getUsePortHopping();
     }
 
     public static void setUseTunnel(int tunnel) {
@@ -632,7 +624,7 @@ public class PreferenceHelper {
     }
 
     public static int getUseTunnel() {
-        return getInt(USE_TUNNEL, TUNNELING_NONE);
+        return getInt(USE_TUNNEL, TUNNELING_AUTOMATICALLY);
     }
 
     public static boolean useIpv6Firewall() {
@@ -709,6 +701,12 @@ public class PreferenceHelper {
         }
     }
 
+    public static void removeKey(String key) {
+        synchronized (LOCK) {
+            preferences.edit().remove(key).apply();
+        }
+    }
+
     public static long getLong(String key, long defValue) {
         synchronized (LOCK) {
             return preferences.getLong(key, defValue);
@@ -851,7 +849,7 @@ public class PreferenceHelper {
 
             @Override
             public byte[] getByteArray(String s) {
-                String encodedString = preferences.getString(s, "");
+                String encodedString = preferences.getString(s, Arrays.toString(Base64.encode(new byte[0], Base64.DEFAULT)));
                 try {
                     return Base64.decode(encodedString, Base64.DEFAULT);
                 } catch (IllegalArgumentException e) {
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 d7f3b42ea7357741ffd86de7237d66e02954b5b2..9655013c600a4fa5f3721e0b1ede34ef5ac09e85 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaysManager.java
@@ -37,7 +37,6 @@ import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseBridges;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseObfs4Kcp;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUsePortHopping;
-import static se.leap.bitmaskclient.base.utils.PreferenceHelper.usesSpecificTunnel;
 
 import android.content.Context;
 import android.util.Log;
@@ -54,10 +53,8 @@ import org.json.JSONObject;
 import java.io.IOException;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Set;
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java
index 9af14eda4e83566c746b90bee7a32ad74101b1a8..2e2497d515347ad0431388984d611aa94af02ead 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerV5.java
@@ -66,6 +66,7 @@ public class ProviderApiManagerV5 extends ProviderApiManagerBase implements IPro
             case SET_UP_PROVIDER:
                 result = setupProvider(provider, parameters);
                 if (result.getBoolean(BROADCAST_RESULT_KEY)) {
+                    serviceCallback.saveProvider(provider);
                     eventSender.sendToReceiverOrBroadcast(receiver, PROVIDER_OK, result, provider);
                 } else {
                     eventSender.sendToReceiverOrBroadcast(receiver, PROVIDER_NOK, result, provider);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
index 63fbde09e06d82af063ed622d7ead5e93b98bc6b..bcb177e2bf32043645d2f38a217fcf574b526cd6 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderManager.java
@@ -45,8 +45,11 @@ public class ProviderManager {
     private boolean addDummyEntry = false;
 
     public static ProviderManager getInstance(AssetManager assetsManager) {
-        if (instance == null)
+        if (instance == null) {
             instance = new ProviderManager(assetsManager);
+        } else {
+            instance.updateCustomProviders();
+        }
 
         return instance;
     }
@@ -63,7 +66,7 @@ public class ProviderManager {
     private ProviderManager(AssetManager assetManager) {
         this.assetsManager = assetManager;
         addDefaultProviders(assetManager);
-        addCustomProviders();
+        updateCustomProviders();
     }
 
     private void addDefaultProviders(AssetManager assetManager) {
@@ -117,7 +120,7 @@ public class ProviderManager {
     }
 
 
-    private void addCustomProviders() {
+    public void updateCustomProviders() {
         customProviders = PreferenceHelper.getCustomProviders();
     }
 
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
index 58fccc6511d989141bd3599cf388dafc1439acf5..d7d8516eb822ac808d090e3687ecfc68cbaca3d2 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/CircumventionSetupFragment.java
@@ -1,5 +1,6 @@
 package se.leap.bitmaskclient.providersetup.fragments;
 
+import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_AUTOMATICALLY;
 import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.isDefaultBitmask;
 
 import android.graphics.Typeface;
@@ -35,14 +36,17 @@ public class CircumventionSetupFragment extends BaseSetupFragment implements Can
             if (binding.rbCircumvention.getId() == checkedId) {
                 PreferenceHelper.useBridges(true);
                 PreferenceHelper.useSnowflake(true);
+                PreferenceHelper.setUseTunnel(TUNNELING_AUTOMATICALLY);
                 binding.tvCircumventionDetailDescription.setVisibility(View.VISIBLE);
                 binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
                 binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.NORMAL);
                 return;
             }
-
+            // otherwise don't use obfuscation
             PreferenceHelper.useBridges(false);
-            PreferenceHelper.useSnowflake(false);
+            PreferenceHelper.resetSnowflakeSettings();
+            PreferenceHelper.setUsePortHopping(false);
+            PreferenceHelper.setUseTunnel(TUNNELING_AUTOMATICALLY);
             binding.tvCircumventionDetailDescription.setVisibility(View.GONE);
             binding.rbPlainVpn.setTypeface(Typeface.DEFAULT, Typeface.BOLD);
             binding.rbCircumvention.setTypeface(Typeface.DEFAULT, Typeface.NORMAL);
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
index 8b4b7ad8b7e51fdf4073b49cbef87bb82f8724df..b9051b1e28b38cae5a8f96a048e272153c3cfa76 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ConfigureProviderFragment.java
@@ -7,11 +7,16 @@ import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
 import static se.leap.bitmaskclient.R.string.app_name;
 import static se.leap.bitmaskclient.R.string.description_configure_provider;
 import static se.leap.bitmaskclient.R.string.description_configure_provider_circumvention;
+import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_AUTOMATICALLY;
 import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_CODE;
 import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
 import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
 import static se.leap.bitmaskclient.base.utils.BuildConfigHelper.isDefaultBitmask;
 import static se.leap.bitmaskclient.base.utils.PreferenceHelper.getUseSnowflake;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.hasSnowflakePrefs;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUsePortHopping;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.setUseTunnel;
+import static se.leap.bitmaskclient.base.utils.PreferenceHelper.useSnowflake;
 import static se.leap.bitmaskclient.base.utils.ViewHelper.animateContainerVisibility;
 import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_VPN_CERTIFICATE;
 import static se.leap.bitmaskclient.providersetup.ProviderAPI.DOWNLOAD_VPN_CERTIFICATE;
@@ -48,6 +53,7 @@ import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
 import java.util.List;
 
+import de.blinkt.openvpn.core.VpnStatus;
 import se.leap.bitmaskclient.R;
 import se.leap.bitmaskclient.base.models.Constants;
 import se.leap.bitmaskclient.base.models.Provider;
@@ -126,7 +132,7 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Prop
     public void onFragmentSelected() {
         super.onFragmentSelected();
         ignoreProviderAPIUpdates = false;
-        binding.detailContainer.setVisibility(getUseSnowflake() ? VISIBLE : GONE);
+        binding.detailContainer.setVisibility(!VpnStatus.isVPNActive() && hasSnowflakePrefs() && getUseSnowflake() ? VISIBLE : GONE);
         binding.tvCircumventionDescription.setText(getUseSnowflake() ? getString(description_configure_provider_circumvention, getString(app_name)) : getString(description_configure_provider, getString(app_name)));
         if (!isDefaultBitmask()) {
             Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.setup_progress_spinner, null);
@@ -138,6 +144,14 @@ public class ConfigureProviderFragment extends BaseSetupFragment implements Prop
         if (ProviderSetupObservable.isSetupRunning()) {
             handleResult(ProviderSetupObservable.getResultCode(), ProviderSetupObservable.getResultData(), true);
         } else {
+            Provider provider = setupActivityCallback.getSelectedProvider();
+            if (provider != null && provider.hasIntroducer()) {
+                // enable automatic selection of bridges
+                useSnowflake(false);
+                setUseTunnel(TUNNELING_AUTOMATICALLY);
+                setUsePortHopping(false);
+                PreferenceHelper.useBridges(true);
+            }
             ProviderSetupObservable.startSetup();
             Bundle parameters = new Bundle();
             parameters.putString(Constants.COUNTRYCODE, PreferenceHelper.getBaseCountry());
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java
index 66b1dd0064e9a8fc169331e9ced8262066c54b12..823004b2391600f6ea89cf0c6fcb64e4fb205ab1 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/fragments/ProviderSelectionFragment.java
@@ -75,6 +75,7 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc
         binding = FProviderSelectionBinding.inflate(inflater, container, false);
 
         radioButtons = new ArrayList<>();
+        // add configured providers
         for (int i = 0; i < viewModel.size(); i++) {
             RadioButton radioButton = new RadioButton(binding.getRoot().getContext());
             radioButton.setText(viewModel.getProviderName(i));
@@ -83,13 +84,14 @@ public class ProviderSelectionFragment extends BaseSetupFragment implements Canc
             radioButtons.add(radioButton);
         }
 
+        // add new provider entry
         RadioButton addProviderRadioButton = new RadioButton(binding.getRoot().getContext());
         addProviderRadioButton.setText(getText(R.string.add_provider));
         addProviderRadioButton.setId(ADD_PROVIDER);
         binding.providerRadioGroup.addView(addProviderRadioButton);
         radioButtons.add(addProviderRadioButton);
 
-
+        // invite code entry
         RadioButton inviteCodeRadioButton = new RadioButton(binding.getRoot().getContext());
         inviteCodeRadioButton.setText(R.string.enter_invite_code);
         inviteCodeRadioButton.setId(INVITE_CODE_PROVIDER);
diff --git a/app/src/main/res/layout/f_settings.xml b/app/src/main/res/layout/f_settings.xml
index e7b4356f92e79f2b6fa5c63ecf5ace924d123c7f..87a97455352ec7a9db5e37d0a6c3f55f2fa8033f 100644
--- a/app/src/main/res/layout/f_settings.xml
+++ b/app/src/main/res/layout/f_settings.xml
@@ -60,9 +60,11 @@
             app:text="@string/automatic_bridge"
             app:subtitle="@string/automatic_bridge_description"
             app:icon="@drawable/bridge_automatic"
+            android:visibility="visible"
             app:singleLine="false" />
 
         <LinearLayout
+            android:id="@+id/bridge_manual_switch_entry"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:orientation="horizontal">
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 57da5bf2ac1e15b8f92128fd0673eeb0f340d766..6351ade7835f2dcbd17699b2ff7992ac6dc1a391 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -250,7 +250,7 @@
     <string name="scan_qr_code">Scan QR Code</string>
     <string name="invalid_code">Invalid code</string>
     <string name="automatic_bridge">Automatic (recommended)</string>
-    <string name="automatic_bridge_description">Connection wil be attemptd using the best available birdges and protocols.</string>
+    <string name="automatic_bridge_description">Connection will be attempted using the best available bridges and protocols.</string>
     <string name="manual_bridge">Manual Configuration</string>
     <string name="manual_bridge_description">Select private bridges and specific protocols</string>
     <string name="censorship_circumvention_description">Manual configuration requires technical understanding. Proceed with caution.</string>
diff --git a/app/src/normal/assets/riseup.net.json b/app/src/normal/assets/riseup.net.json
index 5e14abc38413e5dc23b368e9b25c339a6fbc82a7..407e2e22dd404d0541e6f8601afc7904cb739867 100644
--- a/app/src/normal/assets/riseup.net.json
+++ b/app/src/normal/assets/riseup.net.json
@@ -1,37 +1,37 @@
 {
-  "api_uri":"https://api.black.riseup.net:4430",
-  "api_version":"3",
-  "ca_cert_fingerprint":"SHA256: dd919b7513b4a1368faa20e38cd3314156805677f48b787cdd9b4a92dec64eb0",
-  "ca_cert_uri":"https://black.riseup.net/ca.crt",
-  "default_language":"en",
-  "description":{
-    "en": "Riseup is a non-profit collective in Seattle that provides online communication tools for people and groups working toward liberatory social change."
+  "api_uri": "https://api.black.riseup.net:4430",
+  "api_version": "3",
+  "ca_cert_fingerprint": "SHA256: dd919b7513b4a1368faa20e38cd3314156805677f48b787cdd9b4a92dec64eb0",
+  "ca_cert_uri": "https://black.riseup.net/ca.crt",
+  "default_language": "en",
+  "description": {
+    "en": "Riseup Networks"
   },
-  "domain":"riseup.net",
-  "enrollment_policy":"open",
-  "languages":[
+  "domain": "riseup.net",
+  "enrollment_policy": "open",
+  "languages": [
     "en"
   ],
-  "name":{
-    "en":"Riseup Networks"
+  "name": {
+    "en": "Riseup Networks"
   },
-  "service":{
-    "allow_anonymous":true,
-    "allow_free":true,
-    "allow_limited_bandwidth":false,
-    "allow_paid":false,
-    "allow_registration":false,
-    "allow_unlimited_bandwidth":true,
-    "bandwidth_limit":102400,
-    "default_service_level":1,
-    "levels":{
-      "1":{
-        "description":"Please donate.",
-        "name":"free"
+  "service": {
+    "allow_anonymous": true,
+    "allow_free": true,
+    "allow_limited_bandwidth": false,
+    "allow_paid": false,
+    "allow_registration": false,
+    "allow_unlimited_bandwidth": true,
+    "bandwidth_limit": 102400,
+    "default_service_level": 1,
+    "levels": {
+      "1": {
+        "description": "Please donate.",
+        "name": "free"
       }
     }
   },
-  "services":[
+  "services": [
     "openvpn"
   ]
 }
\ No newline at end of file
diff --git a/app/src/normalProductionFatDebug/assets/demo.bitmask.net.json b/app/src/normalProductionFatDebug/assets/demo.bitmask.net.json
deleted file mode 100644
index f5998c9bd6400807d1b38a371a38a65cb0eaeb62..0000000000000000000000000000000000000000
--- a/app/src/normalProductionFatDebug/assets/demo.bitmask.net.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
-  "api_uri":"https://api.demo.bitmask.net:4430",
-  "api_version":"3",
-  "ca_cert_fingerprint":"SHA256: 40ed9d9c13872c1fcba25abdcf26a7b1bdeded433d74fbe1ccb58fbaaab58e23",
-  "ca_cert_uri":"https://api.demo.bitmask.net/ca.crt",
-  "default_language":"en",
-  "description":{
-    "en":"This is a demo provider"
-  },
-  "domain":"demo.bitmask.net",
-  "enrollment_policy":"open",
-  "languages":[
-    "en"
-  ],
-  "name":{
-    "en":"Demo provider"
-  },
-  "service":{
-    "allow_anonymous":true,
-    "allow_free":true,
-    "allow_limited_bandwidth":false,
-    "allow_paid":false,
-    "allow_registration":false,
-    "allow_unlimited_bandwidth":true,
-    "bandwidth_limit":102400,
-    "default_service_level":1,
-    "levels":{
-      "1":{
-        "description":"Please donate.",
-        "name":"free"
-      }
-    }
-  },
-  "services":[
-    "openvpn"
-  ]
-}
\ No newline at end of file
diff --git a/app/src/normalProductionFatDebug/assets/demo.bitmask.net.pem b/app/src/normalProductionFatDebug/assets/demo.bitmask.net.pem
deleted file mode 100644
index f1ede7ab4593149f590890a691dcd99bc5403801..0000000000000000000000000000000000000000
--- a/app/src/normalProductionFatDebug/assets/demo.bitmask.net.pem
+++ /dev/null
@@ -1,10 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBYTCCAQigAwIBAgIBATAKBggqhkjOPQQDAjAXMRUwEwYDVQQDEwxMRUFQIFJv
-b3QgQ0EwHhcNMjQwMjIxMTEzMTUwWhcNMjkwMjIxMTEzNjUwWjAXMRUwEwYDVQQD
-EwxMRUFQIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARKTm8AKkqK
-aMI7dEarRRGEOPa3i49YE4bGNHxO97h14urXOROJWjnwHJdJ3dJk16oR0HKohXR7
-jSxyukoonJkgo0UwQzAOBgNVHQ8BAf8EBAMCAqQwEgYDVR0TAQH/BAgwBgEB/wIB
-ATAdBgNVHQ4EFgQUMVywfKRY9Ec3n98PVIEu7kyWKHwwCgYIKoZIzj0EAwIDRwAw
-RAIgeSMNJ51+EvNJzqsISauhOTbFxiUnnmV2z/+dxYeCPzUCIEMXM/X2ekzHEz6V
-l7zSfosiYvtQQL3ML3sLnVMmxdmd
------END CERTIFICATE-----
\ No newline at end of file
diff --git a/app/src/normalProductionFatDebug/assets/urls/demo.bitmask.net.url b/app/src/normalProductionFatDebug/assets/urls/demo.bitmask.net.url
deleted file mode 100644
index 459d6d10bad74854c8248f01987028269c994d2b..0000000000000000000000000000000000000000
--- a/app/src/normalProductionFatDebug/assets/urls/demo.bitmask.net.url
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-	"main_url" : "https://demo.bitmask.net",
-	"geoip_url" : "https://menshen.demo.bitmask.net/json"
-}
diff --git a/bitmask-core-android b/bitmask-core-android
index 8b802cc791d1d913c03fe57c251d3eb40a6a59a2..46b7f48299017d7851f8a16ff395dcac9792df97 160000
--- a/bitmask-core-android
+++ b/bitmask-core-android
@@ -1 +1 @@
-Subproject commit 8b802cc791d1d913c03fe57c251d3eb40a6a59a2
+Subproject commit 46b7f48299017d7851f8a16ff395dcac9792df97