diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5b668f57..f67c07974 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+
+* add simple "Use Tor" preference
+
+* Enable TLS v1.2 for officials repo on all devices that support it
+
 ### 0.98.1 (2016-02-14)
 
 * Fix crash when entering only a space into the search dialog
diff --git a/F-Droid/build.gradle b/F-Droid/build.gradle
index 1ad545715..92cabd569 100644
--- a/F-Droid/build.gradle
+++ b/F-Droid/build.gradle
@@ -17,6 +17,7 @@ dependencies {
     compile 'com.google.zxing:core:3.2.1'
     compile 'eu.chainfire:libsuperuser:1.0.0.201602011018'
     compile 'cc.mvdan.accesspoint:library:0.1.3'
+    compile 'info.guardianproject.netcipher:netcipher:1.2.1'
     compile 'commons-net:commons-net:3.4'
     compile 'org.openhab.jmdns:jmdns:3.4.2'
     compile('ch.acra:acra:4.8.2') {
@@ -71,6 +72,7 @@ if (!hasProperty('sourceDeps')) {
             'eu.chainfire:libsuperuser:952c5fc82f9c31d31d2b6a7054ee267dac1685fb037a254888c73c48de661eaf',
             'cc.mvdan.accesspoint:library:dc89a085d6bc40381078b8dd7776b12bde0dbaf8ffbcddb17ec4ebc3edecc7ba',
             'commons-net:commons-net:38cf2eca826b8bcdb236fc1f2e79e0c6dd8e7e0f5c44a3b8e839a1065b2fbe2e',
+            'info.guardianproject.netcipher:netcipher:611ec5bde9d799fd57e1efec5c375f9f460de2cdda98918541decc9a7d02f2ad',
             'org.openhab.jmdns:jmdns:7a4b34b5606bbd2aff7fdfe629edcb0416fccd367fb59a099f210b9aba4f0bce',
             'com.madgag.spongycastle:pkix:6aba9b2210907a3d46dd3dcac782bb3424185290468d102d5207ebdc9796a905',
             'com.madgag.spongycastle:prov:029f26cd6b67c06ffa05702d426d472c141789001bcb15b7262ed86c868e5643',
diff --git a/F-Droid/proguard-rules.pro b/F-Droid/proguard-rules.pro
index 4784add4a..65c2e1e09 100644
--- a/F-Droid/proguard-rules.pro
+++ b/F-Droid/proguard-rules.pro
@@ -10,6 +10,10 @@
 -dontnote android.support.**
 -dontnote **ILicensingService
 
+# StrongHttpsClient and its support classes are totally unused, so the
+# ch.boye.httpclientandroidlib.** classes are also unneeded
+-dontwarn info.guardianproject.netcipher.client.**
+
 # These libraries are known to break if minification is enabled on them. They
 # use reflection to instantiate classes, for example. If the keep flags are
 # removed, proguard will strip classes which are required, which may result in
diff --git a/F-Droid/res/values/strings.xml b/F-Droid/res/values/strings.xml
index bd55734cb..e60b30152 100644
--- a/F-Droid/res/values/strings.xml
+++ b/F-Droid/res/values/strings.xml
@@ -166,6 +166,8 @@
     <string name="next">Next</string>
     <string name="skip">Skip</string>
 
+    <string name="useTor">Use Tor</string>
+    <string name="useTorSummary">Force download traffic through Tor for increased privacy</string>
     <string name="proxy">Proxy</string>
     <string name="enable_proxy_title">Enable HTTP Proxy</string>
     <string name="enable_proxy_summary">Configure HTTP Proxy for all network requests</string>
diff --git a/F-Droid/res/xml/preferences.xml b/F-Droid/res/xml/preferences.xml
index 165b05864..43cb8b184 100644
--- a/F-Droid/res/xml/preferences.xml
+++ b/F-Droid/res/xml/preferences.xml
@@ -48,6 +48,10 @@
             android:title="@string/local_repo_name" />
     </PreferenceCategory>
     <PreferenceCategory android:title="@string/proxy" >
+        <CheckBoxPreference
+            android:key="useTor"
+            android:summary="@string/useTorSummary"
+            android:title="@string/useTor" />
         <CheckBoxPreference
             android:defaultValue="false"
             android:key="enableProxy"
diff --git a/F-Droid/src/org/fdroid/fdroid/FDroid.java b/F-Droid/src/org/fdroid/fdroid/FDroid.java
index 11f2cc73d..eea6abf93 100644
--- a/F-Droid/src/org/fdroid/fdroid/FDroid.java
+++ b/F-Droid/src/org/fdroid/fdroid/FDroid.java
@@ -28,6 +28,7 @@ import android.content.Intent;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -137,6 +138,7 @@ public class FDroid extends AppCompatActivity implements SearchView.OnQueryTextL
     @Override
     protected void onResume() {
         super.onResume();
+        FDroidApp.checkStartTor(this);
         // AppDetails and RepoDetailsActivity set different NFC actions, so reset here
         NfcHelper.setAndroidBeam(this, getApplication().getPackageName());
         checkForAddRepoIntent(getIntent());
@@ -289,6 +291,10 @@ public class FDroid extends AppCompatActivity implements SearchView.OnQueryTextL
             MenuItem btItem = menu.findItem(R.id.action_bluetooth_apk);
             btItem.setVisible(false);
         }
+        if (Build.VERSION.SDK_INT < 10) {
+            MenuItem menuItem = menu.findItem(R.id.action_swap);
+            menuItem.setVisible(false);
+        }
 
         SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
         searchMenuItem = menu.findItem(R.id.action_search);
diff --git a/F-Droid/src/org/fdroid/fdroid/FDroidApp.java b/F-Droid/src/org/fdroid/fdroid/FDroidApp.java
index 969d4ff56..f90207aa1 100644
--- a/F-Droid/src/org/fdroid/fdroid/FDroidApp.java
+++ b/F-Droid/src/org/fdroid/fdroid/FDroidApp.java
@@ -63,6 +63,8 @@ import java.net.URLStreamHandlerFactory;
 import java.security.Security;
 import java.util.Locale;
 
+import info.guardianproject.netcipher.NetCipher;
+import info.guardianproject.netcipher.proxy.OrbotHelper;
 import sun.net.www.protocol.bluetooth.Handler;
 
 @ReportsCrashes(mailTo = "reports@f-droid.org",
@@ -296,6 +298,8 @@ public class FDroidApp extends Application {
                 startService(new Intent(FDroidApp.this, WifiStateChangeService.class));
             }
         });
+
+        configureTor(Preferences.get().isTorEnabled());
     }
 
     @TargetApi(18)
@@ -352,4 +356,28 @@ public class FDroidApp extends Application {
             }
         }
     }
+
+    private static boolean useTor;
+
+    /**
+     * Set the proxy settings based on whether Tor should be enabled or not.
+     */
+    public static void configureTor(boolean enabled) {
+        useTor = enabled;
+        if (useTor) {
+            NetCipher.useTor();
+        } else {
+            NetCipher.clearProxy();
+        }
+    }
+
+    public static void checkStartTor(Context context) {
+        if (useTor) {
+            OrbotHelper.requestStartTor(context);
+        }
+    }
+
+    public static boolean isUsingTor() {
+        return useTor;
+    }
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/Preferences.java b/F-Droid/src/org/fdroid/fdroid/Preferences.java
index d97d210da..c8570e6d4 100644
--- a/F-Droid/src/org/fdroid/fdroid/Preferences.java
+++ b/F-Droid/src/org/fdroid/fdroid/Preferences.java
@@ -25,9 +25,11 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
 
     private static final String TAG = "Preferences";
 
+    private final Context context;
     private final SharedPreferences preferences;
 
     private Preferences(Context context) {
+        this.context = context;
         preferences = PreferenceManager.getDefaultSharedPreferences(context);
         preferences.registerOnSharedPreferenceChangeListener(this);
         if (preferences.getString(PREF_LOCAL_REPO_NAME, null) == null) {
@@ -54,6 +56,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
     public static final String PREF_LOCAL_REPO_NAME = "localRepoName";
     public static final String PREF_LOCAL_REPO_HTTPS = "localRepoHttps";
     public static final String PREF_LANGUAGE = "language";
+    public static final String PREF_USE_TOR = "useTor";
     public static final String PREF_ENABLE_PROXY = "enableProxy";
     public static final String PREF_PROXY_HOST = "proxyHost";
     public static final String PREF_PROXY_PORT = "proxyPort";
@@ -161,6 +164,16 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
         return preferences.getString(PREF_LOCAL_REPO_NAME, getDefaultLocalRepoName());
     }
 
+    /**
+     * This preference's default is set dynamically based on whether Orbot is
+     * installed. If Orbot is installed, default to using Tor, the user can still override
+     */
+    public boolean isTorEnabled() {
+        // TODO enable once Orbot can auto-start after first install
+        //return preferences.getBoolean(PREF_USE_TOR, OrbotHelper.requestStartTor(context));
+        return preferences.getBoolean(PREF_USE_TOR, false);
+    }
+
     public boolean isProxyEnabled() {
         return preferences.getBoolean(PREF_ENABLE_PROXY, DEFAULT_ENABLE_PROXY);
     }
diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/type/SwapType.java b/F-Droid/src/org/fdroid/fdroid/localrepo/type/SwapType.java
index 1a2ee1a78..8e45aafc8 100644
--- a/F-Droid/src/org/fdroid/fdroid/localrepo/type/SwapType.java
+++ b/F-Droid/src/org/fdroid/fdroid/localrepo/type/SwapType.java
@@ -2,6 +2,7 @@ package org.fdroid.fdroid.localrepo.type;
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.AsyncTask;
 import android.support.annotation.NonNull;
 import android.support.v4.content.LocalBroadcastManager;
 
@@ -70,12 +71,13 @@ public abstract class SwapType {
     }
 
     public void startInBackground() {
-        new Thread() {
+        new AsyncTask<Void, Void, Void>() {
             @Override
-            public void run() {
-                SwapType.this.start();
+            protected Void doInBackground(Void... params) {
+                start();
+                return null;
             }
-        }.start();
+        }.execute();
     }
 
     private void ensureRunning() {
@@ -85,21 +87,23 @@ public abstract class SwapType {
     }
 
     public void ensureRunningInBackground() {
-        new Thread() {
+        new AsyncTask<Void, Void, Void>() {
             @Override
-            public void run() {
+            protected Void doInBackground(Void... params) {
                 ensureRunning();
+                return null;
             }
-        }.start();
+        }.execute();
     }
 
     public void stopInBackground() {
-        new Thread() {
+        new AsyncTask<Void, Void, Void>() {
             @Override
-            public void run() {
-                SwapType.this.stop();
+            protected Void doInBackground(Void... params) {
+                stop();
+                return null;
             }
-        }.start();
+        }.execute();
     }
 
 }
diff --git a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
index b43227008..fa012d59f 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/DownloaderFactory.java
@@ -55,9 +55,6 @@ public class DownloaderFactory {
             String macAddress = url.getHost().replace("-", ":");
             return new BluetoothDownloader(context, macAddress, url, destFile);
         }
-        if (isOnionAddress(url)) {
-            return new TorHttpDownloader(context, url, destFile);
-        }
         if (isLocalFile(url)) {
             return new LocalFileDownloader(context, url, destFile);
         }
diff --git a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
index 9cfa0310c..aaaddea13 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/HttpDownloader.java
@@ -1,6 +1,7 @@
 package org.fdroid.fdroid.net;
 
 import android.content.Context;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
@@ -22,8 +23,11 @@ import java.net.Proxy;
 import java.net.SocketAddress;
 import java.net.URL;
 
+import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLHandshakeException;
 
+import info.guardianproject.netcipher.NetCipher;
+
 public class HttpDownloader extends Downloader {
     private static final String TAG = "HttpDownloader";
 
@@ -32,7 +36,7 @@ public class HttpDownloader extends Downloader {
 
     protected HttpURLConnection connection;
     private Credentials credentials;
-    private int statusCode  = -1;
+    private int statusCode = -1;
 
     HttpDownloader(Context context, URL url, File destFile)
             throws FileNotFoundException, MalformedURLException {
@@ -52,6 +56,7 @@ public class HttpDownloader extends Downloader {
      * checking out that follows redirects up to a certain point. I guess though the correct way
      * is probably to check for a loop (keep a list of all URLs redirected to and if you hit the
      * same one twice, bail with an exception).
+     *
      * @throws IOException
      */
     @Override
@@ -92,17 +97,29 @@ public class HttpDownloader extends Downloader {
         if (connection != null) {
             return;
         }
-        Preferences prefs = Preferences.get();
-        if (prefs.isProxyEnabled() && !isSwapUrl()) {
-            SocketAddress sa = new InetSocketAddress(prefs.getProxyHost(), prefs.getProxyPort());
-            Proxy proxy = new Proxy(Proxy.Type.HTTP, sa);
-            connection = (HttpURLConnection) sourceUrl.openConnection(proxy);
-        } else {
-
+        if (isSwapUrl()) {
+            // swap never works with a proxy, its unrouted IP on the same subnet
             connection = (HttpURLConnection) sourceUrl.openConnection();
-            if (credentials != null) {
-                credentials.authenticate(connection);
+        } else {
+            Preferences prefs = Preferences.get();
+            if (prefs.isProxyEnabled()) {
+                // if "Use Tor" is set, NetCipher will ignore these proxy settings
+                SocketAddress sa = new InetSocketAddress(prefs.getProxyHost(), prefs.getProxyPort());
+                NetCipher.setProxy(new Proxy(Proxy.Type.HTTP, sa));
             }
+            connection = NetCipher.getHttpURLConnection(sourceUrl);
+        }
+
+        // workaround until NetCipher supports HTTPS SNI
+        // https://gitlab.com/fdroid/fdroidclient/issues/431
+        if (connection instanceof HttpsURLConnection
+                && !TextUtils.equals(sourceUrl.getHost(), "f-droid.org")
+                && !TextUtils.equals(sourceUrl.getHost(), "guardianproject.info")) {
+            ((HttpsURLConnection) connection).setSSLSocketFactory(HttpsURLConnection.getDefaultSSLSocketFactory());
+        }
+
+        if (credentials != null) {
+            credentials.authenticate(connection);
         }
     }
 
@@ -159,4 +176,4 @@ public class HttpDownloader extends Downloader {
     public void close() {
         connection.disconnect();
     }
-}
\ No newline at end of file
+}
diff --git a/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java b/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java
deleted file mode 100644
index 50bb50fa5..000000000
--- a/F-Droid/src/org/fdroid/fdroid/net/TorHttpDownloader.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.fdroid.fdroid.net;
-
-import android.content.Context;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
-import java.net.MalformedURLException;
-import java.net.Proxy;
-import java.net.SocketAddress;
-import java.net.URL;
-
-public class TorHttpDownloader extends HttpDownloader {
-
-    TorHttpDownloader(Context context, URL url, File destFile)
-            throws FileNotFoundException, MalformedURLException {
-        super(context, url, destFile);
-    }
-
-    @Override
-    protected void setupConnection() throws IOException {
-        SocketAddress sa = new InetSocketAddress("127.0.0.1", 8118);
-        Proxy tor = new Proxy(Proxy.Type.HTTP, sa);
-        connection = (HttpURLConnection) sourceUrl.openConnection(tor);
-    }
-}
diff --git a/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java b/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java
index ccf162460..8c3e3ef69 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java
@@ -87,12 +87,11 @@ public class WifiStateChangeService extends Service {
                         wifiInfo = wifiManager.getConnectionInfo();
                         FDroidApp.ipAddressString = formatIpAddress(wifiInfo.getIpAddress());
                         DhcpInfo dhcpInfo = wifiManager.getDhcpInfo();
-                        if (dhcpInfo == null) {
-                            return null;
-                        }
-                        String netmask = formatIpAddress(dhcpInfo.netmask);
-                        if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) {
-                            FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo();
+                        if (dhcpInfo != null) {
+                            String netmask = formatIpAddress(dhcpInfo.netmask);
+                            if (!TextUtils.isEmpty(FDroidApp.ipAddressString) && netmask != null) {
+                                FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo();
+                            }
                         }
                     } else if (wifiState == WifiManager.WIFI_STATE_DISABLED
                             || wifiState == WifiManager.WIFI_STATE_DISABLING) {
diff --git a/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothClient.java b/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothClient.java
index 44ec1a0d3..ccb907c8d 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothClient.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothClient.java
@@ -1,5 +1,6 @@
 package org.fdroid.fdroid.net.bluetooth;
 
+import android.annotation.TargetApi;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothSocket;
@@ -21,6 +22,7 @@ public class BluetoothClient {
         device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress);
     }
 
+    @TargetApi(10)
     public BluetoothConnection openConnection() throws IOException {
 
         BluetoothSocket socket = null;
diff --git a/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothServer.java b/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothServer.java
index 3c39917f9..848192731 100644
--- a/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothServer.java
+++ b/F-Droid/src/org/fdroid/fdroid/net/bluetooth/BluetoothServer.java
@@ -1,5 +1,6 @@
 package org.fdroid.fdroid.net.bluetooth;
 
+import android.annotation.TargetApi;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothServerSocket;
 import android.bluetooth.BluetoothSocket;
@@ -61,6 +62,7 @@ public class BluetoothServer extends Thread {
         }
     }
 
+    @TargetApi(10)
     @Override
     public void run() {
 
diff --git a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
index b28449847..cc081f817 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/ManageReposActivity.java
@@ -127,6 +127,7 @@ public class ManageReposActivity extends ActionBarActivity {
     @Override
     protected void onResume() {
         super.onResume();
+        FDroidApp.checkStartTor(this);
 
         /* let's see if someone is trying to send us a new repo */
         addRepoFromIntent(getIntent());
diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
index 8e15e6e2f..576aaafa7 100644
--- a/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
+++ b/F-Droid/src/org/fdroid/fdroid/views/fragments/PreferencesFragment.java
@@ -21,6 +21,9 @@ import org.fdroid.fdroid.PreferencesActivity;
 import org.fdroid.fdroid.R;
 import org.fdroid.fdroid.installer.PrivilegedInstaller;
 
+import info.guardianproject.netcipher.NetCipher;
+import info.guardianproject.netcipher.proxy.OrbotHelper;
+
 public class PreferencesFragment extends PreferenceFragment
         implements SharedPreferences.OnSharedPreferenceChangeListener {
 
@@ -43,10 +46,16 @@ public class PreferencesFragment extends PreferenceFragment
         Preferences.PREF_PROXY_PORT,
     };
 
+    private static final int REQUEST_INSTALL_ORBOT = 0x1234;
+    private CheckBoxPreference enableProxyCheckPref;
+    private CheckBoxPreference useTorCheckPref;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         addPreferencesFromResource(R.xml.preferences);
+        useTorCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_USE_TOR);
+        enableProxyCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_ENABLE_PROXY);
     }
 
     private void checkSummary(String key, int resId) {
@@ -279,6 +288,29 @@ public class PreferencesFragment extends PreferenceFragment
 
         initPrivilegedInstallerPreference();
         initManagePrivilegedAppPreference();
+        // this pref's default is dynamically set based on whether Orbot is installed
+        boolean useTor = Preferences.get().isTorEnabled();
+        useTorCheckPref.setDefaultValue(useTor);
+        useTorCheckPref.setChecked(useTor);
+        useTorCheckPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+            @Override
+            public boolean onPreferenceChange(Preference preference, Object enabled) {
+                if ((Boolean) enabled) {
+                    final Activity activity = getActivity();
+                    enableProxyCheckPref.setEnabled(false);
+                    if (OrbotHelper.isOrbotInstalled(activity)) {
+                        NetCipher.useTor();
+                    } else {
+                        Intent intent = OrbotHelper.getOrbotInstallIntent(activity);
+                        activity.startActivityForResult(intent, REQUEST_INSTALL_ORBOT);
+                    }
+                } else {
+                    enableProxyCheckPref.setEnabled(true);
+                    NetCipher.clearProxy();
+                }
+                return true;
+            }
+        });
     }
 
     @Override