diff --git a/briar-android/libs/jtorctl-briar.jar b/briar-android/libs/jtorctl-briar.jar
index 134f85dbcb386b866b1cb9023a9f1fa29e308f84..a299bcdb39e4ec965dd35fc56263dabecce069ad 100644
Binary files a/briar-android/libs/jtorctl-briar.jar and b/briar-android/libs/jtorctl-briar.jar differ
diff --git a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
index b0c651fb6f3b64b5ea66761385ca5f5bbb12d944..e7852bff66fe9340f3c16b4d6f3744f0913ae88e 100644
--- a/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
+++ b/briar-android/src/org/briarproject/plugins/tor/TorPlugin.java
@@ -47,6 +47,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Scanner;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -63,20 +64,21 @@ import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS;
+import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY;
 
 class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 
 	static final TransportId ID = new TransportId("tor");
 
+	private static final String PROP_ONION = "onion";
 	private static final String[] EVENTS = {
 			"CIRC", "ORCONN", "HS_DESC", "NOTICE", "WARN", "ERR"
 	};
 	private static final String OWNER = "__OwningControllerProcess";
 	private static final int SOCKS_PORT = 59050, CONTROL_PORT = 59051;
 	private static final int COOKIE_TIMEOUT = 3000; // Milliseconds
-	private static final int HOSTNAME_TIMEOUT = 30 * 1000; // Milliseconds
-	private static final Pattern ONION =
-			Pattern.compile("[a-z2-7]{16}\\.onion");
+	private static final Pattern ONION = Pattern.compile("[a-z2-7]{16}");
 	private static final int MIN_DESCRIPTORS_PUBLISHED = 3;
 	private static final Logger LOG =
 			Logger.getLogger(TorPlugin.class.getName());
@@ -90,8 +92,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 	private final String architecture;
 	private final int maxLatency, maxIdleTime, pollingInterval, socketTimeout;
 	private final ConnectionStatus connectionStatus;
-	private final File torDirectory, torFile, geoIpFile, configFile, doneFile;
-	private final File cookieFile, hostnameFile;
+	private final File torDirectory, torFile, geoIpFile, configFile;
+	private final File doneFile, cookieFile;
 	private final PowerManager.WakeLock wakeLock;
 
 	private volatile boolean running = false;
@@ -124,7 +126,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		configFile = new File(torDirectory, "torrc");
 		doneFile = new File(torDirectory, "done");
 		cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
-		hostnameFile = new File(torDirectory, "hs/hostname");
 		Object o = appContext.getSystemService(POWER_SERVICE);
 		PowerManager pm = (PowerManager) o;
 		wakeLock = pm.newWakeLock(PARTIAL_WAKE_LOCK, "TorPlugin");
@@ -421,47 +422,39 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 
 	private void publishHiddenService(String port) {
 		if (!running) return;
-		if (!hostnameFile.exists()) {
-			LOG.info("Creating hidden service");
-			try {
-				// Watch for the hostname file being created/updated
-				File serviceDirectory = hostnameFile.getParentFile();
-				serviceDirectory.mkdirs();
-				hostnameFile.createNewFile();
-				CountDownLatch latch = new CountDownLatch(1);
-				FileObserver obs = new WriteObserver(hostnameFile, latch);
-				obs.startWatching();
-				// Use the control connection to update the Tor config
-				List<String> config = Arrays.asList(
-						"HiddenServiceDir " +
-								serviceDirectory.getAbsolutePath(),
-						"HiddenServicePort 80 127.0.0.1:" + port);
-				controlConnection.setConf(config);
-				controlConnection.saveConf();
-				// Wait for the hostname file to be created/updated
-				if (!latch.await(HOSTNAME_TIMEOUT, MILLISECONDS)) {
-					LOG.warning("Hidden service not created");
-					if (LOG.isLoggable(INFO)) listFiles(torDirectory);
-					return;
-				}
-				if (!running) return;
-			} catch (IOException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-			} catch (InterruptedException e) {
-				LOG.warning("Interrupted while creating hidden service");
-				Thread.currentThread().interrupt();
-				return;
-			}
-		}
-		// Publish the hidden service's onion hostname in transport properties
+		LOG.info("Creating hidden service");
+		String privKey = callback.getSettings().get(HS_PRIVKEY);
+		Map<Integer, String> portLines =
+				Collections.singletonMap(80, "127.0.0.1:" + port);
+		Map<String, String> response;
 		try {
-			String hostname = new String(read(hostnameFile), "UTF-8").trim();
-			if (LOG.isLoggable(INFO)) LOG.info("Hidden service " + hostname);
-			TransportProperties p = new TransportProperties();
-			p.put("onion", hostname);
-			callback.mergeLocalProperties(p);
+			// Use the control connection to set up the hidden service
+			if (privKey == null)
+				response = controlConnection.addOnion(portLines);
+			else response = controlConnection.addOnion(privKey, portLines);
 		} catch (IOException e) {
 			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			return;
+		}
+		if (!response.containsKey(HS_ADDRESS)) {
+			LOG.warning("Tor did not return a hidden service address");
+			return;
+		}
+		if (privKey == null && !response.containsKey(HS_PRIVKEY)) {
+			LOG.warning("Tor did not return a private key");
+			return;
+		}
+		// Publish the hidden service's onion hostname in transport properties
+		String hostname = response.get(HS_ADDRESS);
+		if (LOG.isLoggable(INFO)) LOG.info("Hidden service " + hostname);
+		TransportProperties p = new TransportProperties();
+		p.put(PROP_ONION, hostname);
+		callback.mergeLocalProperties(p);
+		if (privKey == null) {
+			// Save the hidden service's private key for next time
+			Settings s = new Settings();
+			s.put(HS_PRIVKEY, response.get(HS_PRIVKEY));
+			callback.mergeSettings(s);
 		}
 	}
 
@@ -551,7 +544,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		if (!isRunning()) return null;
 		TransportProperties p = callback.getRemoteProperties().get(c);
 		if (p == null) return null;
-		String onion = p.get("onion");
+		String onion = p.get(PROP_ONION);
 		if (StringUtils.isNullOrEmpty(onion)) return null;
 		if (!ONION.matcher(onion).matches()) {
 			if (LOG.isLoggable(INFO)) LOG.info("Invalid hostname: " + onion);
@@ -559,10 +552,10 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
 		}
 		try {
 			if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + onion);
-			controlConnection.forgetHiddenService(onion.substring(0, 16));
+			controlConnection.forgetHiddenService(onion);
 			Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", SOCKS_PORT);
 			proxy.resolveAddrLocally(false);
-			Socket s = new SocksSocket(proxy, onion, 80);
+			Socket s = new SocksSocket(proxy, onion + ".onion", 80);
 			s.setSoTimeout(socketTimeout);
 			if (LOG.isLoggable(INFO)) LOG.info("Connected to " + onion);
 			return new TorTransportConnection(this, s);
diff --git a/patches/jtorctl.patch b/patches/jtorctl.patch
index 167af82d22592834bed17fe41098c3096168988e..7d346eff0c87e66b18defd0d28b53e4d4de93af8 100644
--- a/patches/jtorctl.patch
+++ b/patches/jtorctl.patch
@@ -1,8 +1,125 @@
+diff --git a/net/freehaven/tor/control/TorControlCommands.java b/net/freehaven/tor/control/TorControlCommands.java
+index 36482d5..14486e3 100644
+--- a/net/freehaven/tor/control/TorControlCommands.java
++++ b/net/freehaven/tor/control/TorControlCommands.java
+@@ -144,5 +144,8 @@ public interface TorControlCommands {
+         "No such OR",
+     };
+ 
++    public static final String HS_ADDRESS = "onionAddress";
++    public static final String HS_PRIVKEY = "onionPrivKey";
++
+ }
+ 
 diff --git a/net/freehaven/tor/control/TorControlConnection.java b/net/freehaven/tor/control/TorControlConnection.java
-index 9524612..38b1879 100644
+index 9524612..c0f2070 100644
 --- a/net/freehaven/tor/control/TorControlConnection.java
 +++ b/net/freehaven/tor/control/TorControlConnection.java
-@@ -740,7 +740,7 @@ public class TorControlConnection implements TorControlCommands {
+@@ -736,11 +736,111 @@ public class TorControlConnection implements TorControlCommands {
+         sendAndWaitForResponse("TAKEOWNERSHIP\r\n", null);
+     }
+ 
++    /**
++     * Tells Tor to generate and set up a new onion service using the best
++     * supported algorithm.
++     * <p/>
++     * ADD_ONION was added in Tor 0.2.7.1-alpha.
++     */
++    public Map<String,String> addOnion(Map<Integer,String> portLines)
++                                       throws IOException {
++        return addOnion("NEW:BEST", portLines, null);
++    }
++
++    /**
++     * Tells Tor to generate and set up a new onion service using the best
++     * supported algorithm.
++     * <p/>
++     * ADD_ONION was added in Tor 0.2.7.1-alpha.
++     */
++    public Map<String,String> addOnion(Map<Integer,String> portLines,
++                                       boolean ephemeral, boolean detach)
++                                       throws IOException {
++        return addOnion("NEW:BEST", portLines, ephemeral, detach);
++    }
++
++    /**
++     * Tells Tor to set up an onion service using the provided private key.
++     * <p/>
++     * ADD_ONION was added in Tor 0.2.7.1-alpha.
++     */
++    public Map<String,String> addOnion(String privKey,
++                                       Map<Integer,String> portLines)
++                                       throws IOException {
++        return addOnion(privKey, portLines, null);
++    }
++
++    /**
++     * Tells Tor to set up an onion service using the provided private key.
++     * <p/>
++     * ADD_ONION was added in Tor 0.2.7.1-alpha.
++     */
++    public Map<String,String> addOnion(String privKey,
++                                       Map<Integer,String> portLines,
++                                       boolean ephemeral, boolean detach)
++                                       throws IOException {
++        List<String> flags = new ArrayList<String>();
++        if (ephemeral)
++            flags.add("DiscardPK");
++        if (detach)
++            flags.add("Detach");
++        return addOnion(privKey, portLines, flags);
++    }
++
++    /**
++     * Tells Tor to set up an onion service.
++     * <p/>
++     * ADD_ONION was added in Tor 0.2.7.1-alpha.
++     */
++    public Map<String,String> addOnion(String privKey,
++                                       Map<Integer,String> portLines,
++                                       List<String> flags)
++                                       throws IOException {
++        if (privKey.indexOf(':') < 0)
++            throw new IllegalArgumentException("Invalid privKey");
++        if (portLines == null || portLines.size() < 1)
++            throw new IllegalArgumentException("Must provide at least one port line");
++        StringBuilder b = new StringBuilder();
++        b.append("ADD_ONION ").append(privKey);
++        if (flags != null && flags.size() > 0) {
++            b.append(" Flags=");
++            String separator = "";
++            for (String flag : flags) {
++                b.append(separator).append(flag);
++                separator = ",";
++            }
++        }
++        for (Map.Entry<Integer,String> portLine : portLines.entrySet()) {
++            int virtPort = portLine.getKey();
++            String target = portLine.getValue();
++            b.append(" Port=").append(virtPort);
++            if (target != null && target.length() > 0)
++                b.append(",").append(target);
++        }
++        b.append("\r\n");
++        List<ReplyLine> lst = sendAndWaitForResponse(b.toString(), null);
++        Map<String,String> ret = new HashMap<String,String>();
++        ret.put(HS_ADDRESS, (lst.get(0)).msg.split("=", 2)[1]);
++        if (lst.size() > 2)
++            ret.put(HS_PRIVKEY, (lst.get(1)).msg.split("=", 2)[1]);
++        return ret;
++    }
++
++    /**
++     * Tells Tor to take down an onion service previously set up with
++     * addOnion(). The hostname excludes the .onion extension.
++     * <p/>
++     * DEL_ONION was added in Tor 0.2.7.1-alpha.
++     */
++    public void delOnion(String hostname) throws IOException {
++        sendAndWaitForResponse("DEL_ONION " + hostname + "\r\n", null);
++    }
++
+     /** Tells Tor to forget any cached client state relating to the hidden
       * service with the given hostname (excluding the .onion extension).
       */
      public void forgetHiddenService(String hostname) throws IOException {