diff --git a/CHANGES b/CHANGES
index 7f1e98f16016229e81590feabeee1af0c462de4a..b992edf4c19b76480a393d5e65c45bc08d6ac900 100644
--- a/CHANGES
+++ b/CHANGES
@@ -192,6 +192,11 @@ res_pjsip
    created for an endpoint with this setting will have its accountcode set
    to the specified value.
 
+Functions
+------------------
+ * Function AUDIOHOOK_INHERIT has been deprecated. Audiohooks are now
+   unconditionally inhereted through masquerades. As a side benefit, more
+   than one audiohook of a given type may persist through a masquerade now.
 
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 12.3.0 to Asterisk 12.4.0 ------------
diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c
index 725a6f67fe5acbcb4bf4c04c87eefee6d01ab5ee..2362ad296dc1a8d2ad64c2d86f7c7e7ddf322089 100644
--- a/bridges/bridge_native_rtp.c
+++ b/bridges/bridge_native_rtp.c
@@ -405,6 +405,7 @@ static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_
 		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
 		.event_cb = native_rtp_framehook,
 		.consume_cb = native_rtp_framehook_consume,
+		.disable_inheritance = 1,
 	};
 
 	if (!data) {
diff --git a/funcs/func_audiohookinherit.c b/funcs/func_audiohookinherit.c
index ea5c5e9ab66d2d4a67e2bd90a247338104b43d91..28a2a455058b604c9e573e9f55ee4fed67cc65a6 100644
--- a/funcs/func_audiohookinherit.c
+++ b/funcs/func_audiohookinherit.c
@@ -29,260 +29,43 @@
  */
 
 /*** MODULEINFO
-	<support_level>core</support_level>
+	<support_level>deprecated</support_level>
  ***/
 
 #include "asterisk.h"
-#include "asterisk/datastore.h"
 #include "asterisk/channel.h"
 #include "asterisk/logger.h"
-#include "asterisk/audiohook.h"
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
 
 /*** DOCUMENTATION
- 	<function name = "AUDIOHOOK_INHERIT" language="en_US">
+	<function name = "AUDIOHOOK_INHERIT" language="en_US">
 		<synopsis>
-			Set whether an audiohook may be inherited to another channel
+			DEPRECATED: Used to set whether an audiohook may be inherited to another
+			channel. Due to architectural changes in Asterisk 12, audiohook inheritance
+			is performed automatically and this function now lacks function.
 		</synopsis>
-		<syntax>
-			<parameter name="source" required="true">
-				<para>The built-in sources in Asterisk are</para>
-				<enumlist>
-					<enum name="MixMonitor" />
-					<enum name="Chanspy" />
-					<enum name="Volume" />
-					<enum name="Speex" />
-					<enum name="pitch_shift" />
-					<enum name="JACK_HOOK" />
-					<enum name="Mute" />
-				</enumlist>
-				<para>Note that the names are not case-sensitive</para>
-			</parameter>
-		</syntax>
 		<description>
-			<para>By enabling audiohook inheritance on the channel, you are giving
-			permission for an audiohook to be inherited by a descendent channel.
-			Inheritance may be be disabled at any point as well.</para>
-
-			<para>Example scenario:</para>
-			<para>exten => 2000,1,MixMonitor(blah.wav)</para>
-			<para>exten => 2000,n,Set(AUDIOHOOK_INHERIT(MixMonitor)=yes)</para>
-			<para>exten => 2000,n,Dial(SIP/2000)</para>
-			<para>
-			</para>
-			<para>exten => 4000,1,Dial(SIP/4000)</para>
-			<para>
-			</para>
-			<para>exten => 5000,1,MixMonitor(blah2.wav)</para>
-			<para>exten => 5000,n,Dial(SIP/5000)</para>
-			<para>
-			</para>
-			<para>In this basic dialplan scenario, let's consider the following sample calls</para>
-			<para>Call 1: Caller dials 2000. The person who answers then executes an attended</para>
-			<para>        transfer to 4000.</para>
-			<para>Result: Since extension 2000 set MixMonitor to be inheritable, after the</para>
-			<para>        transfer to 4000 has completed, the call will continue to be recorded
-			to blah.wav</para>
-			<para>
-			</para>
-			<para>Call 2: Caller dials 5000. The person who answers then executes an attended</para>
-			<para>        transfer to 4000.</para>
-			<para>Result: Since extension 5000 did not set MixMonitor to be inheritable, the</para>
-			<para>        recording will stop once the call has been transferred to 4000.</para>
+			<para>Prior to Asterisk 12, masquerades would occur under all sorts of
+			situations which were hard to predict. In Asterisk 12, masquerades now only
+			occur as a result of small set of similar operations for which inheriting
+			all	audiohooks from the original channel is now safe, so in Asterisk 12.5+,
+			all audiohooks are inherited without needing other controls expressing
+			which audiohooks should	be inherited under which which conditions.</para>
 		</description>
 	</function>
  ***/
 
-struct inheritable_audiohook {
-	AST_LIST_ENTRY(inheritable_audiohook) list;
-	char source[1];
-};
-
-struct audiohook_inheritance_datastore {
-	AST_LIST_HEAD (, inheritable_audiohook) allowed_list;
-};
-
-static void audiohook_inheritance_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
-static void audiohook_inheritance_destroy (void *data);
-static const struct ast_datastore_info audiohook_inheritance_info = {
-	.type = "audiohook inheritance",
-	.destroy = audiohook_inheritance_destroy,
-	.chan_fixup = audiohook_inheritance_fixup,
-};
-
-/*! \brief Move audiohooks as defined by previous calls to the AUDIOHOOK_INHERIT function
- *
- * Move allowed audiohooks from the old channel to the new channel.
- *
- * \param data The ast_datastore containing audiohook inheritance information that will be moved
- * \param old_chan The "clone" channel from a masquerade. We are moving the audiohook in question off of this channel
- * \param new_chan The "original" channel from a masquerade. We are moving the audiohook in question to this channel
- * \return Void
- */
-static void audiohook_inheritance_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
-{
-	struct inheritable_audiohook *audiohook = NULL;
-	struct audiohook_inheritance_datastore *datastore = data;
-
-	ast_debug(2, "inheritance fixup occurring for channels %s(%p) and %s(%p)", ast_channel_name(old_chan), old_chan, ast_channel_name(new_chan), new_chan);
-
-	AST_LIST_TRAVERSE(&datastore->allowed_list, audiohook, list) {
-		ast_audiohook_move_by_source(old_chan, new_chan, audiohook->source);
-		ast_debug(3, "Moved audiohook %s from %s(%p) to %s(%p)\n",
-			audiohook->source, ast_channel_name(old_chan), old_chan, ast_channel_name(new_chan), new_chan);
-	}
-	return;
-}
-
-/*! \brief Destroy dynamically allocated data on an audiohook_inheritance_datastore
- *
- * \param data Pointer to the audiohook_inheritance_datastore in question.
- * \return Void
- */
-static void audiohook_inheritance_destroy(void *data)
-{
-	struct audiohook_inheritance_datastore *audiohook_inheritance_datastore = data;
-	struct inheritable_audiohook *inheritable_audiohook = NULL;
-
-	while ((inheritable_audiohook = AST_LIST_REMOVE_HEAD(&audiohook_inheritance_datastore->allowed_list, list))) {
-		ast_free(inheritable_audiohook);
-	}
-
-	ast_free(audiohook_inheritance_datastore);
-}
-
-/*! \brief create an audiohook_inheritance_datastore and attach it to a channel
- *
- * \param chan The channel to which we wish to attach the new datastore
- * \return Returns the newly created audiohook_inheritance_datastore or NULL on error
- */
-static struct audiohook_inheritance_datastore *setup_inheritance_datastore(struct ast_channel *chan)
-{
-	struct ast_datastore *datastore = NULL;
-	struct audiohook_inheritance_datastore *audiohook_inheritance_datastore = NULL;
-
-	if (!(datastore = ast_datastore_alloc(&audiohook_inheritance_info, NULL))) {
-		return NULL;
-	}
-
-	if (!(audiohook_inheritance_datastore = ast_calloc(1, sizeof(*audiohook_inheritance_datastore)))) {
-		ast_datastore_free(datastore);
-		return NULL;
-	}
-
-	datastore->data = audiohook_inheritance_datastore;
-	ast_channel_lock(chan);
-	ast_channel_datastore_add(chan, datastore);
-	ast_channel_unlock(chan);
-	return audiohook_inheritance_datastore;
-}
-
-/*! \brief Create a new inheritable_audiohook structure and add it to an audiohook_inheritance_datastore
- *
- * \param audiohook_inheritance_datastore The audiohook_inheritance_datastore we want to add the new inheritable_audiohook to
- * \param source The audiohook source for the newly created inheritable_audiohook
- * \retval 0 Success
- * \retval non-zero Failure
- */
-static int setup_inheritable_audiohook(struct audiohook_inheritance_datastore *audiohook_inheritance_datastore, const char *source)
-{
-	struct inheritable_audiohook *inheritable_audiohook = NULL;
-
-	inheritable_audiohook = ast_calloc(1, sizeof(*inheritable_audiohook) + strlen(source));
-
-	if (!inheritable_audiohook) {
-		return -1;
-	}
-
-	strcpy(inheritable_audiohook->source, source);
-	AST_LIST_INSERT_TAIL(&audiohook_inheritance_datastore->allowed_list, inheritable_audiohook, list);
-	ast_debug(3, "Set audiohook %s to be inheritable\n", source);
-	return 0;
-}
-
-/*! \brief Set the permissibility of inheritance for a particular audiohook source on a channel
- *
- * For details regarding what happens in the function, see the inline comments
- *
- * \param chan The channel we are operating on
- * \param function The name of the dialplan function (AUDIOHOOK_INHERIT)
- * \param data The audiohook source for which we are setting inheritance permissions
- * \param value The value indicating the permission for audiohook inheritance
- */
 static int func_inheritance_write(struct ast_channel *chan, const char *function, char *data, const char *value)
 {
-	int allow;
-	struct ast_datastore *datastore = NULL;
-	struct audiohook_inheritance_datastore *inheritance_datastore = NULL;
-	struct inheritable_audiohook *inheritable_audiohook;
+	static int warned = 0;
 
-	/* Step 1: Get data from function call */
-	if (ast_strlen_zero(data)) {
-		ast_log(LOG_WARNING, "No argument provided to INHERITANCE function.\n");
-		return -1;
+	if (!warned) {
+		ast_log(LOG_NOTICE, "AUDIOHOOK_INHERIT is deprecated and now does nothing.\n");
+		warned++;
 	}
 
-	if (ast_strlen_zero(value)) {
-		ast_log(LOG_WARNING, "No value provided to INHERITANCE function.\n");
-		return -1;
-	}
-
-	if (!chan) {
-		ast_log(LOG_WARNING, "No channel was provided to INHERITANCE function.\n");
-		return -1;
-	}
-
-	allow = ast_true(value);
-
-	/* Step 2: retrieve or set up datastore */
-	ast_channel_lock(chan);
-	if (!(datastore = ast_channel_datastore_find(chan, &audiohook_inheritance_info, NULL))) {
-		ast_channel_unlock(chan);
-		/* In the case where we cannot find the datastore, we can take a few shortcuts */
-		if (!allow) {
-			ast_debug(1, "Audiohook %s is already set to not be inheritable on channel %s\n", data, ast_channel_name(chan));
-			return 0;
-		} else if (!(inheritance_datastore = setup_inheritance_datastore(chan))) {
-			ast_log(LOG_WARNING, "Unable to set up audiohook inheritance datastore on channel %s\n", ast_channel_name(chan));
-			return -1;
-		} else {
-			return setup_inheritable_audiohook(inheritance_datastore, data);
-		}
-	} else {
-		inheritance_datastore = datastore->data;
-	}
-	ast_channel_unlock(chan);
-
-	/* Step 3: Traverse the list to see if we're trying something redundant */
-
-	AST_LIST_TRAVERSE_SAFE_BEGIN(&inheritance_datastore->allowed_list, inheritable_audiohook, list) {
-		if (!strcasecmp(inheritable_audiohook->source, data)) {
-			if (allow) {
-				ast_debug(2, "Audiohook source %s is already set up to be inherited from channel %s\n", data, ast_channel_name(chan));
-				return 0;
-			} else {
-				ast_debug(2, "Removing inheritability of audiohook %s from channel %s\n", data, ast_channel_name(chan));
-				AST_LIST_REMOVE_CURRENT(list);
-				ast_free(inheritable_audiohook);
-				return 0;
-			}
-		}
-	}
-	AST_LIST_TRAVERSE_SAFE_END;
-
-	/* Step 4: There is no step 4 */
-
-	/* Step 5: This means we are addressing an audiohook source which we have not encountered yet for the channel. Create a new inheritable
-	 * audiohook structure if we're allowing inheritance, or just return if not
-	 */
-
-	if (allow) {
-		return setup_inheritable_audiohook(inheritance_datastore, data);
-	} else {
-		ast_debug(1, "Audiohook %s is already set to not be inheritable on channel %s\n", data, ast_channel_name(chan));
-		return 0;
-	}
+	return 0;
 }
 
 static struct ast_custom_function inheritance_function = {
@@ -303,4 +86,4 @@ static int load_module(void)
 		return AST_MODULE_LOAD_SUCCESS;
 	}
 }
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Audiohook inheritance function");
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Audiohook inheritance placeholder function");
diff --git a/include/asterisk/audiohook.h b/include/asterisk/audiohook.h
index 1a4e4696a6a8e6d80ba4674d590b18a5aa76aad7..6b07160924da6209e7a82f3f273a35ca8814cffd 100644
--- a/include/asterisk/audiohook.h
+++ b/include/asterisk/audiohook.h
@@ -197,6 +197,17 @@ void ast_audiohook_detach_list(struct ast_audiohook_list *audiohook_list);
  */
 void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source);
 
+/*! \brief Move all audiohooks from one channel to another
+ *
+ * \note It is required that both old_chan and new_chan are locked prior to calling
+ * this function. Besides needing to protect the data within the channels, not locking
+ * these channels can lead to a potential deadlock.
+ *
+ * \param old_chan The source of the audiohooks being moved
+ * \param new_chan The destination channel for the audiohooks to be moved to
+ */
+void ast_audiohook_move_all(struct ast_channel *old_chan, struct ast_channel *new_chan);
+
 /*!
  * \brief Detach specified source audiohook from channel
  *
diff --git a/include/asterisk/framehook.h b/include/asterisk/framehook.h
index f33927c7b38a82dad0b96ecc1f6a6fc80512925e..8a58323f2ac708ec6680c0c86f0dd4d4b198de51 100644
--- a/include/asterisk/framehook.h
+++ b/include/asterisk/framehook.h
@@ -212,7 +212,19 @@ typedef void (*ast_framehook_destroy_callback)(void *data);
  */
 typedef int (*ast_framehook_consume_callback)(void *data, enum ast_frame_type type);
 
-#define AST_FRAMEHOOK_INTERFACE_VERSION 2
+/*!
+ * \brief This callback is called when a masquerade occurs on a channel with a framehook
+ * \since 12
+ *
+ * \param data, The data pointer provided at framehook initialization.
+ * \param framehook_id, The framehook ID where the framehook lives now
+ * \param old_chan, The channel that was masqueraded.
+ * \param new_chan, The channel that the masqueraded channel became.
+ */
+typedef void (*ast_framehook_chan_fixup_callback)(void *data, int framehook_id,
+	struct ast_channel *old_chan, struct ast_channel *new_chan);
+
+#define AST_FRAMEHOOK_INTERFACE_VERSION 3
 /*! This interface is required for attaching a framehook to a channel. */
 struct ast_framehook_interface {
 	/*! framehook interface version number */
@@ -226,6 +238,13 @@ struct ast_framehook_interface {
 	* frames of a specific type at this time. If this callback is not implemented it is assumed that the
 	* framehook will consume frames of all types. */
 	ast_framehook_consume_callback consume_cb;
+	/*! chan_fixup_cb is optional. This function is called when the channel that a framehook is running
+	 * on is masqueraded and should be used to move any essential framehook data onto the channel the
+	 * old channel was masqueraded to. */
+	ast_framehook_chan_fixup_callback chan_fixup_cb;
+	/*! disable_inheritance is optional. If set to non-zero, when a channel using this framehook is
+	 * masqueraded, detach and destroy the framehook instead of moving it to the new channel. */
+	int disable_inheritance;
 	 /*! This pointer can represent any custom data to be stored on the !framehook. This
 	 * data pointer will be provided during each event callback which allows the framehook
 	 * to store any stateful data associated with the application using the hook. */
@@ -281,6 +300,18 @@ int ast_framehook_detach(struct ast_channel *chan, int framehook_id);
  */
 int ast_framehook_list_destroy(struct ast_channel *chan);
 
+/*!
+ * \brief This is used by the channel API during a masquerade operation
+ * to move all mobile framehooks from the original channel to the clone channel.
+ * \since 12.5.0
+ *
+ * \pre Both channels must be locked prior to this function call.
+ *
+ * \param old_chan The channel being cloned from
+ * \param new_chan The channel being cloned to
+ */
+void ast_framehook_list_fixup(struct ast_channel *old_chan, struct ast_channel *new_chan);
+
 /*!
  * \brief This is used by the channel API push a frame read event to a channel's framehook list.
  * \since 1.8
diff --git a/include/asterisk/res_fax.h b/include/asterisk/res_fax.h
index 4d1ada6ef8efc9fefcd9a5f9e4c941565e7ea194..b0a1a221b10c7ffc10978290a0f4154c046b77ed 100644
--- a/include/asterisk/res_fax.h
+++ b/include/asterisk/res_fax.h
@@ -181,6 +181,10 @@ struct ast_fax_session_details {
 	int gateway_timeout;
 	/*! the id of the faxdetect framehook for this channel */
 	int faxdetect_id;
+	/*! The timeout for this fax detect in seconds */
+	int faxdetect_timeout;
+	/*! flags used for fax detection */
+	int faxdetect_flags;
 };
 
 struct ast_fax_tech;
diff --git a/main/audiohook.c b/main/audiohook.c
index 4dc7c136b42ab74ef5a2a81916f7508a90891af1..549ad31eb44cacb3f1da27cd80f66adfa1429b63 100644
--- a/main/audiohook.c
+++ b/main/audiohook.c
@@ -586,15 +586,10 @@ static struct ast_audiohook *find_audiohook_by_source(struct ast_audiohook_list
 	return NULL;
 }
 
-void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source)
+static void audiohook_move(struct ast_channel *old_chan, struct ast_channel *new_chan, struct ast_audiohook *audiohook)
 {
-	struct ast_audiohook *audiohook;
 	enum ast_audiohook_status oldstatus;
 
-	if (!ast_channel_audiohooks(old_chan) || !(audiohook = find_audiohook_by_source(ast_channel_audiohooks(old_chan), source))) {
-		return;
-	}
-
 	/* By locking both channels and the audiohook, we can assure that
 	 * another thread will not have a chance to read the audiohook's status
 	 * as done, even though ast_audiohook_remove signals the trigger
@@ -610,6 +605,48 @@ void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_chann
 	ast_audiohook_unlock(audiohook);
 }
 
+void ast_audiohook_move_by_source(struct ast_channel *old_chan, struct ast_channel *new_chan, const char *source)
+{
+	struct ast_audiohook *audiohook;
+
+	if (!ast_channel_audiohooks(old_chan)) {
+		return;
+	}
+
+	audiohook = find_audiohook_by_source(ast_channel_audiohooks(old_chan), source);
+	if (!audiohook) {
+		return;
+	}
+
+	audiohook_move(old_chan, new_chan, audiohook);
+}
+
+void ast_audiohook_move_all(struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+	struct ast_audiohook *audiohook;
+	struct ast_audiohook_list *audiohook_list;
+
+	audiohook_list = ast_channel_audiohooks(old_chan);
+	if (!audiohook_list) {
+		return;
+	}
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->spy_list, audiohook, list) {
+		audiohook_move(old_chan, new_chan, audiohook);
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->whisper_list, audiohook, list) {
+		audiohook_move(old_chan, new_chan, audiohook);
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) {
+		audiohook_move(old_chan, new_chan, audiohook);
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+}
+
 /*! \brief Detach specified source audiohook from channel
  * \param chan Channel to detach from
  * \param source Name of source to detach
diff --git a/main/bridge_basic.c b/main/bridge_basic.c
index eb44867f1787ef9e999f8c1821ddc78556437dc5..60ce37a98161d2a4f5974705e46d5d3265e04d12 100644
--- a/main/bridge_basic.c
+++ b/main/bridge_basic.c
@@ -2858,6 +2858,7 @@ static int attach_framehook(struct attended_transfer_properties *props, struct a
 		.event_cb = transfer_target_framehook_cb,
 		.destroy_cb = transfer_target_framehook_destroy_cb,
 		.consume_cb = transfer_target_framehook_consume,
+		.disable_inheritance = 1,
 	};
 
 	ao2_ref(props, +1);
diff --git a/main/channel.c b/main/channel.c
index 7d9f048379adeb275366717c498b24d6b3fabc1a..15f2cbce027833c5d7884480587ebd93f760dd91 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -6578,6 +6578,12 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann
 		AST_LIST_APPEND_LIST(ast_channel_datastores(original), ast_channel_datastores(clonechan), entry);
 	}
 
+	/* Move framehooks over */
+	ast_framehook_list_fixup(clonechan, original);
+
+	/* Move audiohooks over */
+	ast_audiohook_move_all(clonechan, original);
+
 	ast_autochan_new_channel(clonechan, original);
 
 	clone_variables(original, clonechan);
@@ -10295,6 +10301,13 @@ struct suppress_data {
 	int framehook_id;
 };
 
+static void suppress_framehook_fixup_cb(void *data, int framehook_id, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+	struct suppress_data *suppress = data;
+
+	suppress->framehook_id = framehook_id;
+}
+
 static struct ast_frame *suppress_framehook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data)
 {
 	struct suppress_data *suppress = data;
@@ -10346,6 +10359,7 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum
 		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
 		.event_cb = suppress_framehook_event_cb,
 		.destroy_cb = suppress_framehook_destroy_cb,
+		.chan_fixup_cb = suppress_framehook_fixup_cb,
 	};
 	int framehook_id;
 
diff --git a/main/framehook.c b/main/framehook.c
index 84719ef491c1b5d508814bc3207d636fbab87205..0d42b49906b2a65722c8627834bb847e4e69c5c4 100644
--- a/main/framehook.c
+++ b/main/framehook.c
@@ -56,7 +56,16 @@ struct ast_framehook_list {
 	AST_LIST_HEAD_NOLOCK(, ast_framehook) list;
 };
 
-static void framehook_detach_and_destroy(struct ast_framehook *framehook)
+enum framehook_detachment_mode
+{
+	/*! Destroy the framehook outright. */
+	FRAMEHOOK_DETACH_DESTROY = 0,
+	/*! Remove the framehook from the channel, but don't destroy the data since
+	 *  it will be used by a replacement framehook on another channel. */
+	FRAMEHOOK_DETACH_PRESERVE,
+};
+
+static void framehook_detach(struct ast_framehook *framehook, enum framehook_detachment_mode mode)
 {
 	struct ast_frame *frame;
 	frame = framehook->i.event_cb(framehook->chan, NULL, AST_FRAMEHOOK_EVENT_DETACHED, framehook->i.data);
@@ -67,7 +76,7 @@ static void framehook_detach_and_destroy(struct ast_framehook *framehook)
 	}
 	framehook->chan = NULL;
 
-	if (framehook->i.destroy_cb) {
+	if (mode == FRAMEHOOK_DETACH_DESTROY && framehook->i.destroy_cb) {
 		framehook->i.destroy_cb(framehook->i.data);
 	}
 	ast_free(framehook);
@@ -96,7 +105,7 @@ static struct ast_frame *framehook_list_push_event(struct ast_framehook_list *fr
 			if (framehook->detach_and_destroy_me) {
 				/* this guy is signaled for destruction */
 				AST_LIST_REMOVE_CURRENT(list);
-				framehook_detach_and_destroy(framehook);
+				framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
 				continue;
 			}
 
@@ -205,7 +214,7 @@ int ast_framehook_list_destroy(struct ast_channel *chan)
 	}
 	AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(chan)->list, framehook, list) {
 		AST_LIST_REMOVE_CURRENT(list);
-		framehook_detach_and_destroy(framehook);
+		framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
 	}
 	AST_LIST_TRAVERSE_SAFE_END;
 	ast_free(ast_channel_framehooks(chan));
@@ -213,6 +222,42 @@ int ast_framehook_list_destroy(struct ast_channel *chan)
 	return 0;
 }
 
+void ast_framehook_list_fixup(struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+	struct ast_framehook *framehook;
+	int moved_framehook_id;
+
+	if (!ast_channel_framehooks(old_chan)) {
+		return;
+	}
+
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(old_chan)->list, framehook, list) {
+		AST_LIST_REMOVE_CURRENT(list);
+
+		/* If inheritance is not allowed for this framehook, just destroy it. */
+		if (framehook->i.disable_inheritance) {
+			framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
+			continue;
+		}
+
+		/* Otherwise move it to the other channel and perform any fixups set by the framehook interface */
+		moved_framehook_id = ast_framehook_attach(new_chan, &framehook->i);
+
+		if (moved_framehook_id < 0) {
+			ast_log(LOG_WARNING, "Failed framehook copy during masquerade. Expect loss of features.\n");
+			framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
+		} else {
+			if (framehook->i.chan_fixup_cb) {
+				framehook->i.chan_fixup_cb(framehook->i.data, moved_framehook_id,
+					old_chan, new_chan);
+			}
+
+			framehook_detach(framehook, FRAMEHOOK_DETACH_PRESERVE);
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END;
+}
+
 int ast_framehook_list_is_empty(struct ast_framehook_list *framehooks)
 {
 	if (!framehooks) {
diff --git a/res/res_fax.c b/res/res_fax.c
index b9a3b2ec4d3231446ea7a420b8808ba2095e86e4..7583098229535db85ac4200ea1eb7082da074692 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -607,11 +607,47 @@ static void destroy_callback(void *data)
 	}
 }
 
+static void fixup_callback(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
+
 static const struct ast_datastore_info fax_datastore = {
 	.type = "res_fax",
 	.destroy = destroy_callback,
+	.chan_fixup = fixup_callback,
 };
 
+static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_details *details);
+static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags);
+static struct ast_fax_session_details *find_or_create_details(struct ast_channel *chan);
+
+/*! \brief Copies fax detection and gateway framehooks during masquerades
+ *
+ * \note must be called with both old_chan and new_chan locked. Since this
+ *       is only called by do_masquerade, that shouldn't be an issue.
+ */
+static void fixup_callback(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+	struct ast_fax_session_details *old_details = data;
+	struct ast_datastore *datastore = ast_channel_datastore_find(old_chan, &fax_datastore, NULL);
+
+	if (old_details->gateway_id >= 0) {
+		struct ast_fax_session_details *new_details = find_or_create_details(new_chan);
+
+		ast_framehook_detach(old_chan, old_details->gateway_id);
+		fax_gateway_attach(new_chan, new_details);
+		ao2_cleanup(new_details);
+	}
+
+	if (old_details->faxdetect_id >= 0) {
+		ast_framehook_detach(old_chan, old_details->faxdetect_id);
+		fax_detect_attach(new_chan, old_details->faxdetect_timeout, old_details->faxdetect_flags);
+	}
+
+	if (datastore) {
+		ast_channel_datastore_remove(old_chan, datastore);
+		ast_datastore_free(datastore);
+	}
+}
+
 /*! \brief returns a reference counted pointer to a fax datastore, if it exists */
 static struct ast_fax_session_details *find_details(struct ast_channel *chan)
 {
@@ -3431,6 +3467,7 @@ static int fax_gateway_attach(struct ast_channel *chan, struct ast_fax_session_d
 		.version = AST_FRAMEHOOK_INTERFACE_VERSION,
 		.event_cb = fax_gateway_framehook,
 		.destroy_cb = fax_gateway_framehook_destroy,
+		.disable_inheritance = 1, /* Masquerade inheritance is handled through the datastore fixup */
 	};
 
 	ast_string_field_set(details, result, "SUCCESS");
@@ -3700,6 +3737,8 @@ static int fax_detect_attach(struct ast_channel *chan, int timeout, int flags)
 	faxdetect->details = details;
 	ast_channel_lock(chan);
 	details->faxdetect_id = ast_framehook_attach(chan, &fr_hook);
+	details->faxdetect_timeout = timeout;
+	details->faxdetect_flags = flags;
 	ast_channel_unlock(chan);
 
 	if (details->faxdetect_id < 0) {
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index efcee67a2e71a55929abddfab4d02866ed31cb29..b88396f7a404bd6f04090b5be1e78bdca5cd30cd 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -508,6 +508,7 @@ static void refer_blind_callback(struct ast_channel *chan, struct transfer_chann
 			.event_cb = refer_progress_framehook,
 			.destroy_cb = refer_progress_framehook_destroy,
 			.data = refer->progress,
+			.disable_inheritance = 1,
 		};
 
 		refer->progress->transferee = ast_strdup(ast_channel_uniqueid(chan));