Notes on Signaling Interface

22 February 1999

(http://www.cs.cornell.edu/cnrg/telephony/JavaDocs/notes.19feb99.html)


19 Feb 1999


Notes from Friday, Feb. 19 meeting

  1. It is OK to import cnrg.itx.signal into the application, rather than having a signaling daemon running on each of our machines. But keep it small and fast.
  2. Applications will extend AbstractSignalObserver or implement SignalObserver. There will be a few event classes, such as SignalEvent and InviteEvent.
  3. Applications will instantiate a DesktopSignaling object, passing themselves as a parameter. DesktopSignaling will use this parameter to invoke the application's SignalObserver methods.
  4. Connection objects will contain a sequence number, on a per-application basis.
  5. Methods supported by the DesktopSignaling object include:
        Connection Dial(String)       - default call is blocking
        Connection Dial(String,int)   - int timeout in seconds
        void Hangup(Connection)   
        Accept(PropertiesColleciton)  - invoked by app's SignalObserver
        Reject(String)                -  " (String could be reason for reject)
        void sendDTMF(String)         - application requested DTMF digits be sent
    

    Regarding the first parameter to the dial() method: should it be String or UserID? The main thing that UserID does is to make a local copy of the string. String is easier for the application to specify.

  6. All DTMF tones will be detected by the Gateway and sent as signaling event to the appropriate DesktopSignaling interface. If an application wants to request a certain number of DTMF digits, this can be implemented at a higher level than the Gateway/SignalServer.

Sequence of Actions for Placing a Call

This section describes the sequence of events for Dial (in phase 1 is no timeout). Application 1, Desktop Signaling 1, etc. are running on the first computer and Application 2, etc. are running on the second computer.

APPLICATION 1:

Instantiate a DesktopSignaling object. Invoke Dial(String) on your DesktopSignaling object. Example: Connection co = sig.Dial("susie") When Dial returns, if co is NULL, the invitation failed; otherwise the data connection is complete and ready to use.

APPLICATION 2:

Instantiate a DesktopSignaling object. Application's Signal Observer gets an InviteEvent, containing the properties of the data connection of the caller (e.g. abilities, port it will be sending data out on, port it will be taking data in on). The Signal Observer's onInvite() method decides whether to accept or reject. If accept, call the Accept method of Application 2's DesktopSignaling object, passing it the PropertiesCollection object that is contained in the InviteEvent. Otherwise call the Reject method of Application 2's DesktopSignaling object.

DESKTOP SIGNALING 1:

Its Dial method has been invoked. First instantiate a Connection object with all the resources required in it. If you can't do this, return NULL. Otherwise extract the PropertiesCollection and serialize it into an invite SignalPacket.

Next we have to know where DESKTOP SIGNALING 2 is. Construct a UserID from the String parameter passed to dial(). Use Directory Services to look up the UserID; this returns a LocationList. Use the first Location. This contains the IP address where susie is currently working. Use Directory Services to look up the list of "signalingsrv" peers (a LocationList). Find out which one UserID susie is using; now we know the port where susie's DesktopSignaling is currently listening. [ THIS CAN'T BE QUITE RIGHT ].

Send the Invite packet to DESKTOP SIGNALING 2, which is currently waiting on its port. Wait for a packet to come back.

       [ FUTURE: IMPLEMENT TIMEOUT HERE ]

When the next packet comes in, see if it is for the same invite (using the Connection sequence number), and see if it is an accept or reject.

If it is a reject, return a null Connection object. (FUTURE: also return a null Connection object if timeout.) If it is an accept, send a "start" SignalPacket to DESKTOP SIGNALING 2. Then fill in your Connection object with the properties sent back in the accept packet. You now have a complete Connection object; return it to the application. [ Do you also invoke an open method on your Connection object? ]

DESKTOP SIGNALING 2:

At instantiation time, register yourself with Directory Services as UserID "signalingsrv" listening on port such-and-such at IP Address such and such on behalf of application such-and-such. [ Or is it on behalf of "susie"? ]

When the invite SignalPacket comes in, unpack it. Observing that this is an invite, construct a new InviteEvent e and call your SignalObserver's onInvite() routine with that event. The event should contain the information that was included in the packet, such as the source's ConnectionProperties object.

If onInvite calls your Reject() method, send a reject SignalPacket back to DESKTOP SIGNALING 1.

If your Accept() method is invoked, this means that the SignalObserver is accepting the call. Create a complete Connection object, incorporating the properties sent by DESKTOP 1 and the final format decided on, and input/output data ports numbers for net connections.

Serialize the final properties object into an accept packet and send it back to DESKTOP SIGNALING 1.

Go back to waiting on your port. When a START SignalPacket arrives, retrieve the appropriate SigConnection (based on sequence ID) and start the data connection.

Go back to waiting on your port.

DATA EXCHANGE 1:

Connection constructor is called by a DesktopSignaling object when its Dial() routine is invoked. Increment your sequence number and stick it in the object, along with your input channel and output channel. The default constructor is mike->net, net->speaker.

DATA EXCHANGE 2:

Connection constructor is called by a DesktopSignaling object when its Accept() routine is invoked. See DATA EXCHANGE 1 for other constructor stuff.