logo

Restructure of the CDR code to better handle Transfers, Parks, etc.

Created 12 Jun 2007 - 6:50pm
Published on Asterisk :: The Open Source PBX & Telephony Platform

Restructure of CDR Code to Better Handle Transfers

This is a notification/documentation/late night equivalent of powerful sleeping drug,  for the changes I am about to commit into the trunk of Asterisk.

It fixes the problem with CDR's and Transfers.

Problem? What problem?

For those of you who have billing applications built on top of Asterisk, that use CDR's, and were blissfully ignorant of any problems with CDR's, this might be a shock. Best sit down before reading this. Have smelling salts ready, stuff like that. Some chocolate-chip cookies might help.

For the most part, CDR generation within Asterisk is deathly accurate and utterly dependable. If an external party calls into Asterisk, and that call is then connected to some extension within the switch, a CDR is generated and all is well.

Same for an outgoing call-- someone picks up a phone connected to Asterisk, dials a number, and is connected to the dialed party, either internal or external -- a CDR is generated, no problems.

But, when a transfer takes place, either via a zaptel hookflash, using a # key, or a SIPi [0] xfer button, and things start to get sticky.

Usually, the CDR ends up being kind of an amalgam of perhaps several different conversations. It's not exactly easy to say exactly which event ends up in which position in a generated CDR, but basically, a semi-random (but usually correct for the event) time would end up in the CDR generated. The trouble is, there may have been two or three distinct times to choose from, and one of them made it, and the others did not. An example would be if A calls B, and B transfers the call to C, and C transfers the call to D. Now B answered the call, but so did C, and D. Any one of those 3 answer times may have ended up in the generated CDR.

And so on goes the tale. When CDR's were first installed in Asterisk, it wasn't able to transfer calls at that time. Such capabilities were added to Asterisk, but it was not upgraded to make everything work with CDR's. The code was patched and patched to get rid of nagging problems over time, but finally, it's time to take care of the problem. The time has come.

Problems Fixed in Recent Past


The changes I've made to 1.4 and trunk to get rid of some of the most nagging problems over the last 2 or 3 months, can be summed up in a few sentences. First of all, I noticed that in some situations, the answer time was not getting into the CDR for certain kinds of calls. I found that this was because, in the case of most common calls, there are two channels involved. The "old" version of Asterisk was counting on the fact that the originating channel would have the answer time stored in its CDR. But thru a fluke in the way the call was placed, the answer time was instead being placed on the other, the "peer" channel. Or, it would have been, but Asterisk wasn't normally even attaching a CDR to that other channel. So, Asterisk would skip attaching this datum at all, as there was nowhere to put it. My solution was to insure that every channel created automatically got a CDR attached. Thus, at dial time, the two involved channels would always each have a CDR structure, and at calls end, the two CDR's would be merged into a single CDR and posted. This change created some fallout, as some actions now produced a CDR, that did not before (like ringing several phones, or just plain picking up the handset of a zap phone, and setting it back down.).

So, as I said, I made mods to make sure every channel got a CDR when it was created. Dialing merges two CDR's into one.

But, that's not enough! Transfers were still getting lost. So I went deeper.

First, I spotted the fact that ast_bridge_call is a crucial routine in the joining of two channels to make a communication path. I saw that if CDR's could be generated to document the forming and dismantling of a bridge, that no conversation could be lost. As it was, CDRs were being used as a repository of information about common call events, such as the time a channel was opened, the time an answer took place, and the time a hangup occurred.

Indeed, in the "old" code, the major place a call CDR got posted, was in the hangup routine. Thus, when someone hung up, usually a CDR got posted. I commented out the code in the hangup routines to post CDRs. Instead, everything is in the ast_bridge_call routine. The CDR on a channel gets its start time when the channel is first allocated. The bridge code copies out the originating channel's CDR if it exists (doesn't always, even still!), and with it comes the start time for the originating channel. It then, before the channels are bridged, sets the answer time. When the bridge dissolves, the end time is set and the copy of the CDR is posted-- leaving the two channels and their CDRs intact. Both CDR's are reinitialized-- their start times are reset to the current time, and the answer and end times are set to NULL, just in case the channels will be used for other calls.

In many transfer situations, especially assisted transfers, it is common to have two different bridges running simultaneously, and independently of each other. A single CDR cannot possibly cover such situations. So, the bridge approach seemed a practical one for me, to make sure all conversations got logged. And, based on a lot of experiments, it is working.

Another random factoid about Asterisk bridges: A bridge, usually, is totally and blissfully ignorant of any transfers that might be happening while it is alive. Thus, things like #-transfers or hookflashes are entirely hidden from the bridge at the high level. The drivers and other code are busy making copies of channels and swapping things around to make transfers happen (see the masquerade code), at a fairly low level, so no attention has to be paid at higher levels.

Another problem is the interpretation of the data that results. I cut out all the code I had added to further generate CDRs in transfer situations, so that you could clearly see how long a party was held in MOH limbo, how long 3-Way conversations lasted, etc., but I ripped it all out in favor of just reporting bridges. I have spotted only a few situations where a CDR might not report the time someone was on a phone. (I hope to reveal this in mind-boggling detail later; I may end up correcting even these situations, but I do not consider them critical... you shall see...). My conclusion was that, in most situations, you don't really care WHY the call was made... you have who called who, and for how long, and when it comes to billing, that seems to be the meat of the matter, is it not? Here's an example, Bill, at extension 103 in Acme, in Los Angeles, gets a call from Bakersfield. Bill determines the caller needs to talk to someone in Shanghai, so he does him a favor, and transfers him to a number in Shanghai. Bill drops out of the conversation entirely after a short while, but the person from Bakersfield continues to talk to the person in Shanghai for perhaps 2 more hours.
Who gets billed? That's your decision, not mine. I can only try to provide you with the information you'd need for whatever scenario you need information for. I can tell you one thing right now, though. I don't think this system provides a fine enough granularity to make all possible billing arrangements. It may be difficult to impossible to figure out if there was a 3-way conference happening, or exactly how long it might be. It might be difficult or impossible to tell whether party C was participating in a bridge, or how long, given the restricted information available to the bridge. My changes do not give you more than the crude, basic facts of a connection. Which extension originated the connection, how long that connection lasted. All the fancy stuff about all the tricks and turns that happened while the bridge was in place, is left out. Sorry!

If you need more info that what this interface provides, you might try looking into the CEL (Channel Event Logging) facility I've been working on. It's intended to provide you with the finer grained event info. In the meantime, you might be able to use the 'extra' fields that are available in CDR's and channels, like accountcode, amaflags, and userfield, to do what you need to do.

So, What's Different Now?


Ok, now, if you are still awake, you might be asking, so how does this change things? And, that's a good question. Let's look at several common situations, and you can decide for yourself if my changes are good or bad.

Nomenclature:


First a quick review of abbreviations:

HU
hangs up
ANS
answers
HF
hookflash (you know, hang up for a second)
->
dials

Scenario List


I have collected about 20 scenarios. Here is the list:

  1. Simple. A calls B.
  2. Unattended transfer via # on zap phone
  3. Unattended transfer via SIP transfer button
  4. Unattended transfer via zap Hookflash
  5. Attended transfer via two zap hookflashes (3-way conf); 151 hangs up first
  6. Same as (5), but 150 hangs up first.
  7. Same as (5), but 152 hangs up first.
  8. Attended xfer via SIP (Polycom), 201 (polycom) hangs up first
  9. Same as (8), but 200 hangs up first.
  10. Same as (8), but 152 hangs up first.
  11. Attended xfer via SIP (Snom), 200 (snom) hangs up first
  12. Same as (11), but 201 hangs up first.
  13. Same as (11), but 152 hangs up first.
  14. Caller dials, gets app
  15. Caller dials, gets AGI
  16. Caller dials, gets DISA, calls another number
  17. Parking. (152->151; 151 parks 152; 150 picks up parked caller)
  18. Parking. (152->151; 151 parks 152; park times out, rings 151. 151 answers.
  19. Parking. Same as (18), but 151 doesn't answer after the park     times out. (Asterisk will ring back to 151 forever; after some     time 152 hangs up).
  20. The CEL scenario: 152->151; 152 parks the call (dials #700); 152 HU;     150->701 (picks up the parked call); 151 HF; 151->152; 152 ANS;     151 HF (3way); 151 HU; finally, 150/152 HU.
  21. Sip phone forwarding; (Poly set to fwd to ext 150;) 152->poly

So far, correct CDR's are generated in all cases. In one case, one more CDR could be generated, but... you'll see. I have some questions about parking, too... you'll see...!

The Scenario Details


Now, for each and every situation, here are the actual CDR's that will get posted, in default cdr-custom format.

Scenario 1. Simple. A calls B. (152->151)


""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 10:55:00","2007-06-11 10:55:08","2007-06-11 10:55:26","26","18","ANSWERED","DOCUMENTATION","","1181580900.6",""
Simple stuff... 26 second duration, 18 billable seconds. Send the bill!

Scenario 2. unattended transfer via # on zap phone


""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-12 06:18:48","2007-06-12 06:18:58","2007-06-12 06:19:39","51","41","ANSWERED","DOCUMENTATION","","1181650728.5",""

""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/50-1","Dial","Zap/50|30|TtWw","2007-06-12 06:19:39","2007-06-12 06:19:52","2007-06-12 06:20:02","23","10","ANSWERED","DOCUMENTATION","","1181650728.5",""

The first CDR is generated is generated when the # is hit. Note that 52 is the originator for both calls, and uniqueid is the same for both calls. It's as if 152 dialed each person individually. Because the dial command allows either end to transfer, you cannot tell if 52 or 51 actually performed the transfer.

Scenario 3. unattended transfer via SIP transfer button


""fxs.52" <152>","152","s","extension","Zap/52-1","SIP/snom360-08258e78","Dial","SIP/snom360|30|TtWw","2007-06-11 11:12:05","2007-06-11 11:12:12","2007-06-11 11:12:34","29","22","ANSWERED","DOCUMENTATION","","1181581925.16",""

""fxs.52" <152>","152","s","extension","Zap/52-1","SIP/polycom430-082689a0","Dial","SIP/polycom430|30|TtWw","2007-06-11 11:12:34","2007-06-11 11:12:44","2007-06-11 11:12:58","24","14","ANSWERED","DOCUMENTATION","","1181581925.16",""

Basically, the same output from SIP as from Zap interfaces. Compare with scen. 2.

Scenario 4. Unattended transfer via zap Hookflash


""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-12 06:41:25","2007-06-12 06:41:30","2007-06-12 06:42:05","40","35","ANSWERED","DOCUMENTATION","","1181652085.9",""

""fxs.52" <152>","152","150","extension","Zap/52-1","Zap/50-1","Dial","Zap/50|30|TtWw","2007-06-12 06:42:01","2007-06-12 06:42:08","2007-06-12 06:42:15","14","7","ANSWERED","DOCUMENTATION","","1181652118.11",""

In this scenario, hookflash is performed on a Zap/ analog line, and then the phone is hung up before the other party answers. The first CDR is generated the moment that 151 hangs up. Note that the second CDR starts before the first ends.

Scenario 5. attended transfer via two zap hookflashes (3-way conf); 151 hangs up first (152->151; 151 HF; 151->150; 150 ANS; 151 HF (3way); 151 HU; 150/152 HU;)


""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-12 06:49:10","2007-06-12 06:49:16","2007-06-12 06:49:49","39","33","ANSWERED","DOCUMENTATION","","1181652550.16",""

""fxs.51" <151>","151","s","extension","Zap/51-1","Zap/50-1","Dial","Zap/50|30|TtWw","2007-06-12 06:49:21","2007-06-12 06:49:26","2007-06-12 06:49:54","33","28","ANSWERED","DOCUMENTATION","","1181652561.18",""

The first CDR is output when 151 breaks the 3-way by hanging up. The second is generated when either of the remaining two hang up. You can tell it's a transfer situation, because 51 is involved in both CDRs, and they overlap.

Scenario 6. Same as (5), but 150 breaks the 3-way, by hanging up first.


""fxs.51" <151>","151","s","extension","Zap/51-1","Zap/50-1","Dial","Zap/50|30|TtWw","2007-06-11 11:27:09","2007-06-11 11:27:15","2007-06-11 11:27:38","29","23","ANSWERED","DOCUMENTATION","","1181582828.28",""

""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 11:26:51","2007-06-11 11:26:58","2007-06-11 11:27:53","62","55","ANSWERED","DOCUMENTATION","","1181582811.26",""

Scenario 7. Same as (5), but 152 breaks the 3-way, by hanging up first.


""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 11:33:13","2007-06-11 11:33:25","2007-06-11 11:34:28","75","63","ANSWERED","DOCUMENTATION","","1181583193.30",""

""fxs.51" <151>","151","s","extension","Zap/51-1","Zap/50-1","Dial","Zap/50|30|TtWw","2007-06-11 11:33:50","2007-06-11 11:33:55","2007-06-11 11:34:49","59","54","ANSWERED","DOCUMENTATION","","1181583230.32",""

Scenario 8. Attended xfer via SIP (Polycom), 201 (polycom) hangs up first (152->201 (201 is the polycom's extension); poly ANS; poly->trnsfr button; poly->200; 200 (snom) ANS; poly->more; poly->Join (3-way); poly HU;  152/200 HU)


""fxs.52" <152>","152","s","extension","Zap/52-1","SIP/polycom430-0825f528","Dial","SIP/polycom430|30|TtWw","2007-06-11 11:41:43","2007-06-11 11:41:53","2007-06-11 11:46:54","311","301","ANSWERED","DOCUMENTATION","","1181583703.36",""

""polycom" <polycom430>","polycom430","200","extension","SIP/polycom430-08269b70","SIP/snom360-0826dae8","Dial","SIP/snom360|30|TtWw","2007-06-11 11:42:42","2007-06-11 11:42:45","2007-06-11 11:47:24","282","279","ANSWERED","DOCUMENTATION","4567aaa","1181583762.38",""

Same as previous; you can tell they are involved with each other, because SIP/polycom430 is involved in two overlapping bridges. But you can't see how long the 3-way lasted.

Scenario 9. Same as (8), but 200 hangs up first.


""polycom" <polycom430>","polycom430","200","extension","SIP/polycom430-082678b8","SIP/snom360-0826b830","Dial","SIP/snom360|30|TtWw","2007-06-11 12:05:45","2007-06-11 12:05:49","2007-06-11 12:06:58","73","69","ANSWERED","DOCUMENTATION","4567aaa","1181585145.49",""

""fxs.52" <152>","152","s","extension","Zap/52-1","SIP/polycom430-08271e50","Dial","SIP/polycom430|30|TtWw","2007-06-11 12:05:20","2007-06-11 12:05:30","2007-06-11 12:07:06","106","96","ANSWERED","DOCUMENTATION","","1181585120.47",""

Scenario 10. Same as (8), but 152 hangs up first.


""fxs.52" <152>","152","s","extension","Zap/52-1","SIP/polycom430-08271e50","Dial","SIP/polycom430|30|TtWw","2007-06-11 12:17:55","2007-06-11 12:18:04","2007-06-11 12:19:13","78","69","ANSWERED","DOCUMENTATION","","1181585875.51",""

""polycom" <polycom430>","polycom430","200","extension","SIP/polycom430-082685e8","SIP/snom360-0826c560","Dial","SIP/snom360|30|TtWw","2007-06-11 12:18:35","2007-06-11 12:18:39","2007-06-11 12:19:44","69","65","ANSWERED","DOCUMENTATION","4567aaa","1181585915.53",""

Scenario 11. Attended xfer via SIP (Snom), 200 (snom) hangs up first (152->200 (snom); 200 ANS; 200->hold button; 200->151; 151 ANS; 200->Conf button;


Unfortunately; this scenario cannot be followed, as the SNOM does not allow the conversation between 151 and 152 to continue after it hangs up!

Scenario 12. Same as (11), but 151 hangs up first;


"snom360","snom360","151","extension","SIP/snom360-08274c18","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 12:54:56","2007-06-11 12:54:59","2007-06-11 12:55:26","30","27","ANSWERED","DOCUMENTATION","4567aaa","1181588096.72",""

""fxs.52" <152>","152","s","extension","Zap/52-1","SIP/snom360-08272240","Dial","SIP/snom360|30|TtWw","2007-06-11 12:54:15","2007-06-11 12:54:20","2007-06-11 12:56:08","113","108","ANSWERED","DOCUMENTATION","","1181588055.70",""

Scenario 13. Same as (11), but 152 hangs up first.


""fxs.52" <152>","152","s","extension","Zap/52-1","SIP/snom360-08272240","Dial","SIP/snom360|30|TtWw","2007-06-11 13:07:16","2007-06-11 13:07:22","2007-06-11 13:09:36","140","134","ANSWERED","DOCUMENTATION","","1181588836.75",""

"snom360","snom360","151","extension","SIP/snom360-08275a80","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 13:07:38","2007-06-11 13:07:40","2007-06-11 13:11:40","242","240","ANSWERED","DOCUMENTATION","4567aaa","1181588858.77",""

Scenario 14. caller dials, gets app

Using the new functions, CDRstart, CDRanswer, and CDRclose, in an extension like this: (AEL)
      5 => {
           Set(mycdr=${CDRstart()});
           NoOp(CDRanswer says: ${CDRanswer(${mycdr})} );
           Set(CDR(lastapp||${mycdr})=Record+BackGround);
           Set(CDR(lastdata||${mycdr})=whoknows);
           Record(recording:wav);
           Background(recording);
           NoOp(CDRclose says: ${CDRclose(${mycdr})} );
      }

I get this CDR:

""fxs.52" ","152","5","extension","Zap/52-1","","Record+BackGround","whoknows","2007-06-13 12:09:08","2007-06-13 12:09:08","2007-06-13 12:09:43","35","35","ANSWERED","DOCUMENTATION","","1181758146.0",""

ForkCDR is insufficient for this purpose; it relies on hangup events to generate the CDR, which is no longer the case.

Scenario 15. caller dials, gets AGI


No CDR is generated in this scenario; I will have to provide some dialplan apps to cause the CDR to be generated. (See scenario 14).

Scenario 16. Caller dials, gets DISA, calls another number

""fxs.52" <152>","152","s","extension","Zap/52-1","","DISA","2211|extension","2007-06-11 14:59:50","2007-06-11 14:59:53","2007-06-11 15:00:02","12","9","ANSWERED","DOCUMENTATION","","1181595590.3",""

""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 15:00:02","2007-06-11 15:00:09","2007-06-11 15:00:16","14","7","ANSWERED","DOCUMENTATION","","1181595590.3",""

Both incoming and outgoing calls are recorded!

Scenario 17. Parking. (152->151; 151 parks 152; 150 picks up parked caller)

""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 13:20:35","2007-06-11 13:20:47","2007-06-11 13:21:09","34","22","ANSWERED","DOCUMENTATION","","1181589635.79",""

""fxs.50" <150>","150","s","extension","Zap/50-1","Zap/52-1","ParkedCall","701","2007-06-11 13:21:19","2007-06-11 13:21:30","2007-06-11 13:21:43","24","13","ANSWERED","DOCUMENTATION","","1181589679.81",""

I have a problem with this! It turns out that some of the time that that 52 spends listening to MOH will not be recorded in CDRs, and may not possible to detect. The time from the end of CDR1 to the start of CDR includes a 10-second gap during which Zap/52 was listening to MOH. This is one of the "open problems" I have in my notebook on this project.

Scenario 18. Parking. (152->151; 151 parks 152; park times out, rings 151. 151 answers.


""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 13:33:17","2007-06-11 13:33:28","2007-06-11 13:33:50","33","22","ANSWERED","DOCUMENTATION","","1181590397.82",""

""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51||t","2007-06-11 13:33:50","2007-06-11 13:34:40","2007-06-11 13:34:49","59","9","ANSWERED","DOCUMENTATION","","1181590397.82",""

This scenario makes me happy, because CDR1 + CDR2 == total time that Zap/52 is connected.

Scenario 19. Parking. Same as (18), but 151 doesn't answer after the park     times out. (Asterisk will ring back to 151 forever; after some     time 152 hangs up).


""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 14:14:10","2007-06-11 14:14:19","2007-06-11 14:14:33","23","14","ANSWERED","DOCUMENTATION","","1181592850.85",""

In this situation, the 5 minutes that 152 sat, waiting for 151 to pick up the park, before he gave up and hung up, is not recorded in a CDR, because no bridge was formed, and no conversation took place...

Scenario 20. The CEL scenario: 152->151; 152 parks the call (dials #700); 152 HU;     150->701 (picks up the parked call); 151 HF; 151->152; 152 ANS;     151 HF (3way); 151 HU; finally, 150/152 HU.


""fxs.52" <152>","152","s","extension","Zap/52-1","Zap/51-1","Dial","Zap/51|30|TtWw","2007-06-11 17:03:02","2007-06-11 17:03:10","2007-06-11 17:03:35","33","25","ANSWERED","DOCUMENTATION","","1181602982.0",""

""fxs.50" <150>","150","s","extension","Zap/50-1","Zap/51-1","ParkedCall","701","2007-06-11 17:03:55","2007-06-11 17:03:59","2007-06-11 17:04:56","61","57","ANSWERED","DOCUMENTATION","","1181603035.2",""

""fxs.51" <151>","151","s","extension","Zap/51-1","Zap/52-1","Dial","Zap/52|30|TtWw","2007-06-11 17:04:12","2007-06-11 17:04:22","2007-06-11 17:05:14","62","52","ANSWERED","DOCUMENTATION","","1181603052.3",""

Scenario 21. Sip phone forwarding; (Poly set to fwd to ext 150;) 152 dials poly


&"quot;,"","150","extension","Local/150@extension-5288,2","Zap/50-1","Dial","Zap/50|30|TtWw","2007-06-14 10:52:27","2007-06-14 10:52:34","2007-06-14 10:52:35","8","1","ANSWERED","DOCUMENTATION","","1181839947.8",""

""fxs.52"","152","s","extension","Zap/52-1","Local/150@extension-5288,1","Dial","SIP/polycom430|30|TtWw","2007-06-14 10:52:24"",""2007-06-14 10:52:34","2007-06-14 10:52:46"",""22","12","ANSWERED","DOCUMENTATION","","1181839944.5",""

The Local/ channel spec is the hint that ties these two cdrs together. The second CDR has all the info you need to bill SIP/poly for the call out to 151.

The trouble with the ForkCDR() Application

My guess is that the ForkCDR() application (along with NoCDR, and friends, where added in response to the need to be able to generate CDRs via control of the dialplan. The design was adequate for the situation, and quite clever: It provided a mechanism to allow several overlapping CDRs. To do this, it formed the CDR structure into a linked list, and would duplicate the last CDR in the list, and append the new copy to the end of the list. The original would be marked as LOCKED. If more than one ForkCDR were called, the list would grow, all entries being marked "LOCKED", accept the last one. All routines that modified CDR's walked the list, and only did their operation on entries that were not locked.

My main concern with ForkCDR is that it did not really give full control of the CDR process to the dialplan. It still relied on the internal mechanisms to close and post CDRs. Indeed, I plan to fully remove ForkCDR, because it will no longer work as intended in the new environment. It just doesn't fit any more. Sorry. Instead I plan to provide 3 new dialplan functions to allow the dialplan to generate its own CDRs, in addition to those that would get generated anyway. They will be: CDRstart(), CDRanswer(handle), and CDRclose(handle). CDRStart will create a CDR structure in memory, set most of its values from the current channel, set the start time in this CDR to the current time, and return a "handle" to that CDR. CDRanswer(handle) will set the "handle" CDR's answer time in the current channel to the current time. CDRclose will set the "handle" CDR's end time to the current time, and then post and detach it. None of these functions will touch the current channel's CDR in any way.

Thus, we get the same general functionality of ForkCDR, but a bit more power: You can individually create and precisely control the times reported for each of the 3 times in the CDR, and you get to determine when they get posted.

So, if you want to bill for the time that an app is used by an incoming dialer, you can:(in AEL)
      Set(mycdrhandle=${CDRstart()});
      set(nothing=${CDRanswer(${mycdrhandle})});
      SomeApplication(option1, option2);
      set(nothing=${CDRend(${mycdrhandle})}); /* this will cause the CDR to be posted */

Hmmm, it might be nice to be able to set lastapp in that CDR, among other things...!

FYI

Please feel free to experiment with: my CDRfix5 branch [1].