diff --git a/CHANGES b/CHANGES
index e969d85bf47cb75b735f83aee29bbae2621967ac..5cfad794c9b67476ded359e0b1acd16815e85c5f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -67,6 +67,10 @@ app_queue
    When set the wrapuptime on the member is used instead of the wrapuptime
    defined for the queue itself.
 
+ * Added predial handler support for caller and callee channels with the
+   B and b options respectively.  This is similar to the predial support
+   in app_dial.
+
 res_config_sqlite
 ------------------
  * The res_config_sqlite module is now deprecated, users should migrate to the
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 3047648de40124dfbd66cc833601b4f97900372d..052d528b8bb92aaf1752929099a6785c6ab4436f 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -133,6 +133,27 @@
 			<parameter name="queuename" required="true" />
 			<parameter name="options">
 				<optionlist>
+					<option name="b" argsep="^">
+						<para>Before initiating an outgoing call, <literal>Gosub</literal> to the specified
+						location using the newly created channel.  The <literal>Gosub</literal> will be
+						executed for each destination channel.</para>
+						<argument name="context" required="false" />
+						<argument name="exten" required="false" />
+						<argument name="priority" required="true" hasparams="optional" argsep="^">
+							<argument name="arg1" multiple="true" required="true" />
+							<argument name="argN" />
+						</argument>
+					</option>
+					<option name="B" argsep="^">
+						<para>Before initiating the outgoing call(s), <literal>Gosub</literal> to the
+						specified location using the current channel.</para>
+						<argument name="context" required="false" />
+						<argument name="exten" required="false" />
+						<argument name="priority" required="true" hasparams="optional" argsep="^">
+							<argument name="arg1" multiple="true" required="true" />
+							<argument name="argN" />
+						</argument>
+					</option>
 					<option name="C">
 						<para>Mark all calls as "answered elsewhere" when cancelled.</para>
 					</option>
@@ -1325,15 +1346,21 @@ enum {
 	OPT_CALLER_AUTOMIXMON =      (1 << 16),
 	OPT_CALLEE_AUTOMON =         (1 << 17),
 	OPT_CALLER_AUTOMON =         (1 << 18),
+	OPT_PREDIAL_CALLEE =         (1 << 19),
+	OPT_PREDIAL_CALLER =         (1 << 20),
 };
 
 enum {
 	OPT_ARG_CALLEE_GO_ON = 0,
+	OPT_ARG_PREDIAL_CALLEE,
+	OPT_ARG_PREDIAL_CALLER,
 	/* note: this entry _MUST_ be the last one in the enum */
 	OPT_ARG_ARRAY_SIZE
 };
 
 AST_APP_OPTIONS(queue_exec_options, BEGIN_OPTIONS
+	AST_APP_OPTION_ARG('b', OPT_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLEE),
+	AST_APP_OPTION_ARG('B', OPT_PREDIAL_CALLER, OPT_ARG_PREDIAL_CALLER),
 	AST_APP_OPTION('C', OPT_MARK_AS_ANSWERED),
 	AST_APP_OPTION('c', OPT_GO_ON),
 	AST_APP_OPTION('d', OPT_DATA_QUALITY),
@@ -1545,6 +1572,7 @@ struct queue_ent {
 	char announce[PATH_MAX];               /*!< Announcement to play for member when call is answered */
 	char context[AST_MAX_CONTEXT];         /*!< Context when user exits queue */
 	char digits[AST_MAX_EXTENSION];        /*!< Digits entered while in queue */
+	const char *predial_callee;            /*!< Gosub app arguments for outgoing calls.  NULL if not supplied. */
 	int valid_digits;                      /*!< Digits entered correspond to valid extension. Exited */
 	int pos;                               /*!< Where we are in the queue */
 	int prio;                              /*!< Our priority */
@@ -4559,6 +4587,11 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
 	ast_channel_unlock(tmp->chan);
 	ast_channel_unlock(qe->chan);
 
+	/* PREDIAL: Run gosub on the callee's channel */
+	if (qe->predial_callee) {
+		ast_pre_call(tmp->chan, qe->predial_callee);
+	}
+
 	/* Place the call, but don't wait on the answer */
 	if ((res = ast_call(tmp->chan, location, 0))) {
 		/* Again, keep going even if there's an error */
@@ -4615,6 +4648,16 @@ static struct callattempt *find_best(struct callattempt *outgoing)
 static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
 {
 	int ret = 0;
+	struct callattempt *cur;
+
+	if (qe->predial_callee) {
+		ast_autoservice_start(qe->chan);
+		for (cur = outgoing; cur; cur = cur->q_next) {
+			if (cur->stillgoing && cur->chan) {
+				ast_autoservice_start(cur->chan);
+			}
+		}
+	}
 
 	while (ret == 0) {
 		struct callattempt *best = find_best(outgoing);
@@ -4623,18 +4666,23 @@ static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *bus
 			break;
 		}
 		if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
-			struct callattempt *cur;
 			/* Ring everyone who shares this best metric (for ringall) */
 			for (cur = outgoing; cur; cur = cur->q_next) {
 				if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
 					ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
 					ret |= ring_entry(qe, cur, busies);
+					if (qe->predial_callee && cur->chan) {
+						ast_autoservice_start(cur->chan);
+					}
 				}
 			}
 		} else {
 			/* Ring just the best channel */
 			ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
 			ret = ring_entry(qe, best, busies);
+			if (qe->predial_callee && cur->chan) {
+				ast_autoservice_start(best->chan);
+			}
 		}
 
 		/* If we have timed out, break out */
@@ -4644,6 +4692,14 @@ static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *bus
 			break;
 		}
 	}
+	if (qe->predial_callee) {
+		for (cur = outgoing; cur; cur = cur->q_next) {
+			if (cur->stillgoing && cur->chan) {
+				ast_autoservice_stop(cur->chan);
+			}
+		}
+		ast_autoservice_stop(qe->chan);
+	}
 
 	return ret;
 }
@@ -8270,6 +8326,21 @@ static int queue_exec(struct ast_channel *chan, const char *data)
 		S_OR(args.url, ""),
 		S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
 		qe.opos);
+
+	/* PREDIAL: Preprocess any callee gosub arguments. */
+	if (ast_test_flag(&opts, OPT_PREDIAL_CALLEE)
+		&& !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])) {
+		ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLEE]);
+		qe.predial_callee = opt_args[OPT_ARG_PREDIAL_CALLEE];
+	}
+
+	/* PREDIAL: Run gosub on the caller's channel */
+	if (ast_test_flag(&opts, OPT_PREDIAL_CALLER)
+		&& !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
+		ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLER]);
+		ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
+	}
+
 	copy_rules(&qe, args.rule);
 	qe.pr = AST_LIST_FIRST(&qe.qe_rules);
 check_turns: