This post will help you use JAIN SIP in your android application. Before proceeding we need to clarify the need and background for this.
Why do this?
- Limitations of native sip stack.
The stock native android sip stack is built on top of JAIN SIP but it does not have all functionality, there is no support for IM, presence and video calls right now. Google forked SIP Stack and never really updated the SIP API that is shipped by default and used the same package names as the original JAIN SIP project. This proved to be a major hassle to developers that wanted to add SIP capabilities worldwide as they couldn’t use JAIN SIP out of the box.
- JAIN SIP is Java based
An opensource implementation of the stack can be found here. It is a java based SIP stack which allows you to integrate the sip stack seamlessly in your android application without the hassle of NDK.
- A Complete SIP Stack
JAIN SIP is a full implementation of the RFC 3261 Specification and as well as support for several SIP RFCs.
What is the issue using the existing JAIN SIP stack?
If you have tried to use the existing JAIN SIP in any android application you might have run into package name conflicts, these conflicts are resolved now and the stack is ready to be used in android.
How to use JAIN SIP in your android app
- Setup a new android project
- First step is to download the jar from here.
- Include the jar in your project.
Registering a SIP stack in your application
Next we are going to implement this flow and register a sip stack with a server waiting for sip requests.
Step -1: Initialize the Stack
Lets start by creating a singleton class SipStackAndroid and initialize a SIP stack. This class is going to implement SipListener and corresponding methods to process SIP events.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
publicclassSipStackAndroidimplementsSipListener{
privatestaticSipStackAndroid instance=null;
publicstaticSipStack sipStack;
publicstaticSipProvider sipProvider;
publicstaticHeaderFactory headerFactory;
publicstaticAddressFactory addressFactory;
publicstaticMessageFactory messageFactory;
publicstaticSipFactory sipFactory;
publicstaticListeningPoint udpListeningPoint;
publicstaticStringlocalIp;
publicstaticintlocalPort=5080;
publicstaticStringlocalEndpoint=localIp+“:”+localPort;
publicstaticStringtransport=“udp”;
publicstaticStringremoteIp=“23.23.228.238”;
publicstaticintremotePort=5080;
publicstaticStringremoteEndpoint=remoteIp+“:”+remotePort;
publicstaticStringsipUserName;
publicStringsipPassword;
protectedSipStackAndroid(){
initialize();
}
publicstaticSipStackAndroid getInstance(){
if(instance==null){
instance=newSipStackAndroid();
}
returninstance;
}
privatestaticvoidinitialize(){
localIp=getIPAddress(true);
localEndpoint=localIp+“:”+localPort;
remoteEndpoint=remoteIp+“:”+remotePort;
sipStack=null;
sipFactory=SipFactory.getInstance();
sipFactory.setPathName(“gov.nist.android”);
Properties properties=newProperties();
properties.setProperty(“javaxx.sip.OUTBOUND_PROXY”,remoteEndpoint+“/”
+transport);
properties.setProperty(“javaxx.sip.STACK_NAME”,“androidSip”);
try{
// Create SipStack object
sipStack=sipFactory.createSipStack(properties);
System.out.println(“createSipStack “+sipStack);
}catch(PeerUnavailableExceptione){
e.printStackTrace();
System.err.println(e.getMessage());
System.exit(0);
}
try{
headerFactory=sipFactory.createHeaderFactory();
addressFactory=sipFactory.createAddressFactory();
messageFactory=sipFactory.createMessageFactory();
udpListeningPoint=sipStack.createListeningPoint(localIp,
localPort,transport);
sipProvider=sipStack.createSipProvider(udpListeningPoint);
sipProvider.addSipListener(SipStackAndroid.getInstance());
// this.send_register();
}catch(PeerUnavailableExceptione){
e.printStackTrace();
System.err.println(e.getMessage());
System.exit(0);
}catch(Exceptione){
System.out.println(“Creating Listener Points”);
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
|
Step -2: Send REGISTER
The following method sends a Register packet to server.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
privatevoidsend_register(){
try{
System.out.println();
SipStackAndroid.getInstance();
AddressFactory addressFactory=SipStackAndroid.addressFactory;
SipStackAndroid.getInstance();
SipProvider sipProvider=SipStackAndroid.sipProvider;
SipStackAndroid.getInstance();
MessageFactory messageFactory=SipStackAndroid.messageFactory;
SipStackAndroid.getInstance();
HeaderFactory headerFactory=SipStackAndroid.headerFactory;
// Create addresses and via header for the request
Address fromAddress=addressFactory.createAddress(“sip:”
+SipStackAndroid.sipUserName+“@”+SipStackAndroid.remoteIp);
fromAddress.setDisplayName(SipStackAndroid.sipUserName);
Address toAddress=addressFactory.createAddress(“sip:”
+SipStackAndroid.sipUserName+“@”+SipStackAndroid.remoteIp);
toAddress.setDisplayName(SipStackAndroid.sipUserName);
Address contactAddress=createContactAddress();
ArrayList<ViaHeader>viaHeaders=createViaHeader();
URI requestURI=addressFactory.createAddress(
“sip:”+SipStackAndroid.remoteEndpoint).getURI();
// Build the request
finalRequest request=messageFactory.createRequest(requestURI,
Request.REGISTER,sipProvider.getNewCallId(),
headerFactory.createCSeqHeader(1l,Request.REGISTER),
headerFactory.createFromHeader(fromAddress,“c3ff411e”),
headerFactory.createToHeader(toAddress,null),viaHeaders,
headerFactory.createMaxForwardsHeader(70));
// Add the contact header
request.addHeader(headerFactory.createContactHeader(contactAddress));
ExpiresHeader eh=headerFactory.createExpiresHeader(300);
request.addHeader(eh);
// Print the request
System.out.println(request.toString());
// Send the request — triggers an IOException
// sipProvider.sendRequest(request);
ClientTransaction transaction=sipProvider
.getNewClientTransaction(request);
// Send the request statefully, through the client transaction.
transaction.sendRequest();
}catch(Exceptione){
e.printStackTrace();
}
}
privateAddress createContactAddress(){
try{
SipStackAndroid.getInstance();
returnSipStackAndroid.addressFactory.createAddress(“sip:”+SipStackAndroid.sipUserName+“@”
+SipStackAndroid.localEndpoint+“;transport=udp”
+“;registering_acc=23_23_228_238”);
}catch(ParseExceptione){
returnnull;
}
}
privateArrayList<ViaHeader>createViaHeader(){
ArrayList<ViaHeader>viaHeaders=newArrayList<ViaHeader>();
ViaHeader myViaHeader;
try{
SipStackAndroid.getInstance();
myViaHeader=SipStackAndroid.headerFactory.createViaHeader(SipStackAndroid.localIp,SipStackAndroid.localPort,
SipStackAndroid.transport,null);
myViaHeader.setRPort();
viaHeaders.add(myViaHeader);
}catch(ParseExceptione){
e.printStackTrace();
}catch(InvalidArgumentExceptione){
e.printStackTrace();
}
returnviaHeaders;
}
|
Step -3: Send authentication information
In response to the REGISTER packet, server asks for authentication information(407 error). Which will be received in processResponse() method of the class which implements SipListener. The code snippet below sends back authentication to server.
Helper classes like AccountManagerImpl can be found in JAIN-SIP examples.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Override
publicvoidprocessResponse(ResponseEvent arg0){
Response response=(Response)arg0.getResponse();
ClientTransaction tid=arg0.getClientTransaction();
System.out.println(response.getStatusCode());
if(response.getStatusCode()==Response.PROXY_AUTHENTICATION_REQUIRED
||response.getStatusCode()==Response.UNAUTHORIZED){
AuthenticationHelper authenticationHelper=((SipStackExt)sipStack)
.getAuthenticationHelper(newAccountManagerImpl(),
headerFactory);
try{
ClientTransaction inviteTid=authenticationHelper
.handleChallenge(response,tid,sipProvider,5);
inviteTid.sendRequest();
}catch(NullPointerExceptione){
e.printStackTrace();
}catch(SipExceptione){
e.printStackTrace();
}
}
|
Sending a SIP Message
Next up we use the stack to send a message to a peer. Here is the flow which we have to follow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
privatevoidsendMessage(Stringto,Stringmessage)throwsParseException,
InvalidArgumentException,SipException{
SipStackAndroid.getInstance();
SipURI from=SipStackAndroid.addressFactory.createSipURI(SipStackAndroid.getInstance().sipUserName,SipStackAndroid.getInstance().localEndpoint);
SipStackAndroid.getInstance();
Address fromNameAddress=SipStackAndroid.addressFactory.createAddress(from);
SipStackAndroid.getInstance();
// fromNameAddress.setDisplayName(sipUsername);
FromHeader fromHeader=SipStackAndroid.headerFactory.createFromHeader(fromNameAddress,
“Tzt0ZEP92”);
SipStackAndroid.getInstance();
URI toAddress=SipStackAndroid.addressFactory.createURI(to);
SipStackAndroid.getInstance();
Address toNameAddress=SipStackAndroid.addressFactory.createAddress(toAddress);
SipStackAndroid.getInstance();
// toNameAddress.setDisplayName(username);
ToHeader toHeader=SipStackAndroid.headerFactory.createToHeader(toNameAddress,null);
SipStackAndroid.getInstance();
URI requestURI=SipStackAndroid.addressFactory.createURI(to);
// requestURI.setTransportParam(“udp”);
ArrayList<ViaHeader>viaHeaders=createViaHeader();
SipStackAndroid.getInstance();
CallIdHeader callIdHeader=SipStackAndroid.sipProvider.getNewCallId();
SipStackAndroid.getInstance();
CSeqHeader cSeqHeader=SipStackAndroid.headerFactory.createCSeqHeader(50l,
Request.MESSAGE);
SipStackAndroid.getInstance();
MaxForwardsHeader maxForwards=SipStackAndroid.headerFactory
.createMaxForwardsHeader(70);
SipStackAndroid.getInstance();
Request request=SipStackAndroid.messageFactory.createRequest(requestURI,
Request.MESSAGE,callIdHeader,cSeqHeader,fromHeader,
toHeader,viaHeaders,maxForwards);
SipStackAndroid.getInstance();
SupportedHeader supportedHeader=SipStackAndroid.headerFactory
.createSupportedHeader(“replaces, outbound”);
request.addHeader(supportedHeader);
SipStackAndroid.getInstance();
SipURI routeUri=SipStackAndroid.addressFactory.createSipURI(null,SipStackAndroid.getInstance().remoteIp);
routeUri.setTransportParam(SipStackAndroid.transport);
routeUri.setLrParam();
routeUri.setPort(SipStackAndroid.remotePort);
SipStackAndroid.getInstance();
Address routeAddress=SipStackAndroid.addressFactory.createAddress(routeUri);
SipStackAndroid.getInstance();
RouteHeader route=SipStackAndroid.headerFactory.createRouteHeader(routeAddress);
request.addHeader(route);
SipStackAndroid.getInstance();
ContentTypeHeader contentTypeHeader=SipStackAndroid.headerFactory
.createContentTypeHeader(“text”,“plain”);
request.setContent(message,contentTypeHeader);
System.out.println(request);
SipStackAndroid.getInstance();
ClientTransaction transaction=SipStackAndroid.sipProvider
.getNewClientTransaction(request);
// Send the request statefully, through the client transaction.
transaction.sendRequest();
}
|
This will send a message to the server and server is going to ask for authorization which we have already covered.
A basic android sip messenger
A basic sip messenger using JAIN sip is hosted on github restcomm-android-sdk project. You can easily test it out by downloading the apk . An interesting way to test is by using the telestax’s hosted RestComm WebRTC demo .
Register as bob as described below :
Next run the android application and click on register
Now you can send and receive messages to and from android/webrtc page
Resources
What’s Next
Voice and video calling are going to be added in the example application. Sending sip messages is the humble beginning towards a SDK for Android developers which will simplify VOIP communication in applications.
The end goal is to have both and Android SIP Stack that allows native mobile application developers to tinker with low level SIP Code as well as a higher level SDK which allow Android Mobile Application Developers to integrate Communications features into their applications without knowing anything about telecommunications.
Happy coding!