diff --git a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
index 0fc86bb4f..999cd7d14 100644
--- a/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
+++ b/docs/reference/evolution-data-server/evolution-data-server-docs.sgml.in
@@ -193,6 +193,7 @@
       <xi:include href="xml/e-data-book-factory.xml"/>
       <xi:include href="xml/e-data-book-view.xml"/>
       <xi:include href="xml/e-subprocess-book-factory.xml"/>
+      <xi:include href="xml/e-system-locale-watcher.xml"/>
     </chapter>
 
     <chapter>
@@ -345,6 +346,10 @@
     <title>Index of deprecated symbols</title>
     <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-3-30" role="3.30">
+    <title>Index of new symbols in 3.30</title>
+    <xi:include href="xml/api-index-3.30.xml"><xi:fallback /></xi:include>
+  </index>
   <index id="api-index-3-28" role="3.28">
     <title>Index of new symbols in 3.28</title>
     <xi:include href="xml/api-index-3.28.xml"><xi:fallback /></xi:include>
diff --git a/src/addressbook/libedata-book/CMakeLists.txt b/src/addressbook/libedata-book/CMakeLists.txt
index 2bac3a979..994d5f40a 100644
--- a/src/addressbook/libedata-book/CMakeLists.txt
+++ b/src/addressbook/libedata-book/CMakeLists.txt
@@ -27,6 +27,7 @@ set(SOURCES
 	e-data-book-factory.c
 	e-data-book-view.c
 	e-subprocess-book-factory.c
+	e-system-locale-watcher.c
 	ximian-vcard.h
 )
 
@@ -49,6 +50,7 @@ set(HEADERS
 	e-book-backend-cache.h
 	e-book-backend-sqlitedb.h
 	e-subprocess-book-factory.h
+	e-system-locale-watcher.h
 )
 
 if(WITH_LIBDB)
diff --git a/src/addressbook/libedata-book/e-data-book-factory.c b/src/addressbook/libedata-book/e-data-book-factory.c
index d34b65f0d..5527d58d8 100644
--- a/src/addressbook/libedata-book/e-data-book-factory.c
+++ b/src/addressbook/libedata-book/e-data-book-factory.c
@@ -30,10 +30,6 @@
  **/
 #include "evolution-data-server-config.h"
 
-#include <locale.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 #include <glib/gi18n.h>
 
 /* Private D-Bus classes. */
@@ -43,6 +39,7 @@
 #include "e-book-backend-factory.h"
 #include "e-data-book.h"
 #include "e-data-book-factory.h"
+#include "e-system-locale-watcher.h"
 
 #define d(x)
 
@@ -52,6 +49,9 @@
 
 struct _EDataBookFactoryPrivate {
 	EDBusAddressBookFactory *dbus_factory;
+
+	ESystemLocaleWatcher *locale_watcher;
+	gulong notify_locale_id;
 };
 
 /* Forward Declarations */
@@ -116,16 +116,178 @@ data_book_factory_handle_open_address_book_cb (EDBusAddressBookFactory *iface,
 	return TRUE;
 }
 
+static void
+data_book_factory_backend_closed_cb (EBackend *backend,
+				     const gchar *sender,
+				     EDataFactory *data_factory)
+{
+	e_data_factory_backend_closed (data_factory, backend);
+}
+
+static EBackend *
+data_book_factory_create_backend (EDataFactory *data_factory,
+				  EBackendFactory *backend_factory,
+				  ESource *source)
+{
+	EBookBackendFactoryClass *backend_factory_class;
+	EBackend *backend;
+
+	g_return_val_if_fail (E_IS_DATA_BOOK_FACTORY (data_factory), NULL);
+	g_return_val_if_fail (E_IS_BOOK_BACKEND_FACTORY (backend_factory), NULL);
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	backend_factory_class = E_BOOK_BACKEND_FACTORY_GET_CLASS (backend_factory);
+	g_return_val_if_fail (backend_factory_class != NULL, NULL);
+
+	if (g_type_is_a (backend_factory_class->backend_type, G_TYPE_INITABLE)) {
+		GError *local_error = NULL;
+
+		backend = g_initable_new (backend_factory_class->backend_type, NULL, &local_error,
+			"registry", e_data_factory_get_registry (data_factory),
+			"source", source,
+			NULL);
+
+		if (!backend)
+			g_warning ("%s: Failed to create backend: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error");
+
+		g_clear_error (&local_error);
+	} else {
+		backend = g_object_new (backend_factory_class->backend_type,
+			"registry", e_data_factory_get_registry (data_factory),
+			"source", source,
+			NULL);
+	}
+
+	if (backend) {
+		g_signal_connect (backend, "closed",
+			G_CALLBACK (data_book_factory_backend_closed_cb), data_factory);
+	}
+
+	return backend;
+}
+
+static gchar *
+data_book_factory_open_backend (EDataFactory *data_factory,
+				EBackend *backend,
+				GDBusConnection *connection,
+				GCancellable *cancellable,
+				GError **error)
+{
+	EDataBook *data_book;
+	gchar *object_path;
+
+	g_return_val_if_fail (E_IS_DATA_BOOK_FACTORY (data_factory), NULL);
+	g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL);
+	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+
+	/* If the backend already has an EDataBook installed, return its
+	 * object path.  Otherwise we need to install a new EDataBook. */
+	data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend));
+
+	if (data_book != NULL) {
+		object_path = g_strdup (e_data_book_get_object_path (data_book));
+	} else {
+		object_path = e_subprocess_factory_construct_path ();
+
+		/* The EDataBook will attach itself to EBookBackend,
+		 * so no need to call e_book_backend_set_data_book(). */
+		data_book = e_data_book_new (E_BOOK_BACKEND (backend), connection, object_path, error);
+
+		if (data_book) {
+			EDataBookFactory *data_book_factory = E_DATA_BOOK_FACTORY (data_factory);
+			gchar *locale;
+
+			locale = e_system_locale_watcher_dup_locale (data_book_factory->priv->locale_watcher);
+
+			/* Don't set the locale on a new book if we have not
+			 * yet received a notification of a locale change
+			 */
+			if (locale)
+				e_data_book_set_locale (data_book, locale, NULL, NULL);
+
+			g_free (locale);
+		} else {
+			g_free (object_path);
+			object_path = NULL;
+		}
+	}
+
+	g_clear_object (&data_book);
+
+	return object_path;
+}
+
+static void
+data_book_factory_notify_locale_cb (GObject *object,
+				    GParamSpec *pspec,
+				    gpointer user_data)
+{
+	ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+	EDataBookFactory *data_book_factory = E_DATA_BOOK_FACTORY (user_data);
+	gchar *locale;
+
+	locale = e_system_locale_watcher_dup_locale (watcher);
+
+	if (locale) {
+		GSList *backends, *link;
+		GError *local_error = NULL;
+
+		backends = e_data_factory_list_opened_backends (E_DATA_FACTORY (data_book_factory));
+
+		for (link = backends; link; link = g_slist_next (link)) {
+			EBackend *backend = link->data;
+			EDataBook *data_book;
+
+			data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend));
+
+			if (!e_data_book_set_locale (data_book, locale, NULL, &local_error)) {
+				g_warning ("Failed to set locale on addressbook: %s", local_error ? local_error->message : "Unknown error");
+				g_clear_error (&local_error);
+			}
+
+			g_object_unref (data_book);
+		}
+
+		g_slist_free_full (backends, g_object_unref);
+		g_free (locale);
+	}
+}
+
+static void
+data_book_factory_constructed (GObject *object)
+{
+	EDataBookFactory *data_book_factory;
+
+	/* Chain up to parent's method. */
+	G_OBJECT_CLASS (e_data_book_factory_parent_class)->constructed (object);
+
+	data_book_factory = E_DATA_BOOK_FACTORY (object);
+
+	/* Listen to locale changes only when the backends run in the factory process,
+	   aka when the backend-per-process is disabled */
+	if (!e_data_factory_use_backend_per_process (E_DATA_FACTORY (data_book_factory))) {
+		data_book_factory->priv->locale_watcher = e_system_locale_watcher_new ();
+
+		data_book_factory->priv->notify_locale_id = g_signal_connect (
+			data_book_factory->priv->locale_watcher, "notify::locale",
+			G_CALLBACK (data_book_factory_notify_locale_cb), data_book_factory);
+	}
+}
+
 static void
 data_book_factory_dispose (GObject *object)
 {
 	EDataBookFactory *factory;
-	EDataBookFactoryPrivate *priv;
 
 	factory = E_DATA_BOOK_FACTORY (object);
-	priv = factory->priv;
 
-	g_clear_object (&priv->dbus_factory);
+	if (factory->priv->locale_watcher && factory->priv->notify_locale_id) {
+		g_signal_handler_disconnect (factory->priv->locale_watcher, factory->priv->notify_locale_id);
+		factory->priv->notify_locale_id = 0;
+	}
+
+	g_clear_object (&factory->priv->dbus_factory);
+	g_clear_object (&factory->priv->locale_watcher);
 
 	/* Chain up to parent's dispose() method. */
 	G_OBJECT_CLASS (e_data_book_factory_parent_class)->dispose (object);
@@ -154,6 +316,7 @@ e_data_book_factory_class_init (EDataBookFactoryClass *class)
 	g_type_class_add_private (class, sizeof (EDataBookFactoryPrivate));
 
 	object_class = G_OBJECT_CLASS (class);
+	object_class->constructed = data_book_factory_constructed;
 	object_class->dispose = data_book_factory_dispose;
 
 	dbus_server_class = E_DBUS_SERVER_CLASS (class);
@@ -168,6 +331,8 @@ e_data_book_factory_class_init (EDataBookFactoryClass *class)
 	data_factory_class->get_dbus_interface_skeleton = data_book_factory_get_dbus_interface_skeleton;
 	data_factory_class->get_factory_name = data_book_get_factory_name;
 	data_factory_class->complete_open = data_book_complete_open;
+	data_factory_class->create_backend = data_book_factory_create_backend;
+	data_factory_class->open_backend = data_book_factory_open_backend;
 }
 
 static void
diff --git a/src/addressbook/libedata-book/e-subprocess-book-factory.c b/src/addressbook/libedata-book/e-subprocess-book-factory.c
index 406285fb5..80b5ffedf 100644
--- a/src/addressbook/libedata-book/e-subprocess-book-factory.c
+++ b/src/addressbook/libedata-book/e-subprocess-book-factory.c
@@ -25,16 +25,12 @@
 
 #include "evolution-data-server-config.h"
 
-#include <locale.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 #include <glib/gi18n-lib.h>
 
 #include "e-book-backend.h"
 #include "e-book-backend-factory.h"
 #include "e-data-book.h"
-#include "e-dbus-localed.h"
+#include "e-system-locale-watcher.h"
 #include "e-subprocess-book-factory.h"
 
 #include <e-dbus-subprocess-backend.h>
@@ -44,16 +40,10 @@
 	((obj), E_TYPE_SUBPROCESS_BOOK_FACTORY, ESubprocessBookFactoryPrivate))
 
 struct _ESubprocessBookFactoryPrivate {
-	/* Watching "org.freedesktop.locale1" for locale changes */
-	guint localed_watch_id;
-	guint subprocess_watch_id;
-	EDBusLocale1 *localed_proxy;
-	GCancellable *localed_cancel;
-	gchar *locale;
+	ESystemLocaleWatcher *locale_watcher;
+	gulong notify_locale_id;
 };
 
-static GInitableIface *initable_parent_interface;
-
 /* Forward Declarations */
 static void	e_subprocess_book_factory_initable_init
 						(GInitableIface *iface);
@@ -94,17 +84,19 @@ subprocess_book_factory_open (ESubprocessFactory *subprocess_factory,
 			connection, object_path, error);
 
 		if (data_book != NULL) {
-			e_subprocess_factory_set_backend_callbacks (
-				subprocess_factory, backend, data);
+			gchar *locale;
+
+			e_subprocess_factory_set_backend_callbacks (subprocess_factory, backend, data);
+
+			locale = e_system_locale_watcher_dup_locale (subprocess_book_factory->priv->locale_watcher);
 
 			/* Don't set the locale on a new book if we have not
 			 * yet received a notification of a locale change
 			 */
-			if (subprocess_book_factory->priv->locale)
-				e_data_book_set_locale (
-					data_book,
-					subprocess_book_factory->priv->locale,
-					NULL, NULL);
+			if (locale)
+				e_data_book_set_locale (data_book, locale, NULL, NULL);
+
+			g_free (locale);
 		} else {
 			g_free (object_path);
 			object_path = NULL;
@@ -139,211 +131,71 @@ subprocess_book_factory_ref_backend (ESourceRegistry *registry,
 }
 
 static void
-subprocess_book_factory_dispose (GObject *object)
-{
-	ESubprocessBookFactory *subprocess_factory;
-	ESubprocessBookFactoryPrivate *priv;
-
-	subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object);
-	priv = subprocess_factory->priv;
-
-	if (priv->localed_cancel)
-		g_cancellable_cancel (priv->localed_cancel);
-
-	g_clear_object (&priv->localed_cancel);
-	g_clear_object (&priv->localed_proxy);
-
-	if (priv->localed_watch_id > 0)
-		g_bus_unwatch_name (priv->localed_watch_id);
-
-	if (priv->subprocess_watch_id > 0)
-		g_bus_unwatch_name (priv->subprocess_watch_id);
-
-	/* Chain up to parent's dispose() method. */
-	G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->dispose (object);
-}
-
-static void
-subprocess_book_factory_finalize (GObject *object)
-{
-	ESubprocessBookFactory *subprocess_factory;
-
-	subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object);
-
-	g_free (subprocess_factory->priv->locale);
-
-	/* Chain up to parent's finalize() method. */
-	G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->finalize (object);
-}
-
-static gchar *
-subprocess_book_factory_interpret_locale_value (const gchar *value)
+subprocess_book_factory_notify_locale_cb (GObject *object,
+					  GParamSpec *pspec,
+					  gpointer user_data)
 {
-	gchar *interpreted_value = NULL;
-	gchar **split;
-
-	split = g_strsplit (value, "=", 2);
-
-	if (split && split[0] && split[1])
-		interpreted_value = g_strdup (split[1]);
-
-	g_strfreev (split);
+	ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+	ESubprocessBookFactory *subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (user_data);
+	gchar *locale;
 
-	if (!interpreted_value)
-		g_warning ("Failed to interpret locale value: %s", value);
+	locale = e_system_locale_watcher_dup_locale (watcher);
 
-	return interpreted_value;
-}
-
-static gchar *
-subprocess_book_factory_interpret_locale (const gchar * const * locale)
-{
-	gint i;
-	gchar *interpreted_locale = NULL;
-
-	/* Prioritize LC_COLLATE and then LANG values
-	 * in the 'locale' specified by localed.
-	 *
-	 * If localed explicitly specifies no locale, then
-	 * default to checking system locale.
-	 */
 	if (locale) {
-		for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
-			if (strncmp (locale[i], "LC_COLLATE", 10) == 0)
-				interpreted_locale =
-					subprocess_book_factory_interpret_locale_value (locale[i]);
-		}
-
-		for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
-			if (strncmp (locale[i], "LANG", 4) == 0)
-				interpreted_locale =
-					subprocess_book_factory_interpret_locale_value (locale[i]);
-		}
-	}
-
-	if (!interpreted_locale) {
-		const gchar *system_locale = setlocale (LC_COLLATE, NULL);
-
-		interpreted_locale = g_strdup (system_locale);
-	}
-
-	return interpreted_locale;
-}
-
-static void
-subprocess_book_factory_set_locale (ESubprocessBookFactory *subprocess_factory,
-				    const gchar *locale)
-{
-	ESubprocessBookFactoryPrivate *priv = subprocess_factory->priv;
-	GError *error = NULL;
-
-	if (g_strcmp0 (priv->locale, locale) != 0) {
-		GList *backends, *l;
-
-		g_free (priv->locale);
-		priv->locale = g_strdup (locale);
+		GList *backends, *link;
+		GError *local_error = NULL;
 
 		backends = e_subprocess_factory_get_backends_list (E_SUBPROCESS_FACTORY (subprocess_factory));
 
-		for (l = backends; l != NULL; l = g_list_next (l)) {
-			EBackend *backend = l->data;
+		for (link = backends; link; link = g_list_next (link)) {
+			EBackend *backend = link->data;
 			EDataBook *data_book;
 
 			data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend));
 
-			if (!e_data_book_set_locale (data_book, locale, NULL, &error)) {
-				g_warning (
-					"Failed to set locale on addressbook: %s",
-					error->message);
-				g_clear_error (&error);
+			if (!e_data_book_set_locale (data_book, locale, NULL, &local_error)) {
+				g_warning ("Failed to set locale on addressbook: %s", local_error ? local_error->message : "Unknown error");
+				g_clear_error (&local_error);
 			}
 
 			g_object_unref (data_book);
 		}
 
 		g_list_free_full (backends, g_object_unref);
+		g_free (locale);
 	}
 }
 
 static void
-subprocess_book_factory_locale_changed (GObject *object,
-					GParamSpec *pspec,
-					gpointer user_data)
+subprocess_book_factory_constructed (GObject *object)
 {
-	EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object);
-	ESubprocessBookFactory *factory = (ESubprocessBookFactory *) user_data;
-	const gchar * const *locale;
-	gchar *interpreted_locale;
+	ESubprocessBookFactory *subprocess_factory;
 
-	locale = e_dbus_locale1_get_locale (locale_proxy);
-	interpreted_locale = subprocess_book_factory_interpret_locale (locale);
+	/* Chain up to parent's method */
+	G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->constructed (object);
 
-	subprocess_book_factory_set_locale (factory, interpreted_locale);
+	subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object);
+	subprocess_factory->priv->locale_watcher = e_system_locale_watcher_new ();
 
-	g_free (interpreted_locale);
+	subprocess_factory->priv->notify_locale_id = g_signal_connect (
+		subprocess_factory->priv->locale_watcher, "notify::locale",
+		G_CALLBACK (subprocess_book_factory_notify_locale_cb), subprocess_factory);
 }
 
 static void
-subprocess_book_factory_localed_ready (GObject *source_object,
-				       GAsyncResult *res,
-				       gpointer user_data)
+subprocess_book_factory_dispose (GObject *object)
 {
-	ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data;
-	GError *error = NULL;
-
-	subprocess_factory->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error);
+	ESubprocessBookFactory *subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object);
 
-	if (subprocess_factory->priv->localed_proxy == NULL) {
-		g_warning ("Error fetching localed proxy: %s", error->message);
-		g_error_free (error);
+	if (subprocess_factory->priv->locale_watcher && subprocess_factory->priv->notify_locale_id) {
+		g_signal_handler_disconnect (subprocess_factory->priv->locale_watcher, subprocess_factory->priv->notify_locale_id);
+		subprocess_factory->priv->notify_locale_id = 0;
 	}
 
-	g_clear_object (&subprocess_factory->priv->localed_cancel);
-
-	if (subprocess_factory->priv->localed_proxy) {
-		g_signal_connect (
-			subprocess_factory->priv->localed_proxy, "notify::locale",
-			G_CALLBACK (subprocess_book_factory_locale_changed), subprocess_factory);
+	g_clear_object (&subprocess_factory->priv->locale_watcher);
 
-		/* Initial refresh of the locale */
-		subprocess_book_factory_locale_changed (
-			G_OBJECT (subprocess_factory->priv->localed_proxy), NULL, subprocess_factory);
-	}
-}
-
-static void
-subprocess_book_factory_localed_appeared (GDBusConnection *connection,
-					  const gchar *name,
-					  const gchar *name_owner,
-					  gpointer user_data)
-{
-	ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data;
-
-	subprocess_factory->priv->localed_cancel = g_cancellable_new ();
-
-	e_dbus_locale1_proxy_new (
-		connection,
-		G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
-		"org.freedesktop.locale1",
-		"/org/freedesktop/locale1",
-		subprocess_factory->priv->localed_cancel,
-		subprocess_book_factory_localed_ready,
-		subprocess_factory);
-}
-
-static void
-subprocess_book_factory_localed_vanished (GDBusConnection *connection,
-					  const gchar *name,
-					  gpointer user_data)
-{
-	ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data;
-
-	if (subprocess_factory->priv->localed_cancel) {
-		g_cancellable_cancel (subprocess_factory->priv->localed_cancel);
-		g_clear_object (&subprocess_factory->priv->localed_cancel);
-	}
-
-	g_clear_object (&subprocess_factory->priv->localed_proxy);
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->dispose (object);
 }
 
 static void
@@ -355,51 +207,17 @@ e_subprocess_book_factory_class_init (ESubprocessBookFactoryClass *class)
 	g_type_class_add_private (class, sizeof (ESubprocessBookFactoryPrivate));
 
 	object_class = G_OBJECT_CLASS (class);
+	object_class->constructed = subprocess_book_factory_constructed;
 	object_class->dispose = subprocess_book_factory_dispose;
-	object_class->finalize = subprocess_book_factory_finalize;
 
 	subprocess_factory_class = E_SUBPROCESS_FACTORY_CLASS (class);
 	subprocess_factory_class->ref_backend = subprocess_book_factory_ref_backend;
 	subprocess_factory_class->open_data = subprocess_book_factory_open;
 }
 
-static gboolean
-subprocess_book_factory_initable_init (GInitable *initable,
-				       GCancellable *cancellable,
-				       GError **error)
-{
-	ESubprocessBookFactory *subprocess_factory;
-	GBusType bus_type = G_BUS_TYPE_SYSTEM;
-
-	subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (initable);
-
-	/* When running tests, we pretend to be the "org.freedesktop.locale1" service
-	 * on the session bus instead of the real location on the system bus.
-	 */
-	if (g_getenv ("EDS_TESTING") != NULL)
-		bus_type = G_BUS_TYPE_SESSION;
-
-	/* Watch system bus for locale change notifications */
-	subprocess_factory->priv->localed_watch_id =
-		g_bus_watch_name (
-			bus_type,
-			"org.freedesktop.locale1",
-			G_BUS_NAME_WATCHER_FLAGS_NONE,
-			subprocess_book_factory_localed_appeared,
-			subprocess_book_factory_localed_vanished,
-			initable,
-			NULL);
-
-	/* Chain up to parent interface's init() method. */
-	return initable_parent_interface->init (initable, cancellable, error);
-}
-
 static void
 e_subprocess_book_factory_initable_init (GInitableIface *iface)
 {
-	initable_parent_interface = g_type_interface_peek_parent (iface);
-
-	iface->init = subprocess_book_factory_initable_init;
 }
 
 static void
diff --git a/src/addressbook/libedata-book/e-system-locale-watcher.c b/src/addressbook/libedata-book/e-system-locale-watcher.c
new file mode 100644
index 000000000..c8f9b1fa8
--- /dev/null
+++ b/src/addressbook/libedata-book/e-system-locale-watcher.c
@@ -0,0 +1,365 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: e-source-locale-watcher
+ * @include: libedata-book/libedata-book.h
+ * @short_description: Watch changes of system locale
+ *
+ * #ESystemLocaleWatcher watches for changes of system locale.
+ **/
+
+#include "evolution-data-server-config.h"
+
+#include <locale.h>
+#include <string.h>
+
+#include "e-dbus-localed.h"
+
+#include "e-system-locale-watcher.h"
+
+struct _ESystemLocaleWatcherPrivate {
+	GMutex lock;
+
+	/* Watching "org.freedesktop.locale1" for locale changes when not using backend-per-process */
+	guint localed_watch_id;
+	EDBusLocale1 *localed_proxy;
+	GCancellable *localed_cancel;
+	gchar *locale;
+};
+
+G_DEFINE_TYPE (ESystemLocaleWatcher, e_system_locale_watcher, G_TYPE_OBJECT)
+
+enum {
+	PROP_0,
+	PROP_LOCALE
+};
+
+static gchar *
+system_locale_watcher_interpret_locale_value (const gchar *value)
+{
+	gchar *interpreted_value = NULL;
+	gchar **split;
+
+	split = g_strsplit (value, "=", 2);
+
+	if (split && split[0] && split[1])
+		interpreted_value = g_strdup (split[1]);
+
+	g_strfreev (split);
+
+	if (!interpreted_value)
+		g_warning ("Failed to interpret locale value: %s", value);
+
+	return interpreted_value;
+}
+
+static gchar *
+system_locale_watcher_interpret_locale (const gchar * const * locale)
+{
+	gint i;
+	gchar *interpreted_locale = NULL;
+
+	/* Prioritize LC_COLLATE and then LANG values
+	 * in the 'locale' specified by localed.
+	 *
+	 * If localed explicitly specifies no locale, then
+	 * default to checking system locale.
+	 */
+	if (locale) {
+		for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
+			if (strncmp (locale[i], "LC_COLLATE", 10) == 0)
+				interpreted_locale = system_locale_watcher_interpret_locale_value (locale[i]);
+		}
+
+		for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) {
+			if (strncmp (locale[i], "LANG", 4) == 0)
+				interpreted_locale = system_locale_watcher_interpret_locale_value (locale[i]);
+		}
+	}
+
+	if (!interpreted_locale) {
+		const gchar *system_locale = setlocale (LC_COLLATE, NULL);
+
+		interpreted_locale = g_strdup (system_locale);
+	}
+
+	return interpreted_locale;
+}
+
+static void
+system_locale_watcher_set_locale (ESystemLocaleWatcher *watcher,
+				  const gchar *locale)
+{
+	g_mutex_lock (&watcher->priv->lock);
+
+	if (g_strcmp0 (watcher->priv->locale, locale) != 0) {
+
+		g_free (watcher->priv->locale);
+		watcher->priv->locale = g_strdup (locale);
+
+		g_mutex_unlock (&watcher->priv->lock);
+
+		g_object_notify (G_OBJECT (watcher), "locale");
+	} else {
+		g_mutex_unlock (&watcher->priv->lock);
+	}
+}
+
+static void
+system_locale_watcher_locale_changed (GObject *object,
+				      GParamSpec *pspec,
+				      gpointer user_data)
+{
+	EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object);
+	ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data;
+	const gchar * const *locale;
+	gchar *interpreted_locale;
+
+	locale = e_dbus_locale1_get_locale (locale_proxy);
+	interpreted_locale = system_locale_watcher_interpret_locale (locale);
+
+	system_locale_watcher_set_locale (watcher, interpreted_locale);
+
+	g_free (interpreted_locale);
+}
+
+static void
+system_locale_watcher_localed_ready (GObject *source_object,
+				     GAsyncResult *res,
+				     gpointer user_data)
+{
+	ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data;
+	GError *error = NULL;
+
+	watcher->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error);
+
+	if (!watcher->priv->localed_proxy) {
+		g_warning ("Error fetching localed proxy: %s", error ? error->message : "Unknown error");
+		g_clear_error (&error);
+	}
+
+	g_clear_object (&watcher->priv->localed_cancel);
+
+	if (watcher->priv->localed_proxy) {
+		g_signal_connect (
+			watcher->priv->localed_proxy, "notify::locale",
+			G_CALLBACK (system_locale_watcher_locale_changed), watcher);
+
+		/* Initial refresh of the locale */
+		system_locale_watcher_locale_changed (G_OBJECT (watcher->priv->localed_proxy), NULL, watcher);
+	}
+}
+
+static void
+system_locale_watcher_localed_appeared (GDBusConnection *connection,
+					const gchar *name,
+					const gchar *name_owner,
+					gpointer user_data)
+{
+	ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data;
+
+	watcher->priv->localed_cancel = g_cancellable_new ();
+
+	e_dbus_locale1_proxy_new (
+		connection,
+		G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
+		"org.freedesktop.locale1",
+		"/org/freedesktop/locale1",
+		watcher->priv->localed_cancel,
+		system_locale_watcher_localed_ready,
+		watcher);
+}
+
+static void
+system_locale_watcher_localed_vanished (GDBusConnection *connection,
+					const gchar *name,
+					gpointer user_data)
+{
+	ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data;
+
+	if (watcher->priv->localed_cancel) {
+		g_cancellable_cancel (watcher->priv->localed_cancel);
+		g_clear_object (&watcher->priv->localed_cancel);
+	}
+
+	g_clear_object (&watcher->priv->localed_proxy);
+}
+
+static void
+system_locale_watcher_get_property (GObject *object,
+				    guint property_id,
+				    GValue *value,
+				    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_LOCALE:
+			g_value_take_string (
+				value,
+				e_system_locale_watcher_dup_locale (
+				E_SYSTEM_LOCALE_WATCHER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+system_locale_watcher_constructed (GObject *object)
+{
+	ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+	GBusType bus_type = G_BUS_TYPE_SYSTEM;
+
+	/* Chain up to parent's method. */
+	G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->constructed (object);
+
+	/* When running tests, we pretend to be the "org.freedesktop.locale1" service
+	 * on the session bus instead of the real location on the system bus.
+	 */
+	if (g_getenv ("EDS_TESTING") != NULL)
+		bus_type = G_BUS_TYPE_SESSION;
+
+	/* Watch system bus for locale change notifications */
+	watcher->priv->localed_watch_id =
+		g_bus_watch_name (
+			bus_type,
+			"org.freedesktop.locale1",
+			G_BUS_NAME_WATCHER_FLAGS_NONE,
+			system_locale_watcher_localed_appeared,
+			system_locale_watcher_localed_vanished,
+			watcher,
+			NULL);
+}
+
+static void
+system_locale_watcher_dispose (GObject *object)
+{
+	ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+
+	if (watcher->priv->localed_cancel)
+		g_cancellable_cancel (watcher->priv->localed_cancel);
+
+	g_clear_object (&watcher->priv->localed_cancel);
+	g_clear_object (&watcher->priv->localed_proxy);
+
+	if (watcher->priv->localed_watch_id > 0)
+		g_bus_unwatch_name (watcher->priv->localed_watch_id);
+
+	/* Chain up to parent's method. */
+	G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->dispose (object);
+}
+
+static void
+system_locale_watcher_finalize (GObject *object)
+{
+	ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object);
+
+	g_free (watcher->priv->locale);
+	g_mutex_clear (&watcher->priv->lock);
+
+	/* Chain up to parent's method. */
+	G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->finalize (object);
+}
+
+static void
+e_system_locale_watcher_class_init (ESystemLocaleWatcherClass *klass)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (klass, sizeof (ESystemLocaleWatcherPrivate));
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->get_property = system_locale_watcher_get_property;
+	object_class->constructed = system_locale_watcher_constructed;
+	object_class->dispose = system_locale_watcher_dispose;
+	object_class->finalize = system_locale_watcher_finalize;
+
+	/**
+	 * ESystemLocaleWatcher:locale:
+	 *
+	 * Current locale, as detected. It can be %NULL, when the locale
+	 * was not detected yet.
+	 *
+	 * Since: 3.30
+	 **/
+	g_object_class_install_property (
+		object_class,
+		PROP_LOCALE,
+		g_param_spec_string (
+			"locale",
+			"Locale",
+			NULL,
+			NULL,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_system_locale_watcher_init (ESystemLocaleWatcher *watcher)
+{
+	watcher->priv = G_TYPE_INSTANCE_GET_PRIVATE (watcher, E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcherPrivate);
+
+	g_mutex_init (&watcher->priv->lock);
+
+	watcher->priv->locale = NULL;
+}
+
+/**
+ * e_system_locale_watcher_new:
+ *
+ * Creates a new #ESystemLocaleWatcher instance, which listens for D-Bus
+ * notification on locale changes. It uses system bus, unless an environment
+ * variable "EDS_TESTING" is defined, in which case it uses the session bus
+ * instead.
+ *
+ * Returns: (transfer full): a new #ESystemLocaleWatcher
+ *
+ * Since: 3.30
+ **/
+ESystemLocaleWatcher *
+e_system_locale_watcher_new (void)
+{
+	return g_object_new (E_TYPE_SYSTEM_LOCALE_WATCHER, NULL);
+}
+
+/**
+ * e_system_locale_watcher_dup_locale:
+ * @watcher: an #ESystemLocaleWatcher
+ *
+ * Returns the current locale, as detected by the @watcher. The string
+ * is duplicated for thread safety. It can be %NULL, when the locale
+ * was not detected yet.
+ *
+ * Free it with g_free(), when no longer needed.
+ *
+ * Returns: (transfer full) (nullable): the system locale, as detected by the @watcher
+ *
+ * Since: 3.30
+ **/
+gchar *
+e_system_locale_watcher_dup_locale (ESystemLocaleWatcher *watcher)
+{
+	gchar *locale;
+
+	g_return_val_if_fail (E_IS_SYSTEM_LOCALE_WATCHER (watcher), NULL);
+
+	g_mutex_lock (&watcher->priv->lock);
+	locale = g_strdup (watcher->priv->locale);
+	g_mutex_unlock (&watcher->priv->lock);
+
+	return locale;
+}
diff --git a/src/addressbook/libedata-book/e-system-locale-watcher.h b/src/addressbook/libedata-book/e-system-locale-watcher.h
new file mode 100644
index 000000000..e1824b49c
--- /dev/null
+++ b/src/addressbook/libedata-book/e-system-locale-watcher.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__LIBEDATA_BOOK_H_INSIDE__) && !defined (LIBEDATA_BOOK_COMPILATION)
+#error "Only <libedata-book/libedata-book.h> should be included directly."
+#endif
+
+#ifndef E_SYSTEM_LOCALE_WATCHER_H
+#define E_SYSTEM_LOCALE_WATCHER_H
+
+#include <glib-object.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SYSTEM_LOCALE_WATCHER \
+	(e_system_locale_watcher_get_type ())
+#define E_SYSTEM_LOCALE_WATCHER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcher))
+#define E_SYSTEM_LOCALE_WATCHER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcherClass))
+#define E_IS_SYSTEM_LOCALE_WATCHER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_SYSTEM_LOCALE_WATCHER))
+#define E_IS_SYSTEM_LOCALE_WATCHER_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_SYSTEM_LOCALE_WATCHER))
+#define E_SYSTEM_LOCALE_WATCHER_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcherClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESystemLocaleWatcher ESystemLocaleWatcher;
+typedef struct _ESystemLocaleWatcherClass ESystemLocaleWatcherClass;
+typedef struct _ESystemLocaleWatcherPrivate ESystemLocaleWatcherPrivate;
+
+/**
+ * ESystemLocaleWatcher:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ **/
+struct _ESystemLocaleWatcher {
+	/*< private >*/
+	GObject parent;
+	ESystemLocaleWatcherPrivate *priv;
+};
+
+struct _ESystemLocaleWatcherClass {
+	GObjectClass parent_class;
+};
+
+GType		e_system_locale_watcher_get_type	(void) G_GNUC_CONST;
+ESystemLocaleWatcher *
+		e_system_locale_watcher_new		(void);
+gchar *		e_system_locale_watcher_dup_locale	(ESystemLocaleWatcher *watcher);
+
+G_END_DECLS
+
+#endif /* E_SYSTEM_LOCALE_WATCHER_H */
diff --git a/src/addressbook/libedata-book/libedata-book.h b/src/addressbook/libedata-book/libedata-book.h
index 50ee04182..b5db50376 100644
--- a/src/addressbook/libedata-book/libedata-book.h
+++ b/src/addressbook/libedata-book/libedata-book.h
@@ -40,6 +40,7 @@
 #include <libedata-book/e-data-book-view.h>
 #include <libedata-book/e-data-book.h>
 #include <libedata-book/e-subprocess-book-factory.h>
+#include <libedata-book/e-system-locale-watcher.h>
 
 #undef __LIBEDATA_BOOK_H_INSIDE__
 
diff --git a/src/calendar/libedata-cal/e-data-cal-factory.c b/src/calendar/libedata-cal/e-data-cal-factory.c
index baaeb5ffb..808abf7c2 100644
--- a/src/calendar/libedata-cal/e-data-cal-factory.c
+++ b/src/calendar/libedata-cal/e-data-cal-factory.c
@@ -144,6 +144,96 @@ data_cal_complete_open (EDataFactory *data_factory,
 		data_cal_complete_memo_list_open (data_factory, invocation, object_path, bus_name);
 }
 
+static void
+data_cal_factory_backend_closed_cb (EBackend *backend,
+				    const gchar *sender,
+				    EDataFactory *data_factory)
+{
+	e_data_factory_backend_closed (data_factory, backend);
+}
+
+static EBackend *
+data_cal_factory_create_backend (EDataFactory *data_factory,
+				 EBackendFactory *backend_factory,
+				 ESource *source)
+{
+	ECalBackendFactoryClass *backend_factory_class;
+	EBackend *backend;
+
+	g_return_val_if_fail (E_IS_DATA_CAL_FACTORY (data_factory), NULL);
+	g_return_val_if_fail (E_IS_CAL_BACKEND_FACTORY (backend_factory), NULL);
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	backend_factory_class = E_CAL_BACKEND_FACTORY_GET_CLASS (backend_factory);
+	g_return_val_if_fail (backend_factory_class != NULL, NULL);
+
+	if (g_type_is_a (backend_factory_class->backend_type, G_TYPE_INITABLE)) {
+		GError *local_error = NULL;
+
+		backend = g_initable_new (backend_factory_class->backend_type, NULL, &local_error,
+			"kind", backend_factory_class->component_kind,
+			"registry", e_data_factory_get_registry (data_factory),
+			"source", source,
+			NULL);
+
+		if (!backend)
+			g_warning ("%s: Failed to create backend: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error");
+
+		g_clear_error (&local_error);
+	} else {
+		backend = g_object_new (backend_factory_class->backend_type,
+			"kind", backend_factory_class->component_kind,
+			"registry", e_data_factory_get_registry (data_factory),
+			"source", source,
+			NULL);
+	}
+
+	if (backend) {
+		g_signal_connect (backend, "closed",
+			G_CALLBACK (data_cal_factory_backend_closed_cb), data_factory);
+	}
+
+	return backend;
+}
+
+static gchar *
+data_cal_factory_open_backend (EDataFactory *data_factory,
+			       EBackend *backend,
+			       GDBusConnection *connection,
+			       GCancellable *cancellable,
+			       GError **error)
+{
+	EDataCal *data_cal;
+	gchar *object_path;
+
+	g_return_val_if_fail (E_IS_DATA_CAL_FACTORY (data_factory), NULL);
+	g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+
+	/* If the backend already has an EDataCal installed, return its
+	 * object path.  Otherwise we need to install a new EDataCal. */
+	data_cal = e_cal_backend_ref_data_cal (E_CAL_BACKEND (backend));
+
+	if (data_cal) {
+		object_path = g_strdup (e_data_cal_get_object_path (data_cal));
+	} else {
+		object_path = e_subprocess_factory_construct_path ();
+
+		/* The EDataCal will attach itself to ECalBackend,
+		 * so no need to call e_cal_backend_set_data_cal(). */
+		data_cal = e_data_cal_new (E_CAL_BACKEND (backend), connection, object_path, error);
+
+		if (!data_cal) {
+			g_free (object_path);
+			object_path = NULL;
+		}
+	}
+
+	g_clear_object (&data_cal);
+
+	return object_path;
+}
+
 static gboolean
 data_cal_factory_initable_init (GInitable *initable,
 			        GCancellable *cancellable,
@@ -196,6 +286,8 @@ e_data_cal_factory_class_init (EDataCalFactoryClass *class)
 	data_factory_class->get_dbus_interface_skeleton = data_cal_factory_get_dbus_interface_skeleton;
 	data_factory_class->get_factory_name = data_cal_get_factory_name;
 	data_factory_class->complete_open = data_cal_complete_open;
+	data_factory_class->create_backend = data_cal_factory_create_backend;
+	data_factory_class->open_backend = data_cal_factory_open_backend;
 }
 
 static gboolean
diff --git a/src/libebackend/e-data-factory.c b/src/libebackend/e-data-factory.c
index 26faa6a7b..9495da104 100644
--- a/src/libebackend/e-data-factory.c
+++ b/src/libebackend/e-data-factory.c
@@ -85,6 +85,9 @@ struct _EDataFactoryPrivate {
 
 	gboolean reload_supported;
 	gint backend_per_process;
+
+	/* Used only when backend-per-process is disabled, guarded by 'mutex' */
+	GHashTable *opened_backends; /* backend-key ~> OpenedBackendData * */
 };
 
 enum {
@@ -183,6 +186,36 @@ data_factory_subprocess_helper_free (DataFactorySubprocessHelper *helper)
 	}
 }
 
+typedef struct _OpenedBackendData {
+	EBackend *backend;
+	gchar *object_path;
+} OpenedBackendData;
+
+static OpenedBackendData *
+opened_backend_data_new (EBackend *backend, /* assumes ownership of 'backend' */
+			 const gchar *object_path)
+{
+	OpenedBackendData *obd;
+
+	obd = g_new0 (OpenedBackendData, 1);
+	obd->backend = backend;
+	obd->object_path = g_strdup (object_path);
+
+	return obd;
+}
+
+static void
+opened_backend_data_free (gpointer ptr)
+{
+	OpenedBackendData *obd = ptr;
+
+	if (obd) {
+		g_clear_object (&obd->backend);
+		g_free (obd->object_path);
+		g_free (obd);
+	}
+}
+
 static gchar *
 data_factory_construct_subprocess_path (EDataFactory *data_factory)
 {
@@ -287,24 +320,13 @@ data_factory_subprocess_data_free (DataFactorySubprocessData *sd)
 }
 
 static gchar *
-data_factory_dup_subprocess_helper_hash_key (gint override_backend_per_process,
-					     const gchar *factory_name,
+data_factory_dup_subprocess_helper_hash_key (const gchar *factory_name,
 					     const gchar *extension_name,
 					     const gchar *uid,
+					     gboolean backend_per_process,
 					     gboolean backend_factory_share_subprocess)
 {
 	gchar *helper_hash_key;
-	gboolean backend_per_process;
-
-	if (override_backend_per_process == 0 || override_backend_per_process == 1) {
-		backend_per_process = override_backend_per_process == 1;
-	} else {
-		#ifdef ENABLE_BACKEND_PER_PROCESS
-		backend_per_process = TRUE;
-		#else
-		backend_per_process = FALSE;
-		#endif
-	}
 
 	if (backend_per_process) {
 		helper_hash_key = backend_factory_share_subprocess ?
@@ -955,6 +977,8 @@ data_factory_finalize (GObject *object)
 	g_hash_table_destroy (priv->watched_names);
 	g_mutex_clear (&priv->watched_names_lock);
 
+	g_hash_table_destroy (priv->opened_backends);
+
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_data_factory_parent_class)->finalize (object);
 }
@@ -1120,6 +1144,8 @@ e_data_factory_init (EDataFactory *data_factory)
 		(GDestroyNotify) g_free,
 		(GDestroyNotify) watched_names_value_free);
 
+	data_factory->priv->opened_backends = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, opened_backend_data_free);
+
 	data_factory->priv->spawn_subprocess_state = DATA_FACTORY_SPAWN_SUBPROCESS_NONE;
 	data_factory->priv->reload_supported = FALSE;
 	data_factory->priv->backend_per_process = -1;
@@ -1221,6 +1247,50 @@ e_data_factory_construct_path (EDataFactory *data_factory)
 		class->data_object_path_prefix, getpid (), counter);
 }
 
+static void
+data_factory_backend_toggle_notify_cb (gpointer user_data,
+				       GObject *backend,
+				       gboolean is_last_ref)
+{
+	if (is_last_ref) {
+		EDataFactory *data_factory = E_DATA_FACTORY (user_data);
+		gboolean found = FALSE;
+
+		g_object_ref (backend);
+
+		g_object_remove_toggle_ref (
+			backend, data_factory_backend_toggle_notify_cb, user_data);
+
+		g_signal_emit_by_name (backend, "shutdown");
+
+		g_mutex_lock (&data_factory->priv->mutex);
+		if (data_factory->priv->opened_backends) {
+			GHashTableIter iter;
+			gpointer key, value;
+
+			g_hash_table_iter_init (&iter, data_factory->priv->opened_backends);
+			while (g_hash_table_iter_next (&iter, &key, &value)) {
+				OpenedBackendData *obd = value;
+
+				if (obd && obd->backend == (EBackend *) backend) {
+					g_hash_table_remove (data_factory->priv->opened_backends, key);
+					found = TRUE;
+					break;
+				}
+			}
+		}
+		g_mutex_unlock (&data_factory->priv->mutex);
+
+		if (!found) {
+			g_warn_if_reached ();
+			g_object_unref (backend);
+		}
+
+		/* Also drop the "reference" on the data_factory */
+		e_dbus_server_release (E_DBUS_SERVER (data_factory));
+	}
+}
+
 static void
 data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
 				       GDBusMethodInvocation *invocation,
@@ -1239,6 +1309,7 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
 	guint watched_id = 0;
 	gchar *backend_name = NULL;
 	gchar *subprocess_helpers_hash_key;
+	gboolean backend_per_process;
 	const gchar *factory_name;
 	const gchar *filename;
 	const gchar *type_name;
@@ -1258,24 +1329,83 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
 		extension = e_source_get_extension (source, extension_name);
 		backend_name = e_source_backend_dup_backend_name (extension);
 	}
-	g_clear_object (&source);
 
 	if (backend_name && *backend_name) {
 		backend_factory = e_data_factory_ref_backend_factory (
 			data_factory, backend_name, extension_name);
 	}
 
-	g_free (backend_name);
+	class = E_DATA_FACTORY_GET_CLASS (data_factory);
+	backend_per_process = e_data_factory_use_backend_per_process (data_factory);
+
+	if (!backend_per_process && backend_factory) {
+		gchar *object_path = NULL, *backend_key;
+		OpenedBackendData *obd;
+
+		backend_key = g_strconcat (backend_name, "\n", uid, "\n", extension_name, NULL);
+
+		g_mutex_lock (&data_factory->priv->mutex);
+		obd = g_hash_table_lookup (data_factory->priv->opened_backends, backend_key);
+		if (obd) {
+			object_path = g_strdup (obd->object_path);
+			g_object_ref (obd->backend);
+
+			/* Also drop the "reference" on the data_factory, it's held by the obj->backend already */
+			e_dbus_server_release (E_DBUS_SERVER (data_factory));
+		}
+		g_mutex_unlock (&data_factory->priv->mutex);
+
+		if (!object_path) {
+			EBackend *backend;
+
+			backend = e_data_factory_create_backend (data_factory, backend_factory, source);
+			object_path = e_data_factory_open_backend (data_factory, backend, g_dbus_method_invocation_get_connection (invocation), NULL, &error);
+			if (object_path) {
+				g_object_add_toggle_ref (
+					G_OBJECT (backend),
+					data_factory_backend_toggle_notify_cb,
+					data_factory);
+
+				g_mutex_lock (&data_factory->priv->mutex);
+				g_hash_table_insert (data_factory->priv->opened_backends, backend_key,
+					opened_backend_data_new (backend, object_path));
+				g_mutex_unlock (&data_factory->priv->mutex);
+
+				backend_key = NULL;
+
+				/* Do not call e_dbus_server_release() here, otherwise the factory will close
+				   shortly afterwards. The backend holds the "reference" to the factory. */
+				/* e_dbus_server_release (E_DBUS_SERVER (data_factory)); */
+			} else {
+				g_clear_object (&backend);
+			}
+		}
+
+		if (object_path) {
+			EDBusServerClass *server_class;
+
+			g_return_if_fail (class->complete_open != NULL);
 
-	if (backend_factory) {
+			server_class = E_DBUS_SERVER_GET_CLASS (data_factory);
+
+			class->complete_open (data_factory, invocation, object_path, server_class->bus_name, extension_name);
+
+			g_mutex_lock (&priv->spawn_subprocess_lock);
+			priv->spawn_subprocess_state = DATA_FACTORY_SPAWN_SUBPROCESS_NONE;
+			g_cond_signal (&priv->spawn_subprocess_cond);
+			g_mutex_unlock (&priv->spawn_subprocess_lock);
+		}
+
+		g_free (object_path);
+		g_free (backend_key);
+	} else if (backend_factory) {
 		type_name = G_OBJECT_TYPE_NAME (backend_factory);
 
-		class = E_DATA_FACTORY_GET_CLASS (data_factory);
 		factory_name = class->get_factory_name (backend_factory);
 
 		subprocess_helpers_hash_key = data_factory_dup_subprocess_helper_hash_key (
-			e_data_factory_get_backend_per_process (data_factory),
-			factory_name, extension_name, uid, e_backend_factory_share_subprocess (backend_factory));
+			factory_name, extension_name, uid, backend_per_process,
+			e_backend_factory_share_subprocess (backend_factory));
 
 		g_mutex_lock (&priv->mutex);
 		helper = g_hash_table_lookup (
@@ -1297,7 +1427,9 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
 				filename);
 
 			g_object_unref (backend_factory);
+			g_clear_object (&source);
 			g_free (subprocess_helpers_hash_key);
+			g_free (backend_name);
 
 			return;
 		}
@@ -1349,6 +1481,9 @@ data_factory_spawn_subprocess_backend (EDataFactory *data_factory,
 			uid, extension_name);
 	}
 
+	g_clear_object (&source);
+	g_free (backend_name);
+
 	if (error != NULL) {
 		e_dbus_server_release (E_DBUS_SERVER (data_factory));
 
@@ -1454,3 +1589,101 @@ e_data_factory_get_backend_per_process (EDataFactory *data_factory)
 
 	return data_factory->priv->backend_per_process;
 }
+
+gboolean
+e_data_factory_use_backend_per_process (EDataFactory *data_factory)
+{
+	gboolean backend_per_process;
+	gint override_backend_per_process;
+
+	g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), FALSE);
+
+	override_backend_per_process = e_data_factory_get_backend_per_process (data_factory);
+
+	if (override_backend_per_process == 0 || override_backend_per_process == 1) {
+		backend_per_process = override_backend_per_process == 1;
+	} else {
+		#ifdef ENABLE_BACKEND_PER_PROCESS
+		backend_per_process = TRUE;
+		#else
+		backend_per_process = FALSE;
+		#endif
+	}
+
+	return backend_per_process;
+}
+
+/* Used only when backend-per-process is off. Free the returned pointer
+   with g_object_unref(), if not NULL and no longer needed */
+EBackend *
+e_data_factory_create_backend (EDataFactory *data_factory,
+			       EBackendFactory *backend_factory,
+			       ESource *source)
+{
+	EDataFactoryClass *klass;
+
+	g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+	g_return_val_if_fail (E_IS_BACKEND_FACTORY (backend_factory), NULL);
+	g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+	klass = E_DATA_FACTORY_GET_CLASS (data_factory);
+	g_return_val_if_fail (klass->create_backend != NULL, NULL);
+
+	return klass->create_backend (data_factory, backend_factory, source);
+}
+
+/* Used only when backend-per-process is off. Free the returned string
+   with g_free(), when no longer needed */
+gchar *
+e_data_factory_open_backend (EDataFactory *data_factory,
+			     EBackend *backend,
+			     GDBusConnection *connection,
+			     GCancellable *cancellable,
+			     GError **error)
+{
+	EDataFactoryClass *klass;
+
+	g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+	g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
+	g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+
+	klass = E_DATA_FACTORY_GET_CLASS (data_factory);
+	g_return_val_if_fail (klass->open_backend != NULL, NULL);
+
+	return klass->open_backend (data_factory, backend, connection, cancellable, error);
+}
+
+/* Whenever the backend is closed by any of its clients it should call
+   this function, thus the data_factory knows about it */
+void
+e_data_factory_backend_closed (EDataFactory *data_factory,
+			       EBackend *backend)
+{
+	g_return_if_fail (E_IS_DATA_FACTORY (data_factory));
+	g_return_if_fail (E_IS_BACKEND (backend));
+
+	g_object_unref (backend);
+}
+
+/* free with g_list_free_full (list, g_object_unref);, element is 'EBackend *' */
+GSList *
+e_data_factory_list_opened_backends (EDataFactory *data_factory)
+{
+	GSList *backends = NULL;
+	GHashTableIter iter;
+	gpointer value;
+
+	g_return_val_if_fail (E_IS_DATA_FACTORY (data_factory), NULL);
+
+	g_mutex_lock (&data_factory->priv->mutex);
+	g_hash_table_iter_init (&iter, data_factory->priv->opened_backends);
+	while (g_hash_table_iter_next (&iter, NULL, &value)) {
+		OpenedBackendData *obd = value;
+
+		if (obd && obd->backend)
+			backends = g_slist_prepend (backends, g_object_ref (obd->backend));
+	}
+	g_mutex_unlock (&data_factory->priv->mutex);
+
+	return backends;
+}
diff --git a/src/libebackend/e-data-factory.h b/src/libebackend/e-data-factory.h
index 8d4e5a081..7255f56a8 100644
--- a/src/libebackend/e-data-factory.h
+++ b/src/libebackend/e-data-factory.h
@@ -85,7 +85,16 @@ struct _EDataFactoryClass {
 						 const gchar *bus_name,
 						 const gchar *extension_name);
 
-	gpointer reserved[15];
+	EBackend *	(* create_backend)	(EDataFactory *data_factory,
+						 EBackendFactory *backend_factory,
+						 ESource *source);
+	gchar *		(* open_backend)	(EDataFactory *data_factory,
+						 EBackend *backend,
+						 GDBusConnection *connection,
+						 GCancellable *cancellable,
+						 GError **error);
+
+	gpointer reserved[13];
 };
 
 GType		e_data_factory_get_type		(void) G_GNUC_CONST;
@@ -107,6 +116,20 @@ gboolean	e_data_factory_get_reload_supported
 						(EDataFactory *data_factory);
 gint		e_data_factory_get_backend_per_process
 						(EDataFactory *data_factory);
+gboolean	e_data_factory_use_backend_per_process
+						(EDataFactory *data_factory);
+EBackend *	e_data_factory_create_backend	(EDataFactory *data_factory,
+						 EBackendFactory *backend_factory,
+						 ESource *source);
+gchar *		e_data_factory_open_backend	(EDataFactory *data_factory,
+						 EBackend *backend,
+						 GDBusConnection *connection,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_data_factory_backend_closed	(EDataFactory *data_factory,
+						 EBackend *backend);
+GSList *	e_data_factory_list_opened_backends /* EBackend * */
+						(EDataFactory *data_factory);
 
 G_END_DECLS
 
