The initial porting effort was done on UIQ SDK 3.0 and it worked. More over PJ doesn't use any features provided outside Symbian OS standard features, so it should work with any Symbian OS SDK (with the exception of audio device abstraction, which currently uses Audio Streaming API/plug-in and this may not be available on certain Symbian SDK).

Note that Carbide VS is not supported for building the libraries because it has a very annoying "bug" that rejects directory name with dash ('-') character (we tested this in early 2007, so the situation may have changed now).

Porting Status

As of December 2007, the Symbian porting effort is mostly complete. The port has been tested both on S60 3rd Edition emulator and device.

Screenshot of symbian_ua application on S60 emulator

2. Getting and Building the Source

Getting the Source

The Symbian port is available in the SVN trunk. Please see the download page for more info.

Build Requirements and Preparations

Requirements

SDK Installation

Install the SDK in the same drive as PJ's source tree (Symbian build system does not support placing SDK and source files on separate drives), and install it on drive:\Symbian directory (for example "C:\Symbian"), because this is the path configured in PJ's batch files.

Prepare Your config_site.h

Some recommended settings for Symbian are provided in config_site_sample.h, you should include this file in your config_site.h file:

#include <pj/config_site_sample.h>

Building Simple Symbian SIP User Agent Application

The Symbian specific build files are located in build.symbian directory under PJ's source tree. The project files are organized as .mmp files, with each library and executable having its own .mmp file. As usual, these .mmp files are put in bld.inf file.

Running/Debugging the Test or Sample Programs

Emulator Configuration

Currently most test or sample programs are built as Symbian console programs. To enable console mode in the emulator:

Open $(SDK)\epoc32\data\epoc.ini file

add TextShell line at the end of the file. With this settings, the emulator will now start in Console mode (and it will start faster too!).

Enlarge the console screen size, by changing ScreenWidth to 640, and ScreenHeight to 480.

Running Applications with Emulator

To run the application, just run \Symbian\$(SDK)\epoc32\release\winscw\udeb\symbian_ua.exe file.

Note however that debugging is not possible with this approach.

Running Applications with Visual Studio

Open the VS6 project workspace generated by Symbian abld command (invoked by 00.BAT and 01.BAT) for the application, and build the project. All libraries that the application depends (such as PJLIB, PJLIB-UTIL, PJSIP, etc.) must have been built before the application can be built.

After the application has been built, you can run the application in the Emulator by pressing Debug button from Visual Studio. This will launch the Emulator and you should see the output of PJLIB's log in the Emulator.

With this IDE configuration, you can also set breakpoints, watches, and perform step-by-step debugging as usual.

Note that there are some known issues with some applications that you may want to know before running the applications.

Running Applications with CodeWarrior

Change the executable for the debug session by:

selecting Edit ==> WINSCW UDEB Settings from the menu,

select Target ==> Runtime Settings from the tree,

browse symbian_ua.exe executable from the right pane.

Debug the project by selecting Project ==> Debug from the menu.

Note that there are some known issues with some applications that you may want to know before running the applications.

Running/Debugging Applications with Carbide C++

3. Design Changes

The Symbian port requires the following design changes to be made on all libraries and applications. Please see Porting Details section below on the detailed explanation of these.

Threading is Not Supported

Multithreading is not supported. Applications MUST run on a single thread only.

No Polling

Polling is not necessary for Symbian, since ioqueue and timer heap are implemented as Active Objects. Application just needs to call CActiveScheduler::WaitForAnyRequest() to poll the whole application (not only PJ) in a single place.

Sources are Compiled as C++ Sources

Because PJLIB's PJ_TRY/PJ_CATCH framework is implemented with C++'s try/catch construct, all sources which include <pjlib.h> or <pjlib/except.h> MUST be compiled as C++ source. Practically, now all libraries are compiled as C++ sources.

Constant global variables are evil!

For Symbian 9 (S60 3rd Edition included), the libraries may be built as .DSO (Dynamic Shared Object, Symbian term for Dynamic Link Library/DLL). This is controlled by the #ifdef macro in the .MMP files.

With .DSO, Symbian does not really want us to use (const) global variables. If we try to access these variables (that are exported from a .DSO) from application, we will get garbage values since the variables are not initialized properly.

Unfortunately, the libraries do have some (const) global variables. Things like PJ_VERSION, PJ_AF_INET, PJ_SOCK_STREAM, they're all const global variables. So Symbian application can no longer access these const global variables anymore. Instead, it must access these variable through their accessor functions (for example, pj_get_version(), pj_AF_INET(), pj_SOCK_STREAM(), etc.)

4. Porting Details

PJLIB

PJLIB has been implemented and tested successfully (it has passed pjlib-test testing). The following components have been re-implemented specificly for SymbianOS:

PjSymbianOS Singleton Class

File: src/os_symbian.h, src/os_core_symbian.cpp

The PjSymbianOS class is a singleton class to keep global objects such as RSocketServ and RHostResolver instances, and is initialized in pj_init() function. This class also provides other utility functions such as Unicode conversion and socket address conversion between Symbian's TInetAddr and PJLIB's pj_sockaddr_in.

Threading, Synchronization Objects, and Thread Local Storage (TLS)

File: src/os_core_symbian.cpp

For this initial porting effort, it was decided that threading will not be supported, which means that pj_thread_create() will always return failure. Synchronization object APIs (such as mutex and semaphores) are implemented as dummy functions which always return success. Thread local storage (or thread specific data) and atomic variables are supported with emulation, since these functionalities are needed by upper layer libraries.

The decision to disable thread support was made to enable building the libraries as static libraries. If PJLIB has to support Symbian threads, it has to use the Dll namespace, which can only be used by DLL targets.

In the future, it is possible that PJLIB will provide "virtual" threading (cooperative multitasking) by using Active Objects, however it is unlikely that this will be implemented in this first release.

This is one of the major pain in the porting effort. Symbian OS does not support setjmp()/longjmp() very well, and there is no equivalent exception handling framework for the C language (many articles wrongly say that Symbian's Leave/Trap mechanism is equivalent to setjmp()/longjmp(), but clearly it's not!).

The only working solution now seems to map PJ_TRY/PJ_CATCH into C++'s try/catch mechanism. Not all PJLIB's exception constructs are supported, however. For this release, PJ_CATCH macro is not supported (only PJ_CATCH_ANY is supported).

As the implication of this, all sources have been patched to compile cleanly as C++ programs.

Socket Address Byte Ordering

File: src/sock_symbian.cpp

IP address and port in pj_sockaddr_in are stored in network byte order, consistent with other PJLIB targets. These values will be converted to host byte order when PJLIB needs to call Symbian native socket API (and vice versa).

The pj_sock_t socket descriptor is mapped to CPjSocket class (declared in src/os_symbian.h file), which is a class to wrap Symbian's RSocket. The CPjSocket class abstraction is needed because pj_sock_t needs to work with PJLIB's select() API, which Symbian doesn't provide.

Note that there is 1500 bytes compile time limitation on the receive buffer when the socket is used with pj_select(). This limitation only affects the socket when it's used with pj_select(); there is no such limitation when the socket is used directly (with pj_sock_recv() or pj_sock_recvfrom()) or when IOQueue is used.

Note that it's not recommended to mix pj_select() call with IOqueue call, since when pj_ioqueue_recv()/pj_ioqueue_recvfrom() is called, the flag and socket internal buffer used by select() will be cleared, and this will cause packet already received to be dropped..

IOQueue

File: src/ioqueue_symbian.cpp

The PJLIB's IOQueue is reimplemented for Symbian OS. Currently only socket receive and socket accept are implemented asynchronously (non-blocking); socket connect() and socket sending operations are done synchronously (blocking).

PJSDP

PJSUA-LIB

There are couple of places in pjsua_core.c where pjsip_endpt_handle_events() call is replaced by pj_thread_sleep(), since there is no polling for Symbian.

5. Known Issues

Application Notes

Memory Leaks

No memory leak is reported as of now. However, in addition to performing the general shutdown procedure, application must also call CloseSTDLIB() to ensure that STDLIB resources are properly released since PJLIB does use STDLIB.

For sample code to properly shutdown the application, please see pjsua_destroy() implementation in pjsua_core.c file (part of PJSUA-LIB).

Symbian Open Source SIP and media stack porting is a work in progress.
Please send your feedback to pjsippjsip.org mailing-list or to the author:
(C)2003-2007 Benny Prijono <bennylppjsip.org>