Android softphone: SIP Regsitration

This article is a detailed guide to Android softphone SIP Regsitration. Here you can learn how to add reference to Ozeki VoIP SIP SDK, in Visual Studio, and how to create an Android application softphone, which is able to register to a PBX, specified by the user's input. The aim of this tutorial is to help you complete this task as easily and efficiently as possible. Now let's get right into it!

How the system works

andorid sip softphone
Figure 1 - How to perform SIP registration with an android application

What is a softphone?

A softphone is a type of software-based phone. It allows you to make phone calls over an internet connection without needing designated physical hardware, and it can be installed on desktops and mobile devices. In short, softphones help you make telephone calls without an actual telephone.

What is SIP registration?

SIP Registration is the process of binding an endpoint's AOR with its location. The SIP Endpoint sends a SIP REGISTER request to a Registrar, containing its AOR, location, authentication and other information in the message. You may use this feature by enabling the SIP Registration option in your SIP Domain.

What is Ozeki VoIP SIP SDK?

Ozeki VoIP SIP SDK is an excellent software development kit that allows you to establish VoIP calls from your application easily and quickly. You do not need to have expert programming skills, with the most basic programming knowledge you will be able to create extraordinary VoIP solutions with this tool. After download you just integrate Ozeki VoIP SIP SDK into your application and it is ready to make VoIP calls immediately. Ozeki VoIP SDK has extended audio features to ensure high voice quality for your VoIP SIP phone calls.

Download android-voip-sip-registration.zip

The source code explained in this article can be downloaded and used and modified free of charge.
Download: android-voip-sip-registration.zip (135Kb)

MainActivity.cs

using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Views;
using AndroidX.AppCompat.Widget;
using AndroidX.AppCompat.App;
using Android.Widget;
using Android.Text.Method;

namespace SIPRegistration
{

    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
    public class MainActivity : AppCompatActivity
    {
        private static SoftPhone _mySoftphone;

        bool _registrationRequired;

        string _displayName;
        string _userName;
        string _authenticationID;
        string _registerPassword;
        string _domainHost;
        string _domainPort;

        TextView _logs;
        Button _btnRegister;

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            SetContentView(Resource.Layout.activity_main);

            _registrationRequired = true;

            _logs = FindViewById<TextView>(Resource.Id.logs);
            _logs.MovementMethod = new ScrollingMovementMethod();

            _btnRegister = FindViewById<AppCompatButton>(Resource.Id.btnRegister);
            _btnRegister.Click += BtnRegisterOnClick__Event;

            _logs.Text = _logs.Text + "This is a simple Ozeki VoIP SIP SDK demo written in C#.\n";
            _logs.Text = _logs.Text + "It can be used to register to a PBX by using SIP account.\n";
        }

        private void BtnRegisterOnClick__Event(object sender, EventArgs e)
        {
            _displayName = FindViewById<AppCompatEditText>(Resource.Id.displayName).Text;
            _userName = FindViewById<AppCompatEditText>(Resource.Id.userName).Text;
            _authenticationID = FindViewById<AppCompatEditText>(Resource.Id.authenticationID).Text;
            _registerPassword = FindViewById<AppCompatEditText>(Resource.Id.registerPassword).Text;
            _domainHost = FindViewById<AppCompatEditText>(Resource.Id.domainHost).Text;
            _domainPort = FindViewById<AppCompatEditText>(Resource.Id.domainPort).Text;

            if (_displayName != "" && _userName != "" && _authenticationID != "" && _registerPassword != ""
                && _domainHost != "" && _domainPort != "")
            {
                _mySoftphone = new SoftPhone();
                _mySoftphone.PhoneLineStateChanged += mySoftphone_PhoneLineStateChanged;
                _mySoftphone.Register(_registrationRequired, _displayName, _userName, _authenticationID,
                    _registerPassword, _domainHost, Int32.Parse(_domainPort));
            }
            else
            {
                _logs.Text = _logs.Text + "Please enter your credentials!\n";
            }
        }

        private void mySoftphone_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
        {
            if (e.State == RegState.RegistrationSucceeded)
            {
                _logs.Text = _logs.Text + "Registration succeeded - Online!\n";
            }
            else if (e.State == RegState.NotRegistered)
            {
                _logs.Text = _logs.Text + "Not registered.\n";
            }
            else if (e.State == RegState.Error)
            {
                _logs.Text = _logs.Text + "Registration Error!\n";
            }
        }

        public override bool OnCreateOptionsMenu(IMenu menu)
        {
            MenuInflater.Inflate(Resource.Menu.menu_main, menu);
            return true;
        }

        public override bool OnOptionsItemSelected(IMenuItem item)
        {
            int id = item.ItemId;
            if (id == Resource.Id.action_settings)
            {
                return true;
            }

            return base.OnOptionsItemSelected(item);
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
	}
}

	

Code 1 - MainActivity.cs

From this tutorial example you can learn the following:

  • How to add reference to Ozeki VoIP SIP SDK, in Visual Studio
  • How to create an Android application softphone, which is able to:
  • Register to a PBX, specified by the user's input

Related: VoIP SIP Regsitration. If also you wish to build VoIP softphone on Windows, you might also be interested in a similar document, the SIP account registration in Ozeki VoIP SDK for Windows

How to build your solution

If you want to create your own application to reproduce this solution, You have to create a new Android application in Visual Studio. In the following video we will show you how to create a single view Xamarin Application using Visual Studio Community.

Video 1 - How to create a new Xamarin Android application

How to add reference to Ozeki VoIP SIP SDK

To build our application for SIP registration, we will need a .dll file called OzekiSDK.dll. In the following video you will find how to make a reference to the OzekiSDK.dll file. You would like to use the tools, provided by the Ozeki VoIP SIP SDK, You have to add a reference to it in the Solution Explorer. To do this, You need to locate the "References" folder in your Solution Explorer, and right click on the References folder, then Choose "Add Reference." option.

Video 2 - How to add reference to the OzekiSDK.dll

How to add reference

First, right click on your project and a menu will appear (Figure 2). From this menu, click on Add. Then, click on Reference... This will forward you to another window where you are able to browse for references.

add reference
Figure 2 - Right click on the "References" folder, than choose the "Add Reference..." option

How to select reference

Select the "Browse" tab, then click the "Browse" button (Figure 3). If you've installed the Ozeki VoIP SIP SDK to the default installation directory, You can find the needed OzekiSDK.dll file at the path similar to this: C:\Program Files\Ozeki\Ozeki SDK for Android\SDK\MonoAndroid Select the .dll file, and click the "ADD" button If You followed these steps, You can find a new line at the end of the References folder: "VoIPSDK". This line indicates, that You can use the tools, provided by the SDK.

add reference to a visual studio project
Figure 3 - Under the Reference Manager's Browse tab, we need to browse for the "OzekiSDK.dll" file at the installation path.

How to use the source code

In this video I'm going to show you how to create your first android application for SIP registration using the source codes of this page.
To create this application, you will need the source code of MainActivity.cs, SoftPhone.cs, content_main.xml and strings.xml.

Creating the layout for our Android application

In order to have a user interface, we have to modify the main_content.xml file. In the following video I'll show you how to copy, and where to paste the xml source code which is available below.

main_content.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:showIn="@layout/activity_main">

    <TextView
        android:layout_width="300dp"
        android:layout_marginLeft="40dp"
        android:layout_marginTop="20dp"
        android:layout_height="wrap_content"
        android:text="@string/sip_registration"
        android:layout_centerHorizontal="true"
        android:textSize="10pt"/>

    <GridLayout
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="60dp"
        android:layout_centerHorizontal="true"
        android:columnCount="3"
        android:rowCount="1">

    <EditText
        android:id="@+id/displayName"
        android:layout_width="140dp"
        android:layout_height="wrap_content"
        android:hint="@string/displayed_name"
        android:inputType="text"
        android:layout_column="0"
        android:textAlignment="center"
        android:layout_alignParentLeft="true"/>

    <TextView
        android:layout_width="20dp"
        android:layout_height="wrap_content"
        android:layout_column="1"/>

    <EditText
        android:id="@+id/userName"
        android:layout_width="140dp"
        android:layout_height="wrap_content"
        android:hint="@string/user_name"
        android:textAlignment="center"
        android:inputType="text"
        android:layout_column="2"/>

    </GridLayout>

    <EditText
        android:id="@+id/authenticationID"
        android:layout_marginTop="120dp"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:hint="@string/authentication_id"
        android:layout_centerHorizontal="true"
        android:inputType="text" />

    <EditText
        android:id="@+id/registerPassword"
        android:layout_marginTop="180dp"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:hint="@string/register_password"
        android:layout_centerHorizontal="true"
        android:inputType="textPassword" />

    <EditText
        android:id="@+id/domainHost"
        android:layout_marginTop="240dp"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:hint="@string/domain_host"
        android:layout_centerHorizontal="true"
        android:inputType="text" />

    <EditText
        android:id="@+id/domainPort"
        android:layout_height="wrap_content"
        android:layout_width="300dp"
        android:layout_marginTop="290dp"
        android:hint="@string/domain_port"
        android:layout_centerHorizontal="true"
        android:inputType="text" />

    <Button
        android:id="@+id/btnRegister"
        android:layout_height="wrap_content"
        android:layout_width="180dp"
        android:layout_marginTop="350dp"
        android:layout_centerHorizontal="true"
        android:backgroundTint="#FF0000"
        android:textColor="#FFF"
        android:text= "@string/btn_register"/>

    <TextView
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="410dp"
        android:layout_centerHorizontal="true"
        android:text="@string/log_text"/>

    <TextView
        android:id="@+id/logs"
        android:layout_width="300dp"
        android:orientation="vertical"
        android:layout_height="100dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="430dp"
        android:scrollbars = "vertical"/>

</RelativeLayout>
	

Code 2 - content_main.xml which contains the layout of our application

In a xamarin project, the hard coded text has to be in a different location, what you can find in the follwoing location Resources/values/strings.xml.
To require the created text in the strings.xml file, we have to make a reference for it. For example if the stirngs.xml file has a string like this <string name="required_text">Required text</string> , you make a reference for it like this: "@string/required_text"

strings.xml

<resources>
    <string name="app_name">SIPRegistration</string>
    <string name="action_settings">Settings</string>
    <string name="sip_registration">SIP Registration</string>
    <string name="displayed_name">Displayed name</string>
    <string name="user_name">Username</string>
    <string name="authentication_id">Authentication ID</string>
    <string name="register_password">Password</string>
    <string name="domain_host">Host e.g.: 127.0.0.1</string>
    <string name="domain_port">Port e.g.: 5060</string>
    <string name="btn_register">Register</string>
    <string name="log_text">Log:</string>
</resources>
	

Code 3 - strings.xml which contains the texts that are going to be displayed in our application

How to create the layout of the application

The following video demonstrates how to create the layout of the application. In this tutorial you will find where to implement the xml code in your application. Follow along this short tutorial to successfully implement the pieces of code found above.

Video 3 - How to create the layout of the application

How to add functionality to your application

This tutorial shows you how to add functionality to your application. Already we have the layout of the page, now we can start to write some C# code to give it some functionality. In the following example I'll show you how to turn this layout into a fully functioning application.

Video 4 - Writing the SoftPhone class

How to add functionality to the layout

This video tutorial showcases how to add functionality to the layout. We are going to write pieces of code in C# that integrate actual functions into the layout. With these steps done, we can now run and use the example application with Ozeki Phone System.

Video 5 - How to add functionality to the layout

How to use the example application

This video tutorial explains how to use the example application previously created. First we have to open up Ozeki Phone System and create a new phone extension. Then, we can run the example application and successfully complet an SIP registration on a mobile device.

Video 6 - How to use the example application

SIP registration form with a success message

Here in this screenshot taken from a mobile device, you can see that the SIP registration has been completed successfully (Figure 4).

sip registration success message
Figure 4 - SIP registration form with a success message

Source code of the example, written in C#

To separate the softphone from the user interface - from the android application, this time -, the best idea is to use two separate classes in this example:

  • Softphone.cs:

    The softphone's implementation goes here, all of it's events, methods, functions, variables.

  • MainActivity.cs:

    Our class with the OnCreate() method, this class controls the the backend of the main view, interacts with the user, by using a softphone object.

Source code analysis

To understand the source code, the softphone's functions and their usage, we need to discuss the details. In the "using section" we need to add some extra lines, like:

using Ozeki.VoIP;
	

Code 4 - How to import the Ozeki.VoIP to you project

Without these lines we would have to use the namespace information as a label for all tools of the SDK.

What is Softphone.cs used for?

This class is used to introduce how to declare, define and initialize a softphone, how to handle some of the Ozeki VoIP SIP SDK's events and how to use some of that's functions. In other words, we would like to create a "telephone software", which has the same functions (or much more), as an ordinary mobile (or any other) phone. In the MainActivity.cs class we will use this class to create a new softphone, so we can use the functions, we can listen to the events placed here.

SoftPhone.cs

using Ozeki.VoIP;
using System;

namespace SIPRegistration
{
    class SoftPhone
    {
        ISoftPhone _softphone;
        IPhoneLine _phoneLine;

        public event EventHandler<RegistrationStateChangedArgs> PhoneLineStateChanged;

        public SoftPhone()
        {
            _softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);
        }

        public void Register(bool registrationRequired, string displayName, string userName, string authenticationId, string registerPassword, string domainHost, int domainPort)
        {
            try
            {
                var account = new SIPAccount(registrationRequired, displayName, userName, authenticationId, registerPassword, domainHost, domainPort);
                Console.WriteLine("\nCreating SIP account {0}", account);

                _phoneLine = _softphone.CreatePhoneLine(account);
                Console.WriteLine("Phoneline created.");
                _phoneLine.RegistrationStateChanged += phoneLine_PhoneLineStateChanged;

                _softphone.RegisterPhoneLine(_phoneLine);

            }
            catch (Exception ex)
            {
                Console.WriteLine("Error during SIP registration: " + ex.ToString());
            }
        }

        private void phoneLine_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
        {
            var handler = PhoneLineStateChanged;
            if (handler != null)
                handler(this, e);
        }
    }
}
	

Code 5 - Softphone.cs

What objects does the softphone class use?

We need to create a softphone and a phone line object from the ISoftPhone and the IPhoneLine interfaces:

	ISoftPhone softphone;
	IPhoneLine phoneLine;
	

Code 6 - How to use the ISoftPhone and IPhoneLine interfaces

In a constructor, we also initialize this softphone with default parameters.

softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);
	

Code 7 - How to initialize a softphone object

We need to set the port range, indicated by the first two parameters as the minimum port's and the maximum port's number, this is the port's interval. If we have any firewall rule which restricts the usable ports, we can set the usable port range here, which will be used during the calls. These are sample ports only in the example, but a softphone with these parameters can be used in the most of the cases. Please note that, if You are handling conference calls, or handling a lot of lines simultaneously, You will need a wide port range.

How to register to a PBX?

To be able to communicate, we need to register our softphone to a PBX. To do this, the example uses the Register method. We need to create a phone line for this registration, which needs a SIP account and a NAT Traversal method.

SIP account:

At the SIP account's creation we can set (usually, we have to set) the followings:

  • RegistrationRequired: is the registration required or not? This field needs to be set to "true", if we would like to receive incoming calls.
  • DisplayName: a name to be displayed at the called client.
  • UserName: if an other client dials this name (number), we are getting called.
  • AuthenticationId: an identifier to the PBX, like a login name.
  • RegisterPassword: the password to register to the PBX. Works in pair with the authentication ID.
  • DomainHost: a domain name, an IP address, for example.
  • DomainPort: Port number.

The last two values are defining the PBX to try to register to.

		var account = SIPAccount(registrationRequired, displayName, userName, 
authenticationId, registerPassword, domainHost, domainPort);

Code 8 - How to register a SIP account

We will get the values for these parameters from the user, in the MainActivity.cs file.

How to set up a PhoneLine object?

To communicate (and to register to the PBX) we need to create a phone line. We've already created the necessary SIP account so with this we can do the creation:

phoneLine = softphone.CreatePhoneLine(account);
	

Code 9 - How to create a phone using the SIP account

When the application is running, the phone line's state can change. To follow these changes, we need to listen to this change event:

	phoneLine.RegistrationStateChanged += phoneLine_PhoneLineStateChanged;
	

Code 10 - How to create an event handler for a phoneline

When the phone line has been created, we have to call the RegisterPhoneLine() method to register the phone line to the softphone. If the registration is set to required, this method will also send the SIP REGISTER command. We just need to use the following line:

softphone.RegisterPhoneLine(phoneLine);
	

Code 11 - How to register a phoneline

What events are should be handled by the softphone?

In this example, we are using only one; the phone line's event. The phone line can be in several states, for example:

  • Error: occurs, when registration is needed, but ouldn't be made. The phone is unable to communicate through the PBX or other error occurs.
  • NotRegistered: when not registered to the PBX, or already unregistered
  • NoRegNeeded: there is no communication with the server, until the first (made) call. If we've set any invalid information at the SIP account's creation, we can't even make that call. Since our softphone is not registered to the server, we can't receive any calls, even with valid SIP account.
  • RegistrationSucceeded: occurs, when registration is needed, and succeeded. The phone is able to receive and make calls.

There are more states as well, but we will use only this four in this example. The using of these sates will be introduced at the MainActivity.cs chapter.

What is MainActivity.cs used for?

This class will introduce the usage of a softphone object, handles the events, interacts with the user, and uses the opportunities provided by the Softphone class. In this example, you can use an interface to type in the creadentials to connect to a PBX server.

First of all, we must create a softphone object.

static Softphone mySoftphone;
	

Code 12 - How to create a Softphone object

We are handling everything with separated methods. These methods are communicating with each other, and making the source code more understandable and reusable.

How to initialize the softphone?

The first step is to initialize the softphone, we have just created. To do this, we need to call an initializer method with the following lines:

		mySoftphone = new Softphone();
		mySoftphone.PhoneLineStateChanged += mySoftphone_PhoneLineStateChanged;
	

Code 13 - How to add an event handler to a softphone

Call this method in the OnCreate() method. Now, there is a new softphone available to use, and it's already following the phone line's states. We will see how to handle the states below, after we are done with the welcoming and the registration process.

How to handle the phone line states?

We are subscribed to get notified if the phone line's state has been changed. At the mySoftphone_PhoneLineStateChanged method we can set what to do for each state, if we would like to. In this example we are handling four states: If the registration was unsuccessful, we are asking for user inputs (we are calling the correct method for that purpose), again. States for this case:

RegState.Error
	

or

RegState.NotRegistered
	

The "RegistrationFailed" state occurs, when the registration is simply unsuccessful, for example, if the user set up invalid SIP account to register. The "RegistrationTimedOut" state occurs, if the registration request takes too long (for example: in this case, we can programmatically command to try again until it succeeds, or we can ask for a new SIP account creation.)

If the registration has succeeded or there is no need for registration, we are notifying the user about it. Please note that, if there is no need for registration, but we could not reach the PBX (or we could, but with invalid SIP account), we won't notice that until we make our first call. The states to use:

RegState.RegistrationSucceeded
RegState.NoRegNeeded
	

In the next examples, we will work with our softphone if the registration was successful, but in this example, it's enough to notify the user about the succeeded connection.

Summary

After reading this documentation and studying the source code, we must be familiar about how to add reference to the Ozeki VoIP SIP SDK, how to create a Softphone class which provides a softphone object to the user with the input setups. We have a basic, single view android application which is interacting with the user and handling our softphone with the provided opportunities.

The next example will introduce how to:

  • Handle the call's events
  • Accept a call
  • Make a call

There are a lot of more available media handlers, not just only microphones and speakers. To learn more about them, you can continue your learning at the next chapter:

You can also learn how to send Instant Messages, by continuing to develop the "SIP Registration" example:

Related Pages