diff --git a/CHANGES b/CHANGES
index 952ad3b55c06f4febdce9853772e8e724849589b..292cde28a2b884fd93df054ba2eeb2a2b391584e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -311,6 +311,15 @@ AMI (Asterisk Manager Interface)
    account. When set in the general context, it will act as the default
    setting for defined accounts.
 
+ * The 'BridgeAction' event was removed. It technically added no value, as the
+   Bridge Action already receives confirmation of the bridge through a
+   successful completion Event.
+
+ * The 'BridgeExec' events were removed. These events duplicated the events that
+   occur in the Briding API, and are conveyed now through BridgeCreate,
+   BridgeEnter, and BridgeLeave events.
+
+
 AGI (Asterisk Gateway Interface)
 ------------------
  * The manager event AGIExec has been split into AGIExecStart and AGIExecEnd.
diff --git a/UPGRADE.txt b/UPGRADE.txt
index baae655f3b08be3ab7311a5c0cd5743047bc7b59..d60ca36ef08f354684f78fdc84f3809343296966 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -185,6 +185,10 @@ Features:
  - Executing a dynamic feature on the bridge peer in a multi-party bridge will
    execute it on all peers of the activating channel.
 
+ - There is no longer an explicit 'features reload' CLI command. Features can still be
+   reloaded using 'module reload features'.
+
+
 Parking:
  - The arguments for the Park, ParkedCall, and ParkAndAnnounce applications have
    been modified significantly. See the application documents for specific details.
diff --git a/apps/app_directed_pickup.c b/apps/app_directed_pickup.c
index 2a113712014610269157a5ef5cff4a4391d9e8e8..256443d23ef2c42361d03b531af0f86e6e80d075 100644
--- a/apps/app_directed_pickup.c
+++ b/apps/app_directed_pickup.c
@@ -43,7 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/lock.h"
 #include "asterisk/app.h"
-#include "asterisk/features.h"
+#include "asterisk/pickup.h"
 #include "asterisk/manager.h"
 #include "asterisk/callerid.h"
 
@@ -305,7 +305,7 @@ static int pickup_exec(struct ast_channel *chan, const char *data)
 	return 0;
 }
 
-/* Find channel for pick up specified by partial channel name */ 
+/* Find channel for pick up specified by partial channel name */
 static int find_by_part(void *obj, void *arg, void *data, int flags)
 {
 	struct ast_channel *target = obj;/*!< Potential pickup target */
@@ -323,7 +323,7 @@ static int find_by_part(void *obj, void *arg, void *data, int flags)
 	return 0;
 }
 
-/* Attempt to pick up specified by partial channel name */ 
+/* Attempt to pick up specified by partial channel name */
 static int pickup_by_part(struct ast_channel *chan, const char *part)
 {
 	struct ast_channel *target;/*!< Potential pickup target */
diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c
index 639de47493c7ad4108cc414d28e7b3e1b2328761..fc1b75a53e084fae30e15f1b2dcf66963d206810 100644
--- a/channels/chan_gtalk.c
+++ b/channels/chan_gtalk.c
@@ -85,7 +85,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/abstract_jb.h"
 #include "asterisk/jabber.h"
 #include "asterisk/jingle.h"
-#include "asterisk/features.h"
+#include "asterisk/parking.h"
 #include "asterisk/stasis_channels.h"
 
 #define GOOGLE_CONFIG		"gtalk.conf"
@@ -2319,8 +2319,8 @@ static int gtalk_load_config(void)
  * Module loading including tests for configuration or dependencies.
  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
- * configuration file or other non-critical problem return 
+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
+ * configuration file or other non-critical problem return
  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
  */
 static int load_module(void)
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 1aa18b9c560fbb80a42c4d374a1bf3721d55781f..1b5f104e05e18354bc420e4a7ae029aa8a73c0b2 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -259,7 +259,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/cli.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/dsp.h"
-#include "asterisk/features.h"
+#include "asterisk/pickup.h"
+#include "asterisk/parking.h"
 #include "asterisk/srv.h"
 #include "asterisk/astdb.h"
 #include "asterisk/causes.h"
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index c974515773065b1830a7f31ff0ef57c0aa098b0e..1237c7869f32b2a8c2da3964668efc740a6301f5 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -68,7 +68,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/manager.h"
 #include "asterisk/say.h"
 #include "asterisk/astdb.h"
-#include "asterisk/features.h"
+#include "asterisk/pickup.h"
 #include "asterisk/app.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/utils.h"
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index fd0b407172538537853f252926842538f8954adf..9b61cfefb8ec3bf2a39c8a51d2a55c2c1cf22e36 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -73,7 +73,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/musiconhold.h"
 #include "asterisk/causes.h"
 #include "asterisk/indications.h"
-#include "asterisk/features.h"
+#include "asterisk/pickup.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/astdb.h"
 #include "asterisk/features_config.h"
diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h
index 9427473576add1345e50fc515c561646473561d4..b1f6bd471a211d839d5fd2530088fd9472d30eb9 100644
--- a/include/asterisk/_private.h
+++ b/include/asterisk/_private.h
@@ -8,7 +8,7 @@
  *
  * \brief
  * Prototypes for public functions only of internal interest,
- * 
+ *
  */
 
 
@@ -25,7 +25,7 @@ int astdb_init(void);			/*!< Provided by db.c */
 void ast_channels_init(void);		/*!< Provided by channel.c */
 void ast_builtins_init(void);		/*!< Provided by cli.c */
 int ast_cli_perms_init(int reload);	/*!< Provided by cli.c */
-int dnsmgr_init(void);			/*!< Provided by dnsmgr.c */ 
+int dnsmgr_init(void);			/*!< Provided by dnsmgr.c */
 void dnsmgr_start_refresh(void);	/*!< Provided by dnsmgr.c */
 int dnsmgr_reload(void);		/*!< Provided by dnsmgr.c */
 void threadstorage_init(void);		/*!< Provided by threadstorage.c */
@@ -109,9 +109,9 @@ enum ast_module_reload_result ast_module_reload(const char *name);
  */
 void ast_process_pending_reloads(void);
 
-/*! \brief Load XML documentation. Provided by xmldoc.c 
+/*! \brief Load XML documentation. Provided by xmldoc.c
  *  \retval 1 on error.
- *  \retval 0 on success. 
+ *  \retval 0 on success.
  */
 int ast_xmldoc_load_documentation(void);
 
diff --git a/include/asterisk/features.h b/include/asterisk/features.h
index 43edca07a6a07f0c9425dfec7360a5c579f63659..9430bc4cbe4162482723e67da77ea989cb570c23 100644
--- a/include/asterisk/features.h
+++ b/include/asterisk/features.h
@@ -28,30 +28,6 @@
 #include "asterisk/linkedlists.h"
 #include "asterisk/bridge.h"
 
-#define FEATURE_MAX_LEN		11
-#define FEATURE_APP_LEN		64
-#define FEATURE_APP_ARGS_LEN	256
-#define FEATURE_SNAME_LEN	32
-#define FEATURE_EXTEN_LEN	32
-#define FEATURE_MOH_LEN		80  /* same as MAX_MUSICCLASS from channel.h */
-
-#define DEFAULT_PARKINGLOT "default"	/*!< Default parking lot */
-
-#define AST_FEATURE_RETURN_HANGUP           -1
-#define AST_FEATURE_RETURN_SUCCESSBREAK     0
-#define AST_FEATURE_RETURN_PBX_KEEPALIVE    AST_PBX_KEEPALIVE
-#define AST_FEATURE_RETURN_NO_HANGUP_PEER   AST_PBX_NO_HANGUP_PEER
-#define AST_FEATURE_RETURN_PASSDIGITS       21
-#define AST_FEATURE_RETURN_STOREDIGITS      22
-#define AST_FEATURE_RETURN_SUCCESS          23
-#define AST_FEATURE_RETURN_KEEPTRYING       24
-#define AST_FEATURE_RETURN_PARKFAILED       25
-
-#define FEATURE_SENSE_CHAN	(1 << 0)
-#define FEATURE_SENSE_PEER	(1 << 1)
-
-typedef int (*ast_feature_operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, const char *code, int sense, void *data);
-
 /*! \brief main call feature structure */
 
 enum {
@@ -93,54 +69,7 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct as
 int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan,
 		struct ast_bridge_features *features, int play_tone, const char *xfersound);
 
-/*!
- * \brief Test if a channel can be picked up.
- *
- * \param chan Channel to test if can be picked up.
- *
- * \note This function assumes that chan is locked.
- *
- * \return TRUE if channel can be picked up.
- */
-int ast_can_pickup(struct ast_channel *chan);
-
-/*!
- * \brief Find a pickup channel target by group.
- *
- * \param chan channel that initiated pickup.
- *
- * \retval target on success.  The returned channel is locked and reffed.
- * \retval NULL on error.
- */
-struct ast_channel *ast_pickup_find_by_group(struct ast_channel *chan);
-
-/*! \brief Pickup a call */
-int ast_pickup_call(struct ast_channel *chan);
-
-/*!
- * \brief Pickup a call target.
- *
- * \param chan channel that initiated pickup.
- * \param target channel to be picked up.
- *
- * \note This function assumes that target is locked.
- *
- * \retval 0 on success.
- * \retval -1 on failure.
- */
-int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target);
-
-/*!
- * \brief accessor for call pickup message type
- * \since 12.0.0
- *
- * \retval pointer to the stasis message type
- * \retval NULL if not initialized
- */
-struct stasis_message_type *ast_call_pickup_type(void);
 
-/*! \brief Reload call features from features.conf */
-int ast_features_reload(void);
 
 /*!
  * \brief parse L option and read associated channel variables to set warning, warning frequency, and timelimit
diff --git a/include/asterisk/parking.h b/include/asterisk/parking.h
index a835d104a36331a96a4bdbe0b6f6e0f836556633..b9c80d43c67fc5035a25ef72db517aa3f6dcc2cf 100644
--- a/include/asterisk/parking.h
+++ b/include/asterisk/parking.h
@@ -30,6 +30,11 @@
  */
 #define PARK_APPLICATION "Park"
 
+/*!
+ * \brief The default parking lot
+ */
+#define DEFAULT_PARKINGLOT "default"
+
 /*!
  * \brief Defines the type of parked call message being published
  * \since 12
diff --git a/include/asterisk/pickup.h b/include/asterisk/pickup.h
new file mode 100644
index 0000000000000000000000000000000000000000..de5c8961e4bb722a113ee08ab4c58a5b4601e643
--- /dev/null
+++ b/include/asterisk/pickup.h
@@ -0,0 +1,91 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2013, Digium, Inc.
+ *
+ * Matt Jordan <mjordan@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \brief Call Pickup API
+ *
+ * Includes code and algorithms from the Zapata library.
+ *
+ */
+
+#ifndef _AST_FEATURES_H
+#define _AST_FEATURES_H
+
+/*!
+ * \brief Test if a channel can be picked up.
+ *
+ * \param chan Channel to test if can be picked up.
+ *
+ * \note This function assumes that chan is locked.
+ *
+ * \return TRUE if channel can be picked up.
+ */
+int ast_can_pickup(struct ast_channel *chan);
+
+/*!
+ * \brief Find a pickup channel target by group.
+ *
+ * \param chan channel that initiated pickup.
+ *
+ * \retval target on success.  The returned channel is locked and reffed.
+ * \retval NULL on error.
+ */
+struct ast_channel *ast_pickup_find_by_group(struct ast_channel *chan);
+
+/*!
+ * \brief Pickup a call
+ *
+ * \param chan The channel that initiated the pickup
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_pickup_call(struct ast_channel *chan);
+
+/*!
+ * \brief Pickup a call target.
+ *
+ * \param chan channel that initiated pickup.
+ * \param target channel to be picked up.
+ *
+ * \note This function assumes that target is locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target);
+
+/*!
+ * \brief accessor for call pickup message type
+ * \since 12.0.0
+ *
+ * \retval pointer to the stasis message type
+ * \retval NULL if not initialized
+ */
+struct stasis_message_type *ast_call_pickup_type(void);
+
+/*!
+ * \brief Initialize pickup
+ *
+ * \retval 0 on success
+ * \retval non-zero on failure
+ */
+int ast_pickup_init(void);
+
+#endif /* _AST_FEATURES_H */
diff --git a/main/asterisk.c b/main/asterisk.c
index 3d58e2c6af2aae7edd79fe7b19d55c168b6e5fa4..9bd0d858c5922bb3bbd3feb7408f85cbc089a8db 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -202,6 +202,7 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #include "asterisk/cli.h"
 #include "asterisk/channel.h"
 #include "asterisk/translate.h"
+#include "asterisk/pickup.h"
 #include "asterisk/features.h"
 #include "asterisk/acl.h"
 #include "asterisk/ulaw.h"
@@ -4298,6 +4299,11 @@ int main(int argc, char *argv[])
 		exit(1);
 	}
 
+	if (ast_pickup_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
 	if (ast_bridging_init()) {
 		printf("%s", term_quit());
 		exit(1);
diff --git a/main/bridge.c b/main/bridge.c
index 2e2c87bc60e63df2394ef6c73df5d52480f8cd9e..c0e7bd42789d8140eadcf25b7b23cf90e2406f8a 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/bridge.h"
 #include "asterisk/bridge_internal.h"
 #include "asterisk/bridge_channel_internal.h"
+#include "asterisk/bridge_features.h"
 #include "asterisk/bridge_basic.h"
 #include "asterisk/bridge_technology.h"
 #include "asterisk/bridge_channel.h"
diff --git a/main/cdr.c b/main/cdr.c
index f3608f0a98c05564e871fd83bfb18ee397cd2d14..8b4d0ba05c76bce0b2126837c8ea3c1ed8f1c7f4 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -916,17 +916,14 @@ static int snapshot_cep_changed(struct ast_channel_snapshot *old_snapshot,
 		return 0;
 	}
 
-	if (strcmp(new_snapshot->context, old_snapshot->context)
-		|| strcmp(new_snapshot->exten, old_snapshot->exten)
-		|| new_snapshot->priority != old_snapshot->priority) {
-		return 1;
-	}
-
 	/* When Party A is originated to an application and the application exits, the stack
 	 * will attempt to clear the application and restore the dummy originate application
 	 * of "AppDialX". Ignore application changes to AppDialX as a result.
 	 */
-	if (strcmp(new_snapshot->appl, old_snapshot->appl) && strncasecmp(new_snapshot->appl, "appdial", 7)) {
+	if (strcmp(new_snapshot->appl, old_snapshot->appl) && strncasecmp(new_snapshot->appl, "appdial", 7)
+		&& (strcmp(new_snapshot->context, old_snapshot->context)
+		|| strcmp(new_snapshot->exten, old_snapshot->exten)
+		|| new_snapshot->priority != old_snapshot->priority)) {
 		return 1;
 	}
 
diff --git a/main/cel.c b/main/cel.c
index a03d081158128560ffde63c980cc7026ea1b009f..2b5306e21a995656cc0113bbe89415249b718059 100644
--- a/main/cel.c
+++ b/main/cel.c
@@ -61,7 +61,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/stasis_bridges.h"
 #include "asterisk/bridge.h"
 #include "asterisk/parking.h"
-#include "asterisk/features.h"
+#include "asterisk/pickup.h"
 #include "asterisk/core_local.h"
 
 /*** DOCUMENTATION
diff --git a/main/features.c b/main/features.c
index 7162d4e550cfe87541b9596eca961b5d5bc5562e..043ed591f58e2efce4ef394a31d851b2affb33f9 100644
--- a/main/features.c
+++ b/main/features.c
@@ -72,6 +72,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/astobj2.h"
 #include "asterisk/test.h"
 #include "asterisk/bridge.h"
+#include "asterisk/bridge_features.h"
 #include "asterisk/bridge_basic.h"
 #include "asterisk/bridge_after.h"
 #include "asterisk/stasis.h"
@@ -282,26 +283,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 			<para>Bridge together two channels already in the PBX.</para>
 		</description>
 	</manager>
-	<managerEvent language="en_US" name="Pickup">
-		<managerEventInstance class="EVENT_FLAG_CALL">
-			<synopsis>Raised when a call pickup occurs.</synopsis>
-			<syntax>
-				<channel_snapshot/>
-				<channel_snapshot prefix="Target"/>
-			</syntax>
-		</managerEventInstance>
-	</managerEvent>
  ***/
 
-#define DEFAULT_PARK_TIME							45000	/*!< ms */
-#define DEFAULT_PARK_EXTENSION						"700"
-#define DEFAULT_COMEBACK_CONTEXT					"parkedcallstimeout"
-#define DEFAULT_COMEBACK_TO_ORIGIN					1
-#define DEFAULT_COMEBACK_DIAL_TIME					30
-
-#define AST_MAX_WATCHERS 256
-#define MAX_DIAL_FEATURE_OPTIONS 30
-
 /* TODO Scrape all of the parking stuff out of features.c */
 
 typedef enum {
@@ -310,198 +293,6 @@ typedef enum {
 	FEATURE_INTERPRET_CHECK,  /* Used by feature_check */
 } feature_interpret_op;
 
-/*! Parking lot access ramp dialplan usage entry. */
-struct parking_dp_ramp {
-	/*! Next node in the parking lot spaces dialplan list. */
-	AST_LIST_ENTRY(parking_dp_ramp) node;
-	/*! TRUE if the parking lot access extension is exclusive. */
-	unsigned int exclusive:1;
-	/*! Parking lot access extension */
-	char exten[1];
-};
-
-/*! Parking lot dialplan access ramp map */
-AST_LIST_HEAD_NOLOCK(parking_dp_ramp_map, parking_dp_ramp);
-
-/*! Parking lot spaces dialplan usage entry. */
-struct parking_dp_spaces {
-	/*! Next node in the parking lot spaces dialplan list. */
-	AST_LIST_ENTRY(parking_dp_spaces) node;
-	/*! First parking space */
-	int start;
-	/*! Last parking space */
-	int stop;
-};
-
-/*! Parking lot dialplan context space map */
-AST_LIST_HEAD_NOLOCK(parking_dp_space_map, parking_dp_spaces);
-
-/*! Parking lot context dialplan usage entry. */
-struct parking_dp_context {
-	/*! Next node in the parking lot contexts dialplan list. */
-	AST_LIST_ENTRY(parking_dp_context) node;
-	/*! Parking access extensions defined in this context. */
-	struct parking_dp_ramp_map access_extens;
-	/*! Parking spaces defined in this context. */
-	struct parking_dp_space_map spaces;
-	/*! Parking hints defined in this context. */
-	struct parking_dp_space_map hints;
-	/*! Parking lot context name */
-	char context[1];
-};
-
-/*! Parking lot dialplan usage map. */
-AST_LIST_HEAD_NOLOCK(parking_dp_map, parking_dp_context);
-
-/*!
- * \brief Description of one parked call, added to a list while active, then removed.
- * The list belongs to a parkinglot.
- */
-struct parkeduser {
-	struct ast_channel *chan;                   /*!< Parked channel */
-	struct timeval start;                       /*!< Time the park started */
-	int parkingnum;                             /*!< Parking lot space used */
-	char parkingexten[AST_MAX_EXTENSION];       /*!< If set beforehand, parking extension used for this call */
-	char context[AST_MAX_CONTEXT];              /*!< Where to go if our parking time expires */
-	char exten[AST_MAX_EXTENSION];
-	int priority;
-	unsigned int parkingtime;                   /*!< Maximum length in parking lot before return */
-	/*! Method to entertain the caller when parked: AST_CONTROL_RINGING, AST_CONTROL_HOLD, or 0(none) */
-	enum ast_control_frame_type hold_method;
-	unsigned int notquiteyet:1;
-	unsigned int options_specified:1;
-	char peername[AST_CHANNEL_NAME];
-	unsigned char moh_trys;
-	/*! Parking lot this entry belongs to.  Holds a parking lot reference. */
-	struct ast_parkinglot *parkinglot;
-	AST_LIST_ENTRY(parkeduser) list;
-};
-
-/*! Parking lot configuration options. */
-struct parkinglot_cfg {
-	/*! Music class used for parking */
-	char mohclass[MAX_MUSICCLASS];
-	/*! Extension to park calls in this parking lot. */
-	char parkext[AST_MAX_EXTENSION];
-	/*! Context for which parking is made accessible */
-	char parking_con[AST_MAX_CONTEXT];
-	/*! Context that timed-out parked calls are called back on when comebacktoorigin=no */
-	char comebackcontext[AST_MAX_CONTEXT];
-	/*! First available extension for parking */
-	int parking_start;
-	/*! Last available extension for parking */
-	int parking_stop;
-	/*! Default parking time in ms. */
-	unsigned int parkingtime;
-	/*!
-	 * \brief Enable DTMF based transfers on bridge when picking up parked calls.
-	 *
-	 * \details
-	 * none(0)
-	 * AST_FEATURE_FLAG_BYCALLEE
-	 * AST_FEATURE_FLAG_BYCALLER
-	 * AST_FEATURE_FLAG_BYBOTH
-	 */
-	int parkedcalltransfers;
-	/*!
-	 * \brief Enable DTMF based parking on bridge when picking up parked calls.
-	 *
-	 * \details
-	 * none(0)
-	 * AST_FEATURE_FLAG_BYCALLEE
-	 * AST_FEATURE_FLAG_BYCALLER
-	 * AST_FEATURE_FLAG_BYBOTH
-	 */
-	int parkedcallreparking;
-	/*!
-	 * \brief Enable DTMF based hangup on a bridge when pickup up parked calls.
-	 *
-	 * \details
-	 * none(0)
-	 * AST_FEATURE_FLAG_BYCALLEE
-	 * AST_FEATURE_FLAG_BYCALLER
-	 * AST_FEATURE_FLAG_BYBOTH
-	 */
-	int parkedcallhangup;
-	/*!
-	 * \brief Enable DTMF based recording on a bridge when picking up parked calls.
-	 *
-	 * \details
-	 * none(0)
-	 * AST_FEATURE_FLAG_BYCALLEE
-	 * AST_FEATURE_FLAG_BYCALLER
-	 * AST_FEATURE_FLAG_BYBOTH
-	 */
-	int parkedcallrecording;
-
-	/*! Time in seconds to dial the device that parked a timedout parked call */
-	unsigned int comebackdialtime;
-	/*! TRUE if findslot is set to next */
-	unsigned int parkfindnext:1;
-	/*! TRUE if the parking lot is exclusively accessed by parkext */
-	unsigned int parkext_exclusive:1;
-	/*! Add parking hints automatically */
-	unsigned int parkaddhints:1;
-	/*! TRUE if configuration is invalid and the parking lot should not be used. */
-	unsigned int is_invalid:1;
-	/*! TRUE if a timed out parked call goes back to the parker */
-	unsigned int comebacktoorigin:1;
-};
-
-/*! \brief Structure for parking lots which are put in a container. */
-struct ast_parkinglot {
-	/*! Name of the parking lot. */
-	char name[AST_MAX_CONTEXT];
-	/*! Parking lot user configuration. */
-	struct parkinglot_cfg cfg;
-
-	/*! Parking space to start next park search. */
-	int next_parking_space;
-
-	/*! That which bears the_mark shall be deleted if parking lot empty! (Used during reloads.) */
-	unsigned int the_mark:1;
-	/*! TRUE if the parking lot is disabled. */
-	unsigned int disabled:1;
-
-	/*! List of active parkings in this parkinglot */
-	AST_LIST_HEAD(parkinglot_parklist, parkeduser) parkings;
-};
-
-/*! \brief The configured parking lots container. Always at least one  - the default parking lot */
-static struct ao2_container *parkinglots;
-
-/*! Force a config reload to reload regardless of config file timestamp. */
-#ifdef TEST_FRAMEWORK
-static int force_reload_load;
-#endif
-
-/*!
- * \brief Context for parking dialback to parker.
- * \note The need for the context is a KLUDGE.
- *
- * \todo Might be able to eliminate the parking_con_dial context
- * kludge by running app_dial directly in its own thread to
- * simulate a PBX.
- */
-static char parking_con_dial[] = "park-dial";
-
-/*! Ensure that features.conf reloads on one thread at a time. */
-AST_MUTEX_DEFINE_STATIC(features_reload_lock);
-
-static char *registrar = "features";		   /*!< Registrar for operations */
-
-/*! PARK_APP_NAME application arguments */
-AST_DEFINE_APP_ARGS_TYPE(park_app_args,
-	AST_APP_ARG(timeout);		/*!< Time in ms to remain in the parking lot. */
-	AST_APP_ARG(return_con);	/*!< Context to return parked call if timeout. */
-	AST_APP_ARG(return_ext);	/*!< Exten to return parked call if timeout. */
-	AST_APP_ARG(return_pri);	/*!< Priority to return parked call if timeout. */
-	AST_APP_ARG(options);		/*!< Parking option flags. */
-	AST_APP_ARG(pl_name);		/*!< Parking lot name to use if present. */
-	AST_APP_ARG(dummy);			/*!< Place to put any remaining args string. */
-	);
-
-static pthread_t parking_thread;
 struct ast_dial_features {
 	/*! Channel's feature flags. */
 	struct ast_flags my_features;
@@ -509,88 +300,6 @@ struct ast_dial_features {
 	struct ast_flags peer_features;
 };
 
-static struct ast_manager_event_blob *call_pickup_to_ami(struct stasis_message *message);
-
-STASIS_MESSAGE_TYPE_DEFN(
-	ast_call_pickup_type,
-	.to_ami = call_pickup_to_ami);
-
-
-#if defined(ATXFER_NULL_TECH)
-/*!
- * \internal
- * \brief Set the channel technology to the kill technology.
- *
- * \param chan Channel to change technology.
- *
- * \return Nothing
- */
-static void set_kill_chan_tech(struct ast_channel *chan)
-{
-	int idx;
-
-	ast_channel_lock(chan);
-
-	/* Hangup the channel's physical side */
-	if (ast_channel_tech(chan)->hangup) {
-		ast_channel_tech(chan)->hangup(chan);
-	}
-	if (ast_channel_tech_pvt(chan)) {
-		ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n",
-			ast_channel_name(chan));
-		ast_free(ast_channel_tech_pvt(chan));
-		ast_channel_tech_pvt_set(chan, NULL);
-	}
-
-	/* Install the kill technology and wake up anyone waiting on it. */
-	ast_channel_tech_set(chan, &ast_kill_tech);
-	for (idx = 0; idx < AST_MAX_FDS; ++idx) {
-		switch (idx) {
-		case AST_ALERT_FD:
-		case AST_TIMING_FD:
-		case AST_GENERATOR_FD:
-			/* Don't clear these fd's. */
-			break;
-		default:
-			ast_channel_set_fd(chan, idx, -1);
-			break;
-		}
-	}
-	ast_queue_frame(chan, &ast_null_frame);
-
-	ast_channel_unlock(chan);
-}
-#endif	/* defined(ATXFER_NULL_TECH) */
-
-#if defined(ATXFER_NULL_TECH)
-/*!
- * \internal
- * \brief Set the channel name to something unique.
- *
- * \param chan Channel to change name.
- *
- * \return Nothing
- */
-static void set_new_chan_name(struct ast_channel *chan)
-{
-	static int seq_num_last;
-	int seq_num;
-	int len;
-	char *chan_name;
-	char dummy[1];
-
-	/* Create the new channel name string. */
-	ast_channel_lock(chan);
-	seq_num = ast_atomic_fetchadd_int(&seq_num_last, +1);
-	len = snprintf(dummy, sizeof(dummy), "%s<XFER_%x>", ast_channel_name(chan), seq_num) + 1;
-	chan_name = ast_alloca(len);
-	snprintf(chan_name, len, "%s<XFER_%x>", ast_channel_name(chan), seq_num);
-	ast_channel_unlock(chan);
-
-	ast_change_name(chan, chan_name);
-}
-#endif	/* defined(ATXFER_NULL_TECH) */
-
 static void *dial_features_duplicate(void *data)
 {
 	struct ast_dial_features *df = data, *df_copy;
@@ -655,9 +364,6 @@ static int add_features_datastore(struct ast_channel *chan, const struct ast_fla
 	return 0;
 }
 
-/* Forward declarations */
-static void parkinglot_unref(struct ast_parkinglot *parkinglot);
-
 struct ast_bridge_thread_obj
 {
 	struct ast_bridge_config bconfig;
@@ -667,98 +373,12 @@ struct ast_bridge_thread_obj
 	unsigned int return_to_pbx:1;
 };
 
-static int parkinglot_hash_cb(const void *obj, const int flags)
-{
-	const struct ast_parkinglot *parkinglot = obj;
-
-	return ast_str_case_hash(parkinglot->name);
-}
-
-static int parkinglot_cmp_cb(void *obj, void *arg, int flags)
-{
-	struct ast_parkinglot *parkinglot = obj;
-	struct ast_parkinglot *parkinglot2 = arg;
-
-	return !strcasecmp(parkinglot->name, parkinglot2->name) ? CMP_MATCH | CMP_STOP : 0;
-}
-
-/*!
- * \brief store context, extension and priority
- * \param chan, context, ext, pri
- */
-static void set_c_e_p(struct ast_channel *chan, const char *context, const char *ext, int pri)
-{
-	ast_channel_context_set(chan, context);
-	ast_channel_exten_set(chan, ext);
-	ast_channel_priority_set(chan, pri);
-}
-
 static const struct ast_datastore_info channel_app_data_datastore = {
 	.type = "Channel appdata datastore",
 	.destroy = ast_free_ptr,
 };
 
-/*! \brief Notify metermaids that we've changed an extension */
-static void notify_metermaids(const char *exten, char *context, enum ast_device_state state)
-{
-	ast_debug(4, "Notification of state change to metermaids %s@%s\n to state '%s'",
-		exten, context, ast_devstate2str(state));
-
-	ast_devstate_changed(state, AST_DEVSTATE_CACHABLE, "park:%s@%s", exten, context);
-}
-
-/*! \brief metermaids callback from devicestate.c */
-static enum ast_device_state metermaidstate(const char *data)
-{
-	char *context;
-	char *exten;
-
-	context = ast_strdupa(data);
-
-	exten = strsep(&context, "@");
-	if (!context)
-		return AST_DEVICE_INVALID;
-
-	ast_debug(4, "Checking state of exten %s in context %s\n", exten, context);
-
-	if (!ast_exists_extension(NULL, context, exten, 1, NULL))
-		return AST_DEVICE_NOT_INUSE;
-
-	return AST_DEVICE_INUSE;
-}
-
-/*! Options to pass to park_call_full */
-enum ast_park_call_options {
-	/*! Provide ringing to the parked caller instead of music on hold */
-	AST_PARK_OPT_RINGING =   (1 << 0),
-	/*! Randomly choose a parking spot for the caller instead of choosing
-	 *  the first one that is available. */
-	AST_PARK_OPT_RANDOMIZE = (1 << 1),
-	/*! Do not announce the parking number */
-	AST_PARK_OPT_SILENCE = (1 << 2),
-};
-
-/*! Optional additional parking options when parking a call. */
-struct ast_park_call_args {
-	/*! How long to wait in the parking lot before the call gets sent back
-	 *  to the specified return extension (or a best guess at where it came
-	 *  from if not explicitly specified). */
-	int timeout;
-	/*! An output parameter to store the parking space where the parked caller
-	 *  was placed. */
-	int *extout;
-	const char *orig_chan_name;
-	const char *return_con;
-	const char *return_ext;
-	int return_pri;
-	uint32_t flags;
-	/*! Parked user that has already obtained a parking space */
-	struct parkeduser *pu;
-	/*! \brief Parkinglot to be parked in */
-	struct ast_parkinglot *parkinglot;
-};
-
-/*!
+/*
  * \internal
  * \brief Get the extension for a given builtin feature
  *
@@ -1462,472 +1082,6 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a
 	return res;
 }
 
-/*! \brief Output parking event to manager */
-static void post_manager_event(const char *s, struct parkeduser *pu)
-{
-	manager_event(EVENT_FLAG_CALL, s,
-		"Exten: %s\r\n"
-		"Channel: %s\r\n"
-		"Parkinglot: %s\r\n"
-		"CallerIDNum: %s\r\n"
-		"CallerIDName: %s\r\n"
-		"ConnectedLineNum: %s\r\n"
-		"ConnectedLineName: %s\r\n"
-		"UniqueID: %s\r\n",
-		pu->parkingexten,
-		ast_channel_name(pu->chan),
-		pu->parkinglot->name,
-		S_COR(ast_channel_caller(pu->chan)->id.number.valid, ast_channel_caller(pu->chan)->id.number.str, "<unknown>"),
-		S_COR(ast_channel_caller(pu->chan)->id.name.valid, ast_channel_caller(pu->chan)->id.name.str, "<unknown>"),
-		S_COR(ast_channel_connected(pu->chan)->id.number.valid, ast_channel_connected(pu->chan)->id.number.str, "<unknown>"),
-		S_COR(ast_channel_connected(pu->chan)->id.name.valid, ast_channel_connected(pu->chan)->id.name.str, "<unknown>"),
-		ast_channel_uniqueid(pu->chan)
-		);
-}
-
-static char *callback_dialoptions(struct ast_flags *features_callee, struct ast_flags *features_caller, char *options, size_t len)
-{
-	int i = 0;
-	enum {
-		OPT_CALLEE_REDIRECT   = 't',
-		OPT_CALLER_REDIRECT   = 'T',
-		OPT_CALLEE_AUTOMON    = 'w',
-		OPT_CALLER_AUTOMON    = 'W',
-		OPT_CALLEE_DISCONNECT = 'h',
-		OPT_CALLER_DISCONNECT = 'H',
-		OPT_CALLEE_PARKCALL   = 'k',
-		OPT_CALLER_PARKCALL   = 'K',
-	};
-
-	memset(options, 0, len);
-	if (ast_test_flag(features_caller, AST_FEATURE_REDIRECT) && i < len) {
-		options[i++] = OPT_CALLER_REDIRECT;
-	}
-	if (ast_test_flag(features_caller, AST_FEATURE_AUTOMON) && i < len) {
-		options[i++] = OPT_CALLER_AUTOMON;
-	}
-	if (ast_test_flag(features_caller, AST_FEATURE_DISCONNECT) && i < len) {
-		options[i++] = OPT_CALLER_DISCONNECT;
-	}
-	if (ast_test_flag(features_caller, AST_FEATURE_PARKCALL) && i < len) {
-		options[i++] = OPT_CALLER_PARKCALL;
-	}
-
-	if (ast_test_flag(features_callee, AST_FEATURE_REDIRECT) && i < len) {
-		options[i++] = OPT_CALLEE_REDIRECT;
-	}
-	if (ast_test_flag(features_callee, AST_FEATURE_AUTOMON) && i < len) {
-		options[i++] = OPT_CALLEE_AUTOMON;
-	}
-	if (ast_test_flag(features_callee, AST_FEATURE_DISCONNECT) && i < len) {
-		options[i++] = OPT_CALLEE_DISCONNECT;
-	}
-	if (ast_test_flag(features_callee, AST_FEATURE_PARKCALL) && i < len) {
-		options[i++] = OPT_CALLEE_PARKCALL;
-	}
-
-	return options;
-}
-
-/*!
- * \internal
- * \brief Run management on a parked call.
- *
- * \note The parkinglot parkings list is locked on entry.
- *
- * \retval TRUE if the parking completed.
- */
-static int manage_parked_call(struct parkeduser *pu, const struct pollfd *pfds, int nfds, struct pollfd **new_pfds, int *new_nfds, int *ms)
-{
-	struct ast_channel *chan = pu->chan;	/* shorthand */
-	int tms;        /* timeout for this item */
-	int x;          /* fd index in channel */
-
-	tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
-	if (tms > pu->parkingtime) {
-		/*
-		 * Call has been parked too long.
-		 * Stop entertaining the caller.
-		 */
-		switch (pu->hold_method) {
-		case AST_CONTROL_HOLD:
-			ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
-			break;
-		case AST_CONTROL_RINGING:
-			ast_indicate(pu->chan, -1);
-			break;
-		default:
-			break;
-		}
-		pu->hold_method = 0;
-
-		/* Get chan, exten from derived kludge */
-		if (pu->peername[0]) {
-			char *peername;
-			char *dash;
-			char *peername_flat; /* using something like DAHDI/52 for an extension name is NOT a good idea */
-			char parkingslot[AST_MAX_EXTENSION]; /* buffer for parkinglot slot number */
-			int i;
-
-			peername = ast_strdupa(pu->peername);
-			dash = strrchr(peername, '-');
-			if (dash) {
-				*dash = '\0';
-			}
-
-			peername_flat = ast_strdupa(peername);
-			for (i = 0; peername_flat[i]; i++) {
-				if (peername_flat[i] == '/') {
-					peername_flat[i] = '_';
-				}
-			}
-
-			if (!ast_context_find_or_create(NULL, NULL, parking_con_dial, registrar)) {
-				ast_log(LOG_ERROR,
-					"Parking dial context '%s' does not exist and unable to create\n",
-					parking_con_dial);
-			} else {
-				char returnexten[AST_MAX_EXTENSION];
-				char comebackdialtime[AST_MAX_EXTENSION];
-				struct ast_datastore *features_datastore;
-				struct ast_dial_features *dialfeatures;
-
-				if (!strncmp(peername, "Parked/", 7)) {
-					peername += 7;
-				}
-
-				ast_channel_lock(chan);
-				features_datastore = ast_channel_datastore_find(chan, &dial_features_info,
-					NULL);
-				if (features_datastore && (dialfeatures = features_datastore->data)) {
-					char buf[MAX_DIAL_FEATURE_OPTIONS] = {0,};
-
-					snprintf(returnexten, sizeof(returnexten), "%s,%u,%s", peername,
-						pu->parkinglot->cfg.comebackdialtime,
-						callback_dialoptions(&dialfeatures->peer_features,
-							&dialfeatures->my_features, buf, sizeof(buf)));
-				} else { /* Existing default */
-					ast_log(LOG_NOTICE, "Dial features not found on %s, using default!\n",
-						ast_channel_name(chan));
-					snprintf(returnexten, sizeof(returnexten), "%s,%u,t", peername,
-						pu->parkinglot->cfg.comebackdialtime);
-				}
-				ast_channel_unlock(chan);
-
-				snprintf(comebackdialtime, sizeof(comebackdialtime), "%u",
-						pu->parkinglot->cfg.comebackdialtime);
-				pbx_builtin_setvar_helper(chan, "COMEBACKDIALTIME", comebackdialtime);
-
-				pbx_builtin_setvar_helper(chan, "PARKER", peername);
-
-			}
-
-			snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
-			pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parkingslot);
-			pbx_builtin_setvar_helper(chan, "PARKEDLOT", pu->parkinglot->name);
-
-			if (pu->options_specified) {
-				/*
-				 * Park() was called with overriding return arguments, respect
-				 * those arguments.
-				 */
-				set_c_e_p(chan, pu->context, pu->exten, pu->priority);
-			} else if (pu->parkinglot->cfg.comebacktoorigin) {
-				set_c_e_p(chan, parking_con_dial, peername_flat, 1);
-			} else {
-				/* Handle fallback when extensions don't exist here since that logic was removed from pbx */
-				if (ast_exists_extension(chan, pu->parkinglot->cfg.comebackcontext, peername_flat, 1, NULL)) {
-					set_c_e_p(chan, pu->parkinglot->cfg.comebackcontext, peername_flat, 1);
-				} else if (ast_exists_extension(chan, pu->parkinglot->cfg.comebackcontext, "s", 1, NULL)) {
-					ast_verb(2, "Can not start %s at %s,%s,1. Using 's@%s' instead.\n", ast_channel_name(chan),
-						pu->parkinglot->cfg.comebackcontext, peername_flat, pu->parkinglot->cfg.comebackcontext);
-					set_c_e_p(chan, pu->parkinglot->cfg.comebackcontext, "s", 1);
-				} else {
-					ast_verb(2, "Can not start %s at %s,%s,1 and exten 's@%s' does not exist. Using 's@default'\n",
-						ast_channel_name(chan),
-						pu->parkinglot->cfg.comebackcontext, peername_flat,
-						pu->parkinglot->cfg.comebackcontext);
-					set_c_e_p(chan, "default", "s", 1);
-				}
-			}
-		} else {
-			/*
-			 * They've been waiting too long, send them back to where they
-			 * came.  Theoretically they should have their original
-			 * extensions and such, but we copy to be on the safe side.
-			 */
-			set_c_e_p(chan, pu->context, pu->exten, pu->priority);
-		}
-		post_manager_event("ParkedCallTimeOut", pu);
-
-		ast_verb(2, "Timeout for %s parked on %d (%s). Returning to %s,%s,%d\n",
-			ast_channel_name(pu->chan), pu->parkingnum, pu->parkinglot->name, ast_channel_context(pu->chan),
-			ast_channel_exten(pu->chan), ast_channel_priority(pu->chan));
-
-		/* Start up the PBX, or hang them up */
-		if (ast_pbx_start(chan))  {
-			ast_log(LOG_WARNING,
-				"Unable to restart the PBX for user on '%s', hanging them up...\n",
-				ast_channel_name(pu->chan));
-			ast_hangup(chan);
-		}
-
-		/* And take them out of the parking lot */
-		return 1;
-	}
-
-	/* still within parking time, process descriptors */
-	if (pfds) {
-		for (x = 0; x < AST_MAX_FDS; x++) {
-			struct ast_frame *f;
-			int y;
-
-			if (!ast_channel_fd_isset(chan, x)) {
-				continue;	/* nothing on this descriptor */
-			}
-
-			for (y = 0; y < nfds; y++) {
-				if (pfds[y].fd == ast_channel_fd(chan, x)) {
-					/* Found poll record! */
-					break;
-				}
-			}
-			if (y == nfds) {
-				/* Not found */
-				continue;
-			}
-
-			if (!(pfds[y].revents & (POLLIN | POLLERR | POLLPRI))) {
-				/* Next x */
-				continue;
-			}
-
-			if (pfds[y].revents & POLLPRI) {
-				ast_set_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION);
-			} else {
-				ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION);
-			}
-			ast_channel_fdno_set(chan, x);
-
-			/* See if they need servicing */
-			f = ast_read(pu->chan);
-			/* Hangup? */
-			if (!f || (f->frametype == AST_FRAME_CONTROL
-				&& f->subclass.integer == AST_CONTROL_HANGUP)) {
-				if (f) {
-					ast_frfree(f);
-				}
-				post_manager_event("ParkedCallGiveUp", pu);
-
-				/* There's a problem, hang them up */
-				ast_verb(2, "%s got tired of being parked\n", ast_channel_name(chan));
-				ast_hangup(chan);
-
-				/* And take them out of the parking lot */
-				return 1;
-			} else {
-				/* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
-				ast_frfree(f);
-				if (pu->hold_method == AST_CONTROL_HOLD
-					&& pu->moh_trys < 3
-					&& !ast_channel_generatordata(chan)) {
-					ast_debug(1,
-						"MOH on parked call stopped by outside source.  Restarting on channel %s.\n",
-						ast_channel_name(chan));
-					ast_indicate_data(chan, AST_CONTROL_HOLD,
-						S_OR(pu->parkinglot->cfg.mohclass, NULL),
-						(!ast_strlen_zero(pu->parkinglot->cfg.mohclass)
-							? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0));
-					pu->moh_trys++;
-				}
-				break;
-			}
-		} /* End for */
-	}
-
-	/* mark fds for next round */
-	for (x = 0; x < AST_MAX_FDS; x++) {
-		if (ast_channel_fd_isset(chan, x)) {
-			void *tmp = ast_realloc(*new_pfds,
-				(*new_nfds + 1) * sizeof(struct pollfd));
-
-			if (!tmp) {
-				continue;
-			}
-			*new_pfds = tmp;
-			(*new_pfds)[*new_nfds].fd = ast_channel_fd(chan, x);
-			(*new_pfds)[*new_nfds].events = POLLIN | POLLERR | POLLPRI;
-			(*new_pfds)[*new_nfds].revents = 0;
-			(*new_nfds)++;
-		}
-	}
-	/* Keep track of our shortest wait */
-	if (tms < *ms || *ms < 0) {
-		*ms = tms;
-	}
-
-	/* Stay in the parking lot. */
-	return 0;
-}
-
-/*! \brief Run management on parkinglots, called once per parkinglot */
-static void manage_parkinglot(struct ast_parkinglot *curlot, const struct pollfd *pfds, int nfds, struct pollfd **new_pfds, int *new_nfds, int *ms)
-{
-	struct parkeduser *pu;
-	struct ast_context *con;
-
-	/* Lock parkings list */
-	AST_LIST_LOCK(&curlot->parkings);
-	AST_LIST_TRAVERSE_SAFE_BEGIN(&curlot->parkings, pu, list) {
-		if (pu->notquiteyet) { /* Pretend this one isn't here yet */
-			continue;
-		}
-		if (manage_parked_call(pu, pfds, nfds, new_pfds, new_nfds, ms)) {
-			/* Parking is complete for this call so remove it from the parking lot. */
-			con = ast_context_find(pu->parkinglot->cfg.parking_con);
-			if (con) {
-				if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL, 0)) {
-					ast_log(LOG_WARNING,
-						"Whoa, failed to remove the parking extension %s@%s!\n",
-						pu->parkingexten, pu->parkinglot->cfg.parking_con);
-				}
-				notify_metermaids(pu->parkingexten, pu->parkinglot->cfg.parking_con,
-					AST_DEVICE_NOT_INUSE);
-			} else {
-				ast_log(LOG_WARNING,
-					"Whoa, parking lot '%s' context '%s' does not exist.\n",
-					pu->parkinglot->name, pu->parkinglot->cfg.parking_con);
-			}
-			AST_LIST_REMOVE_CURRENT(list);
-			parkinglot_unref(pu->parkinglot);
-			ast_free(pu);
-		}
-	}
-	AST_LIST_TRAVERSE_SAFE_END;
-	AST_LIST_UNLOCK(&curlot->parkings);
-}
-
-/*!
- * \brief Take care of parked calls and unpark them if needed
- * \param ignore unused var.
- *
- * Start inf loop, lock parking lot, check if any parked channels have gone above timeout
- * if so, remove channel from parking lot and return it to the extension that parked it.
- * Check if parked channel decided to hangup, wait until next FD via select().
- */
-static void *do_parking_thread(void *ignore)
-{
-	struct pollfd *pfds = NULL, *new_pfds = NULL;
-	int nfds = 0, new_nfds = 0;
-
-	for (;;) {
-		struct ao2_iterator iter;
-		struct ast_parkinglot *curlot;
-		int ms = -1;	/* poll2 timeout, uninitialized */
-
-		iter = ao2_iterator_init(parkinglots, 0);
-		while ((curlot = ao2_iterator_next(&iter))) {
-			manage_parkinglot(curlot, pfds, nfds, &new_pfds, &new_nfds, &ms);
-			ao2_ref(curlot, -1);
-		}
-		ao2_iterator_destroy(&iter);
-
-		/* Recycle */
-		ast_free(pfds);
-		pfds = new_pfds;
-		nfds = new_nfds;
-		new_pfds = NULL;
-		new_nfds = 0;
-
-		/* Wait for something to happen */
-		ast_poll(pfds, nfds, ms);
-		pthread_testcancel();
-	}
-	/* If this WERE reached, we'd need to free(pfds) */
-	return NULL;	/* Never reached */
-}
-
-AST_APP_OPTIONS(park_call_options, BEGIN_OPTIONS
-	AST_APP_OPTION('r', AST_PARK_OPT_RINGING),
-	AST_APP_OPTION('R', AST_PARK_OPT_RANDOMIZE),
-	AST_APP_OPTION('s', AST_PARK_OPT_SILENCE),
-END_OPTIONS );
-
-/*!
- * \brief Unreference parkinglot object.
- */
-static void parkinglot_unref(struct ast_parkinglot *parkinglot)
-{
-	ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name,
-		ao2_ref(parkinglot, 0) - 1);
-	ao2_ref(parkinglot, -1);
-}
-
-/*! Default configuration for default parking lot. */
-static const struct parkinglot_cfg parkinglot_cfg_default_default = {
-	.mohclass = "default",
-	.parkext = DEFAULT_PARK_EXTENSION,
-	.parking_con = "parkedcalls",
-	.parking_start = 701,
-	.parking_stop = 750,
-	.parkingtime = DEFAULT_PARK_TIME,
-	.comebackdialtime = DEFAULT_COMEBACK_DIAL_TIME,
-	.comebackcontext = DEFAULT_COMEBACK_CONTEXT,
-	.comebacktoorigin = DEFAULT_COMEBACK_TO_ORIGIN,
-};
-
-/*! Default configuration for normal parking lots. */
-static const struct parkinglot_cfg parkinglot_cfg_default = {
-	.parkext = DEFAULT_PARK_EXTENSION,
-	.parkingtime = DEFAULT_PARK_TIME,
-	.comebackdialtime = DEFAULT_COMEBACK_DIAL_TIME,
-	.comebackcontext = DEFAULT_COMEBACK_CONTEXT,
-	.comebacktoorigin = DEFAULT_COMEBACK_TO_ORIGIN,
-};
-
-int ast_features_reload(void)
-{
-	struct ast_context *con;
-	int res;
-
-	ast_mutex_lock(&features_reload_lock);/* Searialize reloading features.conf */
-
-	/*
-	 * Always destroy the parking_con_dial context to remove buildup
-	 * of recalled extensions in the context.  At worst, the parked
-	 * call gets hungup attempting to run an invalid extension when
-	 * we are trying to callback the parker or the preset return
-	 * extension.  This is a small window of opportunity on an
-	 * execution chain that is not expected to happen very often.
-	 */
-	con = ast_context_find(parking_con_dial);
-	if (con) {
-		ast_context_destroy(con, registrar);
-	}
-
-	res = ast_features_config_reload();
-	ast_mutex_unlock(&features_reload_lock);
-
-	return res;
-}
-
-static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
-{
-	switch (cmd) {
-	case CLI_INIT:
-		e->command = "features reload";
-		e->usage =
-			"Usage: features reload\n"
-			"       Reloads configured call features from features.conf\n";
-		return NULL;
-	case CLI_GENERATE:
-		return NULL;
-	}
-	ast_features_reload();
-
-	return CLI_SUCCESS;
-}
-
 enum play_tone_action {
 	PLAYTONE_NONE = 0,
 	PLAYTONE_CHANNEL1 = (1 << 0),
@@ -1971,11 +1125,9 @@ static int action_bridge(struct mansession *s, const struct message *m)
 	enum play_tone_action playtone = parse_playtone(astman_get_header(m, "Tone"));
 	RAII_VAR(struct ast_channel *, chana, NULL, ao2_cleanup);
 	RAII_VAR(struct ast_channel *, chanb, NULL, ao2_cleanup);
-	const char *chana_name;
 	const char *chana_exten;
 	const char *chana_context;
 	int chana_priority;
-	const char *chanb_name;
 	const char *chanb_exten;
 	const char *chanb_context;
 	int chanb_priority;
@@ -1999,7 +1151,6 @@ static int action_bridge(struct mansession *s, const struct message *m)
 	}
 	xfer_cfg_a = ast_get_chan_features_xfer_config(chana);
 	ast_channel_lock(chana);
-	chana_name = ast_strdupa(ast_channel_name(chana));
 	chana_exten = ast_strdupa(ast_channel_exten(chana));
 	chana_context = ast_strdupa(ast_channel_context(chana));
 	chana_priority = ast_channel_priority(chana);
@@ -2016,7 +1167,6 @@ static int action_bridge(struct mansession *s, const struct message *m)
 	}
 	xfer_cfg_b = ast_get_chan_features_xfer_config(chanb);
 	ast_channel_lock(chanb);
-	chanb_name = ast_strdupa(ast_channel_name(chanb));
 	chanb_exten = ast_strdupa(ast_channel_exten(chanb));
 	chanb_context = ast_strdupa(ast_channel_context(chanb));
 	chanb_priority = ast_channel_priority(chanb);
@@ -2047,354 +1197,11 @@ static int action_bridge(struct mansession *s, const struct message *m)
 		return 0;
 	}
 
-	/*** DOCUMENTATION
-		<managerEventInstance>
-			<synopsis>Raised when a bridge is successfully created due to a manager action.</synopsis>
-			<syntax>
-				<parameter name="Response">
-					<enumlist>
-						<enum name="Success"/>
-						<enum name="Failed"/>
-					</enumlist>
-				</parameter>
-			</syntax>
-			<see-also>
-				<ref type="manager">Bridge</ref>
-			</see-also>
-		</managerEventInstance>
-	***/
-/* BUGBUG This event used to use ast_manager_event_multichan. Now channel variables are not included in the event */
-	manager_event(EVENT_FLAG_CALL, "BridgeAction",
-				"Response: Success\r\n"
-				"Channel1: %s\r\n"
-				"Channel2: %s\r\n", chana_name, chanb_name);
-
 	astman_send_ack(s, m, "Channels have been bridged");
 
 	return 0;
 }
 
-static struct ast_cli_entry cli_features[] = {
-	AST_CLI_DEFINE(handle_features_reload, "Reloads configured features"),
-};
-
-/*!
- * The presence of this datastore on the channel indicates that
- * someone is attemting to pickup or has picked up the channel.
- * The purpose is to prevent a race between two channels
- * attempting to pickup the same channel.
- */
-static const struct ast_datastore_info pickup_active = {
-	.type = "pickup-active",
-};
-
-int ast_can_pickup(struct ast_channel *chan)
-{
-	if (!ast_channel_pbx(chan) && !ast_channel_masq(chan) && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
-		&& (ast_channel_state(chan) == AST_STATE_RINGING
-			|| ast_channel_state(chan) == AST_STATE_RING
-			/*
-			 * Check the down state as well because some SIP devices do not
-			 * give 180 ringing when they can just give 183 session progress
-			 * instead.  Issue 14005.  (Some ISDN switches as well for that
-			 * matter.)
-			 */
-			|| ast_channel_state(chan) == AST_STATE_DOWN)
-		&& !ast_channel_datastore_find(chan, &pickup_active, NULL)) {
-		return 1;
-	}
-	return 0;
-}
-
-static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
-{
-	struct ast_channel *target = obj;/*!< Potential pickup target */
-	struct ast_channel *chan = arg;/*!< Channel wanting to pickup call */
-
-	if (chan == target) {
-		return 0;
-	}
-
-	ast_channel_lock(target);
-	if (ast_can_pickup(target)) {
-		/* Lock both channels. */
-		while (ast_channel_trylock(chan)) {
-			ast_channel_unlock(target);
-			sched_yield();
-			ast_channel_lock(target);
-		}
-
-		/*
-		 * Both callgroup and namedcallgroup pickup variants are
-		 * matched independently.  Checking for named group match is
-		 * done last since it's a more expensive operation.
-		 */
-		if ((ast_channel_pickupgroup(chan) & ast_channel_callgroup(target))
-			|| (ast_namedgroups_intersect(ast_channel_named_pickupgroups(chan),
-				ast_channel_named_callgroups(target)))) {
-			struct ao2_container *candidates = data;/*!< Candidate channels found. */
-
-			/* This is a candidate to pickup */
-			ao2_link(candidates, target);
-		}
-		ast_channel_unlock(chan);
-	}
-	ast_channel_unlock(target);
-
-	return 0;
-}
-
-struct ast_channel *ast_pickup_find_by_group(struct ast_channel *chan)
-{
-	struct ao2_container *candidates;/*!< Candidate channels found to pickup. */
-	struct ast_channel *target;/*!< Potential pickup target */
-
-	candidates = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
-	if (!candidates) {
-		return NULL;
-	}
-
-	/* Find all candidate targets by group. */
-	ast_channel_callback(find_channel_by_group, chan, candidates, 0);
-
-	/* Find the oldest pickup target candidate */
-	target = NULL;
-	for (;;) {
-		struct ast_channel *candidate;/*!< Potential new older target */
-		struct ao2_iterator iter;
-
-		iter = ao2_iterator_init(candidates, 0);
-		while ((candidate = ao2_iterator_next(&iter))) {
-			if (!target) {
-				/* First target. */
-				target = candidate;
-				continue;
-			}
-			if (ast_tvcmp(ast_channel_creationtime(candidate), ast_channel_creationtime(target)) < 0) {
-				/* We have a new target. */
-				ast_channel_unref(target);
-				target = candidate;
-				continue;
-			}
-			ast_channel_unref(candidate);
-		}
-		ao2_iterator_destroy(&iter);
-		if (!target) {
-			/* No candidates found. */
-			break;
-		}
-
-		/* The found channel must be locked and ref'd. */
-		ast_channel_lock(target);
-
-		/* Recheck pickup ability */
-		if (ast_can_pickup(target)) {
-			/* This is the channel to pickup. */
-			break;
-		}
-
-		/* Someone else picked it up or the call went away. */
-		ast_channel_unlock(target);
-		ao2_unlink(candidates, target);
-		target = ast_channel_unref(target);
-	}
-	ao2_ref(candidates, -1);
-
-	return target;
-}
-
-/*!
- * \brief Pickup a call
- * \param chan channel that initiated pickup.
- *
- * Walk list of channels, checking it is not itself, channel is pbx one,
- * check that the callgroup for both channels are the same and the channel is ringing.
- * Answer calling channel, flag channel as answered on queue, masq channels together.
- */
-int ast_pickup_call(struct ast_channel *chan)
-{
-	struct ast_channel *target;/*!< Potential pickup target */
-	int res = -1;
-	RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
-	const char *pickup_sound;
-	const char *fail_sound;
-
-	ast_debug(1, "pickup attempt by %s\n", ast_channel_name(chan));
-	ast_channel_lock(chan);
-	pickup_cfg = ast_get_chan_features_pickup_config(chan);
-	if (!pickup_cfg) {
-		ast_log(LOG_ERROR, "Unable to retrieve pickup configuration. Unable to play pickup sounds\n");
-	}
-	pickup_sound = ast_strdupa(pickup_cfg ? pickup_cfg->pickupsound : "");
-	fail_sound = ast_strdupa(pickup_cfg ? pickup_cfg->pickupfailsound : "");
-	ast_channel_unlock(chan);
-
-	/* The found channel is already locked. */
-	target = ast_pickup_find_by_group(chan);
-	if (target) {
-		ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
-
-		res = ast_do_pickup(chan, target);
-		ast_channel_unlock(target);
-		if (!res) {
-			if (!ast_strlen_zero(pickup_sound)) {
-				pbx_builtin_setvar_helper(target, "BRIDGE_PLAY_SOUND", pickup_sound);
-			}
-		} else {
-			ast_log(LOG_WARNING, "pickup %s failed by %s\n", ast_channel_name(target), ast_channel_name(chan));
-		}
-		target = ast_channel_unref(target);
-	}
-
-	if (res < 0) {
-		ast_debug(1, "No call pickup possible... for %s\n", ast_channel_name(chan));
-		if (!ast_strlen_zero(fail_sound)) {
-			ast_answer(chan);
-			ast_stream_and_wait(chan, fail_sound, "");
-		}
-	}
-
-	return res;
-}
-
-static struct ast_manager_event_blob *call_pickup_to_ami(struct stasis_message *message)
-{
-	struct ast_multi_channel_blob *contents = stasis_message_data(message);
-	struct ast_channel_snapshot *chan;
-	struct ast_channel_snapshot *target;
-	struct ast_manager_event_blob *res;
-
-	RAII_VAR(struct ast_str *, channel_str, NULL, ast_free);
-	RAII_VAR(struct ast_str *, target_str, NULL, ast_free);
-
-	chan = ast_multi_channel_blob_get_channel(contents, "channel");
-	target = ast_multi_channel_blob_get_channel(contents, "target");
-
-	ast_assert(chan != NULL && target != NULL);
-
-	if (!(channel_str = ast_manager_build_channel_state_string(chan))) {
-		return NULL;
-	}
-
-	if (!(target_str = ast_manager_build_channel_state_string_prefix(target, "Target"))) {
-		return NULL;
-	}
-
-	res = ast_manager_event_blob_create(EVENT_FLAG_CALL, "Pickup",
-		"%s"
-		"%s",
-		ast_str_buffer(channel_str),
-		ast_str_buffer(target_str));
-
-	return res;
-}
-
-static int send_call_pickup_stasis_message(struct ast_channel *picking_up, struct ast_channel_snapshot *chan, struct ast_channel_snapshot *target)
-{
-	RAII_VAR(struct ast_multi_channel_blob *, pickup_payload, NULL, ao2_cleanup);
-	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-
-	if (!(pickup_payload = ast_multi_channel_blob_create(ast_json_null()))) {
-		return -1;
-	}
-
-	ast_multi_channel_blob_add_channel(pickup_payload, "channel", chan);
-	ast_multi_channel_blob_add_channel(pickup_payload, "target", target);
-
-	if (!(msg = stasis_message_create(ast_call_pickup_type(), pickup_payload))) {
-		return -1;
-	}
-
-	stasis_publish(ast_channel_topic(picking_up), msg);
-	return 0;
-}
-
-int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target)
-{
-	struct ast_party_connected_line connected_caller;
-	struct ast_datastore *ds_pickup;
-	const char *chan_name;/*!< A masquerade changes channel names. */
-	const char *target_name;/*!< A masquerade changes channel names. */
-	int res = -1;
-
-	RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_channel_snapshot *, target_snapshot, NULL, ao2_cleanup);
-
-	target_name = ast_strdupa(ast_channel_name(target));
-	ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, ast_channel_name(chan));
-
-	/* Mark the target to block any call pickup race. */
-	ds_pickup = ast_datastore_alloc(&pickup_active, NULL);
-	if (!ds_pickup) {
-		ast_log(LOG_WARNING,
-			"Unable to create channel datastore on '%s' for call pickup\n", target_name);
-		return -1;
-	}
-	ast_channel_datastore_add(target, ds_pickup);
-
-	ast_party_connected_line_init(&connected_caller);
-	ast_party_connected_line_copy(&connected_caller, ast_channel_connected(target));
-	ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */
-	/* Reset any earlier private connected id representation */
-	ast_party_id_reset(&connected_caller.priv);
-
-	connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-	if (ast_channel_connected_line_sub(NULL, chan, &connected_caller, 0) &&
-		ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
-		ast_channel_update_connected_line(chan, &connected_caller, NULL);
-	}
-	ast_party_connected_line_free(&connected_caller);
-
-	ast_channel_lock(chan);
-	chan_name = ast_strdupa(ast_channel_name(chan));
-	ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(chan));
-	ast_channel_unlock(chan);
-	connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
-
-	if (ast_answer(chan)) {
-		ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
-		goto pickup_failed;
-	}
-
-	if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
-		ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name);
-		goto pickup_failed;
-	}
-
-	ast_channel_queue_connected_line_update(chan, &connected_caller, NULL);
-
-	/* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */
-	ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE);
-
-	if (!(chan_snapshot = ast_channel_snapshot_create(chan))) {
-		goto pickup_failed;
-	}
-
-	if (!(target_snapshot = ast_channel_snapshot_create(target))) {
-		goto pickup_failed;
-	}
-
-	if (ast_channel_move(target, chan)) {
-		ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name,
-			target_name);
-		goto pickup_failed;
-	}
-
-	/* target points to the channel that did the pickup at this point, so use that channel's topic instead of chan */
-	send_call_pickup_stasis_message(target, chan_snapshot, target_snapshot);
-
-	res = 0;
-
-pickup_failed:
-	ast_channel_lock(target);
-	if (!ast_channel_datastore_remove(target, ds_pickup)) {
-		ast_datastore_free(ds_pickup);
-	}
-	ast_party_connected_line_free(&connected_caller);
-
-	return res;
-}
-
 static char *app_bridge = "Bridge";
 
 enum {
@@ -2561,7 +1368,6 @@ int ast_bridge_timelimit(struct ast_channel *chan, struct ast_bridge_config *con
 static int bridge_exec(struct ast_channel *chan, const char *data)
 {
 	RAII_VAR(struct ast_channel *, current_dest_chan, NULL, ao2_cleanup);
-	struct ast_channel *chans[2];
 	char *tmp_data  = NULL;
 	struct ast_flags opts = { 0, };
 	struct ast_bridge_config bconfig = { { 0, }, };
@@ -2595,65 +1401,22 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
 			strlen(args.dest_chan)))) {
 		ast_log(LOG_WARNING, "Bridge failed because channel %s does not exist\n",
 			args.dest_chan);
-		ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
-			"Response: Failed\r\n"
-			"Reason: Channel2 does not exist\r\n"
-			"Channel1: %s\r\n"
-			"Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan);
-		pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "NONEXISTENT");
 		return 0;
 	}
 
 	/* avoid bridge with ourselves */
 	if (chan == current_dest_chan) {
 		ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", ast_channel_name(chan));
-		/*** DOCUMENTATION
-			<managerEventInstance>
-				<synopsis>Raised when an error occurs during bridge creation.</synopsis>
-				<see-also>
-					<ref type="application">Bridge</ref>
-				</see-also>
-			</managerEventInstance>
-		***/
-		ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
-			"Response: Failed\r\n"
-			"Reason: Unable to bridge channel to itself\r\n"
-			"Channel1: %s\r\n"
-			"Channel2: %s\r\n",
-			ast_channel_name(chan), args.dest_chan);
-		pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP");
 		return 0;
 	}
 
 	if (ast_test_flag(&opts, OPT_DURATION_LIMIT)
 		&& !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])
 		&& ast_bridge_timelimit(chan, &bconfig, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit)) {
-		ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
-			"Response: Failed\r\n"
-			"Reason: Cannot setup bridge time limit\r\n"
-			"Channel1: %s\r\n"
-			"Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan);
 		pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE");
 		goto done;
 	}
 
-	chans[0] = chan;
-	chans[1] = current_dest_chan;
-
-	/* Report that the bridge will be successfull */
-	/*** DOCUMENTATION
-		<managerEventInstance>
-			<synopsis>Raised when the bridge is created successfully.</synopsis>
-			<see-also>
-				<ref type="application">Bridge</ref>
-			</see-also>
-		</managerEventInstance>
-	***/
-	ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans,
-		"Response: Success\r\n"
-		"Channel1: %s\r\n"
-		"Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(current_dest_chan));
-
 	if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER))
 		ast_set_flag(&(bconfig.features_callee), AST_FEATURE_REDIRECT);
 	if (ast_test_flag(&opts, OPT_CALLER_TRANSFER))
@@ -2736,84 +1499,28 @@ done:
 	return 0;
 }
 
-#if defined(TEST_FRAMEWORK)
-static int fake_fixup(struct ast_channel *clonechan, struct ast_channel *original)
-{
-	return 0;
-}
-#endif	/* defined(TEST_FRAMEWORK) */
-
-#if defined(TEST_FRAMEWORK)
-static struct ast_channel *create_test_channel(const struct ast_channel_tech *fake_tech)
-{
-	struct ast_channel *test_channel1;
-	struct ast_format tmp_fmt;
-
-	if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
-		NULL, NULL, 0, 0, "TestChannel1"))) {
-		ast_log(LOG_WARNING, "Whoa, test channel creation failed.\n");
-		return NULL;
-	}
-
-	/* normally this is done in the channel driver */
-	ast_format_cap_add(ast_channel_nativeformats(test_channel1), ast_format_set(&tmp_fmt, AST_FORMAT_GSM, 0));
-
-	ast_format_set(ast_channel_writeformat(test_channel1), AST_FORMAT_GSM, 0);
-	ast_format_set(ast_channel_rawwriteformat(test_channel1), AST_FORMAT_GSM, 0);
-	ast_format_set(ast_channel_readformat(test_channel1), AST_FORMAT_GSM, 0);
-	ast_format_set(ast_channel_rawreadformat(test_channel1), AST_FORMAT_GSM, 0);
-
-	ast_channel_tech_set(test_channel1, fake_tech);
-
-	return test_channel1;
-}
-#endif	/* defined(TEST_FRAMEWORK) */
-
 /*! \internal \brief Clean up resources on Asterisk shutdown */
 static void features_shutdown(void)
 {
 	ast_features_config_shutdown();
 
-	ast_cli_unregister_multiple(cli_features, ARRAY_LEN(cli_features));
-	ast_devstate_prov_del("Park");
 	ast_manager_unregister("Bridge");
-	ast_manager_unregister("Park");
 
 	ast_unregister_application(app_bridge);
 
-	STASIS_MESSAGE_TYPE_CLEANUP(ast_call_pickup_type);
-	pthread_cancel(parking_thread);
-	pthread_kill(parking_thread, SIGURG);
-	pthread_join(parking_thread, NULL);
-	ast_context_destroy(NULL, registrar);
-	ao2_ref(parkinglots, -1);
 }
 
 int ast_features_init(void)
 {
 	int res;
 
-	parkinglots = ao2_container_alloc(7, parkinglot_hash_cb, parkinglot_cmp_cb);
-	if (!parkinglots) {
-		return -1;
-	}
-
 	res = ast_features_config_init();
 	if (res) {
 		return res;
 	}
-	ast_cli_register_multiple(cli_features, ARRAY_LEN(cli_features));
-	if (ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL)) {
-		ast_features_config_shutdown();
-		ast_cli_unregister_multiple(cli_features, ARRAY_LEN(cli_features));
-		return -1;
-	}
-	STASIS_MESSAGE_TYPE_INIT(ast_call_pickup_type);
 	res |= ast_register_application2(app_bridge, bridge_exec, NULL, NULL, NULL);
 	res |= ast_manager_register_xml_core("Bridge", EVENT_FLAG_CALL, action_bridge);
 
-	res |= ast_devstate_prov_add("Park", metermaidstate);
-
 	if (res) {
 		features_shutdown();
 	} else {
diff --git a/main/pbx.c b/main/pbx.c
index 27f774ac3c06ce56f42f63f823f2c7e7799215e6..0f0f5114a17bd12e79667272efd728cb54f81795 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -4651,18 +4651,6 @@ static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
 					COLORIZE(COLOR_BRMAGENTA, 0, passdata),
 					"in new stack");
 			}
-			snapshot = ast_channel_snapshot_create(c);
-			if (snapshot) {
-				/* pbx_exec sets application name and data, but we don't want to log
-				 * every exec. Just update the snapshot here instead.
-				 */
-				ast_string_field_set(snapshot, appl, app->name);
-				ast_string_field_set(snapshot, data, passdata);
-				msg = stasis_message_create(ast_channel_snapshot_type(), snapshot);
-				if (msg) {
-					stasis_publish(ast_channel_topic(c), msg);
-				}
-			}
 			return pbx_exec(c, app, passdata);	/* 0 on success, -1 on failure */
 		}
 	} else if (q.swo) {	/* not found here, but in another switch */
diff --git a/main/pickup.c b/main/pickup.c
new file mode 100644
index 0000000000000000000000000000000000000000..611126af14e3a9401350bf07fda70917aa3f9412
--- /dev/null
+++ b/main/pickup.c
@@ -0,0 +1,423 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2013, Digium, Inc.
+ * Copyright (C) 2012, Russell Bryant
+ *
+ * Matt Jordan <mjordan@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Routines implementing call pickup
+ *
+ * \author Matt Jordan <mjordan@digium.com>
+ */
+
+/*!
+ * \li Call pickup uses the configuration file \ref features.conf
+ * \addtogroup configuration_file Configuration Files
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+/*** DOCUMENTATION
+	<managerEvent language="en_US" name="Pickup">
+		<managerEventInstance class="EVENT_FLAG_CALL">
+			<synopsis>Raised when a call pickup occurs.</synopsis>
+			<syntax>
+				<xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+				<parameter name="TargetChannel"/>
+				<parameter name="TargetChannelState"><para>A numeric code for the channel's current state, related to TargetChannelStateDesc</para></parameter>
+				<parameter name="TargetChannelStateDesc">
+					<enumlist>
+						<enum name="Down"/>
+						<enum name="Rsrvd"/>
+						<enum name="OffHook"/>
+						<enum name="Dialing"/>
+						<enum name="Ring"/>
+						<enum name="Ringing"/>
+						<enum name="Up"/>
+						<enum name="Busy"/>
+						<enum name="Dialing Offhook"/>
+						<enum name="Pre-ring"/>
+						<enum name="Unknown"/>
+					</enumlist>
+				</parameter>
+				<parameter name="TargetCallerIDNum"/>
+				<parameter name="TargetCallerIDName"/>
+				<parameter name="TargetConnectedLineNum"/>
+				<parameter name="TargetConnectedLineName"/>
+				<parameter name="TargetAccountCode"/>
+				<parameter name="TargetContext"/>
+				<parameter name="TargetExten"/>
+				<parameter name="TargetPriority"/>
+				<parameter name="TargetUniqueid"/>
+			</syntax>
+		</managerEventInstance>
+	</managerEvent>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/pickup.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/app.h"
+#include "asterisk/callerid.h"
+#include "asterisk/causes.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/features_config.h"
+
+static struct ast_manager_event_blob *call_pickup_to_ami(struct stasis_message *message);
+
+STASIS_MESSAGE_TYPE_DEFN(
+	ast_call_pickup_type,
+	.to_ami = call_pickup_to_ami);
+
+
+/*!
+ * The presence of this datastore on the channel indicates that
+ * someone is attemting to pickup or has picked up the channel.
+ * The purpose is to prevent a race between two channels
+ * attempting to pickup the same channel.
+ */
+static const struct ast_datastore_info pickup_active = {
+	.type = "pickup-active",
+};
+
+int ast_can_pickup(struct ast_channel *chan)
+{
+	if (!ast_channel_pbx(chan) && !ast_channel_masq(chan) && !ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
+		&& (ast_channel_state(chan) == AST_STATE_RINGING
+			|| ast_channel_state(chan) == AST_STATE_RING
+			/*
+			 * Check the down state as well because some SIP devices do not
+			 * give 180 ringing when they can just give 183 session progress
+			 * instead.  Issue 14005.  (Some ISDN switches as well for that
+			 * matter.)
+			 */
+			|| ast_channel_state(chan) == AST_STATE_DOWN)
+		&& !ast_channel_datastore_find(chan, &pickup_active, NULL)) {
+		return 1;
+	}
+	return 0;
+}
+
+static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
+{
+	struct ast_channel *target = obj; /*!< Potential pickup target */
+	struct ast_channel *chan = arg;   /*!< Channel wanting to pickup call */
+
+	if (chan == target) {
+		return 0;
+	}
+
+	ast_channel_lock(target);
+	if (ast_can_pickup(target)) {
+		/* Lock both channels. */
+		while (ast_channel_trylock(chan)) {
+			ast_channel_unlock(target);
+			sched_yield();
+			ast_channel_lock(target);
+		}
+
+		/*
+		 * Both callgroup and namedcallgroup pickup variants are
+		 * matched independently.  Checking for named group match is
+		 * done last since it's a more expensive operation.
+		 */
+		if ((ast_channel_pickupgroup(chan) & ast_channel_callgroup(target))
+			|| (ast_namedgroups_intersect(ast_channel_named_pickupgroups(chan),
+				ast_channel_named_callgroups(target)))) {
+			struct ao2_container *candidates = data;/*!< Candidate channels found. */
+
+			/* This is a candidate to pickup */
+			ao2_link(candidates, target);
+		}
+		ast_channel_unlock(chan);
+	}
+	ast_channel_unlock(target);
+
+	return 0;
+}
+
+struct ast_channel *ast_pickup_find_by_group(struct ast_channel *chan)
+{
+	struct ao2_container *candidates;/*!< Candidate channels found to pickup. */
+	struct ast_channel *target;/*!< Potential pickup target */
+
+	candidates = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+	if (!candidates) {
+		return NULL;
+	}
+
+	/* Find all candidate targets by group. */
+	ast_channel_callback(find_channel_by_group, chan, candidates, 0);
+
+	/* Find the oldest pickup target candidate */
+	target = NULL;
+	for (;;) {
+		struct ast_channel *candidate;/*!< Potential new older target */
+		struct ao2_iterator iter;
+
+		iter = ao2_iterator_init(candidates, 0);
+		while ((candidate = ao2_iterator_next(&iter))) {
+			if (!target) {
+				/* First target. */
+				target = candidate;
+				continue;
+			}
+			if (ast_tvcmp(ast_channel_creationtime(candidate), ast_channel_creationtime(target)) < 0) {
+				/* We have a new target. */
+				ast_channel_unref(target);
+				target = candidate;
+				continue;
+			}
+			ast_channel_unref(candidate);
+		}
+		ao2_iterator_destroy(&iter);
+		if (!target) {
+			/* No candidates found. */
+			break;
+		}
+
+		/* The found channel must be locked and ref'd. */
+		ast_channel_lock(target);
+
+		/* Recheck pickup ability */
+		if (ast_can_pickup(target)) {
+			/* This is the channel to pickup. */
+			break;
+		}
+
+		/* Someone else picked it up or the call went away. */
+		ast_channel_unlock(target);
+		ao2_unlink(candidates, target);
+		target = ast_channel_unref(target);
+	}
+	ao2_ref(candidates, -1);
+
+	return target;
+}
+
+/*!
+ * \brief Pickup a call
+ * \param chan channel that initiated pickup.
+ *
+ * Walk list of channels, checking it is not itself, channel is pbx one,
+ * check that the callgroup for both channels are the same and the channel is ringing.
+ * Answer calling channel, flag channel as answered on queue, masq channels together.
+ */
+int ast_pickup_call(struct ast_channel *chan)
+{
+	struct ast_channel *target;/*!< Potential pickup target */
+	int res = -1;
+	RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
+	const char *pickup_sound;
+	const char *fail_sound;
+
+	ast_debug(1, "Pickup attempt by %s\n", ast_channel_name(chan));
+	ast_channel_lock(chan);
+	pickup_cfg = ast_get_chan_features_pickup_config(chan);
+	if (!pickup_cfg) {
+		ast_log(LOG_ERROR, "Unable to retrieve pickup configuration. Unable to play pickup sounds\n");
+	}
+	pickup_sound = ast_strdupa(pickup_cfg ? pickup_cfg->pickupsound : "");
+	fail_sound = ast_strdupa(pickup_cfg ? pickup_cfg->pickupfailsound : "");
+	ast_channel_unlock(chan);
+
+	/* The found channel is already locked. */
+	target = ast_pickup_find_by_group(chan);
+	if (target) {
+		ast_log(LOG_NOTICE, "Pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
+
+		res = ast_do_pickup(chan, target);
+		ast_channel_unlock(target);
+		if (!res) {
+			if (!ast_strlen_zero(pickup_sound)) {
+				pbx_builtin_setvar_helper(target, "BRIDGE_PLAY_SOUND", pickup_sound);
+			}
+		} else {
+			ast_log(LOG_WARNING, "Pickup %s failed by %s\n", ast_channel_name(target), ast_channel_name(chan));
+		}
+		target = ast_channel_unref(target);
+	}
+
+	if (res < 0) {
+		ast_debug(1, "No call pickup possible... for %s\n", ast_channel_name(chan));
+		if (!ast_strlen_zero(fail_sound)) {
+			ast_answer(chan);
+			ast_stream_and_wait(chan, fail_sound, "");
+		}
+	}
+
+	return res;
+}
+
+static struct ast_manager_event_blob *call_pickup_to_ami(struct stasis_message *message)
+{
+	struct ast_multi_channel_blob *contents = stasis_message_data(message);
+	struct ast_channel_snapshot *chan;
+	struct ast_channel_snapshot *target;
+	struct ast_manager_event_blob *res;
+
+	RAII_VAR(struct ast_str *, channel_str, NULL, ast_free);
+	RAII_VAR(struct ast_str *, target_str, NULL, ast_free);
+
+	chan = ast_multi_channel_blob_get_channel(contents, "channel");
+	target = ast_multi_channel_blob_get_channel(contents, "target");
+
+	ast_assert(chan != NULL && target != NULL);
+
+	if (!(channel_str = ast_manager_build_channel_state_string(chan))) {
+		return NULL;
+	}
+
+	if (!(target_str = ast_manager_build_channel_state_string_prefix(target, "Target"))) {
+		return NULL;
+	}
+
+	res = ast_manager_event_blob_create(EVENT_FLAG_CALL, "Pickup",
+		"%s"
+		"%s",
+		ast_str_buffer(channel_str),
+		ast_str_buffer(target_str));
+
+	return res;
+}
+
+static int send_call_pickup_stasis_message(struct ast_channel *picking_up, struct ast_channel_snapshot *chan, struct ast_channel_snapshot *target)
+{
+	RAII_VAR(struct ast_multi_channel_blob *, pickup_payload, NULL, ao2_cleanup);
+	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+
+	if (!(pickup_payload = ast_multi_channel_blob_create(ast_json_null()))) {
+		return -1;
+	}
+
+	ast_multi_channel_blob_add_channel(pickup_payload, "channel", chan);
+	ast_multi_channel_blob_add_channel(pickup_payload, "target", target);
+
+	if (!(msg = stasis_message_create(ast_call_pickup_type(), pickup_payload))) {
+		return -1;
+	}
+
+	stasis_publish(ast_channel_topic(picking_up), msg);
+	return 0;
+}
+
+int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target)
+{
+	struct ast_party_connected_line connected_caller;
+	struct ast_datastore *ds_pickup;
+	const char *chan_name;/*!< A masquerade changes channel names. */
+	const char *target_name;/*!< A masquerade changes channel names. */
+	int res = -1;
+
+	RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel_snapshot *, target_snapshot, NULL, ao2_cleanup);
+
+	target_name = ast_strdupa(ast_channel_name(target));
+	ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, ast_channel_name(chan));
+
+	/* Mark the target to block any call pickup race. */
+	ds_pickup = ast_datastore_alloc(&pickup_active, NULL);
+	if (!ds_pickup) {
+		ast_log(LOG_WARNING,
+			"Unable to create channel datastore on '%s' for call pickup\n", target_name);
+		return -1;
+	}
+	ast_channel_datastore_add(target, ds_pickup);
+
+	ast_party_connected_line_init(&connected_caller);
+	ast_party_connected_line_copy(&connected_caller, ast_channel_connected(target));
+	ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */
+	/* Reset any earlier private connected id representation */
+	ast_party_id_reset(&connected_caller.priv);
+
+	connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+	if (ast_channel_connected_line_sub(NULL, chan, &connected_caller, 0) &&
+		ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) {
+		ast_channel_update_connected_line(chan, &connected_caller, NULL);
+	}
+	ast_party_connected_line_free(&connected_caller);
+
+	ast_channel_lock(chan);
+	chan_name = ast_strdupa(ast_channel_name(chan));
+	ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(chan));
+	ast_channel_unlock(chan);
+	connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
+
+	if (ast_answer(chan)) {
+		ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name);
+		goto pickup_failed;
+	}
+
+	if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
+		ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name);
+		goto pickup_failed;
+	}
+
+	ast_channel_queue_connected_line_update(chan, &connected_caller, NULL);
+
+	/* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */
+	ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE);
+
+	if (!(chan_snapshot = ast_channel_snapshot_create(chan))) {
+		goto pickup_failed;
+	}
+
+	if (!(target_snapshot = ast_channel_snapshot_create(target))) {
+		goto pickup_failed;
+	}
+
+	if (ast_channel_move(target, chan)) {
+		ast_log(LOG_WARNING, "Unable to complete call pickup of '%s' with '%s'\n",
+			chan_name, target_name);
+		goto pickup_failed;
+	}
+
+	/* target points to the channel that did the pickup at this point, so use that channel's topic instead of chan */
+	send_call_pickup_stasis_message(target, chan_snapshot, target_snapshot);
+
+	res = 0;
+
+pickup_failed:
+	ast_channel_lock(target);
+	if (!ast_channel_datastore_remove(target, ds_pickup)) {
+		ast_datastore_free(ds_pickup);
+	}
+	ast_party_connected_line_free(&connected_caller);
+
+	return res;
+}
+
+/*! \internal \brief Clean up resources on Asterisk shutdown */
+static void pickup_shutdown(void)
+{
+	STASIS_MESSAGE_TYPE_CLEANUP(ast_call_pickup_type);
+}
+
+int ast_pickup_init(void)
+{
+	STASIS_MESSAGE_TYPE_INIT(ast_call_pickup_type);
+	ast_register_atexit(pickup_shutdown);
+
+	return 0;
+}
diff --git a/res/parking/parking_applications.c b/res/parking/parking_applications.c
index 4e52e21d7d7b7f859c71c3c8d62d483aca411a7a..67605c2f7adef3227b22c2ccc0eaa90612fcd421 100644
--- a/res/parking/parking_applications.c
+++ b/res/parking/parking_applications.c
@@ -37,7 +37,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/app.h"
 #include "asterisk/say.h"
-#include "asterisk/features.h"
 #include "asterisk/bridge_basic.h"
 
 /*** DOCUMENTATION
diff --git a/res/parking/parking_manager.c b/res/parking/parking_manager.c
index 70fab285253f9d956b39da3f2ff195319aaee89d..65a9ef00472c19f6357a74dbca57587ee7392444 100644
--- a/res/parking/parking_manager.c
+++ b/res/parking/parking_manager.c
@@ -578,5 +578,6 @@ void unload_parking_manager(void)
 {
 	ast_manager_unregister("Parkinglots");
 	ast_manager_unregister("ParkedCalls");
+	ast_manager_unregister("Park");
 	parking_manager_disable_stasis();
 }
diff --git a/res/res_parking.c b/res/res_parking.c
index 6efb7954342bb81172a262ca247f3bb58917ef64..f0381821d851b56fcf43907ce340ecfd971f2757 100644
--- a/res/res_parking.c
+++ b/res/res_parking.c
@@ -1227,6 +1227,9 @@ static int reload_module(void)
 
 static int unload_module(void)
 {
+
+	/*ast_parking_unregister_bridge_features(parking_provider.module_name);*/
+
 	/* XXX Parking is currently not unloadable due to the fact that it loads features which could cause
 	 *     significant problems if they disappeared while a channel still had access to them.
 	 */
diff --git a/res/stasis/control.c b/res/stasis/control.c
index 94f1d700dce5bdff289526f0bb678773dfa65a47..211566e11b868a1abeb2b6b2d9c603de86c50483 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -34,7 +34,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/dial.h"
 #include "asterisk/bridge.h"
 #include "asterisk/bridge_basic.h"
-#include "asterisk/bridge_features.h"
 #include "asterisk/frame.h"
 #include "asterisk/pbx.h"
 #include "asterisk/musiconhold.h"
diff --git a/tests/test_cdr.c b/tests/test_cdr.c
index 18511af55e558ddb56a898c768614a2bfc5153ab..a3773a98cb12428ab06188544d0bf171afb08b5a 100644
--- a/tests/test_cdr.c
+++ b/tests/test_cdr.c
@@ -43,7 +43,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/causes.h"
 #include "asterisk/time.h"
 #include "asterisk/bridge.h"
-#include "asterisk/bridge_basic.h"
+#include "asterisk/bridge_features.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/stasis_bridges.h"
 
diff --git a/tests/test_cel.c b/tests/test_cel.c
index 395ec0ccca78bd1f632a919c79842c8ebdd0b2e4..247781b4a09e96d1d18e2368bbdbd09200ab394a 100644
--- a/tests/test_cel.c
+++ b/tests/test_cel.c
@@ -44,7 +44,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/causes.h"
 #include "asterisk/time.h"
 #include "asterisk/bridge.h"
-#include "asterisk/bridge_basic.h"
+#include "asterisk/bridge_features.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/stasis_bridges.h"
 #include "asterisk/json.h"