Home Linphone-sdk(liblinphone) unable to register and make a test call in Android
Reply: 0

Linphone-sdk(liblinphone) unable to register and make a test call in Android

Jouke
1#
Jouke Published in 2018-02-14 10:05:48Z

For a project I need to implement VoIP calling. After long considering of many libraries I endid with liblinphone(linphone-sdk).

Unfortunately the documentation of linphone isn't that great that's why I decided to make a quick test application using their tutorial files. The implementation I used and the files used are added below.

I really hope someone is able to answer this because I see a lot of similair questions unanswered on stackoverflow and linphone forums. This would really help to improve the documentation of linphone-sdk in general.

Setup:

  • A FreePBX VoIP server with Asterisk
  • Already configured extensions to try and test the calling
  • Calling already tested. The used ip's work when I call with the official linphone app from my phone to my computer.

Problem:

  • Registering with linphone-sdk to my VoIP server is not registered when looking at the logs of /var/log/asterisk. If I register with an app it does recognize it's connecting.
  • Not able to make a test call although the provided details are correct and tested with released VoIP applications. (I/O error)
  • Logs can be found below

Question:

  • What is the correct way to register and call with linphone-sdk?

Test view

MainActivity.java (Handles the buttons, creates threads for calling and registering based of the tutorial files provided by linphone-sdk)

package com.test.voip;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import org.linphone.core.LinphoneCoreException;
import org.linphone.core.tutorials.TutorialHelloWorld;
import org.linphone.mediastream.Log;
import org.w3c.dom.Text;

public class MainActivity extends AppCompatActivity{
    private TutorialRegistration registrationTest;
    private TutorialHelloWorld callingTest;
    private TextView sipPassword;
    private TextView sipAddress;
    private TextView callAddress;
    private Button buttonRegistration;
    private Button buttonStopRegistration;
    private Button buttonStopCall;
    private Button buttonCall;
    private Handler mHandler =  new Handler() ;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //Run layout
        super.onCreate(savedInstanceState);
        setContentView(R.layout.register);

        sipAddress = (TextView) findViewById(R.id.Address);
        sipPassword = (TextView) findViewById(R.id.Password);
        callAddress = (TextView) findViewById(R.id.CallAddress);

        System.out.println("siplog: " + sipAddress.getText().toString() + sipPassword.getText().toString());
        System.out.println("siplog: call adress= " + callAddress.getText().toString());


        //Create registration & calling object
        registrationTest = new TutorialRegistration();
        callingTest = new TutorialHelloWorld();

        // Get buttons from view
        buttonRegistration = (Button) findViewById(R.id.RegisterButton);
        buttonStopRegistration = (Button) findViewById(R.id.StopRegistrationButton);
        buttonCall = (Button) findViewById(R.id.CallButton);
        buttonStopCall = (Button) findViewById(R.id.StopCallButton);

        buttonRegistration.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                RegistrationThread thread = new RegistrationThread();
                thread.start();
            }
        });
        buttonStopRegistration.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                registrationTest.stopMainLoop();
            }
        });
        buttonCall.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                CallingThread callingThread = new CallingThread();
                callingThread.start();
            }
        });
        buttonStopCall.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                callingTest.stopMainLoop();
            }
        });
    }

    private class RegistrationThread extends Thread {
        @Override
        public void run() {
            super.run();

            try {
                // takes sip uri identity from the command line arguments
                String userSipAddress = sipAddress.getText().toString();
                // takes password from the command line arguments
                String userSipPassword = sipPassword.getText().toString();
                registrationTest.launchTutorial(userSipAddress, userSipPassword);
                System.out.println("siplog: try launching tut registration");
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
    private class CallingThread extends Thread {
        @Override
        public void run() {
            super.run();
            try {
                callingTest.launchTutorial(callAddress.getText().toString());
            } catch (LinphoneCoreException e) {
                e.printStackTrace();
            }
        }
    }
}

Registration.java

/*
TutorialRegistration.java
Copyright (C) 2010  Belledonne Communications SARL

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.test.voip;

import java.nio.ByteBuffer;

import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCallStats;
import org.linphone.core.LinphoneChatMessage;
import org.linphone.core.LinphoneChatRoom;
import org.linphone.core.LinphoneContent;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.LinphoneCore.GlobalState;
import org.linphone.core.LinphoneCore.LogCollectionUploadState;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.core.LinphoneCore.RemoteProvisioningState;
import org.linphone.core.LinphoneAuthInfo;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListener;
import org.linphone.core.LinphoneEvent;
import org.linphone.core.LinphoneFriend;
import org.linphone.core.LinphoneFriendList;
import org.linphone.core.LinphoneInfoMessage;
import org.linphone.core.LinphoneProxyConfig;
import org.linphone.core.PublishState;
import org.linphone.core.SubscriptionState;


/**
 * This program is a _very_ simple usage example of liblinphone.
 * Demonstrating how to initiate a SIP registration from a sip uri identity
 * passed from the command line.
 *
 * First argument must be like sip:jehan@sip.linphone.org, second must be password.
 * <br>
 * ex registration sip:jehan@sip.linphone.org secret
 *
 * Ported from registration.c
 *
 * @author Guillaume Beraudo
 *
 */
public class TutorialRegistration implements LinphoneCoreListener {
    private boolean running;
    private TutorialNotifier TutorialNotifier;


    public TutorialRegistration(TutorialNotifier TutorialNotifier) {
        this.TutorialNotifier = TutorialNotifier;
    }

    public TutorialRegistration() {
        this.TutorialNotifier = new TutorialNotifier();
    }


    /*
     * Registration state notification listener
     */
    public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) {
        write(cfg.getIdentity() + " : "+smessage);
    }

    public void show(LinphoneCore lc) {}
    public void byeReceived(LinphoneCore lc, String from) {}
    public void authInfoRequested(LinphoneCore lc, String realm, String username, String domain) {}
    public void authenticationRequested(LinphoneCore lc, LinphoneAuthInfo authInfo, LinphoneCore.AuthMethod method) {}
    public void displayStatus(LinphoneCore lc, String message) {}
    public void displayMessage(LinphoneCore lc, String message) {}
    public void displayWarning(LinphoneCore lc, String message) {}
    public void globalState(LinphoneCore lc, GlobalState state, String message) {}
    public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {}
    public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {}
    public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg) {}
    public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {}
    public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {}
    public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {}
    public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){}
    public void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf) {}

//    public static void main(String[] args) {
//        // Check tutorial was called with the right number of arguments
//        if (args.length != 2) {
//            throw new IllegalArgumentException("Bad number of arguments");
//        }
//
//        // Create tutorial object
//        TutorialRegistration tutorial = new TutorialRegistration();
//        try {
//            // takes sip uri identity from the command line arguments
//            String userSipAddress = args[1];
//            // takes password from the command line arguments
//            String userSipPassword = args[2];
//            tutorial.launchTutorial(userSipAddress, userSipPassword);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }



    public void launchTutorial(String sipAddress, String password) throws LinphoneCoreException {
        final LinphoneCoreFactory lcFactory = LinphoneCoreFactory.instance();

        // First instantiate the core Linphone object given only a listener.
        // The listener will react to events in Linphone core.
        LinphoneCore lc = lcFactory.createLinphoneCore(this, null);

        System.out.println("siplog: created instance");


        try {

            System.out.println("siplog: try creating linphone adress");
            // Parse identity
            LinphoneAddress address = lcFactory.createLinphoneAddress(sipAddress);
            System.out.println("siplog: linphone address created");
            String username = address.getUserName();
            String domain = address.getDomain();

            System.out.println("siplog: username = " + address.getUserName());
            System.out.println("siplog: domain = " + address.getDomain());

            if (password != null) {
                // create authentication structure from identity and add to linphone
                lc.addAuthInfo(lcFactory.createAuthInfo(username, password, null, domain));
            }

            // create proxy config
            LinphoneProxyConfig proxyCfg = lc.createProxyConfig(sipAddress, domain, null, true);
            proxyCfg.setExpires(2000);
            lc.addProxyConfig(proxyCfg); // add it to linphone
            lc.setDefaultProxyConfig(proxyCfg);



            // main loop for receiving notifications and doing background linphonecore work
            running = true;
            while (running) {
                lc.iterate(); // first iterate initiates registration
                sleep(50);
            }


            // Unregister
            lc.getDefaultProxyConfig().edit();
            lc.getDefaultProxyConfig().enableRegister(false);
            lc.getDefaultProxyConfig().done();
            while(lc.getDefaultProxyConfig().getState() != RegistrationState.RegistrationCleared) {
                lc.iterate();
                sleep(50);
            }

            // Then register again
            lc.getDefaultProxyConfig().edit();
            lc.getDefaultProxyConfig().enableRegister(true);
            lc.getDefaultProxyConfig().done();
            System.out.println("siplog: registered again");

            while(lc.getDefaultProxyConfig().getState() != RegistrationState.RegistrationOk
                    && lc.getDefaultProxyConfig().getState() != RegistrationState.RegistrationFailed) {
                System.out.println("registration has failed");
                lc.iterate();
                sleep(50);
            }

            // Automatic unregistration on exit
        } finally {
            System.out.println("siplog: linphone exit");
            write("Shutting down linphone...");
            // You need to destroy the LinphoneCore object when no longer used
            lc.destroy();
        }
    }

    private void sleep(int ms) {
        try {
            Thread.sleep(ms);
        } catch(InterruptedException ie) {
            write("Interrupted!\nAborting");
            return;
        }
    }

    public void stopMainLoop() {
        System.out.println("siplog: stopped mainloop for registration");
        running=false;
    }


    private void write(String s) {
        TutorialNotifier.notify(s);
    }

    @Override
    public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr,
                                LinphoneChatMessage message) {
        // TODO Auto-generated method stub

    }

    @Override
    public void messageReceivedUnableToDecrypted(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) {

    }

    @Override
    public void transferState(LinphoneCore lc, LinphoneCall call,
                              State new_call_state) {
        // TODO Auto-generated method stub

    }

    @Override
    public void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info) {
        // TODO Auto-generated method stub

    }

    @Override
    public void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev,
                                         SubscriptionState state) {
        // TODO Auto-generated method stub

    }

    @Override
    public void notifyReceived(LinphoneCore lc, LinphoneEvent ev,
                               String eventName, LinphoneContent content) {
        // TODO Auto-generated method stub

    }

    @Override
    public void publishStateChanged(LinphoneCore lc, LinphoneEvent ev,
                                    PublishState state) {
        // TODO Auto-generated method stub

    }

    @Override
    public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr) {
        // TODO Auto-generated method stub

    }

    @Override
    public void configuringStatus(LinphoneCore lc,
                                  RemoteProvisioningState state, String message) {
        // TODO Auto-generated method stub

    }

    @Override
    public void fileTransferProgressIndication(LinphoneCore lc,
                                               LinphoneChatMessage message, LinphoneContent content, int progress) {
        // TODO Auto-generated method stub

    }

    @Override
    public void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message,
                                 LinphoneContent content, byte[] buffer, int size) {
        // TODO Auto-generated method stub

    }

    @Override
    public int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message,
                                LinphoneContent content, ByteBuffer buffer, int size) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void uploadProgressIndication(LinphoneCore lc, int offset, int total) {
        // TODO Auto-generated method stub

    }

    @Override
    public void uploadStateChanged(LinphoneCore lc,
                                   LogCollectionUploadState state, String info) {
        // TODO Auto-generated method stub

    }


    @Override
    public void friendListCreated(LinphoneCore lc, LinphoneFriendList list) {
        // TODO Auto-generated method stub

    }

    @Override
    public void friendListRemoved(LinphoneCore lc, LinphoneFriendList list) {
        // TODO Auto-generated method stub

    }

    @Override
    public void networkReachableChanged(LinphoneCore lc, boolean enable) {

    }

}

Calling.java

/*
TutorialHelloWorld.java
Copyright (C) 2010  Belledonne Communications SARL

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.test.voip;

import java.nio.ByteBuffer;

import org.linphone.core.LinphoneAddress;
import org.linphone.core.LinphoneCall;
import org.linphone.core.LinphoneCall.State;
import org.linphone.core.LinphoneCallStats;
import org.linphone.core.LinphoneChatMessage;
import org.linphone.core.LinphoneChatRoom;
import org.linphone.core.LinphoneContent;
import org.linphone.core.LinphoneCore;
import org.linphone.core.LinphoneCore.EcCalibratorStatus;
import org.linphone.core.LinphoneCore.GlobalState;
import org.linphone.core.LinphoneCore.LogCollectionUploadState;
import org.linphone.core.LinphoneCore.RegistrationState;
import org.linphone.core.LinphoneCore.RemoteProvisioningState;
import org.linphone.core.LinphoneAuthInfo;
import org.linphone.core.LinphoneCoreException;
import org.linphone.core.LinphoneCoreFactory;
import org.linphone.core.LinphoneCoreListener;
import org.linphone.core.LinphoneEvent;
import org.linphone.core.LinphoneFriend;
import org.linphone.core.LinphoneFriendList;
import org.linphone.core.LinphoneInfoMessage;
import org.linphone.core.LinphoneProxyConfig;
import org.linphone.core.PublishState;
import org.linphone.core.SubscriptionState;
import org.linphone.core.tutorials.TutorialNotifier;


/**
 * This program is a _very_ simple usage example of liblinphone.
 * It just takes a sip-uri as first argument and attempts to call it.
 *
 * Ported from helloworld.c
 *
 * @author Guillaume Beraudo
 *
 */
public class Calling implements LinphoneCoreListener {
    private boolean running;
    private org.linphone.core.tutorials.TutorialNotifier TutorialNotifier;


    public Calling(TutorialNotifier TutorialNotifier) {
        this.TutorialNotifier = TutorialNotifier;
    }

    public Calling() {
        this.TutorialNotifier = new TutorialNotifier();
    }



    public void show(LinphoneCore lc) {}
    public void byeReceived(LinphoneCore lc, String from) {}
    public void authInfoRequested(LinphoneCore lc, String realm, String username, String domain) {}
    public void authenticationRequested(LinphoneCore lc, LinphoneAuthInfo authInfo, LinphoneCore.AuthMethod method) {}
    public void displayStatus(LinphoneCore lc, String message) {}
    public void displayMessage(LinphoneCore lc, String message) {}
    public void displayWarning(LinphoneCore lc, String message) {}
    public void globalState(LinphoneCore lc, GlobalState state, String message) {}
    public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg,RegistrationState cstate, String smessage) {}
    public void newSubscriptionRequest(LinphoneCore lc, LinphoneFriend lf,String url) {}
    public void notifyPresenceReceived(LinphoneCore lc, LinphoneFriend lf) {}
    public void textReceived(LinphoneCore lc, LinphoneChatRoom cr,LinphoneAddress from, String message) {}
    public void callStatsUpdated(LinphoneCore lc, LinphoneCall call, LinphoneCallStats stats) {}
    public void ecCalibrationStatus(LinphoneCore lc, EcCalibratorStatus status,int delay_ms, Object data) {}
    public void callEncryptionChanged(LinphoneCore lc, LinphoneCall call,boolean encrypted, String authenticationToken) {}
    public void notifyReceived(LinphoneCore lc, LinphoneCall call, LinphoneAddress from, byte[] event){}
    public void dtmfReceived(LinphoneCore lc, LinphoneCall call, int dtmf) {}
    /*
     * Call state notification listener
     */
    public void callState(LinphoneCore lc, LinphoneCall call, State cstate, String msg){
        write("State: " + msg);

        if (State.CallEnd.equals(cstate))
            running = false;
    }

    public void launchTutorial(String destinationSipAddress) throws LinphoneCoreException {

        // First instantiate the core Linphone object given only a listener.
        // The listener will react to events in Linphone core.
        LinphoneCore lc = LinphoneCoreFactory.instance().createLinphoneCore(this, null);



        try {
            // Send the INVITE message to destination SIP address
            LinphoneCall call = lc.invite(destinationSipAddress);
            if (call == null) {
                write("Could not place call to " + destinationSipAddress);
                write("Aborting");
                return;
            }
            write("Call to " + destinationSipAddress + " is in progress...");



            // main loop for receiving notifications and doing background linphonecore work
            running = true;
            while (running) {
                lc.iterate();
                try{
                    Thread.sleep(50);
                } catch(InterruptedException ie) {
                    write("Interrupted!\nAborting");
                    return;
                }
            }



            if (!State.CallEnd.equals(call.getState())) {
                write("Terminating the call");
                lc.terminateCall(call);
            }
        } finally {
            write("Shutting down...");
            // You need to destroy the LinphoneCore object when no longer used
            lc.destroy();
            write("Exited");
        }
    }


    public void stopMainLoop() {
        running=false;
    }


    private void write(String s) {
        TutorialNotifier.notify(s);
    }

    @Override
    public void messageReceived(LinphoneCore lc, LinphoneChatRoom cr,
            LinphoneChatMessage message) {
        // TODO Auto-generated method stub

    }

    @Override
    public void messageReceivedUnableToDecrypted(LinphoneCore lc, LinphoneChatRoom cr, LinphoneChatMessage message) {

    }

    @Override
    public void transferState(LinphoneCore lc, LinphoneCall call,
            State new_call_state) {
        // TODO Auto-generated method stub

    }

    @Override
    public void infoReceived(LinphoneCore lc, LinphoneCall call, LinphoneInfoMessage info) {
        // TODO Auto-generated method stub

    }

    @Override
    public void subscriptionStateChanged(LinphoneCore lc, LinphoneEvent ev,
            SubscriptionState state) {
        // TODO Auto-generated method stub

    }

    @Override
    public void notifyReceived(LinphoneCore lc, LinphoneEvent ev,
            String eventName, LinphoneContent content) {
        // TODO Auto-generated method stub

    }

    @Override
    public void publishStateChanged(LinphoneCore lc, LinphoneEvent ev,
            PublishState state) {
        // TODO Auto-generated method stub

    }

    @Override
    public void isComposingReceived(LinphoneCore lc, LinphoneChatRoom cr) {
        // TODO Auto-generated method stub

    }

    @Override
    public void configuringStatus(LinphoneCore lc,
            RemoteProvisioningState state, String message) {
        // TODO Auto-generated method stub

    }

    @Override
    public void fileTransferProgressIndication(LinphoneCore lc,
            LinphoneChatMessage message, LinphoneContent content, int progress) {
        // TODO Auto-generated method stub

    }

    @Override
    public void fileTransferRecv(LinphoneCore lc, LinphoneChatMessage message,
            LinphoneContent content, byte[] buffer, int size) {
        // TODO Auto-generated method stub

    }

    @Override
    public int fileTransferSend(LinphoneCore lc, LinphoneChatMessage message,
            LinphoneContent content, ByteBuffer buffer, int size) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void uploadProgressIndication(LinphoneCore lc, int offset, int total) {
        // TODO Auto-generated method stub

    }

    @Override
    public void uploadStateChanged(LinphoneCore lc,
            LogCollectionUploadState state, String info) {
        // TODO Auto-generated method stub

    }


        @Override
        public void friendListCreated(LinphoneCore lc, LinphoneFriendList list) {
                // TODO Auto-generated method stub

        }

        @Override
        public void friendListRemoved(LinphoneCore lc, LinphoneFriendList list) {
                // TODO Auto-generated method stub

        }

    @Override
    public void networkReachableChanged(LinphoneCore lc, boolean enable) {

    }

}

Log for registering (after clicking 'register' button)

I/System.out: siplog: created instance
I/System.out: siplog: try creating linphone adress
I/System.out: siplog: linphone address created
I/System.out: siplog: username = 1000
I/System.out: siplog: domain = 192.168.2.131

Log for calling (after clicking 'call' button)

I/System.out: State: Starting outgoing call
I/System.out: State: Outgoing call in progress
I/System.out: Call to sip:2000@192.168.2.131:5160 is in progress...
I/System.out: State: IO error
I/System.out: State: Call released
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.298035 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO