* #27232: jni: added pjproject checkout as regular git content

We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/pjlib/src/pj/activesock.c b/jni/pjproject-android/pjlib/src/pj/activesock.c
new file mode 100644
index 0000000..b9004ef
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/activesock.c
@@ -0,0 +1,952 @@
+/* $Id: activesock.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/activesock.h>
+#include <pj/compat/socket.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/sock.h>
+#include <pj/string.h>
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+#   include <CFNetwork/CFNetwork.h>
+
+    static pj_bool_t ios_bg_support = PJ_TRUE;
+#endif
+
+#define PJ_ACTIVESOCK_MAX_LOOP	    50
+
+
+enum read_type
+{
+    TYPE_NONE,
+    TYPE_RECV,
+    TYPE_RECV_FROM
+};
+
+enum shutdown_dir
+{
+    SHUT_NONE = 0,
+    SHUT_RX = 1,
+    SHUT_TX = 2
+};
+
+struct read_op
+{
+    pj_ioqueue_op_key_t	 op_key;
+    pj_uint8_t		*pkt;
+    unsigned		 max_size;
+    pj_size_t		 size;
+    pj_sockaddr		 src_addr;
+    int			 src_addr_len;
+};
+
+struct accept_op
+{
+    pj_ioqueue_op_key_t	 op_key;
+    pj_sock_t		 new_sock;
+    pj_sockaddr		 rem_addr;
+    int			 rem_addr_len;
+};
+
+struct send_data
+{
+    pj_uint8_t		*data;
+    pj_ssize_t		 len;
+    pj_ssize_t		 sent;
+    unsigned		 flags;
+};
+
+struct pj_activesock_t
+{
+    pj_ioqueue_key_t	*key;
+    pj_bool_t		 stream_oriented;
+    pj_bool_t		 whole_data;
+    pj_ioqueue_t	*ioqueue;
+    void		*user_data;
+    unsigned		 async_count;
+    unsigned	 	 shutdown;
+    unsigned		 max_loop;
+    pj_activesock_cb	 cb;
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+    int			 bg_setting;
+    pj_sock_t		 sock;
+    CFReadStreamRef	 readStream;
+#endif
+    
+    unsigned		 err_counter;
+    pj_status_t		 last_err;
+
+    struct send_data	 send_data;
+
+    struct read_op	*read_op;
+    pj_uint32_t		 read_flags;
+    enum read_type	 read_type;
+
+    struct accept_op	*accept_op;
+};
+
+
+static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, 
+				     pj_ioqueue_op_key_t *op_key, 
+				     pj_ssize_t bytes_read);
+static void ioqueue_on_write_complete(pj_ioqueue_key_t *key, 
+				      pj_ioqueue_op_key_t *op_key,
+				      pj_ssize_t bytes_sent);
+#if PJ_HAS_TCP
+static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, 
+				       pj_ioqueue_op_key_t *op_key,
+				       pj_sock_t sock, 
+				       pj_status_t status);
+static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, 
+					pj_status_t status);
+#endif
+
+PJ_DEF(void) pj_activesock_cfg_default(pj_activesock_cfg *cfg)
+{
+    pj_bzero(cfg, sizeof(*cfg));
+    cfg->async_cnt = 1;
+    cfg->concurrency = -1;
+    cfg->whole_data = PJ_TRUE;
+}
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+static void activesock_destroy_iphone_os_stream(pj_activesock_t *asock)
+{
+    if (asock->readStream) {
+	CFReadStreamClose(asock->readStream);
+	CFRelease(asock->readStream);
+	asock->readStream = NULL;
+    }
+}
+
+static void activesock_create_iphone_os_stream(pj_activesock_t *asock)
+{
+    if (ios_bg_support && asock->bg_setting && asock->stream_oriented) {
+	activesock_destroy_iphone_os_stream(asock);
+
+	CFStreamCreatePairWithSocket(kCFAllocatorDefault, asock->sock,
+				     &asock->readStream, NULL);
+
+	if (!asock->readStream ||
+	    CFReadStreamSetProperty(asock->readStream,
+				    kCFStreamNetworkServiceType,
+				    kCFStreamNetworkServiceTypeVoIP)
+	    != TRUE ||
+	    CFReadStreamOpen(asock->readStream) != TRUE)
+	{
+	    PJ_LOG(2,("", "Failed to configure TCP transport for VoIP "
+		      "usage. Background mode will not be supported."));
+	    
+	    activesock_destroy_iphone_os_stream(asock);
+	}
+    }
+}
+
+
+PJ_DEF(void) pj_activesock_set_iphone_os_bg(pj_activesock_t *asock,
+					    int val)
+{
+    asock->bg_setting = val;
+    if (asock->bg_setting)
+	activesock_create_iphone_os_stream(asock);
+    else
+	activesock_destroy_iphone_os_stream(asock);
+}
+
+PJ_DEF(void) pj_activesock_enable_iphone_os_bg(pj_bool_t val)
+{
+    ios_bg_support = val;
+}
+#endif
+
+PJ_DEF(pj_status_t) pj_activesock_create( pj_pool_t *pool,
+					  pj_sock_t sock,
+					  int sock_type,
+					  const pj_activesock_cfg *opt,
+					  pj_ioqueue_t *ioqueue,
+					  const pj_activesock_cb *cb,
+					  void *user_data,
+					  pj_activesock_t **p_asock)
+{
+    pj_activesock_t *asock;
+    pj_ioqueue_callback ioq_cb;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && ioqueue && cb && p_asock, PJ_EINVAL);
+    PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL);
+    PJ_ASSERT_RETURN(sock_type==pj_SOCK_STREAM() ||
+		     sock_type==pj_SOCK_DGRAM(), PJ_EINVAL);
+    PJ_ASSERT_RETURN(!opt || opt->async_cnt >= 1, PJ_EINVAL);
+
+    asock = PJ_POOL_ZALLOC_T(pool, pj_activesock_t);
+    asock->ioqueue = ioqueue;
+    asock->stream_oriented = (sock_type == pj_SOCK_STREAM());
+    asock->async_count = (opt? opt->async_cnt : 1);
+    asock->whole_data = (opt? opt->whole_data : 1);
+    asock->max_loop = PJ_ACTIVESOCK_MAX_LOOP;
+    asock->user_data = user_data;
+    pj_memcpy(&asock->cb, cb, sizeof(*cb));
+
+    pj_bzero(&ioq_cb, sizeof(ioq_cb));
+    ioq_cb.on_read_complete = &ioqueue_on_read_complete;
+    ioq_cb.on_write_complete = &ioqueue_on_write_complete;
+#if PJ_HAS_TCP
+    ioq_cb.on_connect_complete = &ioqueue_on_connect_complete;
+    ioq_cb.on_accept_complete = &ioqueue_on_accept_complete;
+#endif
+
+    status = pj_ioqueue_register_sock2(pool, ioqueue, sock,
+                                       (opt? opt->grp_lock : NULL),
+                                       asock, &ioq_cb, &asock->key);
+    if (status != PJ_SUCCESS) {
+	pj_activesock_close(asock);
+	return status;
+    }
+    
+    if (asock->whole_data) {
+	/* Must disable concurrency otherwise there is a race condition */
+	pj_ioqueue_set_concurrency(asock->key, 0);
+    } else if (opt && opt->concurrency >= 0) {
+	pj_ioqueue_set_concurrency(asock->key, opt->concurrency);
+    }
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+    asock->sock = sock;
+    asock->bg_setting = PJ_ACTIVESOCK_TCP_IPHONE_OS_BG;
+#endif
+
+    *p_asock = asock;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_create_udp( pj_pool_t *pool,
+					      const pj_sockaddr *addr,
+					      const pj_activesock_cfg *opt,
+					      pj_ioqueue_t *ioqueue,
+					      const pj_activesock_cb *cb,
+					      void *user_data,
+					      pj_activesock_t **p_asock,
+					      pj_sockaddr *bound_addr)
+{
+    pj_sock_t sock_fd;
+    pj_sockaddr default_addr;
+    pj_status_t status;
+
+    if (addr == NULL) {
+	pj_sockaddr_init(pj_AF_INET(), &default_addr, NULL, 0);
+	addr = &default_addr;
+    }
+
+    status = pj_sock_socket(addr->addr.sa_family, pj_SOCK_DGRAM(), 0, 
+			    &sock_fd);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    status = pj_sock_bind(sock_fd, addr, pj_sockaddr_get_len(addr));
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(sock_fd);
+	return status;
+    }
+
+    status = pj_activesock_create(pool, sock_fd, pj_SOCK_DGRAM(), opt,
+				  ioqueue, cb, user_data, p_asock);
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(sock_fd);
+	return status;
+    }
+
+    if (bound_addr) {
+	int addr_len = sizeof(*bound_addr);
+	status = pj_sock_getsockname(sock_fd, bound_addr, &addr_len);
+	if (status != PJ_SUCCESS) {
+	    pj_activesock_close(*p_asock);
+	    return status;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_activesock_close(pj_activesock_t *asock)
+{
+    PJ_ASSERT_RETURN(asock, PJ_EINVAL);
+    asock->shutdown = SHUT_RX | SHUT_TX;
+    if (asock->key) {
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+	activesock_destroy_iphone_os_stream(asock);
+#endif	
+	
+	pj_ioqueue_unregister(asock->key);
+	asock->key = NULL;
+    }
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_set_user_data( pj_activesock_t *asock,
+						 void *user_data)
+{
+    PJ_ASSERT_RETURN(asock, PJ_EINVAL);
+    asock->user_data = user_data;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(void*) pj_activesock_get_user_data(pj_activesock_t *asock)
+{
+    PJ_ASSERT_RETURN(asock, NULL);
+    return asock->user_data;
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_start_read(pj_activesock_t *asock,
+					     pj_pool_t *pool,
+					     unsigned buff_size,
+					     pj_uint32_t flags)
+{
+    void **readbuf;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL);
+
+    readbuf = (void**) pj_pool_calloc(pool, asock->async_count, 
+				      sizeof(void*));
+
+    for (i=0; i<asock->async_count; ++i) {
+	readbuf[i] = pj_pool_alloc(pool, buff_size);
+    }
+
+    return pj_activesock_start_read2(asock, pool, buff_size, readbuf, flags);
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_start_read2( pj_activesock_t *asock,
+					       pj_pool_t *pool,
+					       unsigned buff_size,
+					       void *readbuf[],
+					       pj_uint32_t flags)
+{
+    unsigned i;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL);
+    PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP);
+    PJ_ASSERT_RETURN(asock->read_op == NULL, PJ_EINVALIDOP);
+
+    asock->read_op = (struct read_op*)
+		     pj_pool_calloc(pool, asock->async_count, 
+				    sizeof(struct read_op));
+    asock->read_type = TYPE_RECV;
+    asock->read_flags = flags;
+
+    for (i=0; i<asock->async_count; ++i) {
+	struct read_op *r = &asock->read_op[i];
+	pj_ssize_t size_to_read;
+
+	r->pkt = (pj_uint8_t*)readbuf[i];
+	size_to_read = r->max_size = buff_size;
+
+	status = pj_ioqueue_recv(asock->key, &r->op_key, r->pkt, &size_to_read,
+				 PJ_IOQUEUE_ALWAYS_ASYNC | flags);
+	PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG);
+
+	if (status != PJ_EPENDING)
+	    return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_start_recvfrom(pj_activesock_t *asock,
+						 pj_pool_t *pool,
+						 unsigned buff_size,
+						 pj_uint32_t flags)
+{
+    void **readbuf;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL);
+
+    readbuf = (void**) pj_pool_calloc(pool, asock->async_count, 
+				      sizeof(void*));
+
+    for (i=0; i<asock->async_count; ++i) {
+	readbuf[i] = pj_pool_alloc(pool, buff_size);
+    }
+
+    return pj_activesock_start_recvfrom2(asock, pool, buff_size, 
+					 readbuf, flags);
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_start_recvfrom2( pj_activesock_t *asock,
+						   pj_pool_t *pool,
+						   unsigned buff_size,
+						   void *readbuf[],
+						   pj_uint32_t flags)
+{
+    unsigned i;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(asock && pool && buff_size, PJ_EINVAL);
+    PJ_ASSERT_RETURN(asock->read_type == TYPE_NONE, PJ_EINVALIDOP);
+
+    asock->read_op = (struct read_op*)
+		     pj_pool_calloc(pool, asock->async_count, 
+				    sizeof(struct read_op));
+    asock->read_type = TYPE_RECV_FROM;
+    asock->read_flags = flags;
+
+    for (i=0; i<asock->async_count; ++i) {
+	struct read_op *r = &asock->read_op[i];
+	pj_ssize_t size_to_read;
+
+	r->pkt = (pj_uint8_t*) readbuf[i];
+	size_to_read = r->max_size = buff_size;
+	r->src_addr_len = sizeof(r->src_addr);
+
+	status = pj_ioqueue_recvfrom(asock->key, &r->op_key, r->pkt,
+				     &size_to_read, 
+				     PJ_IOQUEUE_ALWAYS_ASYNC | flags,
+				     &r->src_addr, &r->src_addr_len);
+	PJ_ASSERT_RETURN(status != PJ_SUCCESS, PJ_EBUG);
+
+	if (status != PJ_EPENDING)
+	    return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+static void ioqueue_on_read_complete(pj_ioqueue_key_t *key, 
+				     pj_ioqueue_op_key_t *op_key, 
+				     pj_ssize_t bytes_read)
+{
+    pj_activesock_t *asock;
+    struct read_op *r = (struct read_op*)op_key;
+    unsigned loop = 0;
+    pj_status_t status;
+
+    asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
+
+    /* Ignore if we've been shutdown */
+    if (asock->shutdown & SHUT_RX)
+	return;
+
+    do {
+	unsigned flags;
+
+	if (bytes_read > 0) {
+	    /*
+	     * We've got new data.
+	     */
+	    pj_size_t remainder;
+	    pj_bool_t ret;
+
+	    /* Append this new data to existing data. If socket is stream 
+	     * oriented, user might have left some data in the buffer. 
+	     * Otherwise if socket is datagram there will be nothing in 
+	     * existing packet hence the packet will contain only the new
+	     * packet.
+	     */
+	    r->size += bytes_read;
+
+	    /* Set default remainder to zero */
+	    remainder = 0;
+
+	    /* And return value to TRUE */
+	    ret = PJ_TRUE;
+
+	    /* Notify callback */
+	    if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) {
+		ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size,
+						PJ_SUCCESS, &remainder);
+	    } else if (asock->read_type == TYPE_RECV_FROM && 
+		       asock->cb.on_data_recvfrom) 
+	    {
+		ret = (*asock->cb.on_data_recvfrom)(asock, r->pkt, r->size,
+						    &r->src_addr, 
+						    r->src_addr_len,
+						    PJ_SUCCESS);
+	    }
+
+	    /* If callback returns false, we have been destroyed! */
+	    if (!ret)
+		return;
+
+	    /* Only stream oriented socket may leave data in the packet */
+	    if (asock->stream_oriented) {
+		r->size = remainder;
+	    } else {
+		r->size = 0;
+	    }
+
+	} else if (bytes_read <= 0 &&
+		   -bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
+		   -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) && 
+		   (asock->stream_oriented ||
+		    -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))) 
+	{
+	    pj_size_t remainder;
+	    pj_bool_t ret;
+
+	    if (bytes_read == 0) {
+		/* For stream/connection oriented socket, this means the 
+		 * connection has been closed. For datagram sockets, it means
+		 * we've received datagram with zero length.
+		 */
+		if (asock->stream_oriented)
+		    status = PJ_EEOF;
+		else
+		    status = PJ_SUCCESS;
+	    } else {
+		/* This means we've got an error. If this is stream/connection
+		 * oriented, it means connection has been closed. For datagram
+		 * sockets, it means we've got some error (e.g. EWOULDBLOCK).
+		 */
+		status = (pj_status_t)-bytes_read;
+	    }
+
+	    /* Set default remainder to zero */
+	    remainder = 0;
+
+	    /* And return value to TRUE */
+	    ret = PJ_TRUE;
+
+	    /* Notify callback */
+	    if (asock->read_type == TYPE_RECV && asock->cb.on_data_read) {
+		/* For connection oriented socket, we still need to report 
+		 * the remainder data (if any) to the user to let user do 
+		 * processing with the remainder data before it closes the
+		 * connection.
+		 * If there is no remainder data, set the packet to NULL.
+		 */
+
+		/* Shouldn't set the packet to NULL, as there may be active 
+		 * socket user, such as SSL socket, that needs to have access
+		 * to the read buffer packet.
+		 */
+		//ret = (*asock->cb.on_data_read)(asock, (r->size? r->pkt:NULL),
+		//				r->size, status, &remainder);
+		ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size,
+						status, &remainder);
+
+	    } else if (asock->read_type == TYPE_RECV_FROM && 
+		       asock->cb.on_data_recvfrom) 
+	    {
+		/* This would always be datagram oriented hence there's 
+		 * nothing in the packet. We can't be sure if there will be
+		 * anything useful in the source_addr, so just put NULL
+		 * there too.
+		 */
+		/* In some scenarios, status may be PJ_SUCCESS. The upper 
+		 * layer application may not expect the callback to be called
+		 * with successful status and NULL data, so lets not call the
+		 * callback if the status is PJ_SUCCESS.
+		 */
+		if (status != PJ_SUCCESS ) {
+		    ret = (*asock->cb.on_data_recvfrom)(asock, NULL, 0,
+							NULL, 0, status);
+		}
+	    }
+
+	    /* If callback returns false, we have been destroyed! */
+	    if (!ret)
+		return;
+
+	    /* Also stop further read if we've been shutdown */
+	    if (asock->shutdown & SHUT_RX)
+		return;
+
+	    /* Only stream oriented socket may leave data in the packet */
+	    if (asock->stream_oriented) {
+		r->size = remainder;
+	    } else {
+		r->size = 0;
+	    }
+	}
+
+	/* Read next data. We limit ourselves to processing max_loop immediate
+	 * data, so when the loop counter has exceeded this value, force the
+	 * read()/recvfrom() to return pending operation to allow the program
+	 * to do other jobs.
+	 */
+	bytes_read = r->max_size - r->size;
+	flags = asock->read_flags;
+	if (++loop >= asock->max_loop)
+	    flags |= PJ_IOQUEUE_ALWAYS_ASYNC;
+
+	if (asock->read_type == TYPE_RECV) {
+	    status = pj_ioqueue_recv(key, op_key, r->pkt + r->size, 
+				     &bytes_read, flags);
+	} else {
+	    r->src_addr_len = sizeof(r->src_addr);
+	    status = pj_ioqueue_recvfrom(key, op_key, r->pkt + r->size,
+				         &bytes_read, flags,
+					 &r->src_addr, &r->src_addr_len);
+	}
+
+	if (status == PJ_SUCCESS) {
+	    /* Immediate data */
+	    ;
+	} else if (status != PJ_EPENDING && status != PJ_ECANCELLED) {
+	    /* Error */
+	    bytes_read = -status;
+	} else {
+	    break;
+	}
+    } while (1);
+
+}
+
+
+static pj_status_t send_remaining(pj_activesock_t *asock, 
+				  pj_ioqueue_op_key_t *send_key)
+{
+    struct send_data *sd = (struct send_data*)send_key->activesock_data;
+    pj_status_t status;
+
+    do {
+	pj_ssize_t size;
+
+	size = sd->len - sd->sent;
+	status = pj_ioqueue_send(asock->key, send_key, 
+				 sd->data+sd->sent, &size, sd->flags);
+	if (status != PJ_SUCCESS) {
+	    /* Pending or error */
+	    break;
+	}
+
+	sd->sent += size;
+	if (sd->sent == sd->len) {
+	    /* The whole data has been sent. */
+	    return PJ_SUCCESS;
+	}
+
+    } while (sd->sent < sd->len);
+
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_send( pj_activesock_t *asock,
+					pj_ioqueue_op_key_t *send_key,
+					const void *data,
+					pj_ssize_t *size,
+					unsigned flags)
+{
+    PJ_ASSERT_RETURN(asock && send_key && data && size, PJ_EINVAL);
+
+    if (asock->shutdown & SHUT_TX)
+	return PJ_EINVALIDOP;
+
+    send_key->activesock_data = NULL;
+
+    if (asock->whole_data) {
+	pj_ssize_t whole;
+	pj_status_t status;
+
+	whole = *size;
+
+	status = pj_ioqueue_send(asock->key, send_key, data, size, flags);
+	if (status != PJ_SUCCESS) {
+	    /* Pending or error */
+	    return status;
+	}
+
+	if (*size == whole) {
+	    /* The whole data has been sent. */
+	    return PJ_SUCCESS;
+	}
+
+	/* Data was partially sent */
+	asock->send_data.data = (pj_uint8_t*)data;
+	asock->send_data.len = whole;
+	asock->send_data.sent = *size;
+	asock->send_data.flags = flags;
+	send_key->activesock_data = &asock->send_data;
+
+	/* Try again */
+	status = send_remaining(asock, send_key);
+	if (status == PJ_SUCCESS) {
+	    *size = whole;
+	}
+	return status;
+
+    } else {
+	return pj_ioqueue_send(asock->key, send_key, data, size, flags);
+    }
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_sendto( pj_activesock_t *asock,
+					  pj_ioqueue_op_key_t *send_key,
+					  const void *data,
+					  pj_ssize_t *size,
+					  unsigned flags,
+					  const pj_sockaddr_t *addr,
+					  int addr_len)
+{
+    PJ_ASSERT_RETURN(asock && send_key && data && size && addr && addr_len, 
+		     PJ_EINVAL);
+
+    if (asock->shutdown & SHUT_TX)
+	return PJ_EINVALIDOP;
+
+    return pj_ioqueue_sendto(asock->key, send_key, data, size, flags,
+			     addr, addr_len);
+}
+
+
+static void ioqueue_on_write_complete(pj_ioqueue_key_t *key, 
+				      pj_ioqueue_op_key_t *op_key,
+				      pj_ssize_t bytes_sent)
+{
+    pj_activesock_t *asock;
+
+    asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
+
+    /* Ignore if we've been shutdown. This may cause data to be partially
+     * sent even when 'wholedata' was requested if the OS only sent partial
+     * buffer.
+     */
+    if (asock->shutdown & SHUT_TX)
+	return;
+
+    if (bytes_sent > 0 && op_key->activesock_data) {
+	/* whole_data is requested. Make sure we send all the data */
+	struct send_data *sd = (struct send_data*)op_key->activesock_data;
+
+	sd->sent += bytes_sent;
+	if (sd->sent == sd->len) {
+	    /* all has been sent */
+	    bytes_sent = sd->sent;
+	    op_key->activesock_data = NULL;
+	} else {
+	    /* send remaining data */
+	    pj_status_t status;
+
+	    status = send_remaining(asock, op_key);
+	    if (status == PJ_EPENDING)
+		return;
+	    else if (status == PJ_SUCCESS)
+		bytes_sent = sd->sent;
+	    else
+		bytes_sent = -status;
+
+	    op_key->activesock_data = NULL;
+	}
+    } 
+
+    if (asock->cb.on_data_sent) {
+	pj_bool_t ret;
+
+	ret = (*asock->cb.on_data_sent)(asock, op_key, bytes_sent);
+
+	/* If callback returns false, we have been destroyed! */
+	if (!ret)
+	    return;
+    }
+}
+
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_activesock_start_accept(pj_activesock_t *asock,
+					       pj_pool_t *pool)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(asock, PJ_EINVAL);
+    PJ_ASSERT_RETURN(asock->accept_op==NULL, PJ_EINVALIDOP);
+
+    /* Ignore if we've been shutdown */
+    if (asock->shutdown)
+	return PJ_EINVALIDOP;
+
+    asock->accept_op = (struct accept_op*)
+		       pj_pool_calloc(pool, asock->async_count,
+				      sizeof(struct accept_op));
+    for (i=0; i<asock->async_count; ++i) {
+	struct accept_op *a = &asock->accept_op[i];
+	pj_status_t status;
+
+	do {
+	    a->new_sock = PJ_INVALID_SOCKET;
+	    a->rem_addr_len = sizeof(a->rem_addr);
+
+	    status = pj_ioqueue_accept(asock->key, &a->op_key, &a->new_sock,
+				       NULL, &a->rem_addr, &a->rem_addr_len);
+	    if (status == PJ_SUCCESS) {
+		/* We've got immediate connection. Not sure if it's a good
+		 * idea to call the callback now (probably application will
+		 * not be prepared to process it), so lets just silently
+		 * close the socket.
+		 */
+		pj_sock_close(a->new_sock);
+	    }
+	} while (status == PJ_SUCCESS);
+
+	if (status != PJ_EPENDING) {
+	    return status;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key, 
+				       pj_ioqueue_op_key_t *op_key,
+				       pj_sock_t new_sock, 
+				       pj_status_t status)
+{
+    pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
+    struct accept_op *accept_op = (struct accept_op*) op_key;
+
+    PJ_UNUSED_ARG(new_sock);
+
+    /* Ignore if we've been shutdown */
+    if (asock->shutdown)
+	return;
+
+    do {
+	if (status == asock->last_err && status != PJ_SUCCESS) {
+	    asock->err_counter++;
+	    if (asock->err_counter >= PJ_ACTIVESOCK_MAX_CONSECUTIVE_ACCEPT_ERROR) {
+		PJ_LOG(3, ("", "Received %d consecutive errors: %d for the accept()"
+			       " operation, stopping further ioqueue accepts.",
+			       asock->err_counter, asock->last_err));
+		
+		if ((status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) && 
+		    (asock->cb.on_accept_complete2)) 
+		{
+		    (*asock->cb.on_accept_complete2)(asock, 
+						     accept_op->new_sock,
+						     &accept_op->rem_addr,
+						     accept_op->rem_addr_len,
+						     PJ_ESOCKETSTOP);
+		}
+		return;
+	    }
+	} else {
+	    asock->err_counter = 0;
+	    asock->last_err = status;
+	}
+
+	if (status==PJ_SUCCESS && (asock->cb.on_accept_complete2 || 
+				   asock->cb.on_accept_complete)) {
+	    pj_bool_t ret;
+
+	    /* Notify callback */
+	    if (asock->cb.on_accept_complete2) {
+		ret = (*asock->cb.on_accept_complete2)(asock, 
+						       accept_op->new_sock,
+						       &accept_op->rem_addr,
+						       accept_op->rem_addr_len,
+						       status);
+	    } else {
+		ret = (*asock->cb.on_accept_complete)(asock, 
+						      accept_op->new_sock,
+						      &accept_op->rem_addr,
+						      accept_op->rem_addr_len);	    
+	    }
+
+	    /* If callback returns false, we have been destroyed! */
+	    if (!ret)
+		return;
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+	    activesock_create_iphone_os_stream(asock);
+#endif
+	} else if (status==PJ_SUCCESS) {
+	    /* Application doesn't handle the new socket, we need to 
+	     * close it to avoid resource leak.
+	     */
+	    pj_sock_close(accept_op->new_sock);
+	}
+
+	/* Don't start another accept() if we've been shutdown */
+	if (asock->shutdown)
+	    return;
+
+	/* Prepare next accept() */
+	accept_op->new_sock = PJ_INVALID_SOCKET;
+	accept_op->rem_addr_len = sizeof(accept_op->rem_addr);
+
+	status = pj_ioqueue_accept(asock->key, op_key, &accept_op->new_sock,
+				   NULL, &accept_op->rem_addr, 
+				   &accept_op->rem_addr_len);
+
+    } while (status != PJ_EPENDING && status != PJ_ECANCELLED);
+}
+
+
+PJ_DEF(pj_status_t) pj_activesock_start_connect( pj_activesock_t *asock,
+						 pj_pool_t *pool,
+						 const pj_sockaddr_t *remaddr,
+						 int addr_len)
+{
+    PJ_UNUSED_ARG(pool);
+
+    if (asock->shutdown)
+	return PJ_EINVALIDOP;
+
+    return pj_ioqueue_connect(asock->key, remaddr, addr_len);
+}
+
+static void ioqueue_on_connect_complete(pj_ioqueue_key_t *key, 
+					pj_status_t status)
+{
+    pj_activesock_t *asock = (pj_activesock_t*) pj_ioqueue_get_user_data(key);
+
+    /* Ignore if we've been shutdown */
+    if (asock->shutdown)
+	return;
+
+    if (asock->cb.on_connect_complete) {
+	pj_bool_t ret;
+
+	ret = (*asock->cb.on_connect_complete)(asock, status);
+
+	if (!ret) {
+	    /* We've been destroyed */
+	    return;
+	}
+	
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+	activesock_create_iphone_os_stream(asock);
+#endif
+	
+    }
+}
+#endif	/* PJ_HAS_TCP */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/addr_resolv_linux_kernel.c b/jni/pjproject-android/pjlib/src/pj/addr_resolv_linux_kernel.c
new file mode 100644
index 0000000..c6de670
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/addr_resolv_linux_kernel.c
@@ -0,0 +1,26 @@
+/* $Id: addr_resolv_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/addr_resolv.h>
+
+PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
+{
+    return -1;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/addr_resolv_sock.c b/jni/pjproject-android/pjlib/src/pj/addr_resolv_sock.c
new file mode 100644
index 0000000..84f7ed8
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/addr_resolv_sock.c
@@ -0,0 +1,282 @@
+/* $Id: addr_resolv_sock.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/addr_resolv.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/errno.h>
+#include <pj/ip_helper.h>
+#include <pj/compat/socket.h>
+
+#if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0
+#   include <CoreFoundation/CFString.h>
+#   include <CFNetwork/CFHost.h>
+#endif
+
+PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *hostname, pj_hostent *phe)
+{
+    struct hostent *he;
+    char copy[PJ_MAX_HOSTNAME];
+
+    pj_assert(hostname && hostname ->slen < PJ_MAX_HOSTNAME);
+    
+    if (hostname->slen >= PJ_MAX_HOSTNAME)
+	return PJ_ENAMETOOLONG;
+
+    pj_memcpy(copy, hostname->ptr, hostname->slen);
+    copy[ hostname->slen ] = '\0';
+
+    he = gethostbyname(copy);
+    if (!he) {
+	return PJ_ERESOLVE;
+	/* DO NOT use pj_get_netos_error() since host resolution error
+	 * is reported in h_errno instead of errno!
+	return pj_get_netos_error();
+	 */
+    }
+
+    phe->h_name = he->h_name;
+    phe->h_aliases = he->h_aliases;
+    phe->h_addrtype = he->h_addrtype;
+    phe->h_length = he->h_length;
+    phe->h_addr_list = he->h_addr_list;
+
+    return PJ_SUCCESS;
+}
+
+/* Resolve IPv4/IPv6 address */
+PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename,
+				   unsigned *count, pj_addrinfo ai[])
+{
+#if defined(PJ_SOCK_HAS_GETADDRINFO) && PJ_SOCK_HAS_GETADDRINFO!=0
+    char nodecopy[PJ_MAX_HOSTNAME];
+    pj_bool_t has_addr = PJ_FALSE;
+    unsigned i;
+#if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0
+    CFStringRef hostname;
+    CFHostRef hostRef;
+    pj_status_t status = PJ_SUCCESS;
+#else
+    int rc;
+    struct addrinfo hint, *res, *orig_res;
+#endif
+
+    PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL);
+    PJ_ASSERT_RETURN(nodename->ptr && nodename->slen, PJ_EINVAL);
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 ||
+		     af==PJ_AF_UNSPEC, PJ_EINVAL);
+
+    /* Check if nodename is IP address */
+    pj_bzero(&ai[0], sizeof(ai[0]));
+    if ((af==PJ_AF_INET || af==PJ_AF_UNSPEC) &&
+	pj_inet_pton(PJ_AF_INET, nodename,
+		     &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS)
+    {
+	af = PJ_AF_INET;
+	has_addr = PJ_TRUE;
+    } else if ((af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) &&
+	       pj_inet_pton(PJ_AF_INET6, nodename,
+	                    &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS)
+    {
+	af = PJ_AF_INET6;
+	has_addr = PJ_TRUE;
+    }
+
+    if (has_addr) {
+	pj_str_t tmp;
+
+	tmp.ptr = ai[0].ai_canonname;
+	pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME);
+	ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af;
+	*count = 1;
+
+	return PJ_SUCCESS;
+    }
+
+    /* Copy node name to null terminated string. */
+    if (nodename->slen >= PJ_MAX_HOSTNAME)
+	return PJ_ENAMETOOLONG;
+    pj_memcpy(nodecopy, nodename->ptr, nodename->slen);
+    nodecopy[nodename->slen] = '\0';
+
+#if defined(PJ_GETADDRINFO_USE_CFHOST) && PJ_GETADDRINFO_USE_CFHOST!=0
+    hostname =  CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, nodecopy,
+						kCFStringEncodingASCII,
+						kCFAllocatorNull);
+    hostRef = CFHostCreateWithName(kCFAllocatorDefault, hostname);
+    if (CFHostStartInfoResolution(hostRef, kCFHostAddresses, nil)) {
+	CFArrayRef addrRef = CFHostGetAddressing(hostRef, nil);
+	i = 0;
+	if (addrRef != nil) {
+	    CFIndex idx, naddr;
+	    
+	    naddr = CFArrayGetCount(addrRef);
+	    for (idx = 0; idx < naddr && i < *count; idx++) {
+		struct sockaddr *addr;
+		
+		addr = (struct sockaddr *)
+		       CFDataGetBytePtr(CFArrayGetValueAtIndex(addrRef, idx));
+		/* This should not happen. */
+		pj_assert(addr);
+		
+		/* Ignore unwanted address families */
+		if (af!=PJ_AF_UNSPEC && addr->sa_family != af)
+		    continue;
+
+		/* Store canonical name */
+		pj_ansi_strcpy(ai[i].ai_canonname, nodecopy);
+		
+		/* Store address */
+		PJ_ASSERT_ON_FAIL(sizeof(*addr) <= sizeof(pj_sockaddr),
+				  continue);
+		pj_memcpy(&ai[i].ai_addr, addr, sizeof(*addr));
+		PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr);
+		
+		i++;
+	    }
+	}
+	
+	*count = i;
+    } else {
+	status = PJ_ERESOLVE;
+    }
+    
+    CFRelease(hostRef);
+    CFRelease(hostname);
+    
+    return status;
+#else
+    /* Call getaddrinfo() */
+    pj_bzero(&hint, sizeof(hint));
+    hint.ai_family = af;
+
+    rc = getaddrinfo(nodecopy, NULL, &hint, &res);
+    if (rc != 0)
+	return PJ_ERESOLVE;
+
+    orig_res = res;
+
+    /* Enumerate each item in the result */
+    for (i=0; i<*count && res; res=res->ai_next) {
+	/* Ignore unwanted address families */
+	if (af!=PJ_AF_UNSPEC && res->ai_family != af)
+	    continue;
+
+	/* Store canonical name (possibly truncating the name) */
+	if (res->ai_canonname) {
+	    pj_ansi_strncpy(ai[i].ai_canonname, res->ai_canonname,
+			    sizeof(ai[i].ai_canonname));
+	    ai[i].ai_canonname[sizeof(ai[i].ai_canonname)-1] = '\0';
+	} else {
+	    pj_ansi_strcpy(ai[i].ai_canonname, nodecopy);
+	}
+
+	/* Store address */
+	PJ_ASSERT_ON_FAIL(res->ai_addrlen <= sizeof(pj_sockaddr), continue);
+	pj_memcpy(&ai[i].ai_addr, res->ai_addr, res->ai_addrlen);
+	PJ_SOCKADDR_RESET_LEN(&ai[i].ai_addr);
+
+	/* Next slot */
+	++i;
+    }
+
+    *count = i;
+
+    freeaddrinfo(orig_res);
+
+    /* Done */
+    return PJ_SUCCESS;
+#endif
+
+#else	/* PJ_SOCK_HAS_GETADDRINFO */
+    pj_bool_t has_addr = PJ_FALSE;
+
+    PJ_ASSERT_RETURN(count && *count, PJ_EINVAL);
+
+    /* Check if nodename is IP address */
+    pj_bzero(&ai[0], sizeof(ai[0]));
+    if ((af==PJ_AF_INET || af==PJ_AF_UNSPEC) &&
+	pj_inet_pton(PJ_AF_INET, nodename,
+		     &ai[0].ai_addr.ipv4.sin_addr) == PJ_SUCCESS)
+    {
+	af = PJ_AF_INET;
+	has_addr = PJ_TRUE;
+    }
+    else if ((af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) &&
+	     pj_inet_pton(PJ_AF_INET6, nodename,
+			  &ai[0].ai_addr.ipv6.sin6_addr) == PJ_SUCCESS)
+    {
+	af = PJ_AF_INET6;
+	has_addr = PJ_TRUE;
+    }
+
+    if (has_addr) {
+	pj_str_t tmp;
+
+	tmp.ptr = ai[0].ai_canonname;
+	pj_strncpy_with_null(&tmp, nodename, PJ_MAX_HOSTNAME);
+	ai[0].ai_addr.addr.sa_family = (pj_uint16_t)af;
+	*count = 1;
+
+	return PJ_SUCCESS;
+    }
+
+    if (af == PJ_AF_INET || af == PJ_AF_UNSPEC) {
+	pj_hostent he;
+	unsigned i, max_count;
+	pj_status_t status;
+	
+	/* VC6 complains that "he" is uninitialized */
+	#ifdef _MSC_VER
+	pj_bzero(&he, sizeof(he));
+	#endif
+
+	status = pj_gethostbyname(nodename, &he);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	max_count = *count;
+	*count = 0;
+
+	pj_bzero(ai, max_count * sizeof(pj_addrinfo));
+
+	for (i=0; he.h_addr_list[i] && *count<max_count; ++i) {
+	    pj_ansi_strncpy(ai[*count].ai_canonname, he.h_name,
+			    sizeof(ai[*count].ai_canonname));
+	    ai[*count].ai_canonname[sizeof(ai[*count].ai_canonname)-1] = '\0';
+
+	    ai[*count].ai_addr.ipv4.sin_family = PJ_AF_INET;
+	    pj_memcpy(&ai[*count].ai_addr.ipv4.sin_addr,
+		      he.h_addr_list[i], he.h_length);
+	    PJ_SOCKADDR_RESET_LEN(&ai[*count].ai_addr);
+
+	    (*count)++;
+	}
+
+	return PJ_SUCCESS;
+
+    } else {
+	/* IPv6 is not supported */
+	*count = 0;
+
+	return PJ_EIPV6NOTSUP;
+    }
+#endif	/* PJ_SOCK_HAS_GETADDRINFO */
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/addr_resolv_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/addr_resolv_symbian.cpp
new file mode 100644
index 0000000..ba29057
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/addr_resolv_symbian.cpp
@@ -0,0 +1,201 @@
+/* $Id: addr_resolv_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/addr_resolv.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/ip_helper.h>
+#include <pj/log.h>
+#include <pj/sock.h>
+#include <pj/string.h>
+#include <pj/unicode.h>
+
+#include "os_symbian.h"
+ 
+#define THIS_FILE 	"addr_resolv_symbian.cpp"
+#define TRACE_ME	0
+
+
+// PJLIB API: resolve hostname
+PJ_DEF(pj_status_t) pj_gethostbyname(const pj_str_t *name, pj_hostent *he)
+{
+    static pj_addrinfo ai;
+    static char *aliases[2];
+    static char *addrlist[2];
+    unsigned count = 1;
+    pj_status_t status;
+    
+    status = pj_getaddrinfo(PJ_AF_INET, name, &count, &ai);
+    if (status != PJ_SUCCESS)
+    	return status;
+    
+    aliases[0] = ai.ai_canonname;
+    aliases[1] = NULL;
+    
+    addrlist[0] = (char*) &ai.ai_addr.ipv4.sin_addr;
+    addrlist[1] = NULL;
+    
+    pj_bzero(he, sizeof(*he));
+    he->h_name = aliases[0];
+    he->h_aliases = aliases;
+    he->h_addrtype = PJ_AF_INET;
+    he->h_length = 4;
+    he->h_addr_list = addrlist;
+    
+    return PJ_SUCCESS;
+}
+
+
+// Resolve for specific address family
+static pj_status_t getaddrinfo_by_af(int af, const pj_str_t *name,
+				     unsigned *count, pj_addrinfo ai[]) 
+{
+    unsigned i;
+    pj_status_t status;
+    
+    PJ_ASSERT_RETURN(name && count && ai, PJ_EINVAL);
+
+#if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6
+    if (af == PJ_AF_INET6)
+    	return PJ_EIPV6NOTSUP;
+#endif
+	
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+
+    // Get resolver for the specified address family
+    RHostResolver &resv = PjSymbianOS::Instance()->GetResolver(af);
+
+    // Convert name to Unicode
+    wchar_t name16[PJ_MAX_HOSTNAME];
+    pj_ansi_to_unicode(name->ptr, name->slen, name16, PJ_ARRAY_SIZE(name16));
+    TPtrC16 data((const TUint16*)name16);
+
+    // Resolve!
+    TNameEntry nameEntry;
+    TRequestStatus reqStatus;
+    
+    resv.GetByName(data, nameEntry, reqStatus);
+    User::WaitForRequest(reqStatus);
+    
+    // Iterate each result
+    i = 0;
+    while (reqStatus == KErrNone && i < *count) {
+    	
+		// Get the resolved TInetAddr
+		TInetAddr inetAddr(nameEntry().iAddr);
+		int addrlen;
+
+#if TRACE_ME
+		if (1) {
+			pj_sockaddr a;
+			char ipaddr[PJ_INET6_ADDRSTRLEN+2];
+			int namelen;
+			
+			namelen = sizeof(pj_sockaddr);
+			if (PjSymbianOS::Addr2pj(inetAddr, a, &namelen, 
+									 PJ_FALSE) == PJ_SUCCESS) 
+			{
+				PJ_LOG(5,(THIS_FILE, "resolve %.*s: %s", 
+						(int)name->slen, name->ptr,
+						pj_sockaddr_print(&a, ipaddr, sizeof(ipaddr), 2)));
+			}
+		}
+#endif
+		
+		// Ignore if this is not the same address family
+		// Not a good idea, as Symbian mapps IPv4 to IPv6.
+		//fam = inetAddr.Family();
+		//if (fam != af) {
+		//    resv.Next(nameEntry, reqStatus);
+		//    User::WaitForRequest(reqStatus);
+		//    continue;
+		//}
+		
+		// Convert IP address first to get IPv4 mapped address
+		addrlen = sizeof(ai[i].ai_addr);
+		status = PjSymbianOS::Addr2pj(inetAddr, ai[i].ai_addr, 
+									  &addrlen, PJ_TRUE);
+		if (status != PJ_SUCCESS)
+		    return status;
+		
+		// Ignore if address family doesn't match
+		if (ai[i].ai_addr.addr.sa_family != af) {
+		    resv.Next(nameEntry, reqStatus);
+		    User::WaitForRequest(reqStatus);
+		    continue;
+		}
+
+		// Convert the official address to ANSI.
+		pj_unicode_to_ansi((const wchar_t*)nameEntry().iName.Ptr(), 
+				   nameEntry().iName.Length(),
+			       	   ai[i].ai_canonname, sizeof(ai[i].ai_canonname));
+	
+		// Next
+		++i;
+		resv.Next(nameEntry, reqStatus);
+		User::WaitForRequest(reqStatus);
+    }
+
+    *count = i;
+    return PJ_SUCCESS;
+}
+
+/* Resolve IPv4/IPv6 address */
+PJ_DEF(pj_status_t) pj_getaddrinfo(int af, const pj_str_t *nodename,
+				   unsigned *count, pj_addrinfo ai[]) 
+{
+    unsigned start;
+    pj_status_t status = PJ_EAFNOTSUP;
+    
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC,
+    		     PJ_EAFNOTSUP);
+    PJ_ASSERT_RETURN(nodename && count && *count && ai, PJ_EINVAL);
+    
+    start = 0;
+    
+    if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) {
+        unsigned max = *count;
+    	status = getaddrinfo_by_af(PJ_AF_INET6, nodename, 
+    				   &max, &ai[start]);
+    	if (status == PJ_SUCCESS) {
+    	    (*count) -= max;
+    	    start += max;
+    	}
+    }
+    
+    if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) {
+        unsigned max = *count;
+    	status = getaddrinfo_by_af(PJ_AF_INET, nodename, 
+    				   &max, &ai[start]);
+    	if (status == PJ_SUCCESS) {
+    	    (*count) -= max;
+    	    start += max;
+    	}
+    }
+    
+    *count = start;
+    
+    if (*count) {
+    	return PJ_SUCCESS;
+    } else {
+    	return status!=PJ_SUCCESS ? status : PJ_ENOTFOUND;
+    }
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/array.c b/jni/pjproject-android/pjlib/src/pj/array.c
new file mode 100644
index 0000000..5b39d19
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/array.c
@@ -0,0 +1,71 @@
+/* $Id: array.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/array.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+PJ_DEF(void) pj_array_insert( void *array,
+			      unsigned elem_size,
+			      unsigned count,
+			      unsigned pos,
+			      const void *value)
+{
+    if (count && pos < count) {
+	pj_memmove( (char*)array + (pos+1)*elem_size,
+		    (char*)array + pos*elem_size,
+		    (count-pos)*elem_size);
+    }
+    pj_memmove((char*)array + pos*elem_size, value, elem_size);
+}
+
+PJ_DEF(void) pj_array_erase( void *array,
+			     unsigned elem_size,
+			     unsigned count,
+			     unsigned pos)
+{
+    pj_assert(count != 0);
+    if (pos < count-1) {
+	pj_memmove( (char*)array + pos*elem_size,
+		    (char*)array + (pos+1)*elem_size,
+		    (count-pos-1)*elem_size);
+    }
+}
+
+PJ_DEF(pj_status_t) pj_array_find( const void *array, 
+				   unsigned elem_size, 
+				   unsigned count, 
+				   pj_status_t (*matching)(const void *value),
+				   void **result)
+{
+    unsigned i;
+    const char *char_array = (const char*)array;
+    for (i=0; i<count; ++i) {
+	if ( (*matching)(char_array) == PJ_SUCCESS) {
+	    if (result) {
+		*result = (void*)char_array;
+	    }
+	    return PJ_SUCCESS;
+	}
+	char_array += elem_size;
+    }
+    return PJ_ENOTFOUND;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/compat/longjmp_i386.S b/jni/pjproject-android/pjlib/src/pj/compat/longjmp_i386.S
new file mode 100644
index 0000000..613d7fe
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/compat/longjmp_i386.S
@@ -0,0 +1,42 @@
+/* longjmp for i386.
+   Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the GNU C Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#define _ASM
+#define _SETJMP_H
+#define PJ_LINUX_KERNEL	    1
+#include <pj/compat/setjmp.h>
+
+.global __longjmp
+.type   __longjmp,%function
+.align 4
+__longjmp:
+	movl 4(%esp), %ecx	/* User's jmp_buf in %ecx.  */
+	movl 8(%esp), %eax	/* Second argument is return value.  */
+	/* Save the return address now.  */
+	movl (JB_PC*4)(%ecx), %edx
+	/* Restore registers.  */
+	movl (JB_BX*4)(%ecx), %ebx
+	movl (JB_SI*4)(%ecx), %esi
+	movl (JB_DI*4)(%ecx), %edi
+	movl (JB_BP*4)(%ecx), %ebp
+	movl (JB_SP*4)(%ecx), %esp
+	/* Jump to saved PC.  */
+	jmp *%edx
+.size __longjmp,.-__longjmp
+
diff --git a/jni/pjproject-android/pjlib/src/pj/compat/setjmp_i386.S b/jni/pjproject-android/pjlib/src/pj/compat/setjmp_i386.S
new file mode 100644
index 0000000..9cdaaff
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/compat/setjmp_i386.S
@@ -0,0 +1,61 @@
+/* setjmp for i386, ELF version.
+   Copyright (C) 1995, 1996, 1997, 2000, 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#define _ASM
+#define _SETJMP_H
+#define PJ_LINUX_KERNEL	    1
+#include <pj/compat/setjmp.h>
+
+
+.global __sigsetjmp
+.type   __sigsetjmp,%function
+.align 4
+
+__sigsetjmp:
+        movl 4   (%esp), %eax
+     	/* Save registers.  */
+        movl %ebx, (0 *4)(%eax)
+        movl %esi, (1 *4)(%eax)
+        movl %edi, (2 *4)(%eax)
+	/* Save SP as it will be after we return.  */
+        leal 4(%esp), %ecx       
+        movl %ecx, (4 *4)(%eax)
+	/* Save PC we are returning to now.  */
+        movl 0(%esp), %ecx       
+        movl %ecx, (5 *4)(%eax)
+	/* Save caller's frame pointer.  */
+        movl %ebp, (3 *4)(%eax)  
+
+	/* Make a tail call to __sigjmp_save; it takes the same args.  */
+#ifdef	__PIC__
+	/* We cannot use the PLT, because it requires that %ebx be set, but
+           we can't save and restore our caller's value.  Instead, we do an
+           indirect jump through the GOT, using for the temporary register
+           %ecx, which is call-clobbered.  */
+	call .Lhere
+.Lhere:
+	popl %ecx
+	addl $_GLOBAL_OFFSET_TABLE_+[.- .Lhere  ], %ecx
+	movl    __sigjmp_save    @GOT  (%ecx), %ecx
+	jmp *%ecx
+#else
+	jmp   __sigjmp_save
+#endif
+.size __sigsetjmp,.-__sigsetjmp
+
diff --git a/jni/pjproject-android/pjlib/src/pj/compat/sigjmp.c b/jni/pjproject-android/pjlib/src/pj/compat/sigjmp.c
new file mode 100644
index 0000000..912763b
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/compat/sigjmp.c
@@ -0,0 +1,40 @@
+/* $Id: sigjmp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/config.h>
+#include <pj/compat/setjmp.h>
+
+int __sigjmp_save(sigjmp_buf env, int savemask)
+{
+    return 0;
+}
+
+extern int __sigsetjmp(pj_jmp_buf env, int savemask);
+extern void __longjmp(pj_jmp_buf env, int val) __attribute__((noreturn));
+
+PJ_DEF(int) pj_setjmp(pj_jmp_buf env)
+{
+    return __sigsetjmp(env, 0);
+}
+
+PJ_DEF(void) pj_longjmp(pj_jmp_buf env, int val)
+{
+    __longjmp(env, val);
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/compat/string.c b/jni/pjproject-android/pjlib/src/pj/compat/string.c
new file mode 100644
index 0000000..012f79b
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/compat/string.c
@@ -0,0 +1,45 @@
+/* $Id: string.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/types.h>
+#include <pj/compat/string.h>
+#include <pj/ctype.h>
+
+PJ_DEF(int) strcasecmp(const char *s1, const char *s2)
+{
+    while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
+	if (!*s1++)
+	    return 0;
+	++s2;
+    }
+    return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
+}
+
+PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len)
+{
+    if (!len) return 0;
+
+    while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
+	if (!*s1++ || --len <= 0)
+	    return 0;
+	++s2;
+    }
+    return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/compat/string_compat.c b/jni/pjproject-android/pjlib/src/pj/compat/string_compat.c
new file mode 100644
index 0000000..e2370ac
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/compat/string_compat.c
@@ -0,0 +1,83 @@
+/* $Id: string_compat.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/types.h>
+#include <pj/compat/string.h>
+#include <pj/ctype.h>
+#include <pj/assert.h>
+
+
+#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H != 0
+/* Nothing to do */
+#else
+PJ_DEF(int) strcasecmp(const char *s1, const char *s2)
+{
+    while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
+	if (!*s1++)
+	    return 0;
+	++s2;
+    }
+    return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
+}
+
+PJ_DEF(int) strncasecmp(const char *s1, const char *s2, int len)
+{
+    if (!len) return 0;
+
+    while ((*s1==*s2) || (pj_tolower(*s1)==pj_tolower(*s2))) {
+	if (!*s1++ || --len <= 0)
+	    return 0;
+	++s2;
+    }
+    return (pj_tolower(*s1) < pj_tolower(*s2)) ? -1 : 1;
+}
+#endif
+
+#if defined(PJ_HAS_NO_SNPRINTF) && PJ_HAS_NO_SNPRINTF != 0
+
+PJ_DEF(int) snprintf(char *s1, pj_size_t len, const char *s2, ...)
+{
+    int ret;
+    va_list arg;
+
+    PJ_UNUSED_ARG(len);
+
+    va_start(arg, s2);
+    ret = vsprintf(s1, s2, arg);
+    va_end(arg);
+    
+    return ret;
+}
+
+PJ_DEF(int) vsnprintf(char *s1, pj_size_t len, const char *s2, va_list arg)
+{
+#define MARK_CHAR   ((char)255)
+    int rc;
+
+    s1[len-1] = MARK_CHAR;
+
+    rc = vsprintf(s1,s2,arg);
+
+    pj_assert(s1[len-1] == MARK_CHAR || s1[len-1] == '\0');
+
+    return rc;
+}
+
+#endif
+
diff --git a/jni/pjproject-android/pjlib/src/pj/config.c b/jni/pjproject-android/pjlib/src/pj/config.c
new file mode 100644
index 0000000..709af53
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/config.c
@@ -0,0 +1,89 @@
+/* $Id: config.c 4112 2012-04-27 09:47:20Z bennylp $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/config.h>
+#include <pj/log.h>
+#include <pj/ioqueue.h>
+
+static const char *id = "config.c";
+
+#define PJ_MAKE_VERSION3_1(a,b,d) 	#a "." #b d
+#define PJ_MAKE_VERSION3_2(a,b,d)	PJ_MAKE_VERSION3_1(a,b,d)
+
+#define PJ_MAKE_VERSION4_1(a,b,c,d) 	#a "." #b "." #c d
+#define PJ_MAKE_VERSION4_2(a,b,c,d)	PJ_MAKE_VERSION4_1(a,b,c,d)
+
+#if PJ_VERSION_NUM_REV
+PJ_DEF_DATA(const char*) PJ_VERSION = PJ_MAKE_VERSION4_2(PJ_VERSION_NUM_MAJOR,
+						         PJ_VERSION_NUM_MINOR,
+						         PJ_VERSION_NUM_REV,
+						         PJ_VERSION_NUM_EXTRA);
+#else
+PJ_DEF_DATA(const char*) PJ_VERSION = PJ_MAKE_VERSION3_2(PJ_VERSION_NUM_MAJOR,
+						         PJ_VERSION_NUM_MINOR,
+						         PJ_VERSION_NUM_EXTRA);
+#endif
+
+/*
+ * Get PJLIB version string.
+ */
+PJ_DEF(const char*) pj_get_version(void)
+{
+    return PJ_VERSION;
+}
+
+PJ_DEF(void) pj_dump_config(void)
+{
+    PJ_LOG(3, (id, "PJLIB (c)2008-2009 Teluu Inc."));
+    PJ_LOG(3, (id, "Dumping configurations:"));
+    PJ_LOG(3, (id, " PJ_VERSION                : %s", PJ_VERSION));
+    PJ_LOG(3, (id, " PJ_M_NAME                 : %s", PJ_M_NAME));
+    PJ_LOG(3, (id, " PJ_HAS_PENTIUM            : %d", PJ_HAS_PENTIUM));
+    PJ_LOG(3, (id, " PJ_OS_NAME                : %s", PJ_OS_NAME));
+    PJ_LOG(3, (id, " PJ_CC_NAME/VER_(1,2,3)    : %s-%d.%d.%d", PJ_CC_NAME,
+	       PJ_CC_VER_1, PJ_CC_VER_2, PJ_CC_VER_3));
+    PJ_LOG(3, (id, " PJ_IS_(BIG/LITTLE)_ENDIAN : %s", 
+	       (PJ_IS_BIG_ENDIAN?"big-endian":"little-endian")));
+    PJ_LOG(3, (id, " PJ_HAS_INT64              : %d", PJ_HAS_INT64));
+    PJ_LOG(3, (id, " PJ_HAS_FLOATING_POINT     : %d", PJ_HAS_FLOATING_POINT));
+    PJ_LOG(3, (id, " PJ_DEBUG                  : %d", PJ_DEBUG));
+    PJ_LOG(3, (id, " PJ_FUNCTIONS_ARE_INLINED  : %d", PJ_FUNCTIONS_ARE_INLINED));
+    PJ_LOG(3, (id, " PJ_LOG_MAX_LEVEL          : %d", PJ_LOG_MAX_LEVEL));
+    PJ_LOG(3, (id, " PJ_LOG_MAX_SIZE           : %d", PJ_LOG_MAX_SIZE));
+    PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER   : %d", PJ_LOG_USE_STACK_BUFFER));
+    PJ_LOG(3, (id, " PJ_POOL_DEBUG             : %d", PJ_POOL_DEBUG));
+    PJ_LOG(3, (id, " PJ_HAS_POOL_ALT_API       : %d", PJ_HAS_POOL_ALT_API));
+    PJ_LOG(3, (id, " PJ_HAS_TCP                : %d", PJ_HAS_TCP));
+    PJ_LOG(3, (id, " PJ_MAX_HOSTNAME           : %d", PJ_MAX_HOSTNAME));
+    PJ_LOG(3, (id, " ioqueue type              : %s", pj_ioqueue_name()));
+    PJ_LOG(3, (id, " PJ_IOQUEUE_MAX_HANDLES    : %d", PJ_IOQUEUE_MAX_HANDLES));
+    PJ_LOG(3, (id, " PJ_IOQUEUE_HAS_SAFE_UNREG : %d", PJ_IOQUEUE_HAS_SAFE_UNREG));
+    PJ_LOG(3, (id, " PJ_HAS_THREADS            : %d", PJ_HAS_THREADS));
+    PJ_LOG(3, (id, " PJ_LOG_USE_STACK_BUFFER   : %d", PJ_LOG_USE_STACK_BUFFER));
+    PJ_LOG(3, (id, " PJ_HAS_SEMAPHORE          : %d", PJ_HAS_SEMAPHORE));
+    PJ_LOG(3, (id, " PJ_HAS_EVENT_OBJ          : %d", PJ_HAS_EVENT_OBJ));
+    PJ_LOG(3, (id, " PJ_ENABLE_EXTRA_CHECK     : %d", PJ_ENABLE_EXTRA_CHECK));
+    PJ_LOG(3, (id, " PJ_HAS_EXCEPTION_NAMES    : %d", PJ_HAS_EXCEPTION_NAMES));
+    PJ_LOG(3, (id, " PJ_MAX_EXCEPTION_ID       : %d", PJ_MAX_EXCEPTION_ID));
+    PJ_LOG(3, (id, " PJ_EXCEPTION_USE_WIN32_SEH: %d", PJ_EXCEPTION_USE_WIN32_SEH));
+    PJ_LOG(3, (id, " PJ_TIMESTAMP_USE_RDTSC:   : %d", PJ_TIMESTAMP_USE_RDTSC));
+    PJ_LOG(3, (id, " PJ_OS_HAS_CHECK_STACK     : %d", PJ_OS_HAS_CHECK_STACK));
+    PJ_LOG(3, (id, " PJ_HAS_HIGH_RES_TIMER     : %d", PJ_HAS_HIGH_RES_TIMER));
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ctype.c b/jni/pjproject-android/pjlib/src/pj/ctype.c
new file mode 100644
index 0000000..bb04342
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ctype.c
@@ -0,0 +1,28 @@
+/* $Id: ctype.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ctype.h>
+
+/*
+char pj_hex_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+			'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+*/
+
+int pjlib_ctype_c_dummy_symbol;
+
diff --git a/jni/pjproject-android/pjlib/src/pj/errno.c b/jni/pjproject-android/pjlib/src/pj/errno.c
new file mode 100644
index 0000000..da20a95
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/errno.c
@@ -0,0 +1,322 @@
+/* $Id: errno.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/compat/string.h>
+#include <pj/compat/stdarg.h>
+#include <pj/assert.h>
+
+/* Prototype for platform specific error message, which will be defined 
+ * in separate file.
+ */
+PJ_BEGIN_DECL
+
+    PJ_DECL(int) platform_strerror(pj_os_err_type code, 
+                              	   char *buf, pj_size_t bufsize );
+PJ_END_DECL
+
+#ifndef PJLIB_MAX_ERR_MSG_HANDLER
+#	define PJLIB_MAX_ERR_MSG_HANDLER   10
+#endif
+
+/* Error message handler. */
+static unsigned err_msg_hnd_cnt;
+static struct err_msg_hnd
+{
+    pj_status_t	    begin;
+    pj_status_t	    end;
+    pj_str_t	  (*strerror)(pj_status_t, char*, pj_size_t);
+
+} err_msg_hnd[PJLIB_MAX_ERR_MSG_HANDLER];
+
+/* PJLIB's own error codes/messages */
+#if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0
+
+static const struct 
+{
+    int code;
+    const char *msg;
+} err_str[] = 
+{
+    PJ_BUILD_ERR(PJ_EUNKNOWN,      "Unknown Error" ),
+    PJ_BUILD_ERR(PJ_EPENDING,      "Pending operation" ),
+    PJ_BUILD_ERR(PJ_ETOOMANYCONN,  "Too many connecting sockets" ),
+    PJ_BUILD_ERR(PJ_EINVAL,        "Invalid value or argument" ),
+    PJ_BUILD_ERR(PJ_ENAMETOOLONG,  "Name too long" ),
+    PJ_BUILD_ERR(PJ_ENOTFOUND,     "Not found" ),
+    PJ_BUILD_ERR(PJ_ENOMEM,        "Not enough memory" ),
+    PJ_BUILD_ERR(PJ_EBUG,          "BUG DETECTED!" ),
+    PJ_BUILD_ERR(PJ_ETIMEDOUT,     "Operation timed out" ),
+    PJ_BUILD_ERR(PJ_ETOOMANY,      "Too many objects of the specified type"),
+    PJ_BUILD_ERR(PJ_EBUSY,         "Object is busy"),
+    PJ_BUILD_ERR(PJ_ENOTSUP,	   "Option/operation is not supported"),
+    PJ_BUILD_ERR(PJ_EINVALIDOP,	   "Invalid operation"),
+    PJ_BUILD_ERR(PJ_ECANCELLED,    "Operation cancelled"),
+    PJ_BUILD_ERR(PJ_EEXISTS,       "Object already exists" ),
+    PJ_BUILD_ERR(PJ_EEOF,	   "End of file" ),
+    PJ_BUILD_ERR(PJ_ETOOBIG,	   "Size is too big"),
+    PJ_BUILD_ERR(PJ_ERESOLVE,	   "gethostbyname() has returned error"),
+    PJ_BUILD_ERR(PJ_ETOOSMALL,	   "Size is too short"),
+    PJ_BUILD_ERR(PJ_EIGNORED,	   "Ignored"),
+    PJ_BUILD_ERR(PJ_EIPV6NOTSUP,   "IPv6 is not supported"),
+    PJ_BUILD_ERR(PJ_EAFNOTSUP,	   "Unsupported address family"),
+    PJ_BUILD_ERR(PJ_EGONE,	   "Object no longer exists"),
+    PJ_BUILD_ERR(PJ_ESOCKETSTOP,   "Socket is in bad state")
+};
+#endif	/* PJ_HAS_ERROR_STRING */
+
+
+/*
+ * pjlib_error()
+ *
+ * Retrieve message string for PJLIB's own error code.
+ */
+static int pjlib_error(pj_status_t code, char *buf, pj_size_t size)
+{
+#if defined(PJ_HAS_ERROR_STRING) && PJ_HAS_ERROR_STRING!=0
+    unsigned i;
+
+    for (i=0; i<sizeof(err_str)/sizeof(err_str[0]); ++i) {
+        if (err_str[i].code == code) {
+            pj_size_t len = strlen(err_str[i].msg);
+            if (len >= size) len = size-1;
+            pj_memcpy(buf, err_str[i].msg, len);
+            buf[len] = '\0';
+            return (int)len;
+        }
+    }
+#endif
+
+    return pj_ansi_snprintf( buf, size, "Unknown pjlib error %d", code);
+}
+
+#define IN_RANGE(val,start,end)	    ((val)>=(start) && (val)<(end))
+
+/* Register strerror handle. */
+PJ_DEF(pj_status_t) pj_register_strerror( pj_status_t start,
+					  pj_status_t space,
+					  pj_error_callback f)
+{
+    unsigned i;
+
+    /* Check arguments. */
+    PJ_ASSERT_RETURN(start && space && f, PJ_EINVAL);
+
+    /* Check if there aren't too many handlers registered. */
+    PJ_ASSERT_RETURN(err_msg_hnd_cnt < PJ_ARRAY_SIZE(err_msg_hnd),
+		     PJ_ETOOMANY);
+
+    /* Start error must be greater than PJ_ERRNO_START_USER */
+    PJ_ASSERT_RETURN(start >= PJ_ERRNO_START_USER, PJ_EEXISTS);
+
+    /* Check that no existing handler has covered the specified range. */
+    for (i=0; i<err_msg_hnd_cnt; ++i) {
+	if (IN_RANGE(start, err_msg_hnd[i].begin, err_msg_hnd[i].end) ||
+	    IN_RANGE(start+space-1, err_msg_hnd[i].begin, err_msg_hnd[i].end))
+	{
+	    if (err_msg_hnd[i].begin == start && 
+		err_msg_hnd[i].end == (start+space) &&
+		err_msg_hnd[i].strerror == f)
+	    {
+		/* The same range and handler has already been registered */
+		return PJ_SUCCESS;
+	    }
+
+	    return PJ_EEXISTS;
+	}
+    }
+
+    /* Register the handler. */
+    err_msg_hnd[err_msg_hnd_cnt].begin = start;
+    err_msg_hnd[err_msg_hnd_cnt].end = start + space;
+    err_msg_hnd[err_msg_hnd_cnt].strerror = f;
+
+    ++err_msg_hnd_cnt;
+
+    return PJ_SUCCESS;
+}
+
+/* Internal PJLIB function called by pj_shutdown() to clear error handlers */
+void pj_errno_clear_handlers(void)
+{
+    err_msg_hnd_cnt = 0;
+    pj_bzero(err_msg_hnd, sizeof(err_msg_hnd));
+}
+
+
+/*
+ * pj_strerror()
+ */
+PJ_DEF(pj_str_t) pj_strerror( pj_status_t statcode, 
+			      char *buf, pj_size_t bufsize )
+{
+    int len = -1;
+    pj_str_t errstr;
+
+    pj_assert(buf && bufsize);
+
+    if (statcode == PJ_SUCCESS) {
+	len = pj_ansi_snprintf( buf, bufsize, "Success");
+
+    } else if (statcode < PJ_ERRNO_START + PJ_ERRNO_SPACE_SIZE) {
+        len = pj_ansi_snprintf( buf, bufsize, "Unknown error %d", statcode);
+
+    } else if (statcode < PJ_ERRNO_START_STATUS + PJ_ERRNO_SPACE_SIZE) {
+        len = pjlib_error(statcode, buf, bufsize);
+
+    } else if (statcode < PJ_ERRNO_START_SYS + PJ_ERRNO_SPACE_SIZE) {
+        len = platform_strerror(PJ_STATUS_TO_OS(statcode), buf, bufsize);
+
+    } else {
+	unsigned i;
+
+	/* Find user handler to get the error message. */
+	for (i=0; i<err_msg_hnd_cnt; ++i) {
+	    if (IN_RANGE(statcode, err_msg_hnd[i].begin, err_msg_hnd[i].end)) {
+		return (*err_msg_hnd[i].strerror)(statcode, buf, bufsize);
+	    }
+	}
+
+	/* Handler not found! */
+	len = pj_ansi_snprintf( buf, bufsize, "Unknown error %d", statcode);
+    }
+
+    if (len < 1) {
+        *buf = '\0';
+        len = 0;
+    }
+
+    errstr.ptr = buf;
+    errstr.slen = len;
+
+    return errstr;
+}
+
+#if PJ_LOG_MAX_LEVEL >= 1
+static void invoke_log(const char *sender, int level, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(sender, level, format, arg);
+    va_end(arg);
+}
+
+static void pj_perror_imp(int log_level, const char *sender, 
+			  pj_status_t status,
+		          const char *title_fmt, va_list marker)
+{
+    char titlebuf[PJ_PERROR_TITLE_BUF_SIZE];
+    char errmsg[PJ_ERR_MSG_SIZE];
+    int len;
+
+    /* Build the title */
+    len = pj_ansi_vsnprintf(titlebuf, sizeof(titlebuf), title_fmt, marker);
+    if (len < 0 || len >= (int)sizeof(titlebuf))
+	pj_ansi_strcpy(titlebuf, "Error");
+
+    /* Get the error */
+    pj_strerror(status, errmsg, sizeof(errmsg));
+
+    /* Send to log */
+    invoke_log(sender, log_level, "%s: %s", titlebuf, errmsg);
+}
+
+PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status,
+		       const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(log_level, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+
+PJ_DEF(void) pj_perror_1(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(1, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+
+#else /* #if PJ_LOG_MAX_LEVEL >= 1 */
+PJ_DEF(void) pj_perror(int log_level, const char *sender, pj_status_t status,
+		       const char *title_fmt, ...)
+{
+}
+#endif	/* #if PJ_LOG_MAX_LEVEL >= 1 */
+
+
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_DEF(void) pj_perror_2(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(2, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_DEF(void) pj_perror_3(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(3, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_DEF(void) pj_perror_4(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(4, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_DEF(void) pj_perror_5(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(5, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_DEF(void) pj_perror_6(const char *sender, pj_status_t status,
+			 const char *title_fmt, ...)
+{
+    va_list marker;
+    va_start(marker, title_fmt);
+    pj_perror_imp(6, sender, status, title_fmt, marker);
+    va_end(marker);
+}
+#endif
+
diff --git a/jni/pjproject-android/pjlib/src/pj/except.c b/jni/pjproject-android/pjlib/src/pj/except.c
new file mode 100644
index 0000000..e3984df
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/except.c
@@ -0,0 +1,185 @@
+/* $Id: except.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/except.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+
+static long thread_local_id = -1;
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+    static const char *exception_id_names[PJ_MAX_EXCEPTION_ID];
+#else
+    /*
+     * Start from 1 (not 0)!!!
+     * Exception 0 is reserved for normal path of setjmp()!!!
+     */
+    static int last_exception_id = 1;
+#endif  /* PJ_HAS_EXCEPTION_NAMES */
+
+
+#if !defined(PJ_EXCEPTION_USE_WIN32_SEH) || PJ_EXCEPTION_USE_WIN32_SEH==0
+PJ_DEF(void) pj_throw_exception_(int exception_id)
+{
+    struct pj_exception_state_t *handler;
+
+    handler = (struct pj_exception_state_t*) 
+	      pj_thread_local_get(thread_local_id);
+    if (handler == NULL) {
+        PJ_LOG(1,("except.c", "!!!FATAL: unhandled exception %s!\n", 
+		   pj_exception_id_name(exception_id)));
+        pj_assert(handler != NULL);
+        /* This will crash the system! */
+    }
+    pj_pop_exception_handler_(handler);
+    pj_longjmp(handler->state, exception_id);
+}
+
+static void exception_cleanup(void)
+{
+    if (thread_local_id != -1) {
+	pj_thread_local_free(thread_local_id);
+	thread_local_id = -1;
+    }
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+    {
+	unsigned i;
+	for (i=0; i<PJ_MAX_EXCEPTION_ID; ++i)
+	    exception_id_names[i] = NULL;
+    }
+#else
+    last_exception_id = 1;
+#endif
+}
+
+PJ_DEF(void) pj_push_exception_handler_(struct pj_exception_state_t *rec)
+{
+    struct pj_exception_state_t *parent_handler = NULL;
+
+    if (thread_local_id == -1) {
+	pj_thread_local_alloc(&thread_local_id);
+	pj_assert(thread_local_id != -1);
+	pj_atexit(&exception_cleanup);
+    }
+    parent_handler = (struct pj_exception_state_t *)
+		      pj_thread_local_get(thread_local_id);
+    rec->prev = parent_handler;
+    pj_thread_local_set(thread_local_id, rec);
+}
+
+PJ_DEF(void) pj_pop_exception_handler_(struct pj_exception_state_t *rec)
+{
+    struct pj_exception_state_t *handler;
+
+    handler = (struct pj_exception_state_t *)
+	      pj_thread_local_get(thread_local_id);
+    if (handler && handler==rec) {
+	pj_thread_local_set(thread_local_id, handler->prev);
+    }
+}
+#endif
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+                                           pj_exception_id_t *id)
+{
+    unsigned i;
+
+    pj_enter_critical_section();
+
+    /*
+     * Start from 1 (not 0)!!!
+     * Exception 0 is reserved for normal path of setjmp()!!!
+     */
+    for (i=1; i<PJ_MAX_EXCEPTION_ID; ++i) {
+        if (exception_id_names[i] == NULL) {
+            exception_id_names[i] = name;
+            *id = i;
+            pj_leave_critical_section();
+            return PJ_SUCCESS;
+        }
+    }
+
+    pj_leave_critical_section();
+    return PJ_ETOOMANY;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+    /*
+     * Start from 1 (not 0)!!!
+     * Exception 0 is reserved for normal path of setjmp()!!!
+     */
+    PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, PJ_EINVAL);
+    
+    pj_enter_critical_section();
+    exception_id_names[id] = NULL;
+    pj_leave_critical_section();
+
+    return PJ_SUCCESS;
+
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+    static char unknown_name[32];
+
+    /*
+     * Start from 1 (not 0)!!!
+     * Exception 0 is reserved for normal path of setjmp()!!!
+     */
+    PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, "<Invalid ID>");
+
+    if (exception_id_names[id] == NULL) {
+        pj_ansi_snprintf(unknown_name, sizeof(unknown_name), 
+			 "exception %d", id);
+	return unknown_name;
+    }
+
+    return exception_id_names[id];
+}
+
+#else   /* PJ_HAS_EXCEPTION_NAMES */
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+                                           pj_exception_id_t *id)
+{
+    PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY);
+
+    *id = last_exception_id++;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+    return "";
+}
+
+#endif  /* PJ_HAS_EXCEPTION_NAMES */
+
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/exception_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/exception_symbian.cpp
new file mode 100644
index 0000000..f2dae4c
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/exception_symbian.cpp
@@ -0,0 +1,116 @@
+/* $Id: exception_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/except.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/errno.h>
+
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+    static const char *exception_id_names[PJ_MAX_EXCEPTION_ID];
+#else
+    /*
+     * Start from 1 (not 0)!!!
+     * Exception 0 is reserved for normal path of setjmp()!!!
+     */
+    static int last_exception_id = 1;
+#endif  /* PJ_HAS_EXCEPTION_NAMES */
+
+
+#if defined(PJ_HAS_EXCEPTION_NAMES) && PJ_HAS_EXCEPTION_NAMES != 0
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+                                           pj_exception_id_t *id)
+{
+    unsigned i;
+
+    pj_enter_critical_section();
+
+    /*
+     * Start from 1 (not 0)!!!
+     * Exception 0 is reserved for normal path of setjmp()!!!
+     */
+    for (i=1; i<PJ_MAX_EXCEPTION_ID; ++i) {
+        if (exception_id_names[i] == NULL) {
+            exception_id_names[i] = name;
+            *id = i;
+            pj_leave_critical_section();
+            return PJ_SUCCESS;
+        }
+    }
+
+    pj_leave_critical_section();
+    return PJ_ETOOMANY;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+    /*
+     * Start from 1 (not 0)!!!
+     * Exception 0 is reserved for normal path of setjmp()!!!
+     */
+    PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, PJ_EINVAL);
+    
+    pj_enter_critical_section();
+    exception_id_names[id] = NULL;
+    pj_leave_critical_section();
+
+    return PJ_SUCCESS;
+
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+    /*
+     * Start from 1 (not 0)!!!
+     * Exception 0 is reserved for normal path of setjmp()!!!
+     */
+    PJ_ASSERT_RETURN(id>0 && id<PJ_MAX_EXCEPTION_ID, "<Invalid ID>");
+
+    if (exception_id_names[id] == NULL)
+        return "<Unallocated ID>";
+
+    return exception_id_names[id];
+}
+
+#else   /* PJ_HAS_EXCEPTION_NAMES */
+PJ_DEF(pj_status_t) pj_exception_id_alloc( const char *name,
+                                           pj_exception_id_t *id)
+{
+    PJ_ASSERT_RETURN(last_exception_id < PJ_MAX_EXCEPTION_ID-1, PJ_ETOOMANY);
+
+    *id = last_exception_id++;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_exception_id_free( pj_exception_id_t id )
+{
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(const char*) pj_exception_id_name(pj_exception_id_t id)
+{
+    return "";
+}
+
+#endif  /* PJ_HAS_EXCEPTION_NAMES */
+
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/extra-exports.c b/jni/pjproject-android/pjlib/src/pj/extra-exports.c
new file mode 100644
index 0000000..dcd5e5e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/extra-exports.c
@@ -0,0 +1,41 @@
+/* $Id: extra-exports.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <linux/module.h>
+#include <linux/syscalls.h>
+
+EXPORT_SYMBOL(sys_select);
+
+EXPORT_SYMBOL(sys_epoll_create);
+EXPORT_SYMBOL(sys_epoll_ctl);
+EXPORT_SYMBOL(sys_epoll_wait);
+
+EXPORT_SYMBOL(sys_socket);
+EXPORT_SYMBOL(sys_bind);
+EXPORT_SYMBOL(sys_getpeername);
+EXPORT_SYMBOL(sys_getsockname);
+EXPORT_SYMBOL(sys_sendto);
+EXPORT_SYMBOL(sys_recvfrom);
+EXPORT_SYMBOL(sys_getsockopt);
+EXPORT_SYMBOL(sys_setsockopt);
+EXPORT_SYMBOL(sys_listen);
+EXPORT_SYMBOL(sys_shutdown);
+EXPORT_SYMBOL(sys_connect);
+EXPORT_SYMBOL(sys_accept);
+
diff --git a/jni/pjproject-android/pjlib/src/pj/fifobuf.c b/jni/pjproject-android/pjlib/src/pj/fifobuf.c
new file mode 100644
index 0000000..62f66cc
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/fifobuf.c
@@ -0,0 +1,189 @@
+/* $Id: fifobuf.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/fifobuf.h>
+#include <pj/log.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+
+#define THIS_FILE   "fifobuf"
+
+#define SZ  sizeof(unsigned)
+
+PJ_DEF(void) pj_fifobuf_init (pj_fifobuf_t *fifobuf, void *buffer, unsigned size)
+{
+    PJ_CHECK_STACK();
+
+    PJ_LOG(6, (THIS_FILE, 
+	       "fifobuf_init fifobuf=%p buffer=%p, size=%d", 
+	       fifobuf, buffer, size));
+
+    fifobuf->first = (char*)buffer;
+    fifobuf->last = fifobuf->first + size;
+    fifobuf->ubegin = fifobuf->uend = fifobuf->first;
+    fifobuf->full = 0;
+}
+
+PJ_DEF(unsigned) pj_fifobuf_max_size (pj_fifobuf_t *fifobuf)
+{
+    unsigned s1, s2;
+
+    PJ_CHECK_STACK();
+
+    if (fifobuf->uend >= fifobuf->ubegin) {
+	s1 = (unsigned)(fifobuf->last - fifobuf->uend);
+	s2 = (unsigned)(fifobuf->ubegin - fifobuf->first);
+    } else {
+	s1 = s2 = (unsigned)(fifobuf->ubegin - fifobuf->uend);
+    }
+    
+    return s1<s2 ? s2 : s1;
+}
+
+PJ_DEF(void*) pj_fifobuf_alloc (pj_fifobuf_t *fifobuf, unsigned size)
+{
+    unsigned available;
+    char *start;
+
+    PJ_CHECK_STACK();
+
+    if (fifobuf->full) {
+	PJ_LOG(6, (THIS_FILE, 
+		   "fifobuf_alloc fifobuf=%p, size=%d: full!", 
+		   fifobuf, size));
+	return NULL;
+    }
+
+    /* try to allocate from the end part of the fifo */
+    if (fifobuf->uend >= fifobuf->ubegin) {
+	available = (unsigned)(fifobuf->last - fifobuf->uend);
+	if (available >= size+SZ) {
+	    char *ptr = fifobuf->uend;
+	    fifobuf->uend += (size+SZ);
+	    if (fifobuf->uend == fifobuf->last)
+		fifobuf->uend = fifobuf->first;
+	    if (fifobuf->uend == fifobuf->ubegin)
+		fifobuf->full = 1;
+	    *(unsigned*)ptr = size+SZ;
+	    ptr += SZ;
+
+	    PJ_LOG(6, (THIS_FILE, 
+		       "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", 
+		       fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
+	    return ptr;
+	}
+    }
+
+    /* try to allocate from the start part of the fifo */
+    start = (fifobuf->uend <= fifobuf->ubegin) ? fifobuf->uend : fifobuf->first;
+    available = (unsigned)(fifobuf->ubegin - start);
+    if (available >= size+SZ) {
+	char *ptr = start;
+	fifobuf->uend = start + size + SZ;
+	if (fifobuf->uend == fifobuf->ubegin)
+	    fifobuf->full = 1;
+	*(unsigned*)ptr = size+SZ;
+	ptr += SZ;
+
+	PJ_LOG(6, (THIS_FILE, 
+		   "fifobuf_alloc fifobuf=%p, size=%d: returning %p, p1=%p, p2=%p", 
+		   fifobuf, size, ptr, fifobuf->ubegin, fifobuf->uend));
+	return ptr;
+    }
+
+    PJ_LOG(6, (THIS_FILE, 
+	       "fifobuf_alloc fifobuf=%p, size=%d: no space left! p1=%p, p2=%p", 
+	       fifobuf, size, fifobuf->ubegin, fifobuf->uend));
+    return NULL;
+}
+
+PJ_DEF(pj_status_t) pj_fifobuf_unalloc (pj_fifobuf_t *fifobuf, void *buf)
+{
+    char *ptr = (char*)buf;
+    char *endptr;
+    unsigned sz;
+
+    PJ_CHECK_STACK();
+
+    ptr -= SZ;
+    sz = *(unsigned*)ptr;
+
+    endptr = fifobuf->uend;
+    if (endptr == fifobuf->first)
+	endptr = fifobuf->last;
+
+    if (ptr+sz != endptr) {
+	pj_assert(!"Invalid pointer to undo alloc");
+	return -1;
+    }
+
+    fifobuf->uend = ptr;
+    fifobuf->full = 0;
+
+    PJ_LOG(6, (THIS_FILE, 
+	       "fifobuf_unalloc fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", 
+	       fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
+
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_fifobuf_free (pj_fifobuf_t *fifobuf, void *buf)
+{
+    char *ptr = (char*)buf;
+    char *end;
+    unsigned sz;
+
+    PJ_CHECK_STACK();
+
+    ptr -= SZ;
+    if (ptr < fifobuf->first || ptr >= fifobuf->last) {
+	pj_assert(!"Invalid pointer to free");
+	return -1;
+    }
+
+    if (ptr != fifobuf->ubegin && ptr != fifobuf->first) {
+	pj_assert(!"Invalid free() sequence!");
+	return -1;
+    }
+
+    end = (fifobuf->uend > fifobuf->ubegin) ? fifobuf->uend : fifobuf->last;
+    sz = *(unsigned*)ptr;
+    if (ptr+sz > end) {
+	pj_assert(!"Invalid size!");
+	return -1;
+    }
+
+    fifobuf->ubegin = ptr + sz;
+
+    /* Rollover */
+    if (fifobuf->ubegin == fifobuf->last)
+	fifobuf->ubegin = fifobuf->first;
+
+    /* Reset if fifobuf is empty */
+    if (fifobuf->ubegin == fifobuf->uend)
+	fifobuf->ubegin = fifobuf->uend = fifobuf->first;
+
+    fifobuf->full = 0;
+
+    PJ_LOG(6, (THIS_FILE, 
+	       "fifobuf_free fifobuf=%p, ptr=%p, size=%d, p1=%p, p2=%p", 
+	       fifobuf, buf, sz, fifobuf->ubegin, fifobuf->uend));
+
+    return 0;
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/file_access_unistd.c b/jni/pjproject-android/pjlib/src/pj/file_access_unistd.c
new file mode 100644
index 0000000..b48a3bc
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/file_access_unistd.c
@@ -0,0 +1,114 @@
+/* $Id: file_access_unistd.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/file_access.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>	/* rename() */
+#include <errno.h>
+
+/*
+ * pj_file_exists()
+ */
+PJ_DEF(pj_bool_t) pj_file_exists(const char *filename)
+{
+    struct stat buf;
+
+    PJ_ASSERT_RETURN(filename, 0);
+
+    if (stat(filename, &buf) != 0)
+	return 0;
+
+    return PJ_TRUE;
+}
+
+
+/*
+ * pj_file_size()
+ */
+PJ_DEF(pj_off_t) pj_file_size(const char *filename)
+{
+    struct stat buf;
+
+    PJ_ASSERT_RETURN(filename, -1);
+
+    if (stat(filename, &buf) != 0)
+	return -1;
+
+    return buf.st_size;
+}
+
+
+/*
+ * pj_file_delete()
+ */
+PJ_DEF(pj_status_t) pj_file_delete(const char *filename)
+{
+    PJ_ASSERT_RETURN(filename, PJ_EINVAL);
+
+    if (unlink(filename)!=0) {
+	return PJ_RETURN_OS_ERROR(errno);
+    }
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_file_move()
+ */
+PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname)
+{
+    PJ_ASSERT_RETURN(oldname && newname, PJ_EINVAL);
+
+    if (rename(oldname, newname) != 0) {
+	return PJ_RETURN_OS_ERROR(errno);
+    }
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_file_getstat()
+ */
+PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, 
+				    pj_file_stat *statbuf)
+{
+    struct stat buf;
+
+    PJ_ASSERT_RETURN(filename && statbuf, PJ_EINVAL);
+
+    if (stat(filename, &buf) != 0) {
+	return PJ_RETURN_OS_ERROR(errno);
+    }
+
+    statbuf->size = buf.st_size;
+    statbuf->ctime.sec = buf.st_ctime;
+    statbuf->ctime.msec = 0;
+    statbuf->mtime.sec = buf.st_mtime;
+    statbuf->mtime.msec = 0;
+    statbuf->atime.sec = buf.st_atime;
+    statbuf->atime.msec = 0;
+
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/file_access_win32.c b/jni/pjproject-android/pjlib/src/pj/file_access_win32.c
new file mode 100644
index 0000000..e737fff
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/file_access_win32.c
@@ -0,0 +1,215 @@
+/* $Id: file_access_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/file_access.h>
+#include <pj/unicode.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <windows.h>
+#include <time.h>
+
+#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
+    /* WinCE lacks READ_CONTROL so we must use GENERIC_READ */
+#   define CONTROL_ACCESS   GENERIC_READ
+#else
+#   define CONTROL_ACCESS   READ_CONTROL
+#endif
+
+
+/*
+ * pj_file_exists()
+ */
+PJ_DEF(pj_bool_t) pj_file_exists(const char *filename)
+{
+    PJ_DECL_UNICODE_TEMP_BUF(wfilename,256)
+    HANDLE hFile;
+
+    PJ_ASSERT_RETURN(filename != NULL, 0);
+
+    hFile = CreateFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename)), 
+		       CONTROL_ACCESS, 
+		       FILE_SHARE_READ, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return 0;
+
+    CloseHandle(hFile);
+    return PJ_TRUE;
+}
+
+
+/*
+ * pj_file_size()
+ */
+PJ_DEF(pj_off_t) pj_file_size(const char *filename)
+{
+    PJ_DECL_UNICODE_TEMP_BUF(wfilename,256)
+    HANDLE hFile;
+    DWORD sizeLo, sizeHi;
+    pj_off_t size;
+
+    PJ_ASSERT_RETURN(filename != NULL, -1);
+
+    hFile = CreateFile(PJ_STRING_TO_NATIVE(filename, wfilename,sizeof(wfilename)), 
+		       CONTROL_ACCESS, 
+                       FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return -1;
+
+    sizeLo = GetFileSize(hFile, &sizeHi);
+    if (sizeLo == INVALID_FILE_SIZE) {
+        DWORD dwStatus = GetLastError();
+        if (dwStatus != NO_ERROR) {
+            CloseHandle(hFile);
+            return -1;
+        }
+    }
+
+    size = sizeHi;
+    size = (size << 32) + sizeLo;
+
+    CloseHandle(hFile);
+    return size;
+}
+
+
+/*
+ * pj_file_delete()
+ */
+PJ_DEF(pj_status_t) pj_file_delete(const char *filename)
+{
+    PJ_DECL_UNICODE_TEMP_BUF(wfilename,256)
+
+    PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);
+
+    if (DeleteFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename))) == FALSE)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_file_move()
+ */
+PJ_DEF(pj_status_t) pj_file_move( const char *oldname, const char *newname)
+{
+    PJ_DECL_UNICODE_TEMP_BUF(woldname,256)
+    PJ_DECL_UNICODE_TEMP_BUF(wnewname,256)
+    BOOL rc;
+
+    PJ_ASSERT_RETURN(oldname!=NULL && newname!=NULL, PJ_EINVAL);
+
+#if PJ_WIN32_WINNT >= 0x0400
+    rc = MoveFileEx(PJ_STRING_TO_NATIVE(oldname,woldname,sizeof(woldname)), 
+		    PJ_STRING_TO_NATIVE(newname,wnewname,sizeof(wnewname)), 
+                    MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING);
+#else
+    rc = MoveFile(PJ_STRING_TO_NATIVE(oldname,woldname,sizeof(woldname)), 
+		  PJ_STRING_TO_NATIVE(newname,wnewname,sizeof(wnewname)));
+#endif
+
+    if (!rc)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t file_time_to_time_val(const FILETIME *file_time,
+                                         pj_time_val *time_val)
+{
+    FILETIME local_file_time;
+    SYSTEMTIME localTime;
+    pj_parsed_time pt;
+
+    if (!FileTimeToLocalFileTime(file_time, &local_file_time))
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    if (!FileTimeToSystemTime(file_time, &localTime))
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    //if (!SystemTimeToTzSpecificLocalTime(NULL, &systemTime, &localTime))
+    //    return PJ_RETURN_OS_ERROR(GetLastError());
+
+    pj_bzero(&pt, sizeof(pt));
+    pt.year = localTime.wYear;
+    pt.mon = localTime.wMonth-1;
+    pt.day = localTime.wDay;
+    pt.wday = localTime.wDayOfWeek;
+
+    pt.hour = localTime.wHour;
+    pt.min = localTime.wMinute;
+    pt.sec = localTime.wSecond;
+    pt.msec = localTime.wMilliseconds;
+
+    return pj_time_encode(&pt, time_val);
+}
+
+/*
+ * pj_file_getstat()
+ */
+PJ_DEF(pj_status_t) pj_file_getstat(const char *filename, pj_file_stat *stat)
+{
+    PJ_DECL_UNICODE_TEMP_BUF(wfilename,256)
+    HANDLE hFile;
+    DWORD sizeLo, sizeHi;
+    FILETIME creationTime, accessTime, writeTime;
+
+    PJ_ASSERT_RETURN(filename!=NULL && stat!=NULL, PJ_EINVAL);
+
+    hFile = CreateFile(PJ_STRING_TO_NATIVE(filename,wfilename,sizeof(wfilename)), 
+		       CONTROL_ACCESS, 
+		       FILE_SHARE_READ, NULL,
+                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    sizeLo = GetFileSize(hFile, &sizeHi);
+    if (sizeLo == INVALID_FILE_SIZE) {
+        DWORD dwStatus = GetLastError();
+        if (dwStatus != NO_ERROR) {
+            CloseHandle(hFile);
+            return PJ_RETURN_OS_ERROR(dwStatus);
+        }
+    }
+
+    stat->size = sizeHi;
+    stat->size = (stat->size << 32) + sizeLo;
+
+    if (GetFileTime(hFile, &creationTime, &accessTime, &writeTime)==FALSE) {
+        DWORD dwStatus = GetLastError();
+        CloseHandle(hFile);
+        return PJ_RETURN_OS_ERROR(dwStatus);
+    }
+
+    CloseHandle(hFile);
+
+    if (file_time_to_time_val(&creationTime, &stat->ctime) != PJ_SUCCESS)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+
+    file_time_to_time_val(&accessTime, &stat->atime);
+    file_time_to_time_val(&writeTime, &stat->mtime);
+
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/file_io_ansi.c b/jni/pjproject-android/pjlib/src/pj/file_io_ansi.c
new file mode 100644
index 0000000..487d496
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/file_io_ansi.c
@@ -0,0 +1,170 @@
+/* $Id: file_io_ansi.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/file_io.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <stdio.h>
+#include <errno.h>
+
+PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
+                                  const char *pathname, 
+                                  unsigned flags,
+                                  pj_oshandle_t *fd)
+{
+    char mode[8];
+    char *p = mode;
+
+    PJ_ASSERT_RETURN(pathname && fd, PJ_EINVAL);
+    PJ_UNUSED_ARG(pool);
+
+    if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
+        if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
+            *p++ = 'a';
+            if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY)
+                *p++ = '+';
+        } else {
+            /* This is invalid.
+             * Can not specify PJ_O_RDONLY with PJ_O_APPEND! 
+             */
+        }
+    } else {
+        if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
+            *p++ = 'r';
+            if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY)
+                *p++ = '+';
+        } else {
+            *p++ = 'w';
+        }
+    }
+
+    if (p==mode)
+        return PJ_EINVAL;
+
+    *p++ = 'b';
+    *p++ = '\0';
+
+    *fd = fopen(pathname, mode);
+    if (*fd == NULL)
+        return PJ_RETURN_OS_ERROR(errno);
+    
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
+{
+    PJ_ASSERT_RETURN(fd, PJ_EINVAL);
+    if (fclose((FILE*)fd) != 0)
+        return PJ_RETURN_OS_ERROR(errno);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
+                                   const void *data,
+                                   pj_ssize_t *size)
+{
+    size_t written;
+
+    clearerr((FILE*)fd);
+    written = fwrite(data, 1, *size, (FILE*)fd);
+    if (ferror((FILE*)fd)) {
+        *size = -1;
+        return PJ_RETURN_OS_ERROR(errno);
+    }
+
+    *size = written;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
+                                  void *data,
+                                  pj_ssize_t *size)
+{
+    size_t bytes;
+
+    clearerr((FILE*)fd);
+    bytes = fread(data, 1, *size, (FILE*)fd);
+    if (ferror((FILE*)fd)) {
+        *size = -1;
+        return PJ_RETURN_OS_ERROR(errno);
+    }
+
+    *size = bytes;
+    return PJ_SUCCESS;
+}
+
+/*
+PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
+{
+    PJ_UNUSED_ARG(access);
+    return feof((FILE*)fd) ? PJ_TRUE : 0;
+}
+*/
+
+PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
+                                    pj_off_t offset,
+                                    enum pj_file_seek_type whence)
+{
+    int mode;
+
+    switch (whence) {
+    case PJ_SEEK_SET:
+        mode = SEEK_SET; break;
+    case PJ_SEEK_CUR:
+        mode = SEEK_CUR; break;
+    case PJ_SEEK_END:
+        mode = SEEK_END; break;
+    default:
+        pj_assert(!"Invalid whence in file_setpos");
+        return PJ_EINVAL;
+    }
+
+    if (fseek((FILE*)fd, (long)offset, mode) != 0)
+        return PJ_RETURN_OS_ERROR(errno);
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
+                                    pj_off_t *pos)
+{
+    long offset;
+
+    offset = ftell((FILE*)fd);
+    if (offset == -1) {
+        *pos = -1;
+        return PJ_RETURN_OS_ERROR(errno);
+    }
+
+    *pos = offset;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_flush(pj_oshandle_t fd)
+{
+    int rc;
+
+    rc = fflush((FILE*)fd);
+    if (rc == EOF) {
+	return PJ_RETURN_OS_ERROR(errno);
+    }
+
+    return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/file_io_win32.c b/jni/pjproject-android/pjlib/src/pj/file_io_win32.c
new file mode 100644
index 0000000..cf72e4d
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/file_io_win32.c
@@ -0,0 +1,240 @@
+/* $Id: file_io_win32.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/file_io.h>
+#include <pj/unicode.h>
+#include <pj/errno.h>
+#include <pj/assert.h>
+
+#include <windows.h>
+
+#ifndef INVALID_SET_FILE_POINTER
+#   define INVALID_SET_FILE_POINTER     ((DWORD)-1)
+#endif
+
+/**
+ * Check for end-of-file condition on the specified descriptor.
+ *
+ * @param fd            The file descriptor.
+ * @param access        The desired access.
+ *
+ * @return              Non-zero if file is EOF.
+ */
+PJ_DECL(pj_bool_t) pj_file_eof(pj_oshandle_t fd, 
+                               enum pj_file_access access);
+
+
+PJ_DEF(pj_status_t) pj_file_open( pj_pool_t *pool,
+                                  const char *pathname, 
+                                  unsigned flags,
+                                  pj_oshandle_t *fd)
+{
+    PJ_DECL_UNICODE_TEMP_BUF(wpathname,256)
+    HANDLE hFile;
+    DWORD dwDesiredAccess = 0;
+    DWORD dwShareMode = 0;
+    DWORD dwCreationDisposition = 0;
+    DWORD dwFlagsAndAttributes = 0;
+
+    PJ_UNUSED_ARG(pool);
+
+    PJ_ASSERT_RETURN(pathname!=NULL, PJ_EINVAL);
+
+    if ((flags & PJ_O_WRONLY) == PJ_O_WRONLY) {
+        dwDesiredAccess |= GENERIC_WRITE;
+        if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
+#if !defined(PJ_WIN32_WINCE) || !PJ_WIN32_WINCE
+	    /* FILE_APPEND_DATA is invalid on WM2003 and WM5, but it seems
+	     * to be working on WM6. All are tested on emulator though.
+	     * Removing this also seem to work (i.e. data is appended), so
+	     * I guess this flag is "optional".
+	     * See http://trac.pjsip.org/repos/ticket/825
+	     */
+            dwDesiredAccess |= FILE_APPEND_DATA;
+#endif
+	    dwCreationDisposition |= OPEN_ALWAYS;
+        } else {
+            dwDesiredAccess &= ~(FILE_APPEND_DATA);
+            dwCreationDisposition |= CREATE_ALWAYS;
+        }
+    }
+    if ((flags & PJ_O_RDONLY) == PJ_O_RDONLY) {
+        dwDesiredAccess |= GENERIC_READ;
+        if (flags == PJ_O_RDONLY)
+            dwCreationDisposition |= OPEN_EXISTING;
+    }
+
+    if (dwDesiredAccess == 0) {
+        pj_assert(!"Invalid file open flags");
+        return PJ_EINVAL;
+    }
+
+    dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+    dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+
+    hFile = CreateFile(PJ_STRING_TO_NATIVE(pathname,wpathname,sizeof(wpathname)), 
+		       dwDesiredAccess, dwShareMode, NULL,
+                       dwCreationDisposition, dwFlagsAndAttributes, NULL);
+    if (hFile == INVALID_HANDLE_VALUE) {
+        *fd = 0;
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+
+    if ((flags & PJ_O_APPEND) == PJ_O_APPEND) {
+	pj_status_t status;
+
+	status = pj_file_setpos(hFile, 0, PJ_SEEK_END);
+	if (status != PJ_SUCCESS) {
+	    pj_file_close(hFile);
+	    return status;
+	}
+    }
+
+    *fd = hFile;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_close(pj_oshandle_t fd)
+{
+    if (CloseHandle(fd)==0)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_write( pj_oshandle_t fd,
+                                   const void *data,
+                                   pj_ssize_t *size)
+{
+    BOOL rc;
+    DWORD bytesWritten;
+
+    rc = WriteFile(fd, data, (DWORD)*size, &bytesWritten, NULL);
+    if (!rc) {
+        *size = -1;
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+
+    *size = bytesWritten;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_read( pj_oshandle_t fd,
+                                  void *data,
+                                  pj_ssize_t *size)
+{
+    BOOL rc;
+    DWORD bytesRead;
+
+    rc = ReadFile(fd, data, (DWORD)*size, &bytesRead, NULL);
+    if (!rc) {
+        *size = -1;
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+
+    *size = bytesRead;
+    return PJ_SUCCESS;
+}
+
+/*
+PJ_DEF(pj_bool_t) pj_file_eof(pj_oshandle_t fd, enum pj_file_access access)
+{
+    BOOL rc;
+    DWORD dummy = 0, bytes;
+    DWORD dwStatus;
+
+    if ((access & PJ_O_RDONLY) == PJ_O_RDONLY) {
+        rc = ReadFile(fd, &dummy, 0, &bytes, NULL);
+    } else if ((access & PJ_O_WRONLY) == PJ_O_WRONLY) {
+        rc = WriteFile(fd, &dummy, 0, &bytes, NULL);
+    } else {
+        pj_assert(!"Invalid access");
+        return PJ_TRUE;
+    }
+
+    dwStatus = GetLastError();
+    if (dwStatus==ERROR_HANDLE_EOF)
+        return PJ_TRUE;
+
+    return 0;
+}
+*/
+
+PJ_DEF(pj_status_t) pj_file_setpos( pj_oshandle_t fd,
+                                    pj_off_t offset,
+                                    enum pj_file_seek_type whence)
+{
+    DWORD dwMoveMethod;
+    DWORD dwNewPos;
+    LONG  hi32;
+
+    if (whence == PJ_SEEK_SET)
+        dwMoveMethod = FILE_BEGIN;
+    else if (whence == PJ_SEEK_CUR)
+        dwMoveMethod = FILE_CURRENT;
+    else if (whence == PJ_SEEK_END)
+        dwMoveMethod = FILE_END;
+    else {
+        pj_assert(!"Invalid whence in file_setpos");
+        return PJ_EINVAL;
+    }
+
+    hi32 = (LONG)(offset >> 32);
+    dwNewPos = SetFilePointer(fd, (long)offset, &hi32, dwMoveMethod);
+    if (dwNewPos == (DWORD)INVALID_SET_FILE_POINTER) {
+        DWORD dwStatus = GetLastError();
+        if (dwStatus != 0)
+            return PJ_RETURN_OS_ERROR(dwStatus);
+        /* dwNewPos actually is not an error. */
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_getpos( pj_oshandle_t fd,
+                                    pj_off_t *pos)
+{
+    LONG hi32 = 0;
+    DWORD lo32;
+
+    lo32 = SetFilePointer(fd, 0, &hi32, FILE_CURRENT);
+    if (lo32 == (DWORD)INVALID_SET_FILE_POINTER) {
+        DWORD dwStatus = GetLastError();
+        if (dwStatus != 0)
+            return PJ_RETURN_OS_ERROR(dwStatus);
+    }
+
+    *pos = hi32;
+    *pos = (*pos << 32) + lo32;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_file_flush(pj_oshandle_t fd)
+{
+    BOOL rc;
+
+    rc = FlushFileBuffers(fd);
+
+    if (!rc) {
+	DWORD dwStatus = GetLastError();
+        if (dwStatus != 0)
+            return PJ_RETURN_OS_ERROR(dwStatus);
+    }
+
+    return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/guid.c b/jni/pjproject-android/pjlib/src/pj/guid.c
new file mode 100644
index 0000000..88b6c37
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/guid.c
@@ -0,0 +1,48 @@
+/* $Id: guid.c 4208 2012-07-18 07:52:33Z ming $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ctype.h>
+#include <pj/guid.h>
+#include <pj/pool.h>
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string_lower(pj_str_t *str)
+{
+    int i;
+
+    pj_generate_unique_string(str);
+    for (i = 0; i < str->slen; i++)
+	str->ptr[i] = (char)pj_tolower(str->ptr[i]);
+
+    return str;
+}
+
+PJ_DEF(void) pj_create_unique_string(pj_pool_t *pool, pj_str_t *str)
+{
+    str->ptr = (char*)pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH);
+    pj_generate_unique_string(str);
+}
+
+PJ_DEF(void) pj_create_unique_string_lower(pj_pool_t *pool, pj_str_t *str)
+{
+    int i;
+
+    pj_create_unique_string(pool, str);
+    for (i = 0; i < str->slen; i++)
+	str->ptr[i] = (char)pj_tolower(str->ptr[i]);
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/guid_simple.c b/jni/pjproject-android/pjlib/src/pj/guid_simple.c
new file mode 100644
index 0000000..8bf8ae9
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/guid_simple.c
@@ -0,0 +1,81 @@
+/* $Id: guid_simple.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/guid.h>
+#include <pj/assert.h>
+#include <pj/rand.h>
+#include <pj/os.h>
+#include <pj/string.h>
+
+PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=32;
+
+static char guid_chars[64];
+
+PJ_DEF(unsigned) pj_GUID_STRING_LENGTH()
+{
+    return PJ_GUID_STRING_LENGTH;
+}
+
+static void init_guid_chars(void)
+{
+    char *p = guid_chars;
+    unsigned i;
+
+    for (i=0; i<10; ++i)
+	*p++ = '0'+i;
+
+    for (i=0; i<26; ++i) {
+	*p++ = 'a'+i;
+	*p++ = 'A'+i;
+    }
+
+    *p++ = '-';
+    *p++ = '.';
+}
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+    char *p, *end;
+
+    PJ_CHECK_STACK();
+
+    if (guid_chars[0] == '\0') {
+	pj_enter_critical_section();
+	if (guid_chars[0] == '\0') {
+	    init_guid_chars();
+	}
+	pj_leave_critical_section();
+    }
+
+    /* This would only work if PJ_GUID_STRING_LENGTH is multiple of 2 bytes */
+    pj_assert(PJ_GUID_STRING_LENGTH % 2 == 0);
+
+    for (p=str->ptr, end=p+PJ_GUID_STRING_LENGTH; p<end; ) {
+	pj_uint32_t rand_val = pj_rand();
+	pj_uint32_t rand_idx = RAND_MAX;
+
+	for ( ; rand_idx>0 && p<end; rand_idx>>=8, rand_val>>=8, p++) {
+	    *p = guid_chars[(rand_val & 0xFF) & 63];
+	}
+    }
+
+    str->slen = PJ_GUID_STRING_LENGTH;
+    return str;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/guid_uuid.c b/jni/pjproject-android/pjlib/src/pj/guid_uuid.c
new file mode 100644
index 0000000..b850bed
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/guid_uuid.c
@@ -0,0 +1,53 @@
+/* $Id: guid_uuid.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/guid.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/os.h>
+#include <pj/string.h>
+
+#include <uuid/uuid.h>
+
+PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=36;
+
+PJ_DEF(unsigned) pj_GUID_STRING_LENGTH()
+{
+    return PJ_GUID_STRING_LENGTH;
+}
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+    enum {GUID_LEN = 36};
+    char sguid[GUID_LEN + 1];
+    uuid_t uuid = {0};
+    
+    PJ_ASSERT_RETURN(GUID_LEN <= PJ_GUID_STRING_LENGTH, NULL);
+    PJ_ASSERT_RETURN(str->ptr != NULL, NULL);
+    PJ_CHECK_STACK();
+    
+    uuid_generate(uuid);
+    uuid_unparse(uuid, sguid);
+    
+    pj_memcpy(str->ptr, sguid, GUID_LEN);
+    str->slen = GUID_LEN;
+
+    return str;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/guid_win32.c b/jni/pjproject-android/pjlib/src/pj/guid_win32.c
new file mode 100644
index 0000000..c40fcad
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/guid_win32.c
@@ -0,0 +1,72 @@
+/* $Id: guid_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/guid.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <windows.h>
+#include <objbase.h>
+#include <pj/os.h>
+
+
+PJ_DEF_DATA(const unsigned) PJ_GUID_STRING_LENGTH=32;
+
+PJ_DEF(unsigned) pj_GUID_STRING_LENGTH()
+{
+    return PJ_GUID_STRING_LENGTH;
+}
+
+PJ_INLINE(void) hex2digit(unsigned value, char *p)
+{
+    static char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+			 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+    *p++ = hex[ (value & 0xF0) >> 4 ];
+    *p++ = hex[ (value & 0x0F) ];
+}
+
+static void guid_to_str( GUID *guid, pj_str_t *str )
+{
+    unsigned i;
+    const unsigned char *src = (const unsigned char*)guid;
+    char *dst = str->ptr;
+
+    guid->Data1 = pj_ntohl(guid->Data1);
+    guid->Data2 = pj_ntohs(guid->Data2);
+    guid->Data3 = pj_ntohs(guid->Data3);
+
+    for (i=0; i<16; ++i) {
+	hex2digit( *src, dst );
+	dst += 2;
+	++src;
+    }
+    str->slen = 32;
+}
+
+
+PJ_DEF(pj_str_t*) pj_generate_unique_string(pj_str_t *str)
+{
+    GUID guid;
+
+    PJ_CHECK_STACK();
+
+    CoCreateGuid(&guid);
+    guid_to_str( &guid, str );
+    return str;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/hash.c b/jni/pjproject-android/pjlib/src/pj/hash.c
new file mode 100644
index 0000000..ab58fb4
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/hash.c
@@ -0,0 +1,378 @@
+/* $Id: hash.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/hash.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/ctype.h>
+#include <pj/assert.h>
+
+/**
+ * The hash multiplier used to calculate hash value.
+ */
+#define PJ_HASH_MULTIPLIER	33
+
+
+struct pj_hash_entry
+{
+    struct pj_hash_entry *next;
+    void *key;
+    pj_uint32_t hash;
+    pj_uint32_t keylen;
+    void *value;
+};
+
+
+struct pj_hash_table_t
+{
+    pj_hash_entry     **table;
+    unsigned		count, rows;
+    pj_hash_iterator_t	iterator;
+};
+
+
+
+PJ_DEF(pj_uint32_t) pj_hash_calc(pj_uint32_t hash, const void *key, 
+				 unsigned keylen)
+{
+    PJ_CHECK_STACK();
+
+    if (keylen==PJ_HASH_KEY_STRING) {
+	const pj_uint8_t *p = (const pj_uint8_t*)key;
+	for ( ; *p; ++p ) {
+	    hash = (hash * PJ_HASH_MULTIPLIER) + *p;
+	}
+    } else {
+	const pj_uint8_t *p = (const pj_uint8_t*)key,
+			      *end = p + keylen;
+	for ( ; p!=end; ++p) {
+	    hash = (hash * PJ_HASH_MULTIPLIER) + *p;
+	}
+    }
+    return hash;
+}
+
+PJ_DEF(pj_uint32_t) pj_hash_calc_tolower( pj_uint32_t hval,
+                                          char *result,
+                                          const pj_str_t *key)
+{
+    long i;
+
+#if defined(PJ_HASH_USE_OWN_TOLOWER) && PJ_HASH_USE_OWN_TOLOWER != 0
+    for (i=0; i<key->slen; ++i) {
+	pj_uint8_t c = key->ptr[i];
+        char lower;
+	if (c & 64)
+	    lower = (char)(c | 32);
+	else
+	    lower = (char)c;
+	if (result)
+            result[i] = lower;
+	hval = hval * PJ_HASH_MULTIPLIER + lower;
+    }
+#else
+    for (i=0; i<key->slen; ++i) {
+        char lower = (char)pj_tolower(key->ptr[i]);
+	if (result)
+            result[i] = lower;
+	hval = hval * PJ_HASH_MULTIPLIER + lower;
+    }
+#endif
+
+    return hval;
+}
+
+
+PJ_DEF(pj_hash_table_t*) pj_hash_create(pj_pool_t *pool, unsigned size)
+{
+    pj_hash_table_t *h;
+    unsigned table_size;
+    
+    /* Check that PJ_HASH_ENTRY_BUF_SIZE is correct. */
+    PJ_ASSERT_RETURN(sizeof(pj_hash_entry)<=PJ_HASH_ENTRY_BUF_SIZE, NULL);
+
+    h = PJ_POOL_ALLOC_T(pool, pj_hash_table_t);
+    h->count = 0;
+
+    PJ_LOG( 6, ("hashtbl", "hash table %p created from pool %s", h, pj_pool_getobjname(pool)));
+
+    /* size must be 2^n - 1.
+       round-up the size to this rule, except when size is 2^n, then size
+       will be round-down to 2^n-1.
+     */
+    table_size = 8;
+    do {
+	table_size <<= 1;    
+    } while (table_size < size);
+    table_size -= 1;
+    
+    h->rows = table_size;
+    h->table = (pj_hash_entry**)
+    	       pj_pool_calloc(pool, table_size+1, sizeof(pj_hash_entry*));
+    return h;
+}
+
+static pj_hash_entry **find_entry( pj_pool_t *pool, pj_hash_table_t *ht, 
+				   const void *key, unsigned keylen,
+				   void *val, pj_uint32_t *hval,
+				   void *entry_buf, pj_bool_t lower)
+{
+    pj_uint32_t hash;
+    pj_hash_entry **p_entry, *entry;
+
+    if (hval && *hval != 0) {
+	hash = *hval;
+	if (keylen==PJ_HASH_KEY_STRING) {
+	    keylen = (unsigned)pj_ansi_strlen((const char*)key);
+	}
+    } else {
+	/* This slightly differs with pj_hash_calc() because we need 
+	 * to get the keylen when keylen is PJ_HASH_KEY_STRING.
+	 */
+	hash=0;
+	if (keylen==PJ_HASH_KEY_STRING) {
+	    const pj_uint8_t *p = (const pj_uint8_t*)key;
+	    for ( ; *p; ++p ) {
+                if (lower)
+                    hash = hash * PJ_HASH_MULTIPLIER + pj_tolower(*p);
+                else 
+		    hash = hash * PJ_HASH_MULTIPLIER + *p;
+	    }
+	    keylen = (unsigned)(p - (const unsigned char*)key);
+	} else {
+	    const pj_uint8_t *p = (const pj_uint8_t*)key,
+				  *end = p + keylen;
+	    for ( ; p!=end; ++p) {
+		if (lower)
+                    hash = hash * PJ_HASH_MULTIPLIER + pj_tolower(*p);
+                else
+                    hash = hash * PJ_HASH_MULTIPLIER + *p;
+	    }
+	}
+
+	/* Report back the computed hash. */
+	if (hval)
+	    *hval = hash;
+    }
+
+    /* scan the linked list */
+    for (p_entry = &ht->table[hash & ht->rows], entry=*p_entry; 
+	 entry; 
+	 p_entry = &entry->next, entry = *p_entry)
+    {
+	if (entry->hash==hash && entry->keylen==keylen &&
+            ((lower && pj_ansi_strnicmp((const char*)entry->key,
+        			        (const char*)key, keylen)==0) ||
+	     (!lower && pj_memcmp(entry->key, key, keylen)==0)))
+	{
+	    break;
+	}
+    }
+
+    if (entry || val==NULL)
+	return p_entry;
+
+    /* Entry not found, create a new one. 
+     * If entry_buf is specified, use it. Otherwise allocate from pool.
+     */
+    if (entry_buf) {
+	entry = (pj_hash_entry*)entry_buf;
+    } else {
+	/* Pool must be specified! */
+	PJ_ASSERT_RETURN(pool != NULL, NULL);
+
+	entry = PJ_POOL_ALLOC_T(pool, pj_hash_entry);
+	PJ_LOG(6, ("hashtbl", 
+		   "%p: New p_entry %p created, pool used=%u, cap=%u", 
+		   ht, entry,  pj_pool_get_used_size(pool), 
+		   pj_pool_get_capacity(pool)));
+    }
+    entry->next = NULL;
+    entry->hash = hash;
+    if (pool) {
+	entry->key = pj_pool_alloc(pool, keylen);
+	pj_memcpy(entry->key, key, keylen);
+    } else {
+	entry->key = (void*)key;
+    }
+    entry->keylen = keylen;
+    entry->value = val;
+    *p_entry = entry;
+    
+    ++ht->count;
+    
+    return p_entry;
+}
+
+PJ_DEF(void *) pj_hash_get( pj_hash_table_t *ht,
+			    const void *key, unsigned keylen,
+			    pj_uint32_t *hval)
+{
+    pj_hash_entry *entry;
+    entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL, PJ_FALSE);
+    return entry ? entry->value : NULL;
+}
+
+PJ_DEF(void *) pj_hash_get_lower( pj_hash_table_t *ht,
+			          const void *key, unsigned keylen,
+			          pj_uint32_t *hval)
+{
+    pj_hash_entry *entry;
+    entry = *find_entry( NULL, ht, key, keylen, NULL, hval, NULL, PJ_TRUE);
+    return entry ? entry->value : NULL;
+}
+
+static void hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+	              const void *key, unsigned keylen, pj_uint32_t hval,
+		      void *value, void *entry_buf, pj_bool_t lower )
+{
+    pj_hash_entry **p_entry;
+
+    p_entry = find_entry( pool, ht, key, keylen, value, &hval, entry_buf,
+                          lower);
+    if (*p_entry) {
+	if (value == NULL) {
+	    /* delete entry */
+	    PJ_LOG(6, ("hashtbl", "%p: p_entry %p deleted", ht, *p_entry));
+	    *p_entry = (*p_entry)->next;
+	    --ht->count;
+	    
+	} else {
+	    /* overwrite */
+	    (*p_entry)->value = value;
+	    PJ_LOG(6, ("hashtbl", "%p: p_entry %p value set to %p", ht, 
+		       *p_entry, value));
+	}
+    }
+}
+
+PJ_DEF(void) pj_hash_set( pj_pool_t *pool, pj_hash_table_t *ht,
+			  const void *key, unsigned keylen, pj_uint32_t hval,
+			  void *value )
+{
+    hash_set(pool, ht, key, keylen, hval, value, NULL, PJ_FALSE);
+}
+
+PJ_DEF(void) pj_hash_set_lower( pj_pool_t *pool, pj_hash_table_t *ht,
+			        const void *key, unsigned keylen,
+                                pj_uint32_t hval, void *value )
+{
+    hash_set(pool, ht, key, keylen, hval, value, NULL, PJ_TRUE);
+}
+
+PJ_DEF(void) pj_hash_set_np( pj_hash_table_t *ht,
+			     const void *key, unsigned keylen, 
+			     pj_uint32_t hval, pj_hash_entry_buf entry_buf, 
+			     void *value)
+{
+    hash_set(NULL, ht, key, keylen, hval, value, (void *)entry_buf, PJ_FALSE);
+}
+
+PJ_DEF(void) pj_hash_set_np_lower( pj_hash_table_t *ht,
+			           const void *key, unsigned keylen,
+			           pj_uint32_t hval,
+                                   pj_hash_entry_buf entry_buf,
+			           void *value)
+{
+    hash_set(NULL, ht, key, keylen, hval, value, (void *)entry_buf, PJ_TRUE);
+}
+
+PJ_DEF(unsigned) pj_hash_count( pj_hash_table_t *ht )
+{
+    return ht->count;
+}
+
+PJ_DEF(pj_hash_iterator_t*) pj_hash_first( pj_hash_table_t *ht,
+					   pj_hash_iterator_t *it )
+{
+    it->index = 0;
+    it->entry = NULL;
+
+    for (; it->index <= ht->rows; ++it->index) {
+	it->entry = ht->table[it->index];
+	if (it->entry) {
+	    break;
+	}
+    }
+
+    return it->entry ? it : NULL;
+}
+
+PJ_DEF(pj_hash_iterator_t*) pj_hash_next( pj_hash_table_t *ht, 
+					  pj_hash_iterator_t *it )
+{
+    it->entry = it->entry->next;
+    if (it->entry) {
+	return it;
+    }
+
+    for (++it->index; it->index <= ht->rows; ++it->index) {
+	it->entry = ht->table[it->index];
+	if (it->entry) {
+	    break;
+	}
+    }
+
+    return it->entry ? it : NULL;
+}
+
+PJ_DEF(void*) pj_hash_this( pj_hash_table_t *ht, pj_hash_iterator_t *it )
+{
+    PJ_CHECK_STACK();
+    PJ_UNUSED_ARG(ht);
+    return it->entry->value;
+}
+
+#if 0
+void pj_hash_dump_collision( pj_hash_table_t *ht )
+{
+    unsigned min=0xFFFFFFFF, max=0;
+    unsigned i;
+    char line[120];
+    int len, totlen = 0;
+
+    for (i=0; i<=ht->rows; ++i) {
+	unsigned count = 0;    
+	pj_hash_entry *entry = ht->table[i];
+	while (entry) {
+	    ++count;
+	    entry = entry->next;
+	}
+	if (count < min)
+	    min = count;
+	if (count > max)
+	    max = count;
+	len = pj_snprintf( line+totlen, sizeof(line)-totlen, "%3d:%3d ", i, count);
+	if (len < 1)
+	    break;
+	totlen += len;
+
+	if ((i+1) % 10 == 0) {
+	    line[totlen] = '\0';
+	    PJ_LOG(4,(__FILE__, line));
+	}
+    }
+
+    PJ_LOG(4,(__FILE__,"Count: %d, min: %d, max: %d\n", ht->count, min, max));
+}
+#endif
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ioqueue_common_abs.c b/jni/pjproject-android/pjlib/src/pj/ioqueue_common_abs.c
new file mode 100644
index 0000000..7319f53
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ioqueue_common_abs.c
@@ -0,0 +1,1336 @@
+/* $Id: ioqueue_common_abs.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * ioqueue_common_abs.c
+ *
+ * This contains common functionalities to emulate proactor pattern with
+ * various event dispatching mechanisms (e.g. select, epoll).
+ *
+ * This file will be included by the appropriate ioqueue implementation.
+ * This file is NOT supposed to be compiled as stand-alone source.
+ */
+
+#define PENDING_RETRY	2
+
+static void ioqueue_init( pj_ioqueue_t *ioqueue )
+{
+    ioqueue->lock = NULL;
+    ioqueue->auto_delete_lock = 0;
+    ioqueue->default_concurrency = PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY;
+}
+
+static pj_status_t ioqueue_destroy(pj_ioqueue_t *ioqueue)
+{
+    if (ioqueue->auto_delete_lock && ioqueue->lock ) {
+	pj_lock_release(ioqueue->lock);
+        return pj_lock_destroy(ioqueue->lock);
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_set_lock()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue, 
+					 pj_lock_t *lock,
+					 pj_bool_t auto_delete )
+{
+    PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL);
+
+    if (ioqueue->auto_delete_lock && ioqueue->lock) {
+        pj_lock_destroy(ioqueue->lock);
+    }
+
+    ioqueue->lock = lock;
+    ioqueue->auto_delete_lock = auto_delete;
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t ioqueue_init_key( pj_pool_t *pool,
+                                     pj_ioqueue_t *ioqueue,
+                                     pj_ioqueue_key_t *key,
+                                     pj_sock_t sock,
+                                     pj_grp_lock_t *grp_lock,
+                                     void *user_data,
+                                     const pj_ioqueue_callback *cb)
+{
+    pj_status_t rc;
+    int optlen;
+
+    PJ_UNUSED_ARG(pool);
+
+    key->ioqueue = ioqueue;
+    key->fd = sock;
+    key->user_data = user_data;
+    pj_list_init(&key->read_list);
+    pj_list_init(&key->write_list);
+#if PJ_HAS_TCP
+    pj_list_init(&key->accept_list);
+    key->connecting = 0;
+#endif
+
+    /* Save callback. */
+    pj_memcpy(&key->cb, cb, sizeof(pj_ioqueue_callback));
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Set initial reference count to 1 */
+    pj_assert(key->ref_count == 0);
+    ++key->ref_count;
+
+    key->closing = 0;
+#endif
+
+    rc = pj_ioqueue_set_concurrency(key, ioqueue->default_concurrency);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    /* Get socket type. When socket type is datagram, some optimization
+     * will be performed during send to allow parallel send operations.
+     */
+    optlen = sizeof(key->fd_type);
+    rc = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), pj_SO_TYPE(),
+                            &key->fd_type, &optlen);
+    if (rc != PJ_SUCCESS)
+        key->fd_type = pj_SOCK_STREAM();
+
+    /* Create mutex for the key. */
+#if !PJ_IOQUEUE_HAS_SAFE_UNREG
+    rc = pj_lock_create_simple_mutex(poll, NULL, &key->lock);
+#endif
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    /* Group lock */
+    key->grp_lock = grp_lock;
+    if (key->grp_lock) {
+	pj_grp_lock_add_ref_dbg(key->grp_lock, "ioqueue", 0);
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_get_user_data()
+ *
+ * Obtain value associated with a key.
+ */
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+    PJ_ASSERT_RETURN(key != NULL, NULL);
+    return key->user_data;
+}
+
+/*
+ * pj_ioqueue_set_user_data()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
+                                              void *user_data,
+                                              void **old_data)
+{
+    PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+    if (old_data)
+        *old_data = key->user_data;
+    key->user_data = user_data;
+
+    return PJ_SUCCESS;
+}
+
+PJ_INLINE(int) key_has_pending_write(pj_ioqueue_key_t *key)
+{
+    return !pj_list_empty(&key->write_list);
+}
+
+PJ_INLINE(int) key_has_pending_read(pj_ioqueue_key_t *key)
+{
+    return !pj_list_empty(&key->read_list);
+}
+
+PJ_INLINE(int) key_has_pending_accept(pj_ioqueue_key_t *key)
+{
+#if PJ_HAS_TCP
+    return !pj_list_empty(&key->accept_list);
+#else
+    PJ_UNUSED_ARG(key);
+    return 0;
+#endif
+}
+
+PJ_INLINE(int) key_has_pending_connect(pj_ioqueue_key_t *key)
+{
+    return key->connecting;
+}
+
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+#   define IS_CLOSING(key)  (key->closing)
+#else
+#   define IS_CLOSING(key)  (0)
+#endif
+
+
+/*
+ * ioqueue_dispatch_event()
+ *
+ * Report occurence of an event in the key to be processed by the
+ * framework.
+ */
+void ioqueue_dispatch_write_event(pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h)
+{
+    /* Lock the key. */
+    pj_ioqueue_lock_key(h);
+
+    if (IS_CLOSING(h)) {
+	pj_ioqueue_unlock_key(h);
+	return;
+    }
+
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
+    if (h->connecting) {
+	/* Completion of connect() operation */
+	pj_status_t status;
+	pj_bool_t has_lock;
+
+	/* Clear operation. */
+	h->connecting = 0;
+
+        ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT);
+        ioqueue_remove_from_set(ioqueue, h, EXCEPTION_EVENT);
+
+
+#if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0)
+	/* from connect(2): 
+	 * On Linux, use getsockopt to read the SO_ERROR option at
+	 * level SOL_SOCKET to determine whether connect() completed
+	 * successfully (if SO_ERROR is zero).
+	 */
+	{
+	  int value;
+	  int vallen = sizeof(value);
+	  int gs_rc = pj_sock_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, 
+					 &value, &vallen);
+	  if (gs_rc != 0) {
+	    /* Argh!! What to do now??? 
+	     * Just indicate that the socket is connected. The
+	     * application will get error as soon as it tries to use
+	     * the socket to send/receive.
+	     */
+	      status = PJ_SUCCESS;
+	  } else {
+	      status = PJ_STATUS_FROM_OS(value);
+	  }
+ 	}
+#elif (defined(PJ_WIN32) && PJ_WIN32!=0) || (defined(PJ_WIN64) && PJ_WIN64!=0) 
+	status = PJ_SUCCESS; /* success */
+#else
+	/* Excellent information in D.J. Bernstein page:
+	 * http://cr.yp.to/docs/connect.html
+	 *
+	 * Seems like the most portable way of detecting connect()
+	 * failure is to call getpeername(). If socket is connected,
+	 * getpeername() will return 0. If the socket is not connected,
+	 * it will return ENOTCONN, and read(fd, &ch, 1) will produce
+	 * the right errno through error slippage. This is a combination
+	 * of suggestions from Douglas C. Schmidt and Ken Keys.
+	 */
+	{
+	    struct sockaddr_in addr;
+	    int addrlen = sizeof(addr);
+
+	    status = pj_sock_getpeername(h->fd, (struct sockaddr*)&addr,
+				         &addrlen);
+	}
+#endif
+
+        /* Unlock; from this point we don't need to hold key's mutex
+	 * (unless concurrency is disabled, which in this case we should
+	 * hold the mutex while calling the callback) */
+	if (h->allow_concurrent) {
+	    /* concurrency may be changed while we're in the callback, so
+	     * save it to a flag.
+	     */
+	    has_lock = PJ_FALSE;
+	    pj_ioqueue_unlock_key(h);
+	} else {
+	    has_lock = PJ_TRUE;
+	}
+
+	/* Call callback. */
+        if (h->cb.on_connect_complete && !IS_CLOSING(h))
+	    (*h->cb.on_connect_complete)(h, status);
+
+	/* Unlock if we still hold the lock */
+	if (has_lock) {
+	    pj_ioqueue_unlock_key(h);
+	}
+
+        /* Done. */
+
+    } else 
+#endif /* PJ_HAS_TCP */
+    if (key_has_pending_write(h)) {
+	/* Socket is writable. */
+        struct write_operation *write_op;
+        pj_ssize_t sent;
+        pj_status_t send_rc = PJ_SUCCESS;
+
+        /* Get the first in the queue. */
+        write_op = h->write_list.next;
+
+        /* For datagrams, we can remove the write_op from the list
+         * so that send() can work in parallel.
+         */
+        if (h->fd_type == pj_SOCK_DGRAM()) {
+            pj_list_erase(write_op);
+
+            if (pj_list_empty(&h->write_list))
+                ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT);
+
+        }
+
+        /* Send the data. 
+         * Unfortunately we must do this while holding key's mutex, thus
+         * preventing parallel write on a single key.. :-((
+         */
+        sent = write_op->size - write_op->written;
+        if (write_op->op == PJ_IOQUEUE_OP_SEND) {
+            send_rc = pj_sock_send(h->fd, write_op->buf+write_op->written,
+                                   &sent, write_op->flags);
+	    /* Can't do this. We only clear "op" after we're finished sending
+	     * the whole buffer.
+	     */
+	    //write_op->op = 0;
+        } else if (write_op->op == PJ_IOQUEUE_OP_SEND_TO) {
+	    int retry = 2;
+	    while (--retry >= 0) {
+		send_rc = pj_sock_sendto(h->fd, 
+					 write_op->buf+write_op->written,
+					 &sent, write_op->flags,
+					 &write_op->rmt_addr, 
+					 write_op->rmt_addrlen);
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+	    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+		/* Special treatment for dead UDP sockets here, see ticket #1107 */
+		if (send_rc==PJ_STATUS_FROM_OS(EPIPE) && !IS_CLOSING(h) &&
+		    h->fd_type==pj_SOCK_DGRAM())
+		{
+		    PJ_PERROR(4,(THIS_FILE, send_rc,
+				 "Send error for socket %d, retrying",
+				 h->fd));
+		    replace_udp_sock(h);
+		    continue;
+		}
+#endif
+		break;
+	    }
+
+	    /* Can't do this. We only clear "op" after we're finished sending
+	     * the whole buffer.
+	     */
+	    //write_op->op = 0;
+        } else {
+            pj_assert(!"Invalid operation type!");
+	    write_op->op = PJ_IOQUEUE_OP_NONE;
+            send_rc = PJ_EBUG;
+        }
+
+        if (send_rc == PJ_SUCCESS) {
+            write_op->written += sent;
+        } else {
+            pj_assert(send_rc > 0);
+            write_op->written = -send_rc;
+        }
+
+        /* Are we finished with this buffer? */
+        if (send_rc!=PJ_SUCCESS || 
+            write_op->written == (pj_ssize_t)write_op->size ||
+            h->fd_type == pj_SOCK_DGRAM()) 
+        {
+	    pj_bool_t has_lock;
+
+	    write_op->op = PJ_IOQUEUE_OP_NONE;
+
+            if (h->fd_type != pj_SOCK_DGRAM()) {
+                /* Write completion of the whole stream. */
+                pj_list_erase(write_op);
+
+                /* Clear operation if there's no more data to send. */
+                if (pj_list_empty(&h->write_list))
+                    ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT);
+
+            }
+
+	    /* Unlock; from this point we don't need to hold key's mutex
+	     * (unless concurrency is disabled, which in this case we should
+	     * hold the mutex while calling the callback) */
+	    if (h->allow_concurrent) {
+		/* concurrency may be changed while we're in the callback, so
+		 * save it to a flag.
+		 */
+		has_lock = PJ_FALSE;
+		pj_ioqueue_unlock_key(h);
+		PJ_RACE_ME(5);
+	    } else {
+		has_lock = PJ_TRUE;
+	    }
+
+	    /* Call callback. */
+            if (h->cb.on_write_complete && !IS_CLOSING(h)) {
+	        (*h->cb.on_write_complete)(h, 
+                                           (pj_ioqueue_op_key_t*)write_op,
+                                           write_op->written);
+            }
+
+	    if (has_lock) {
+		pj_ioqueue_unlock_key(h);
+	    }
+
+        } else {
+            pj_ioqueue_unlock_key(h);
+        }
+
+        /* Done. */
+    } else {
+        /*
+         * This is normal; execution may fall here when multiple threads
+         * are signalled for the same event, but only one thread eventually
+         * able to process the event.
+         */
+	pj_ioqueue_unlock_key(h);
+    }
+}
+
+void ioqueue_dispatch_read_event( pj_ioqueue_t *ioqueue, pj_ioqueue_key_t *h )
+{
+    pj_status_t rc;
+
+    /* Lock the key. */
+    pj_ioqueue_lock_key(h);
+
+    if (IS_CLOSING(h)) {
+	pj_ioqueue_unlock_key(h);
+	return;
+    }
+
+#   if PJ_HAS_TCP
+    if (!pj_list_empty(&h->accept_list)) {
+
+        struct accept_operation *accept_op;
+	pj_bool_t has_lock;
+	
+        /* Get one accept operation from the list. */
+	accept_op = h->accept_list.next;
+        pj_list_erase(accept_op);
+        accept_op->op = PJ_IOQUEUE_OP_NONE;
+
+	/* Clear bit in fdset if there is no more pending accept */
+        if (pj_list_empty(&h->accept_list))
+            ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT);
+
+	rc=pj_sock_accept(h->fd, accept_op->accept_fd, 
+                          accept_op->rmt_addr, accept_op->addrlen);
+	if (rc==PJ_SUCCESS && accept_op->local_addr) {
+	    rc = pj_sock_getsockname(*accept_op->accept_fd, 
+                                     accept_op->local_addr,
+				     accept_op->addrlen);
+	}
+
+	/* Unlock; from this point we don't need to hold key's mutex
+	 * (unless concurrency is disabled, which in this case we should
+	 * hold the mutex while calling the callback) */
+	if (h->allow_concurrent) {
+	    /* concurrency may be changed while we're in the callback, so
+	     * save it to a flag.
+	     */
+	    has_lock = PJ_FALSE;
+	    pj_ioqueue_unlock_key(h);
+	    PJ_RACE_ME(5);
+	} else {
+	    has_lock = PJ_TRUE;
+	}
+
+	/* Call callback. */
+        if (h->cb.on_accept_complete && !IS_CLOSING(h)) {
+	    (*h->cb.on_accept_complete)(h, 
+                                        (pj_ioqueue_op_key_t*)accept_op,
+                                        *accept_op->accept_fd, rc);
+	}
+
+	if (has_lock) {
+	    pj_ioqueue_unlock_key(h);
+	}
+    }
+    else
+#   endif
+    if (key_has_pending_read(h)) {
+        struct read_operation *read_op;
+        pj_ssize_t bytes_read;
+	pj_bool_t has_lock;
+
+        /* Get one pending read operation from the list. */
+        read_op = h->read_list.next;
+        pj_list_erase(read_op);
+
+        /* Clear fdset if there is no pending read. */
+        if (pj_list_empty(&h->read_list))
+            ioqueue_remove_from_set(ioqueue, h, READABLE_EVENT);
+
+        bytes_read = read_op->size;
+
+	if ((read_op->op == PJ_IOQUEUE_OP_RECV_FROM)) {
+	    read_op->op = PJ_IOQUEUE_OP_NONE;
+	    rc = pj_sock_recvfrom(h->fd, read_op->buf, &bytes_read, 
+				  read_op->flags,
+				  read_op->rmt_addr, 
+                                  read_op->rmt_addrlen);
+	} else if ((read_op->op == PJ_IOQUEUE_OP_RECV)) {
+	    read_op->op = PJ_IOQUEUE_OP_NONE;
+	    rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 
+			      read_op->flags);
+        } else {
+            pj_assert(read_op->op == PJ_IOQUEUE_OP_READ);
+	    read_op->op = PJ_IOQUEUE_OP_NONE;
+            /*
+             * User has specified pj_ioqueue_read().
+             * On Win32, we should do ReadFile(). But because we got
+             * here because of select() anyway, user must have put a
+             * socket descriptor on h->fd, which in this case we can
+             * just call pj_sock_recv() instead of ReadFile().
+             * On Unix, user may put a file in h->fd, so we'll have
+             * to call read() here.
+             * This may not compile on systems which doesn't have 
+             * read(). That's why we only specify PJ_LINUX here so
+             * that error is easier to catch.
+             */
+#	    if defined(PJ_WIN32) && PJ_WIN32 != 0 || \
+	       defined(PJ_WIN64) && PJ_WIN64 != 0 || \
+	       defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0
+                rc = pj_sock_recv(h->fd, read_op->buf, &bytes_read, 
+				  read_op->flags);
+                //rc = ReadFile((HANDLE)h->fd, read_op->buf, read_op->size,
+                //              &bytes_read, NULL);
+#           elif (defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0)
+                bytes_read = read(h->fd, read_op->buf, bytes_read);
+                rc = (bytes_read >= 0) ? PJ_SUCCESS : pj_get_os_error();
+#	    elif defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL != 0
+                bytes_read = sys_read(h->fd, read_op->buf, bytes_read);
+                rc = (bytes_read >= 0) ? PJ_SUCCESS : -bytes_read;
+#           else
+#               error "Implement read() for this platform!"
+#           endif
+        }
+	
+	if (rc != PJ_SUCCESS) {
+#	    if (defined(PJ_WIN32) && PJ_WIN32 != 0) || \
+	       (defined(PJ_WIN64) && PJ_WIN64 != 0) 
+	    /* On Win32, for UDP, WSAECONNRESET on the receive side 
+	     * indicates that previous sending has triggered ICMP Port 
+	     * Unreachable message.
+	     * But we wouldn't know at this point which one of previous 
+	     * key that has triggered the error, since UDP socket can
+	     * be shared!
+	     * So we'll just ignore it!
+	     */
+
+	    if (rc == PJ_STATUS_FROM_OS(WSAECONNRESET)) {
+		//PJ_LOG(4,(THIS_FILE, 
+                //          "Ignored ICMP port unreach. on key=%p", h));
+	    }
+#	    endif
+
+            /* In any case we would report this to caller. */
+            bytes_read = -rc;
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+	    /* Special treatment for dead UDP sockets here, see ticket #1107 */
+	    if (rc == PJ_STATUS_FROM_OS(ENOTCONN) && !IS_CLOSING(h) &&
+		h->fd_type==pj_SOCK_DGRAM())
+	    {
+		replace_udp_sock(h);
+	    }
+#endif
+	}
+
+	/* Unlock; from this point we don't need to hold key's mutex
+	 * (unless concurrency is disabled, which in this case we should
+	 * hold the mutex while calling the callback) */
+	if (h->allow_concurrent) {
+	    /* concurrency may be changed while we're in the callback, so
+	     * save it to a flag.
+	     */
+	    has_lock = PJ_FALSE;
+	    pj_ioqueue_unlock_key(h);
+	    PJ_RACE_ME(5);
+	} else {
+	    has_lock = PJ_TRUE;
+	}
+
+	/* Call callback. */
+        if (h->cb.on_read_complete && !IS_CLOSING(h)) {
+	    (*h->cb.on_read_complete)(h, 
+                                      (pj_ioqueue_op_key_t*)read_op,
+                                      bytes_read);
+        }
+
+	if (has_lock) {
+	    pj_ioqueue_unlock_key(h);
+	}
+
+    } else {
+        /*
+         * This is normal; execution may fall here when multiple threads
+         * are signalled for the same event, but only one thread eventually
+         * able to process the event.
+         */
+	pj_ioqueue_unlock_key(h);
+    }
+}
+
+
+void ioqueue_dispatch_exception_event( pj_ioqueue_t *ioqueue, 
+                                       pj_ioqueue_key_t *h )
+{
+    pj_bool_t has_lock;
+
+    pj_ioqueue_lock_key(h);
+
+    if (!h->connecting) {
+        /* It is possible that more than one thread was woken up, thus
+         * the remaining thread will see h->connecting as zero because
+         * it has been processed by other thread.
+         */
+	pj_ioqueue_unlock_key(h);
+        return;
+    }
+
+    if (IS_CLOSING(h)) {
+	pj_ioqueue_unlock_key(h);
+	return;
+    }
+
+    /* Clear operation. */
+    h->connecting = 0;
+
+    ioqueue_remove_from_set(ioqueue, h, WRITEABLE_EVENT);
+    ioqueue_remove_from_set(ioqueue, h, EXCEPTION_EVENT);
+
+    /* Unlock; from this point we don't need to hold key's mutex
+     * (unless concurrency is disabled, which in this case we should
+     * hold the mutex while calling the callback) */
+    if (h->allow_concurrent) {
+	/* concurrency may be changed while we're in the callback, so
+	 * save it to a flag.
+	 */
+	has_lock = PJ_FALSE;
+	pj_ioqueue_unlock_key(h);
+	PJ_RACE_ME(5);
+    } else {
+	has_lock = PJ_TRUE;
+    }
+
+    /* Call callback. */
+    if (h->cb.on_connect_complete && !IS_CLOSING(h)) {
+	pj_status_t status = -1;
+#if (defined(PJ_HAS_SO_ERROR) && PJ_HAS_SO_ERROR!=0)
+	int value;
+	int vallen = sizeof(value);
+	int gs_rc = pj_sock_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, 
+				       &value, &vallen);
+	if (gs_rc == 0) {
+	    status = PJ_RETURN_OS_ERROR(value);
+	}
+#endif
+
+	(*h->cb.on_connect_complete)(h, status);
+    }
+
+    if (has_lock) {
+	pj_ioqueue_unlock_key(h);
+    }
+}
+
+/*
+ * pj_ioqueue_recv()
+ *
+ * Start asynchronous recv() from the socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recv(  pj_ioqueue_key_t *key,
+                                      pj_ioqueue_op_key_t *op_key,
+				      void *buffer,
+				      pj_ssize_t *length,
+				      unsigned flags )
+{
+    struct read_operation *read_op;
+
+    PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
+    PJ_CHECK_STACK();
+
+    /* Check if key is closing (need to do this first before accessing
+     * other variables, since they might have been destroyed. See ticket
+     * #469).
+     */
+    if (IS_CLOSING(key))
+	return PJ_ECANCELLED;
+
+    read_op = (struct read_operation*)op_key;
+    read_op->op = PJ_IOQUEUE_OP_NONE;
+
+    /* Try to see if there's data immediately available. 
+     */
+    if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
+	pj_status_t status;
+	pj_ssize_t size;
+
+	size = *length;
+	status = pj_sock_recv(key->fd, buffer, &size, flags);
+	if (status == PJ_SUCCESS) {
+	    /* Yes! Data is available! */
+	    *length = size;
+	    return PJ_SUCCESS;
+	} else {
+	    /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+	     * the error to caller.
+	     */
+	    if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
+		return status;
+	}
+    }
+
+    flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
+
+    /*
+     * No data is immediately available.
+     * Must schedule asynchronous operation to the ioqueue.
+     */
+    read_op->op = PJ_IOQUEUE_OP_RECV;
+    read_op->buf = buffer;
+    read_op->size = *length;
+    read_op->flags = flags;
+
+    pj_ioqueue_lock_key(key);
+    /* Check again. Handle may have been closed after the previous check
+     * in multithreaded app. If we add bad handle to the set it will
+     * corrupt the ioqueue set. See #913
+     */
+    if (IS_CLOSING(key)) {
+	pj_ioqueue_unlock_key(key);
+	return PJ_ECANCELLED;
+    }
+    pj_list_insert_before(&key->read_list, read_op);
+    ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT);
+    pj_ioqueue_unlock_key(key);
+
+    return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_recvfrom()
+ *
+ * Start asynchronous recvfrom() from the socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
+                                         pj_ioqueue_op_key_t *op_key,
+				         void *buffer,
+				         pj_ssize_t *length,
+                                         unsigned flags,
+				         pj_sockaddr_t *addr,
+				         int *addrlen)
+{
+    struct read_operation *read_op;
+
+    PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
+    PJ_CHECK_STACK();
+
+    /* Check if key is closing. */
+    if (IS_CLOSING(key))
+	return PJ_ECANCELLED;
+
+    read_op = (struct read_operation*)op_key;
+    read_op->op = PJ_IOQUEUE_OP_NONE;
+
+    /* Try to see if there's data immediately available. 
+     */
+    if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
+	pj_status_t status;
+	pj_ssize_t size;
+
+	size = *length;
+	status = pj_sock_recvfrom(key->fd, buffer, &size, flags,
+				  addr, addrlen);
+	if (status == PJ_SUCCESS) {
+	    /* Yes! Data is available! */
+	    *length = size;
+	    return PJ_SUCCESS;
+	} else {
+	    /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+	     * the error to caller.
+	     */
+	    if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
+		return status;
+	}
+    }
+
+    flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
+
+    /*
+     * No data is immediately available.
+     * Must schedule asynchronous operation to the ioqueue.
+     */
+    read_op->op = PJ_IOQUEUE_OP_RECV_FROM;
+    read_op->buf = buffer;
+    read_op->size = *length;
+    read_op->flags = flags;
+    read_op->rmt_addr = addr;
+    read_op->rmt_addrlen = addrlen;
+
+    pj_ioqueue_lock_key(key);
+    /* Check again. Handle may have been closed after the previous check
+     * in multithreaded app. If we add bad handle to the set it will
+     * corrupt the ioqueue set. See #913
+     */
+    if (IS_CLOSING(key)) {
+	pj_ioqueue_unlock_key(key);
+	return PJ_ECANCELLED;
+    }
+    pj_list_insert_before(&key->read_list, read_op);
+    ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT);
+    pj_ioqueue_unlock_key(key);
+
+    return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_send()
+ *
+ * Start asynchronous send() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
+                                     pj_ioqueue_op_key_t *op_key,
+			             const void *data,
+			             pj_ssize_t *length,
+                                     unsigned flags)
+{
+    struct write_operation *write_op;
+    pj_status_t status;
+    unsigned retry;
+    pj_ssize_t sent;
+
+    PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL);
+    PJ_CHECK_STACK();
+
+    /* Check if key is closing. */
+    if (IS_CLOSING(key))
+	return PJ_ECANCELLED;
+
+    /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write. */
+    flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
+
+    /* Fast track:
+     *   Try to send data immediately, only if there's no pending write!
+     * Note:
+     *  We are speculating that the list is empty here without properly
+     *  acquiring ioqueue's mutex first. This is intentional, to maximize
+     *  performance via parallelism.
+     *
+     *  This should be safe, because:
+     *      - by convention, we require caller to make sure that the
+     *        key is not unregistered while other threads are invoking
+     *        an operation on the same key.
+     *      - pj_list_empty() is safe to be invoked by multiple threads,
+     *        even when other threads are modifying the list.
+     */
+    if (pj_list_empty(&key->write_list)) {
+        /*
+         * See if data can be sent immediately.
+         */
+        sent = *length;
+        status = pj_sock_send(key->fd, data, &sent, flags);
+        if (status == PJ_SUCCESS) {
+            /* Success! */
+            *length = sent;
+            return PJ_SUCCESS;
+        } else {
+            /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+             * the error to caller.
+             */
+            if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
+                return status;
+            }
+        }
+    }
+
+    /*
+     * Schedule asynchronous send.
+     */
+    write_op = (struct write_operation*)op_key;
+
+    /* Spin if write_op has pending operation */
+    for (retry=0; write_op->op != 0 && retry<PENDING_RETRY; ++retry)
+	pj_thread_sleep(0);
+
+    /* Last chance */
+    if (write_op->op) {
+	/* Unable to send packet because there is already pending write in the
+	 * write_op. We could not put the operation into the write_op
+	 * because write_op already contains a pending operation! And
+	 * we could not send the packet directly with send() either,
+	 * because that will break the order of the packet. So we can
+	 * only return error here.
+	 *
+	 * This could happen for example in multithreads program,
+	 * where polling is done by one thread, while other threads are doing
+	 * the sending only. If the polling thread runs on lower priority
+	 * than the sending thread, then it's possible that the pending
+	 * write flag is not cleared in-time because clearing is only done
+	 * during polling. 
+	 *
+	 * Aplication should specify multiple write operation keys on
+	 * situation like this.
+	 */
+	//pj_assert(!"ioqueue: there is pending operation on this key!");
+	return PJ_EBUSY;
+    }
+
+    write_op->op = PJ_IOQUEUE_OP_SEND;
+    write_op->buf = (char*)data;
+    write_op->size = *length;
+    write_op->written = 0;
+    write_op->flags = flags;
+    
+    pj_ioqueue_lock_key(key);
+    /* Check again. Handle may have been closed after the previous check
+     * in multithreaded app. If we add bad handle to the set it will
+     * corrupt the ioqueue set. See #913
+     */
+    if (IS_CLOSING(key)) {
+	pj_ioqueue_unlock_key(key);
+	return PJ_ECANCELLED;
+    }
+    pj_list_insert_before(&key->write_list, write_op);
+    ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT);
+    pj_ioqueue_unlock_key(key);
+
+    return PJ_EPENDING;
+}
+
+
+/*
+ * pj_ioqueue_sendto()
+ *
+ * Start asynchronous write() to the descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
+                                       pj_ioqueue_op_key_t *op_key,
+			               const void *data,
+			               pj_ssize_t *length,
+                                       pj_uint32_t flags,
+			               const pj_sockaddr_t *addr,
+			               int addrlen)
+{
+    struct write_operation *write_op;
+    unsigned retry;
+    pj_bool_t restart_retry = PJ_FALSE;
+    pj_status_t status;
+    pj_ssize_t sent;
+
+    PJ_ASSERT_RETURN(key && op_key && data && length, PJ_EINVAL);
+    PJ_CHECK_STACK();
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+	    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+retry_on_restart:
+#else
+    PJ_UNUSED_ARG(restart_retry);
+#endif
+    /* Check if key is closing. */
+    if (IS_CLOSING(key))
+	return PJ_ECANCELLED;
+
+    /* We can not use PJ_IOQUEUE_ALWAYS_ASYNC for socket write */
+    flags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
+
+    /* Fast track:
+     *   Try to send data immediately, only if there's no pending write!
+     * Note:
+     *  We are speculating that the list is empty here without properly
+     *  acquiring ioqueue's mutex first. This is intentional, to maximize
+     *  performance via parallelism.
+     *
+     *  This should be safe, because:
+     *      - by convention, we require caller to make sure that the
+     *        key is not unregistered while other threads are invoking
+     *        an operation on the same key.
+     *      - pj_list_empty() is safe to be invoked by multiple threads,
+     *        even when other threads are modifying the list.
+     */
+    if (pj_list_empty(&key->write_list)) {
+        /*
+         * See if data can be sent immediately.
+         */
+        sent = *length;
+        status = pj_sock_sendto(key->fd, data, &sent, flags, addr, addrlen);
+        if (status == PJ_SUCCESS) {
+            /* Success! */
+            *length = sent;
+            return PJ_SUCCESS;
+        } else {
+            /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+             * the error to caller.
+             */
+            if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+	    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+		/* Special treatment for dead UDP sockets here, see ticket #1107 */
+		if (status==PJ_STATUS_FROM_OS(EPIPE) && !IS_CLOSING(key) &&
+		    key->fd_type==pj_SOCK_DGRAM() && !restart_retry)
+		{
+		    PJ_PERROR(4,(THIS_FILE, status,
+				 "Send error for socket %d, retrying",
+				 key->fd));
+		    replace_udp_sock(key);
+		    restart_retry = PJ_TRUE;
+		    goto retry_on_restart;
+		}
+#endif
+
+                return status;
+            }
+	    status = status;
+        }
+    }
+
+    /*
+     * Check that address storage can hold the address parameter.
+     */
+    PJ_ASSERT_RETURN(addrlen <= (int)sizeof(pj_sockaddr_in), PJ_EBUG);
+
+    /*
+     * Schedule asynchronous send.
+     */
+    write_op = (struct write_operation*)op_key;
+    
+    /* Spin if write_op has pending operation */
+    for (retry=0; write_op->op != 0 && retry<PENDING_RETRY; ++retry)
+	pj_thread_sleep(0);
+
+    /* Last chance */
+    if (write_op->op) {
+	/* Unable to send packet because there is already pending write on the
+	 * write_op. We could not put the operation into the write_op
+	 * because write_op already contains a pending operation! And
+	 * we could not send the packet directly with sendto() either,
+	 * because that will break the order of the packet. So we can
+	 * only return error here.
+	 *
+	 * This could happen for example in multithreads program,
+	 * where polling is done by one thread, while other threads are doing
+	 * the sending only. If the polling thread runs on lower priority
+	 * than the sending thread, then it's possible that the pending
+	 * write flag is not cleared in-time because clearing is only done
+	 * during polling. 
+	 *
+	 * Aplication should specify multiple write operation keys on
+	 * situation like this.
+	 */
+	//pj_assert(!"ioqueue: there is pending operation on this key!");
+	return PJ_EBUSY;
+    }
+
+    write_op->op = PJ_IOQUEUE_OP_SEND_TO;
+    write_op->buf = (char*)data;
+    write_op->size = *length;
+    write_op->written = 0;
+    write_op->flags = flags;
+    pj_memcpy(&write_op->rmt_addr, addr, addrlen);
+    write_op->rmt_addrlen = addrlen;
+    
+    pj_ioqueue_lock_key(key);
+    /* Check again. Handle may have been closed after the previous check
+     * in multithreaded app. If we add bad handle to the set it will
+     * corrupt the ioqueue set. See #913
+     */
+    if (IS_CLOSING(key)) {
+	pj_ioqueue_unlock_key(key);
+	return PJ_ECANCELLED;
+    }
+    pj_list_insert_before(&key->write_list, write_op);
+    ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT);
+    pj_ioqueue_unlock_key(key);
+
+    return PJ_EPENDING;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
+                                       pj_ioqueue_op_key_t *op_key,
+			               pj_sock_t *new_sock,
+			               pj_sockaddr_t *local,
+			               pj_sockaddr_t *remote,
+			               int *addrlen)
+{
+    struct accept_operation *accept_op;
+    pj_status_t status;
+
+    /* check parameters. All must be specified! */
+    PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);
+
+    /* Check if key is closing. */
+    if (IS_CLOSING(key))
+	return PJ_ECANCELLED;
+
+    accept_op = (struct accept_operation*)op_key;
+    accept_op->op = PJ_IOQUEUE_OP_NONE;
+
+    /* Fast track:
+     *  See if there's new connection available immediately.
+     */
+    if (pj_list_empty(&key->accept_list)) {
+        status = pj_sock_accept(key->fd, new_sock, remote, addrlen);
+        if (status == PJ_SUCCESS) {
+            /* Yes! New connection is available! */
+            if (local && addrlen) {
+                status = pj_sock_getsockname(*new_sock, local, addrlen);
+                if (status != PJ_SUCCESS) {
+                    pj_sock_close(*new_sock);
+                    *new_sock = PJ_INVALID_SOCKET;
+                    return status;
+                }
+            }
+            return PJ_SUCCESS;
+        } else {
+            /* If error is not EWOULDBLOCK (or EAGAIN on Linux), report
+             * the error to caller.
+             */
+            if (status != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL)) {
+                return status;
+            }
+        }
+    }
+
+    /*
+     * No connection is available immediately.
+     * Schedule accept() operation to be completed when there is incoming
+     * connection available.
+     */
+    accept_op->op = PJ_IOQUEUE_OP_ACCEPT;
+    accept_op->accept_fd = new_sock;
+    accept_op->rmt_addr = remote;
+    accept_op->addrlen= addrlen;
+    accept_op->local_addr = local;
+
+    pj_ioqueue_lock_key(key);
+    /* Check again. Handle may have been closed after the previous check
+     * in multithreaded app. If we add bad handle to the set it will
+     * corrupt the ioqueue set. See #913
+     */
+    if (IS_CLOSING(key)) {
+	pj_ioqueue_unlock_key(key);
+	return PJ_ECANCELLED;
+    }
+    pj_list_insert_before(&key->accept_list, accept_op);
+    ioqueue_add_to_set(key->ioqueue, key, READABLE_EVENT);
+    pj_ioqueue_unlock_key(key);
+
+    return PJ_EPENDING;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
+					const pj_sockaddr_t *addr,
+					int addrlen )
+{
+    pj_status_t status;
+    
+    /* check parameters. All must be specified! */
+    PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL);
+
+    /* Check if key is closing. */
+    if (IS_CLOSING(key))
+	return PJ_ECANCELLED;
+
+    /* Check if socket has not been marked for connecting */
+    if (key->connecting != 0)
+        return PJ_EPENDING;
+    
+    status = pj_sock_connect(key->fd, addr, addrlen);
+    if (status == PJ_SUCCESS) {
+	/* Connected! */
+	return PJ_SUCCESS;
+    } else {
+	if (status == PJ_STATUS_FROM_OS(PJ_BLOCKING_CONNECT_ERROR_VAL)) {
+	    /* Pending! */
+	    pj_ioqueue_lock_key(key);
+	    /* Check again. Handle may have been closed after the previous 
+	     * check in multithreaded app. See #913
+	     */
+	    if (IS_CLOSING(key)) {
+		pj_ioqueue_unlock_key(key);
+		return PJ_ECANCELLED;
+	    }
+	    key->connecting = PJ_TRUE;
+            ioqueue_add_to_set(key->ioqueue, key, WRITEABLE_EVENT);
+            ioqueue_add_to_set(key->ioqueue, key, EXCEPTION_EVENT);
+            pj_ioqueue_unlock_key(key);
+	    return PJ_EPENDING;
+	} else {
+	    /* Error! */
+	    return status;
+	}
+    }
+}
+#endif	/* PJ_HAS_TCP */
+
+
+PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
+				     pj_size_t size )
+{
+    pj_bzero(op_key, size);
+}
+
+
+/*
+ * pj_ioqueue_is_pending()
+ */
+PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key,
+                                         pj_ioqueue_op_key_t *op_key )
+{
+    struct generic_operation *op_rec;
+
+    PJ_UNUSED_ARG(key);
+
+    op_rec = (struct generic_operation*)op_key;
+    return op_rec->op != 0;
+}
+
+
+/*
+ * pj_ioqueue_post_completion()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
+                                                pj_ioqueue_op_key_t *op_key,
+                                                pj_ssize_t bytes_status )
+{
+    struct generic_operation *op_rec;
+
+    /*
+     * Find the operation key in all pending operation list to
+     * really make sure that it's still there; then call the callback.
+     */
+    pj_ioqueue_lock_key(key);
+
+    /* Find the operation in the pending read list. */
+    op_rec = (struct generic_operation*)key->read_list.next;
+    while (op_rec != (void*)&key->read_list) {
+        if (op_rec == (void*)op_key) {
+            pj_list_erase(op_rec);
+            op_rec->op = PJ_IOQUEUE_OP_NONE;
+            pj_ioqueue_unlock_key(key);
+
+            (*key->cb.on_read_complete)(key, op_key, bytes_status);
+            return PJ_SUCCESS;
+        }
+        op_rec = op_rec->next;
+    }
+
+    /* Find the operation in the pending write list. */
+    op_rec = (struct generic_operation*)key->write_list.next;
+    while (op_rec != (void*)&key->write_list) {
+        if (op_rec == (void*)op_key) {
+            pj_list_erase(op_rec);
+            op_rec->op = PJ_IOQUEUE_OP_NONE;
+            pj_ioqueue_unlock_key(key);
+
+            (*key->cb.on_write_complete)(key, op_key, bytes_status);
+            return PJ_SUCCESS;
+        }
+        op_rec = op_rec->next;
+    }
+
+    /* Find the operation in the pending accept list. */
+    op_rec = (struct generic_operation*)key->accept_list.next;
+    while (op_rec != (void*)&key->accept_list) {
+        if (op_rec == (void*)op_key) {
+            pj_list_erase(op_rec);
+            op_rec->op = PJ_IOQUEUE_OP_NONE;
+            pj_ioqueue_unlock_key(key);
+
+            (*key->cb.on_accept_complete)(key, op_key, 
+                                          PJ_INVALID_SOCKET,
+                                          (pj_status_t)bytes_status);
+            return PJ_SUCCESS;
+        }
+        op_rec = op_rec->next;
+    }
+
+    pj_ioqueue_unlock_key(key);
+    
+    return PJ_EINVALIDOP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency( pj_ioqueue_t *ioqueue,
+							pj_bool_t allow)
+{
+    PJ_ASSERT_RETURN(ioqueue != NULL, PJ_EINVAL);
+    ioqueue->default_concurrency = allow;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key,
+					       pj_bool_t allow)
+{
+    PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+    /* PJ_IOQUEUE_HAS_SAFE_UNREG must be enabled if concurrency is
+     * disabled.
+     */
+    PJ_ASSERT_RETURN(allow || PJ_IOQUEUE_HAS_SAFE_UNREG, PJ_EINVAL);
+
+    key->allow_concurrent = allow;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key)
+{
+    if (key->grp_lock)
+	return pj_grp_lock_acquire(key->grp_lock);
+    else
+	return pj_lock_acquire(key->lock);
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key)
+{
+    if (key->grp_lock)
+	return pj_grp_lock_release(key->grp_lock);
+    else
+	return pj_lock_release(key->lock);
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ioqueue_common_abs.h b/jni/pjproject-android/pjlib/src/pj/ioqueue_common_abs.h
new file mode 100644
index 0000000..3bdbb52
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ioqueue_common_abs.h
@@ -0,0 +1,140 @@
+/* $Id */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/* ioqueue_common_abs.h
+ *
+ * This file contains private declarations for abstracting various 
+ * event polling/dispatching mechanisms (e.g. select, poll, epoll) 
+ * to the ioqueue. 
+ */
+
+#include <pj/list.h>
+
+/*
+ * The select ioqueue relies on socket functions (pj_sock_xxx()) to return
+ * the correct error code.
+ */
+#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100)
+#   error "Proper error reporting must be enabled for ioqueue to work!"
+#endif
+
+
+struct generic_operation
+{
+    PJ_DECL_LIST_MEMBER(struct generic_operation);
+    pj_ioqueue_operation_e  op;
+};
+
+struct read_operation
+{
+    PJ_DECL_LIST_MEMBER(struct read_operation);
+    pj_ioqueue_operation_e  op;
+
+    void		   *buf;
+    pj_size_t		    size;
+    unsigned                flags;
+    pj_sockaddr_t	   *rmt_addr;
+    int			   *rmt_addrlen;
+};
+
+struct write_operation
+{
+    PJ_DECL_LIST_MEMBER(struct write_operation);
+    pj_ioqueue_operation_e  op;
+
+    char		   *buf;
+    pj_size_t		    size;
+    pj_ssize_t              written;
+    unsigned                flags;
+    pj_sockaddr_in	    rmt_addr;
+    int			    rmt_addrlen;
+};
+
+struct accept_operation
+{
+    PJ_DECL_LIST_MEMBER(struct accept_operation);
+    pj_ioqueue_operation_e  op;
+
+    pj_sock_t              *accept_fd;
+    pj_sockaddr_t	   *local_addr;
+    pj_sockaddr_t	   *rmt_addr;
+    int			   *addrlen;
+};
+
+union operation_key
+{
+    struct generic_operation generic;
+    struct read_operation    read;
+    struct write_operation   write;
+#if PJ_HAS_TCP
+    struct accept_operation  accept;
+#endif
+};
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+#   define UNREG_FIELDS			\
+	unsigned	    ref_count;	\
+	pj_bool_t	    closing;	\
+	pj_time_val	    free_time;	\
+	
+#else
+#   define UNREG_FIELDS
+#endif
+
+#define DECLARE_COMMON_KEY                          \
+    PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t);   \
+    pj_ioqueue_t           *ioqueue;                \
+    pj_grp_lock_t 	   *grp_lock;		    \
+    pj_lock_t              *lock;                   \
+    pj_bool_t		    inside_callback;	    \
+    pj_bool_t		    destroy_requested;	    \
+    pj_bool_t		    allow_concurrent;	    \
+    pj_sock_t		    fd;                     \
+    int                     fd_type;                \
+    void		   *user_data;              \
+    pj_ioqueue_callback	    cb;                     \
+    int                     connecting;             \
+    struct read_operation   read_list;              \
+    struct write_operation  write_list;             \
+    struct accept_operation accept_list;	    \
+    UNREG_FIELDS
+
+
+#define DECLARE_COMMON_IOQUEUE                      \
+    pj_lock_t          *lock;                       \
+    pj_bool_t           auto_delete_lock;	    \
+    pj_bool_t		default_concurrency;
+
+
+enum ioqueue_event_type
+{
+    NO_EVENT,
+    READABLE_EVENT,
+    WRITEABLE_EVENT,
+    EXCEPTION_EVENT,
+};
+
+static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
+                                pj_ioqueue_key_t *key,
+                                enum ioqueue_event_type event_type );
+static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
+                                     pj_ioqueue_key_t *key, 
+                                     enum ioqueue_event_type event_type);
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ioqueue_dummy.c b/jni/pjproject-android/pjlib/src/pj/ioqueue_dummy.c
new file mode 100644
index 0000000..6ea5bb7
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ioqueue_dummy.c
@@ -0,0 +1,194 @@
+/* $Id: ioqueue_dummy.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+#include <pj/errno.h>
+
+#define THIS_FILE   "ioqueue"
+
+#define PJ_IOQUEUE_IS_READ_OP(op)   \
+	((op & PJ_IOQUEUE_OP_READ)  || (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op)  \
+	((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+#  define PJ_IOQUEUE_IS_ACCEPT_OP(op)	(op & PJ_IOQUEUE_OP_ACCEPT)
+#  define PJ_IOQUEUE_IS_CONNECT_OP(op)	(op & PJ_IOQUEUE_OP_CONNECT)
+#else
+#  define PJ_IOQUEUE_IS_ACCEPT_OP(op)	0
+#  define PJ_IOQUEUE_IS_CONNECT_OP(op)	0
+#endif
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+#  define VALIDATE_FD_SET		1
+#else
+#  define VALIDATE_FD_SET		0
+#endif
+
+struct pj_ioqueue_key_t
+{
+    PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+    pj_sock_t		    fd;
+    pj_ioqueue_operation_e  op;
+    void		   *user_data;
+    pj_ioqueue_callback	    cb;
+};
+
+struct pj_ioqueue_t
+{
+};
+
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, 
+				       pj_size_t max_fd,
+				       int max_threads,
+				       pj_ioqueue_t **ptr_ioqueue)
+{
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioque, 
+					 pj_lock_t *lock,
+					 pj_bool_t auto_delete )
+{
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+					      pj_ioqueue_t *ioque,
+					      pj_sock_t sock,
+					      void *user_data,
+					      const pj_ioqueue_callback *cb,
+					      pj_ioqueue_key_t **ptr_key)
+{
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+					   pj_ioqueue_key_t *key)
+{
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+    return NULL;
+}
+
+
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_read( pj_ioqueue_t *ioque,
+				     pj_ioqueue_key_t *key,
+				     void *buffer,
+				     pj_size_t buflen)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_t *ioque,
+				     pj_ioqueue_key_t *key,
+				     void *buffer,
+				     pj_size_t buflen,
+				     unsigned flags)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+					 pj_ioqueue_key_t *key,
+					 void *buffer,
+					 pj_size_t buflen,
+					 unsigned flags,
+					 pj_sockaddr_t *addr,
+					 int *addrlen)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_write( pj_ioqueue_t *ioque,
+				      pj_ioqueue_key_t *key,
+				      const void *data,
+				      pj_size_t datalen)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_t *ioque,
+				     pj_ioqueue_key_t *key,
+				     const void *data,
+				     pj_size_t datalen,
+				     unsigned flags)
+{
+    return -1;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+				       pj_ioqueue_key_t *key,
+				       const void *data,
+				       pj_size_t datalen,
+				       unsigned flags,
+				       const pj_sockaddr_t *addr,
+				       int addrlen)
+{
+    return -1;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+				       pj_ioqueue_key_t *key,
+				       pj_sock_t *new_sock,
+				       pj_sockaddr_t *local,
+				       pj_sockaddr_t *remote,
+				      int *addrlen)
+{
+    return -1;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+					pj_ioqueue_key_t *key,
+					const pj_sockaddr_t *addr,
+					int addrlen )
+{
+    return -1;
+}
+#endif	/* PJ_HAS_TCP */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ioqueue_epoll.c b/jni/pjproject-android/pjlib/src/pj/ioqueue_epoll.c
new file mode 100644
index 0000000..eea57a9
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ioqueue_epoll.c
@@ -0,0 +1,825 @@
+/* $Id: ioqueue_epoll.c 4528 2013-05-30 07:01:11Z ming $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+/*
+ * ioqueue_epoll.c
+ *
+ * This is the implementation of IOQueue framework using /dev/epoll
+ * API in _both_ Linux user-mode and kernel-mode.
+ */
+
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/sock.h>
+#include <pj/compat/socket.h>
+#include <pj/rand.h>
+
+#if !defined(PJ_LINUX_KERNEL) || PJ_LINUX_KERNEL==0
+    /*
+     * Linux user mode
+     */
+#   include <sys/epoll.h>
+#   include <errno.h>
+#   include <unistd.h>
+
+#   define epoll_data		data.ptr
+#   define epoll_data_type	void*
+#   define ioctl_val_type	unsigned long
+#   define getsockopt_val_ptr	int*
+#   define os_getsockopt	getsockopt
+#   define os_ioctl		ioctl
+#   define os_read		read
+#   define os_close		close
+#   define os_epoll_create	epoll_create
+#   define os_epoll_ctl		epoll_ctl
+#   define os_epoll_wait	epoll_wait
+#else
+    /*
+     * Linux kernel mode.
+     */
+#   include <linux/config.h>
+#   include <linux/version.h>
+#   if defined(MODVERSIONS)
+#	include <linux/modversions.h>
+#   endif
+#   include <linux/kernel.h>
+#   include <linux/poll.h>
+#   include <linux/eventpoll.h>
+#   include <linux/syscalls.h>
+#   include <linux/errno.h>
+#   include <linux/unistd.h>
+#   include <asm/ioctls.h>
+    enum EPOLL_EVENTS
+    {
+	EPOLLIN = 0x001,
+	EPOLLOUT = 0x004,
+	EPOLLERR = 0x008,
+    };
+#   define os_epoll_create		sys_epoll_create
+    static int os_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
+    {
+	long rc;
+	mm_segment_t oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	rc = sys_epoll_ctl(epfd, op, fd, event);
+	set_fs(oldfs);
+	if (rc) {
+	    errno = -rc;
+	    return -1;
+	} else {
+	    return 0;
+	}
+    }
+    static int os_epoll_wait(int epfd, struct epoll_event *events,
+			  int maxevents, int timeout)
+    {
+	int count;
+	mm_segment_t oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	count = sys_epoll_wait(epfd, events, maxevents, timeout);
+	set_fs(oldfs);
+	return count;
+    }
+#   define os_close		sys_close
+#   define os_getsockopt	pj_sock_getsockopt
+    static int os_read(int fd, void *buf, size_t len)
+    {
+	long rc;
+	mm_segment_t oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	rc = sys_read(fd, buf, len);
+	set_fs(oldfs);
+	if (rc) {
+	    errno = -rc;
+	    return -1;
+	} else {
+	    return 0;
+	}
+    }
+#   define socklen_t		unsigned
+#   define ioctl_val_type	unsigned long
+    int ioctl(int fd, int opt, ioctl_val_type value);
+    static int os_ioctl(int fd, int opt, ioctl_val_type value)
+    {
+	int rc;
+        mm_segment_t oldfs = get_fs();
+	set_fs(KERNEL_DS);
+	rc = ioctl(fd, opt, value);
+	set_fs(oldfs);
+	if (rc < 0) {
+	    errno = -rc;
+	    return rc;
+	} else
+	    return rc;
+    }
+#   define getsockopt_val_ptr	char*
+
+#   define epoll_data		data
+#   define epoll_data_type	__u32
+#endif
+
+#define THIS_FILE   "ioq_epoll"
+
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(expr)
+
+/*
+ * Include common ioqueue abstraction.
+ */
+#include "ioqueue_common_abs.h"
+
+/*
+ * This describes each key.
+ */
+struct pj_ioqueue_key_t
+{
+    DECLARE_COMMON_KEY
+};
+
+struct queue
+{
+    pj_ioqueue_key_t	    *key;
+    enum ioqueue_event_type  event_type;
+};
+
+/*
+ * This describes the I/O queue.
+ */
+struct pj_ioqueue_t
+{
+    DECLARE_COMMON_IOQUEUE
+
+    unsigned		max, count;
+    //pj_ioqueue_key_t	hlist;
+    pj_ioqueue_key_t	active_list;    
+    int			epfd;
+    //struct epoll_event *events;
+    //struct queue       *queue;
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    pj_mutex_t	       *ref_cnt_mutex;
+    pj_ioqueue_key_t	closing_list;
+    pj_ioqueue_key_t	free_list;
+#endif
+};
+
+/* Include implementation for common abstraction after we declare
+ * pj_ioqueue_key_t and pj_ioqueue_t.
+ */
+#include "ioqueue_common_abs.c"
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Scan closing keys to be put to free list again */
+static void scan_closing_keys(pj_ioqueue_t *ioqueue);
+#endif
+
+/*
+ * pj_ioqueue_name()
+ */
+PJ_DEF(const char*) pj_ioqueue_name(void)
+{
+#if defined(PJ_LINUX_KERNEL) && PJ_LINUX_KERNEL!=0
+	return "epoll-kernel";
+#else
+	return "epoll";
+#endif
+}
+
+/*
+ * pj_ioqueue_create()
+ *
+ * Create select ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, 
+                                       pj_size_t max_fd,
+                                       pj_ioqueue_t **p_ioqueue)
+{
+    pj_ioqueue_t *ioqueue;
+    pj_status_t rc;
+    pj_lock_t *lock;
+    int i;
+
+    /* Check that arguments are valid. */
+    PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && 
+                     max_fd > 0, PJ_EINVAL);
+
+    /* Check that size of pj_ioqueue_op_key_t is sufficient */
+    PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
+                     sizeof(union operation_key), PJ_EBUG);
+
+    ioqueue = pj_pool_alloc(pool, sizeof(pj_ioqueue_t));
+
+    ioqueue_init(ioqueue);
+
+    ioqueue->max = max_fd;
+    ioqueue->count = 0;
+    pj_list_init(&ioqueue->active_list);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* When safe unregistration is used (the default), we pre-create
+     * all keys and put them in the free list.
+     */
+
+    /* Mutex to protect key's reference counter 
+     * We don't want to use key's mutex or ioqueue's mutex because
+     * that would create deadlock situation in some cases.
+     */
+    rc = pj_mutex_create_simple(pool, NULL, &ioqueue->ref_cnt_mutex);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+
+    /* Init key list */
+    pj_list_init(&ioqueue->free_list);
+    pj_list_init(&ioqueue->closing_list);
+
+
+    /* Pre-create all keys according to max_fd */
+    for ( i=0; i<max_fd; ++i) {
+	pj_ioqueue_key_t *key;
+
+	key = PJ_POOL_ALLOC_T(pool, pj_ioqueue_key_t);
+	key->ref_count = 0;
+	rc = pj_lock_create_recursive_mutex(pool, NULL, &key->lock);
+	if (rc != PJ_SUCCESS) {
+	    key = ioqueue->free_list.next;
+	    while (key != &ioqueue->free_list) {
+		pj_lock_destroy(key->lock);
+		key = key->next;
+	    }
+	    pj_mutex_destroy(ioqueue->ref_cnt_mutex);
+	    return rc;
+	}
+
+	pj_list_push_back(&ioqueue->free_list, key);
+    }
+#endif
+
+    rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE);
+    if (rc != PJ_SUCCESS)
+        return rc;
+
+    ioqueue->epfd = os_epoll_create(max_fd);
+    if (ioqueue->epfd < 0) {
+	ioqueue_destroy(ioqueue);
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+    }
+    
+    /*ioqueue->events = pj_pool_calloc(pool, max_fd, sizeof(struct epoll_event));
+    PJ_ASSERT_RETURN(ioqueue->events != NULL, PJ_ENOMEM);
+
+    ioqueue->queue = pj_pool_calloc(pool, max_fd, sizeof(struct queue));
+    PJ_ASSERT_RETURN(ioqueue->queue != NULL, PJ_ENOMEM);
+   */
+    PJ_LOG(4, ("pjlib", "epoll I/O Queue created (%p)", ioqueue));
+
+    *p_ioqueue = ioqueue;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_destroy()
+ *
+ * Destroy ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
+{
+    pj_ioqueue_key_t *key;
+
+    PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ioqueue->epfd > 0, PJ_EINVALIDOP);
+
+    pj_lock_acquire(ioqueue->lock);
+    os_close(ioqueue->epfd);
+    ioqueue->epfd = 0;
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Destroy reference counters */
+    key = ioqueue->active_list.next;
+    while (key != &ioqueue->active_list) {
+	pj_lock_destroy(key->lock);
+	key = key->next;
+    }
+
+    key = ioqueue->closing_list.next;
+    while (key != &ioqueue->closing_list) {
+	pj_lock_destroy(key->lock);
+	key = key->next;
+    }
+
+    key = ioqueue->free_list.next;
+    while (key != &ioqueue->free_list) {
+	pj_lock_destroy(key->lock);
+	key = key->next;
+    }
+
+    pj_mutex_destroy(ioqueue->ref_cnt_mutex);
+#endif
+    return ioqueue_destroy(ioqueue);
+}
+
+/*
+ * pj_ioqueue_register_sock()
+ *
+ * Register a socket to ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool,
+					      pj_ioqueue_t *ioqueue,
+					      pj_sock_t sock,
+					      pj_grp_lock_t *grp_lock,
+					      void *user_data,
+					      const pj_ioqueue_callback *cb,
+                                              pj_ioqueue_key_t **p_key)
+{
+    pj_ioqueue_key_t *key = NULL;
+    pj_uint32_t value;
+    struct epoll_event ev;
+    int status;
+    pj_status_t rc = PJ_SUCCESS;
+    
+    PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
+                     cb && p_key, PJ_EINVAL);
+
+    pj_lock_acquire(ioqueue->lock);
+
+    if (ioqueue->count >= ioqueue->max) {
+        rc = PJ_ETOOMANY;
+	TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: too many files"));
+	goto on_return;
+    }
+
+    /* Set socket to nonblocking. */
+    value = 1;
+    if ((rc=os_ioctl(sock, FIONBIO, (ioctl_val_type)&value))) {
+	TRACE_((THIS_FILE, "pj_ioqueue_register_sock error: ioctl rc=%d", 
+                rc));
+        rc = pj_get_netos_error();
+	goto on_return;
+    }
+
+    /* If safe unregistration (PJ_IOQUEUE_HAS_SAFE_UNREG) is used, get
+     * the key from the free list. Otherwise allocate a new one. 
+     */
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+
+    /* Scan closing_keys first to let them come back to free_list */
+    scan_closing_keys(ioqueue);
+
+    pj_assert(!pj_list_empty(&ioqueue->free_list));
+    if (pj_list_empty(&ioqueue->free_list)) {
+	rc = PJ_ETOOMANY;
+	goto on_return;
+    }
+
+    key = ioqueue->free_list.next;
+    pj_list_erase(key);
+#else
+    /* Create key. */
+    key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+#endif
+
+    rc = ioqueue_init_key(pool, ioqueue, key, sock, grp_lock, user_data, cb);
+    if (rc != PJ_SUCCESS) {
+	key = NULL;
+	goto on_return;
+    }
+
+    /* Create key's mutex */
+ /*   rc = pj_mutex_create_recursive(pool, NULL, &key->mutex);
+    if (rc != PJ_SUCCESS) {
+	key = NULL;
+	goto on_return;
+    }
+*/
+    /* os_epoll_ctl. */
+    ev.events = EPOLLIN | EPOLLERR;
+    ev.epoll_data = (epoll_data_type)key;
+    status = os_epoll_ctl(ioqueue->epfd, EPOLL_CTL_ADD, sock, &ev);
+    if (status < 0) {
+	rc = pj_get_os_error();
+	pj_lock_destroy(key->lock);
+	key = NULL;
+	TRACE_((THIS_FILE, 
+                "pj_ioqueue_register_sock error: os_epoll_ctl rc=%d", 
+                status));
+	goto on_return;
+    }
+    
+    /* Register */
+    pj_list_insert_before(&ioqueue->active_list, key);
+    ++ioqueue->count;
+
+    //TRACE_((THIS_FILE, "socket registered, count=%d", ioqueue->count));
+
+on_return:
+    if (rc != PJ_SUCCESS) {
+	if (key && key->grp_lock)
+	    pj_grp_lock_dec_ref_dbg(key->grp_lock, "ioqueue", 0);
+    }
+    *p_key = key;
+    pj_lock_release(ioqueue->lock);
+    
+    return rc;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+					      pj_ioqueue_t *ioqueue,
+					      pj_sock_t sock,
+					      void *user_data,
+					      const pj_ioqueue_callback *cb,
+					      pj_ioqueue_key_t **p_key)
+{
+    return pj_ioqueue_register_sock2(pool, ioqueue, sock, NULL, user_data,
+                                     cb, p_key);
+}
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Increment key's reference counter */
+static void increment_counter(pj_ioqueue_key_t *key)
+{
+    pj_mutex_lock(key->ioqueue->ref_cnt_mutex);
+    ++key->ref_count;
+    pj_mutex_unlock(key->ioqueue->ref_cnt_mutex);
+}
+
+/* Decrement the key's reference counter, and when the counter reach zero,
+ * destroy the key.
+ *
+ * Note: MUST NOT CALL THIS FUNCTION WHILE HOLDING ioqueue's LOCK.
+ */
+static void decrement_counter(pj_ioqueue_key_t *key)
+{
+    pj_lock_acquire(key->ioqueue->lock);
+    pj_mutex_lock(key->ioqueue->ref_cnt_mutex);
+    --key->ref_count;
+    if (key->ref_count == 0) {
+
+	pj_assert(key->closing == 1);
+	pj_gettickcount(&key->free_time);
+	key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY;
+	pj_time_val_normalize(&key->free_time);
+
+	pj_list_erase(key);
+	pj_list_push_back(&key->ioqueue->closing_list, key);
+
+    }
+    pj_mutex_unlock(key->ioqueue->ref_cnt_mutex);
+    pj_lock_release(key->ioqueue->lock);
+}
+#endif
+
+/*
+ * pj_ioqueue_unregister()
+ *
+ * Unregister handle from ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
+{
+    pj_ioqueue_t *ioqueue;
+    struct epoll_event ev;
+    int status;
+    
+    PJ_ASSERT_RETURN(key != NULL, PJ_EINVAL);
+
+    ioqueue = key->ioqueue;
+
+    /* Lock the key to make sure no callback is simultaneously modifying
+     * the key. We need to lock the key before ioqueue here to prevent
+     * deadlock.
+     */
+    pj_ioqueue_lock_key(key);
+
+    /* Also lock ioqueue */
+    pj_lock_acquire(ioqueue->lock);
+
+    pj_assert(ioqueue->count > 0);
+    --ioqueue->count;
+#if !PJ_IOQUEUE_HAS_SAFE_UNREG
+    pj_list_erase(key);
+#endif
+
+    ev.events = 0;
+    ev.epoll_data = (epoll_data_type)key;
+    status = os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_DEL, key->fd, &ev);
+    if (status != 0) {
+	pj_status_t rc = pj_get_os_error();
+	pj_lock_release(ioqueue->lock);
+	return rc;
+    }
+
+    /* Destroy the key. */
+    pj_sock_close(key->fd);
+
+    pj_lock_release(ioqueue->lock);
+
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Mark key is closing. */
+    key->closing = 1;
+
+    /* Decrement counter. */
+    decrement_counter(key);
+
+    /* Done. */
+    if (key->grp_lock) {
+	/* just dec_ref and unlock. we will set grp_lock to NULL
+	 * elsewhere */
+	pj_grp_lock_t *grp_lock = key->grp_lock;
+	// Don't set grp_lock to NULL otherwise the other thread
+	// will crash. Just leave it as dangling pointer, but this
+	// should be safe
+	//key->grp_lock = NULL;
+	pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0);
+	pj_grp_lock_release(grp_lock);
+    } else {
+	pj_ioqueue_unlock_key(key);
+    }
+#else
+    if (key->grp_lock) {
+	/* set grp_lock to NULL and unlock */
+	pj_grp_lock_t *grp_lock = key->grp_lock;
+	// Don't set grp_lock to NULL otherwise the other thread
+	// will crash. Just leave it as dangling pointer, but this
+	// should be safe
+	//key->grp_lock = NULL;
+	pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0);
+	pj_grp_lock_release(grp_lock);
+    } else {
+	pj_ioqueue_unlock_key(key);
+    }
+
+    pj_lock_destroy(key->lock);
+#endif
+
+    return PJ_SUCCESS;
+}
+
+/* ioqueue_remove_from_set()
+ * This function is called from ioqueue_dispatch_event() to instruct
+ * the ioqueue to remove the specified descriptor from ioqueue's descriptor
+ * set for the specified event.
+ */
+static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
+                                     pj_ioqueue_key_t *key, 
+                                     enum ioqueue_event_type event_type)
+{
+    if (event_type == WRITEABLE_EVENT) {
+	struct epoll_event ev;
+
+	ev.events = EPOLLIN | EPOLLERR;
+	ev.epoll_data = (epoll_data_type)key;
+	os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_MOD, key->fd, &ev);
+    }	
+}
+
+/*
+ * ioqueue_add_to_set()
+ * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc
+ * to instruct the ioqueue to add the specified handle to ioqueue's descriptor
+ * set for the specified event.
+ */
+static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
+                                pj_ioqueue_key_t *key,
+                                enum ioqueue_event_type event_type )
+{
+    if (event_type == WRITEABLE_EVENT) {
+	struct epoll_event ev;
+
+	ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
+	ev.epoll_data = (epoll_data_type)key;
+	os_epoll_ctl( ioqueue->epfd, EPOLL_CTL_MOD, key->fd, &ev);
+    }	
+}
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Scan closing keys to be put to free list again */
+static void scan_closing_keys(pj_ioqueue_t *ioqueue)
+{
+    pj_time_val now;
+    pj_ioqueue_key_t *h;
+
+    pj_gettickcount(&now);
+    h = ioqueue->closing_list.next;
+    while (h != &ioqueue->closing_list) {
+	pj_ioqueue_key_t *next = h->next;
+
+	pj_assert(h->closing != 0);
+
+	if (PJ_TIME_VAL_GTE(now, h->free_time)) {
+	    pj_list_erase(h);
+	    // Don't set grp_lock to NULL otherwise the other thread
+	    // will crash. Just leave it as dangling pointer, but this
+	    // should be safe
+	    //h->grp_lock = NULL;
+	    pj_list_push_back(&ioqueue->free_list, h);
+	}
+	h = next;
+    }
+}
+#endif
+
+/*
+ * pj_ioqueue_poll()
+ *
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
+{
+    int i, count, processed;
+    int msec;
+    //struct epoll_event *events = ioqueue->events;
+    //struct queue *queue = ioqueue->queue;
+    struct epoll_event events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
+    struct queue queue[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
+    pj_timestamp t1, t2;
+    
+    PJ_CHECK_STACK();
+
+    msec = timeout ? PJ_TIME_VAL_MSEC(*timeout) : 9000;
+
+    TRACE_((THIS_FILE, "start os_epoll_wait, msec=%d", msec));
+    pj_get_timestamp(&t1);
+ 
+    //count = os_epoll_wait( ioqueue->epfd, events, ioqueue->max, msec);
+    count = os_epoll_wait( ioqueue->epfd, events, PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL, msec);
+    if (count == 0) {
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Check the closing keys only when there's no activity and when there are
+     * pending closing keys.
+     */
+    if (count == 0 && !pj_list_empty(&ioqueue->closing_list)) {
+	pj_lock_acquire(ioqueue->lock);
+	scan_closing_keys(ioqueue);
+	pj_lock_release(ioqueue->lock);
+    }
+#endif
+	TRACE_((THIS_FILE, "os_epoll_wait timed out"));
+	return count;
+    }
+    else if (count < 0) {
+	TRACE_((THIS_FILE, "os_epoll_wait error"));
+	return -pj_get_netos_error();
+    }
+
+    pj_get_timestamp(&t2);
+    TRACE_((THIS_FILE, "os_epoll_wait returns %d, time=%d usec",
+		       count, pj_elapsed_usec(&t1, &t2)));
+
+    /* Lock ioqueue. */
+    pj_lock_acquire(ioqueue->lock);
+
+    for (processed=0, i=0; i<count; ++i) {
+	pj_ioqueue_key_t *h = (pj_ioqueue_key_t*)(epoll_data_type)
+				events[i].epoll_data;
+
+	TRACE_((THIS_FILE, "event %d: events=%d", i, events[i].events));
+
+	/*
+	 * Check readability.
+	 */
+	if ((events[i].events & EPOLLIN) && 
+	    (key_has_pending_read(h) || key_has_pending_accept(h)) && !IS_CLOSING(h) ) {
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	    increment_counter(h);
+#endif
+	    queue[processed].key = h;
+	    queue[processed].event_type = READABLE_EVENT;
+	    ++processed;
+	    continue;
+	}
+
+	/*
+	 * Check for writeability.
+	 */
+	if ((events[i].events & EPOLLOUT) && key_has_pending_write(h) && !IS_CLOSING(h)) {
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	    increment_counter(h);
+#endif
+	    queue[processed].key = h;
+	    queue[processed].event_type = WRITEABLE_EVENT;
+	    ++processed;
+	    continue;
+	}
+
+#if PJ_HAS_TCP
+	/*
+	 * Check for completion of connect() operation.
+	 */
+	if ((events[i].events & EPOLLOUT) && (h->connecting) && !IS_CLOSING(h)) {
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	    increment_counter(h);
+#endif
+	    queue[processed].key = h;
+	    queue[processed].event_type = WRITEABLE_EVENT;
+	    ++processed;
+	    continue;
+	}
+#endif /* PJ_HAS_TCP */
+
+	/*
+	 * Check for error condition.
+	 */
+	if ((events[i].events & EPOLLERR) && !IS_CLOSING(h)) {
+	    /*
+	     * We need to handle this exception event.  If it's related to us
+	     * connecting, report it as such.  If not, just report it as a
+	     * read event and the higher layers will handle it.
+	     */
+	    if (h->connecting) {
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+		increment_counter(h);
+#endif
+		queue[processed].key = h;
+		queue[processed].event_type = EXCEPTION_EVENT;
+		++processed;
+	    } else if (key_has_pending_read(h) || key_has_pending_accept(h)) {
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+		increment_counter(h);
+#endif
+		queue[processed].key = h;
+		queue[processed].event_type = READABLE_EVENT;
+		++processed;
+	    }
+	    continue;
+	}
+    }
+    for (i=0; i<processed; ++i) {
+	if (queue[i].key->grp_lock)
+	    pj_grp_lock_add_ref_dbg(queue[i].key->grp_lock, "ioqueue", 0);
+    }
+
+    PJ_RACE_ME(5);
+
+    pj_lock_release(ioqueue->lock);
+
+    PJ_RACE_ME(5);
+
+    /* Now process the events. */
+    for (i=0; i<processed; ++i) {
+	switch (queue[i].event_type) {
+        case READABLE_EVENT:
+            ioqueue_dispatch_read_event(ioqueue, queue[i].key);
+            break;
+        case WRITEABLE_EVENT:
+            ioqueue_dispatch_write_event(ioqueue, queue[i].key);
+            break;
+        case EXCEPTION_EVENT:
+            ioqueue_dispatch_exception_event(ioqueue, queue[i].key);
+            break;
+        case NO_EVENT:
+            pj_assert(!"Invalid event!");
+            break;
+        }
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	decrement_counter(queue[i].key);
+#endif
+
+	if (queue[i].key->grp_lock)
+	    pj_grp_lock_dec_ref_dbg(queue[i].key->grp_lock,
+	                            "ioqueue", 0);
+    }
+
+    /* Special case:
+     * When epoll returns > 0 but no descriptors are actually set!
+     */
+    if (count > 0 && !processed && msec > 0) {
+	pj_thread_sleep(msec);
+    }
+
+    pj_get_timestamp(&t1);
+    TRACE_((THIS_FILE, "ioqueue_poll() returns %d, time=%d usec",
+		       processed, pj_elapsed_usec(&t2, &t1)));
+
+    return processed;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ioqueue_linux_kernel.c b/jni/pjproject-android/pjlib/src/pj/ioqueue_linux_kernel.c
new file mode 100644
index 0000000..706da06
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ioqueue_linux_kernel.c
@@ -0,0 +1,162 @@
+/* $Id: ioqueue_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+
+#define THIS_FILE   "ioqueue"
+
+#define PJ_IOQUEUE_IS_READ_OP(op)   \
+	((op & PJ_IOQUEUE_OP_READ)  || (op & PJ_IOQUEUE_OP_RECV_FROM))
+#define PJ_IOQUEUE_IS_WRITE_OP(op)  \
+	((op & PJ_IOQUEUE_OP_WRITE) || (op & PJ_IOQUEUE_OP_SEND_TO))
+
+
+#if PJ_HAS_TCP
+#  define PJ_IOQUEUE_IS_ACCEPT_OP(op)	(op & PJ_IOQUEUE_OP_ACCEPT)
+#  define PJ_IOQUEUE_IS_CONNECT_OP(op)	(op & PJ_IOQUEUE_OP_CONNECT)
+#else
+#  define PJ_IOQUEUE_IS_ACCEPT_OP(op)	0
+#  define PJ_IOQUEUE_IS_CONNECT_OP(op)	0
+#endif
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+#  define VALIDATE_FD_SET		1
+#else
+#  define VALIDATE_FD_SET		0
+#endif
+
+struct pj_ioqueue_key_t
+{
+    PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t)
+    pj_sock_t		    fd;
+    pj_ioqueue_operation_e  op;
+    void		   *user_data;
+    pj_ioqueue_callback	    cb;
+};
+
+struct pj_ioqueue_t
+{
+};
+
+PJ_DEF(pj_ioqueue_t*) pj_ioqueue_create(pj_pool_t *pool, pj_size_t max_fd)
+{
+    return NULL;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioque)
+{
+    return 0;
+}
+
+PJ_DEF(pj_ioqueue_key_t*) pj_ioqueue_register( pj_pool_t *pool,
+					       pj_ioqueue_t *ioque,
+					       pj_oshandle_t sock,
+					       void *user_data,
+					       const pj_ioqueue_callback *cb)
+{
+    return NULL;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_t *ioque,
+					   pj_ioqueue_key_t *key)
+{
+    return -1;
+}
+
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+    return NULL;
+}
+
+
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioque, const pj_time_val *timeout)
+{
+    return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_read( pj_ioqueue_t *ioque,
+			     pj_ioqueue_key_t *key,
+			     void *buffer,
+			     pj_size_t buflen)
+{
+    return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_recvfrom( pj_ioqueue_t *ioque,
+				 pj_ioqueue_key_t *key,
+				 void *buffer,
+				 pj_size_t buflen,
+				 pj_sockaddr_t *addr,
+				 int *addrlen)
+{
+    return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_write( pj_ioqueue_t *ioque,
+			      pj_ioqueue_key_t *key,
+			      const void *data,
+			      pj_size_t datalen)
+{
+    return -1;
+}
+
+PJ_DEF(int) pj_ioqueue_sendto( pj_ioqueue_t *ioque,
+			       pj_ioqueue_key_t *key,
+			       const void *data,
+			       pj_size_t datalen,
+			       const pj_sockaddr_t *addr,
+			       int addrlen)
+{
+    return -1;
+}
+
+#if PJ_HAS_TCP
+/*
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(int) pj_ioqueue_accept( pj_ioqueue_t *ioqueue,
+			       pj_ioqueue_key_t *key,
+			       pj_sock_t *new_sock,
+			       pj_sockaddr_t *local,
+			       pj_sockaddr_t *remote,
+			       int *addrlen)
+{
+    return -1;
+}
+
+/*
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_t *ioqueue,
+					pj_ioqueue_key_t *key,
+					const pj_sockaddr_t *addr,
+					int addrlen )
+{
+    return -1;
+}
+#endif	/* PJ_HAS_TCP */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ioqueue_select.c b/jni/pjproject-android/pjlib/src/pj/ioqueue_select.c
new file mode 100644
index 0000000..d2e4491
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ioqueue_select.c
@@ -0,0 +1,988 @@
+/* $Id: ioqueue_select.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * sock_select.c
+ *
+ * This is the implementation of IOQueue using pj_sock_select().
+ * It runs anywhere where pj_sock_select() is available (currently
+ * Win32, Linux, Linux kernel, etc.).
+ */
+
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/list.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/sock.h>
+#include <pj/compat/socket.h>
+#include <pj/sock_select.h>
+#include <pj/sock_qos.h>
+#include <pj/errno.h>
+#include <pj/rand.h>
+
+/* Now that we have access to OS'es <sys/select>, lets check again that
+ * PJ_IOQUEUE_MAX_HANDLES is not greater than FD_SETSIZE
+ */
+#if PJ_IOQUEUE_MAX_HANDLES > FD_SETSIZE
+#   error "PJ_IOQUEUE_MAX_HANDLES cannot be greater than FD_SETSIZE"
+#endif
+
+
+/*
+ * Include declaration from common abstraction.
+ */
+#include "ioqueue_common_abs.h"
+
+/*
+ * ISSUES with ioqueue_select()
+ *
+ * EAGAIN/EWOULDBLOCK error in recv():
+ *  - when multiple threads are working with the ioqueue, application
+ *    may receive EAGAIN or EWOULDBLOCK in the receive callback.
+ *    This error happens because more than one thread is watching for
+ *    the same descriptor set, so when all of them call recv() or recvfrom()
+ *    simultaneously, only one will succeed and the rest will get the error.
+ *
+ */
+#define THIS_FILE   "ioq_select"
+
+/*
+ * The select ioqueue relies on socket functions (pj_sock_xxx()) to return
+ * the correct error code.
+ */
+#if PJ_RETURN_OS_ERROR(100) != PJ_STATUS_FROM_OS(100)
+#   error "Error reporting must be enabled for this function to work!"
+#endif
+
+/*
+ * During debugging build, VALIDATE_FD_SET is set.
+ * This will check the validity of the fd_sets.
+ */
+/*
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+#  define VALIDATE_FD_SET		1
+#else
+#  define VALIDATE_FD_SET		0
+#endif
+*/
+#define VALIDATE_FD_SET     0
+
+#if 0
+#  define TRACE__(args)	PJ_LOG(3,args)
+#else
+#  define TRACE__(args)
+#endif
+
+/*
+ * This describes each key.
+ */
+struct pj_ioqueue_key_t
+{
+    DECLARE_COMMON_KEY
+};
+
+/*
+ * This describes the I/O queue itself.
+ */
+struct pj_ioqueue_t
+{
+    DECLARE_COMMON_IOQUEUE
+
+    unsigned		max, count;	/* Max and current key count	    */
+    int			nfds;		/* The largest fd value (for select)*/
+    pj_ioqueue_key_t	active_list;	/* List of active keys.		    */
+    pj_fd_set_t		rfdset;
+    pj_fd_set_t		wfdset;
+#if PJ_HAS_TCP
+    pj_fd_set_t		xfdset;
+#endif
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    pj_mutex_t	       *ref_cnt_mutex;
+    pj_ioqueue_key_t	closing_list;
+    pj_ioqueue_key_t	free_list;
+#endif
+};
+
+/* Proto */
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+	    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+static pj_status_t replace_udp_sock(pj_ioqueue_key_t *h);
+#endif
+
+/* Include implementation for common abstraction after we declare
+ * pj_ioqueue_key_t and pj_ioqueue_t.
+ */
+#include "ioqueue_common_abs.c"
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Scan closing keys to be put to free list again */
+static void scan_closing_keys(pj_ioqueue_t *ioqueue);
+#endif
+
+/*
+ * pj_ioqueue_name()
+ */
+PJ_DEF(const char*) pj_ioqueue_name(void)
+{
+    return "select";
+}
+
+/* 
+ * Scan the socket descriptor sets for the largest descriptor.
+ * This value is needed by select().
+ */
+#if defined(PJ_SELECT_NEEDS_NFDS) && PJ_SELECT_NEEDS_NFDS!=0
+static void rescan_fdset(pj_ioqueue_t *ioqueue)
+{
+    pj_ioqueue_key_t *key = ioqueue->active_list.next;
+    int max = 0;
+
+    while (key != &ioqueue->active_list) {
+	if (key->fd > max)
+	    max = key->fd;
+	key = key->next;
+    }
+
+    ioqueue->nfds = max;
+}
+#else
+static void rescan_fdset(pj_ioqueue_t *ioqueue)
+{
+    ioqueue->nfds = FD_SETSIZE-1;
+}
+#endif
+
+
+/*
+ * pj_ioqueue_create()
+ *
+ * Create select ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, 
+                                       pj_size_t max_fd,
+                                       pj_ioqueue_t **p_ioqueue)
+{
+    pj_ioqueue_t *ioqueue;
+    pj_lock_t *lock;
+    unsigned i;
+    pj_status_t rc;
+
+    /* Check that arguments are valid. */
+    PJ_ASSERT_RETURN(pool != NULL && p_ioqueue != NULL && 
+                     max_fd > 0 && max_fd <= PJ_IOQUEUE_MAX_HANDLES, 
+                     PJ_EINVAL);
+
+    /* Check that size of pj_ioqueue_op_key_t is sufficient */
+    PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >=
+                     sizeof(union operation_key), PJ_EBUG);
+
+    /* Create and init common ioqueue stuffs */
+    ioqueue = PJ_POOL_ALLOC_T(pool, pj_ioqueue_t);
+    ioqueue_init(ioqueue);
+
+    ioqueue->max = (unsigned)max_fd;
+    ioqueue->count = 0;
+    PJ_FD_ZERO(&ioqueue->rfdset);
+    PJ_FD_ZERO(&ioqueue->wfdset);
+#if PJ_HAS_TCP
+    PJ_FD_ZERO(&ioqueue->xfdset);
+#endif
+    pj_list_init(&ioqueue->active_list);
+
+    rescan_fdset(ioqueue);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* When safe unregistration is used (the default), we pre-create
+     * all keys and put them in the free list.
+     */
+
+    /* Mutex to protect key's reference counter 
+     * We don't want to use key's mutex or ioqueue's mutex because
+     * that would create deadlock situation in some cases.
+     */
+    rc = pj_mutex_create_simple(pool, NULL, &ioqueue->ref_cnt_mutex);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+
+    /* Init key list */
+    pj_list_init(&ioqueue->free_list);
+    pj_list_init(&ioqueue->closing_list);
+
+
+    /* Pre-create all keys according to max_fd */
+    for (i=0; i<max_fd; ++i) {
+	pj_ioqueue_key_t *key;
+
+	key = PJ_POOL_ALLOC_T(pool, pj_ioqueue_key_t);
+	key->ref_count = 0;
+	rc = pj_lock_create_recursive_mutex(pool, NULL, &key->lock);
+	if (rc != PJ_SUCCESS) {
+	    key = ioqueue->free_list.next;
+	    while (key != &ioqueue->free_list) {
+		pj_lock_destroy(key->lock);
+		key = key->next;
+	    }
+	    pj_mutex_destroy(ioqueue->ref_cnt_mutex);
+	    return rc;
+	}
+
+	pj_list_push_back(&ioqueue->free_list, key);
+    }
+#endif
+
+    /* Create and init ioqueue mutex */
+    rc = pj_lock_create_simple_mutex(pool, "ioq%p", &lock);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    rc = pj_ioqueue_set_lock(ioqueue, lock, PJ_TRUE);
+    if (rc != PJ_SUCCESS)
+        return rc;
+
+    PJ_LOG(4, ("pjlib", "select() I/O Queue created (%p)", ioqueue));
+
+    *p_ioqueue = ioqueue;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_destroy()
+ *
+ * Destroy ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy(pj_ioqueue_t *ioqueue)
+{
+    pj_ioqueue_key_t *key;
+
+    PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
+
+    pj_lock_acquire(ioqueue->lock);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Destroy reference counters */
+    key = ioqueue->active_list.next;
+    while (key != &ioqueue->active_list) {
+	pj_lock_destroy(key->lock);
+	key = key->next;
+    }
+
+    key = ioqueue->closing_list.next;
+    while (key != &ioqueue->closing_list) {
+	pj_lock_destroy(key->lock);
+	key = key->next;
+    }
+
+    key = ioqueue->free_list.next;
+    while (key != &ioqueue->free_list) {
+	pj_lock_destroy(key->lock);
+	key = key->next;
+    }
+
+    pj_mutex_destroy(ioqueue->ref_cnt_mutex);
+#endif
+
+    return ioqueue_destroy(ioqueue);
+}
+
+
+/*
+ * pj_ioqueue_register_sock()
+ *
+ * Register socket handle to ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool,
+					      pj_ioqueue_t *ioqueue,
+					      pj_sock_t sock,
+					      pj_grp_lock_t *grp_lock,
+					      void *user_data,
+					      const pj_ioqueue_callback *cb,
+                                              pj_ioqueue_key_t **p_key)
+{
+    pj_ioqueue_key_t *key = NULL;
+#if defined(PJ_WIN32) && PJ_WIN32!=0 || \
+    defined(PJ_WIN64) && PJ_WIN64 != 0 || \
+    defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
+    u_long value;
+#else
+    pj_uint32_t value;
+#endif
+    pj_status_t rc = PJ_SUCCESS;
+    
+    PJ_ASSERT_RETURN(pool && ioqueue && sock != PJ_INVALID_SOCKET &&
+                     cb && p_key, PJ_EINVAL);
+
+    pj_lock_acquire(ioqueue->lock);
+
+    if (ioqueue->count >= ioqueue->max) {
+        rc = PJ_ETOOMANY;
+	goto on_return;
+    }
+
+    /* If safe unregistration (PJ_IOQUEUE_HAS_SAFE_UNREG) is used, get
+     * the key from the free list. Otherwise allocate a new one. 
+     */
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+
+    /* Scan closing_keys first to let them come back to free_list */
+    scan_closing_keys(ioqueue);
+
+    pj_assert(!pj_list_empty(&ioqueue->free_list));
+    if (pj_list_empty(&ioqueue->free_list)) {
+	rc = PJ_ETOOMANY;
+	goto on_return;
+    }
+
+    key = ioqueue->free_list.next;
+    pj_list_erase(key);
+#else
+    key = (pj_ioqueue_key_t*)pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+#endif
+
+    rc = ioqueue_init_key(pool, ioqueue, key, sock, grp_lock, user_data, cb);
+    if (rc != PJ_SUCCESS) {
+	key = NULL;
+	goto on_return;
+    }
+
+    /* Set socket to nonblocking. */
+    value = 1;
+#if defined(PJ_WIN32) && PJ_WIN32!=0 || \
+    defined(PJ_WIN64) && PJ_WIN64 != 0 || \
+    defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
+    if (ioctlsocket(sock, FIONBIO, &value)) {
+#else
+    if (ioctl(sock, FIONBIO, &value)) {
+#endif
+        rc = pj_get_netos_error();
+	goto on_return;
+    }
+
+
+    /* Put in active list. */
+    pj_list_insert_before(&ioqueue->active_list, key);
+    ++ioqueue->count;
+
+    /* Rescan fdset to get max descriptor */
+    rescan_fdset(ioqueue);
+
+on_return:
+    /* On error, socket may be left in non-blocking mode. */
+    if (rc != PJ_SUCCESS) {
+	if (key && key->grp_lock)
+	    pj_grp_lock_dec_ref_dbg(key->grp_lock, "ioqueue", 0);
+    }
+    *p_key = key;
+    pj_lock_release(ioqueue->lock);
+    
+    return rc;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+					      pj_ioqueue_t *ioqueue,
+					      pj_sock_t sock,
+					      void *user_data,
+					      const pj_ioqueue_callback *cb,
+					      pj_ioqueue_key_t **p_key)
+{
+    return pj_ioqueue_register_sock2(pool, ioqueue, sock, NULL, user_data,
+                                     cb, p_key);
+}
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Increment key's reference counter */
+static void increment_counter(pj_ioqueue_key_t *key)
+{
+    pj_mutex_lock(key->ioqueue->ref_cnt_mutex);
+    ++key->ref_count;
+    pj_mutex_unlock(key->ioqueue->ref_cnt_mutex);
+}
+
+/* Decrement the key's reference counter, and when the counter reach zero,
+ * destroy the key.
+ *
+ * Note: MUST NOT CALL THIS FUNCTION WHILE HOLDING ioqueue's LOCK.
+ */
+static void decrement_counter(pj_ioqueue_key_t *key)
+{
+    pj_lock_acquire(key->ioqueue->lock);
+    pj_mutex_lock(key->ioqueue->ref_cnt_mutex);
+    --key->ref_count;
+    if (key->ref_count == 0) {
+
+	pj_assert(key->closing == 1);
+	pj_gettickcount(&key->free_time);
+	key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY;
+	pj_time_val_normalize(&key->free_time);
+
+	pj_list_erase(key);
+	pj_list_push_back(&key->ioqueue->closing_list, key);
+	/* Rescan fdset to get max descriptor */
+	rescan_fdset(key->ioqueue);
+    }
+    pj_mutex_unlock(key->ioqueue->ref_cnt_mutex);
+    pj_lock_release(key->ioqueue->lock);
+}
+#endif
+
+
+/*
+ * pj_ioqueue_unregister()
+ *
+ * Unregister handle from ioqueue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key)
+{
+    pj_ioqueue_t *ioqueue;
+
+    PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+    ioqueue = key->ioqueue;
+
+    /* Lock the key to make sure no callback is simultaneously modifying
+     * the key. We need to lock the key before ioqueue here to prevent
+     * deadlock.
+     */
+    pj_ioqueue_lock_key(key);
+
+    /* Also lock ioqueue */
+    pj_lock_acquire(ioqueue->lock);
+
+    pj_assert(ioqueue->count > 0);
+    --ioqueue->count;
+#if !PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Ticket #520, key will be erased more than once */
+    pj_list_erase(key);
+#endif
+    PJ_FD_CLR(key->fd, &ioqueue->rfdset);
+    PJ_FD_CLR(key->fd, &ioqueue->wfdset);
+#if PJ_HAS_TCP
+    PJ_FD_CLR(key->fd, &ioqueue->xfdset);
+#endif
+
+    /* Close socket. */
+    pj_sock_close(key->fd);
+
+    /* Clear callback */
+    key->cb.on_accept_complete = NULL;
+    key->cb.on_connect_complete = NULL;
+    key->cb.on_read_complete = NULL;
+    key->cb.on_write_complete = NULL;
+
+    /* Must release ioqueue lock first before decrementing counter, to
+     * prevent deadlock.
+     */
+    pj_lock_release(ioqueue->lock);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Mark key is closing. */
+    key->closing = 1;
+
+    /* Decrement counter. */
+    decrement_counter(key);
+
+    /* Done. */
+    if (key->grp_lock) {
+	/* just dec_ref and unlock. we will set grp_lock to NULL
+	 * elsewhere */
+	pj_grp_lock_t *grp_lock = key->grp_lock;
+	// Don't set grp_lock to NULL otherwise the other thread
+	// will crash. Just leave it as dangling pointer, but this
+	// should be safe
+	//key->grp_lock = NULL;
+	pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0);
+	pj_grp_lock_release(grp_lock);
+    } else {
+	pj_ioqueue_unlock_key(key);
+    }
+#else
+    if (key->grp_lock) {
+	/* set grp_lock to NULL and unlock */
+	pj_grp_lock_t *grp_lock = key->grp_lock;
+	// Don't set grp_lock to NULL otherwise the other thread
+	// will crash. Just leave it as dangling pointer, but this
+	// should be safe
+	//key->grp_lock = NULL;
+	pj_grp_lock_dec_ref_dbg(grp_lock, "ioqueue", 0);
+	pj_grp_lock_release(grp_lock);
+    } else {
+	pj_ioqueue_unlock_key(key);
+    }
+
+    pj_lock_destroy(key->lock);
+#endif
+
+    return PJ_SUCCESS;
+}
+
+
+/* This supposed to check whether the fd_set values are consistent
+ * with the operation currently set in each key.
+ */
+#if VALIDATE_FD_SET
+static void validate_sets(const pj_ioqueue_t *ioqueue,
+			  const pj_fd_set_t *rfdset,
+			  const pj_fd_set_t *wfdset,
+			  const pj_fd_set_t *xfdset)
+{
+    pj_ioqueue_key_t *key;
+
+    /*
+     * This basicly would not work anymore.
+     * We need to lock key before performing the check, but we can't do
+     * so because we're holding ioqueue mutex. If we acquire key's mutex
+     * now, the will cause deadlock.
+     */
+    pj_assert(0);
+
+    key = ioqueue->active_list.next;
+    while (key != &ioqueue->active_list) {
+	if (!pj_list_empty(&key->read_list)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+	    || !pj_list_empty(&key->accept_list)
+#endif
+	    ) 
+	{
+	    pj_assert(PJ_FD_ISSET(key->fd, rfdset));
+	} 
+	else {
+	    pj_assert(PJ_FD_ISSET(key->fd, rfdset) == 0);
+	}
+	if (!pj_list_empty(&key->write_list)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+	    || key->connecting
+#endif
+	   )
+	{
+	    pj_assert(PJ_FD_ISSET(key->fd, wfdset));
+	}
+	else {
+	    pj_assert(PJ_FD_ISSET(key->fd, wfdset) == 0);
+	}
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+	if (key->connecting)
+	{
+	    pj_assert(PJ_FD_ISSET(key->fd, xfdset));
+	}
+	else {
+	    pj_assert(PJ_FD_ISSET(key->fd, xfdset) == 0);
+	}
+#endif /* PJ_HAS_TCP */
+
+	key = key->next;
+    }
+}
+#endif	/* VALIDATE_FD_SET */
+
+
+/* ioqueue_remove_from_set()
+ * This function is called from ioqueue_dispatch_event() to instruct
+ * the ioqueue to remove the specified descriptor from ioqueue's descriptor
+ * set for the specified event.
+ */
+static void ioqueue_remove_from_set( pj_ioqueue_t *ioqueue,
+                                     pj_ioqueue_key_t *key, 
+                                     enum ioqueue_event_type event_type)
+{
+    pj_lock_acquire(ioqueue->lock);
+
+    if (event_type == READABLE_EVENT)
+        PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->rfdset);
+    else if (event_type == WRITEABLE_EVENT)
+        PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->wfdset);
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
+    else if (event_type == EXCEPTION_EVENT)
+        PJ_FD_CLR((pj_sock_t)key->fd, &ioqueue->xfdset);
+#endif
+    else
+        pj_assert(0);
+
+    pj_lock_release(ioqueue->lock);
+}
+
+/*
+ * ioqueue_add_to_set()
+ * This function is called from pj_ioqueue_recv(), pj_ioqueue_send() etc
+ * to instruct the ioqueue to add the specified handle to ioqueue's descriptor
+ * set for the specified event.
+ */
+static void ioqueue_add_to_set( pj_ioqueue_t *ioqueue,
+                                pj_ioqueue_key_t *key,
+                                enum ioqueue_event_type event_type )
+{
+    pj_lock_acquire(ioqueue->lock);
+
+    if (event_type == READABLE_EVENT)
+        PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->rfdset);
+    else if (event_type == WRITEABLE_EVENT)
+        PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->wfdset);
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
+    else if (event_type == EXCEPTION_EVENT)
+        PJ_FD_SET((pj_sock_t)key->fd, &ioqueue->xfdset);
+#endif
+    else
+        pj_assert(0);
+
+    pj_lock_release(ioqueue->lock);
+}
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Scan closing keys to be put to free list again */
+static void scan_closing_keys(pj_ioqueue_t *ioqueue)
+{
+    pj_time_val now;
+    pj_ioqueue_key_t *h;
+
+    pj_gettickcount(&now);
+    h = ioqueue->closing_list.next;
+    while (h != &ioqueue->closing_list) {
+	pj_ioqueue_key_t *next = h->next;
+
+	pj_assert(h->closing != 0);
+
+	if (PJ_TIME_VAL_GTE(now, h->free_time)) {
+	    pj_list_erase(h);
+	    // Don't set grp_lock to NULL otherwise the other thread
+	    // will crash. Just leave it as dangling pointer, but this
+	    // should be safe
+	    //h->grp_lock = NULL;
+	    pj_list_push_back(&ioqueue->free_list, h);
+	}
+	h = next;
+    }
+}
+#endif
+
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+static pj_status_t replace_udp_sock(pj_ioqueue_key_t *h)
+{
+    enum flags {
+	HAS_PEER_ADDR = 1,
+	HAS_QOS = 2
+    };
+    pj_sock_t old_sock, new_sock = PJ_INVALID_SOCKET;
+    pj_sockaddr local_addr, rem_addr;
+    int val, addr_len;
+    pj_fd_set_t *fds[3];
+    unsigned i, fds_cnt, flags=0;
+    pj_qos_params qos_params;
+    unsigned msec;
+    pj_status_t status;
+
+    pj_lock_acquire(h->ioqueue->lock);
+
+    old_sock = h->fd;
+
+    /* Can only replace UDP socket */
+    pj_assert(h->fd_type == pj_SOCK_DGRAM());
+
+    PJ_LOG(4,(THIS_FILE, "Attempting to replace UDP socket %d", old_sock));
+
+    /* Investigate the old socket */
+    addr_len = sizeof(local_addr);
+    status = pj_sock_getsockname(old_sock, &local_addr, &addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+    
+    addr_len = sizeof(rem_addr);
+    status = pj_sock_getpeername(old_sock, &rem_addr, &addr_len);
+    if (status == PJ_SUCCESS)
+	flags |= HAS_PEER_ADDR;
+
+    status = pj_sock_get_qos_params(old_sock, &qos_params);
+    if (status == PJ_SUCCESS)
+	flags |= HAS_QOS;
+
+    /* We're done with the old socket, close it otherwise we'll get
+     * error in bind()
+     */
+    pj_sock_close(old_sock);
+
+    /* Prepare the new socket */
+    status = pj_sock_socket(local_addr.addr.sa_family, PJ_SOCK_DGRAM, 0,
+			    &new_sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Even after the socket is closed, we'll still get "Address in use"
+     * errors, so force it with SO_REUSEADDR
+     */
+    val = 1;
+    status = pj_sock_setsockopt(new_sock, SOL_SOCKET, SO_REUSEADDR,
+				&val, sizeof(val));
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* The loop is silly, but what else can we do? */
+    addr_len = pj_sockaddr_get_len(&local_addr);
+    for (msec=20; ; msec<1000? msec=msec*2 : 1000) {
+	status = pj_sock_bind(new_sock, &local_addr, addr_len);
+	if (status != PJ_STATUS_FROM_OS(EADDRINUSE))
+	    break;
+	PJ_LOG(4,(THIS_FILE, "Address is still in use, retrying.."));
+	pj_thread_sleep(msec);
+    }
+
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (flags & HAS_QOS) {
+	status = pj_sock_set_qos_params(new_sock, &qos_params);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    if (flags & HAS_PEER_ADDR) {
+	status = pj_sock_connect(new_sock, &rem_addr, addr_len);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    /* Set socket to nonblocking. */
+    val = 1;
+#if defined(PJ_WIN32) && PJ_WIN32!=0 || \
+    defined(PJ_WIN64) && PJ_WIN64 != 0 || \
+    defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
+    if (ioctlsocket(new_sock, FIONBIO, &val)) {
+#else
+    if (ioctl(new_sock, FIONBIO, &val)) {
+#endif
+        status = pj_get_netos_error();
+	goto on_error;
+    }
+
+    /* Replace the occurrence of old socket with new socket in the
+     * fd sets.
+     */
+    fds_cnt = 0;
+    fds[fds_cnt++] = &h->ioqueue->rfdset;
+    fds[fds_cnt++] = &h->ioqueue->wfdset;
+#if PJ_HAS_TCP
+    fds[fds_cnt++] = &h->ioqueue->xfdset;
+#endif
+
+    for (i=0; i<fds_cnt; ++i) {
+	if (PJ_FD_ISSET(old_sock, fds[i])) {
+	    PJ_FD_CLR(old_sock, fds[i]);
+	    PJ_FD_SET(new_sock, fds[i]);
+	}
+    }
+
+    /* And finally replace the fd in the key */
+    h->fd = new_sock;
+
+    PJ_LOG(4,(THIS_FILE, "UDP has been replaced successfully!"));
+
+    pj_lock_release(h->ioqueue->lock);
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (new_sock != PJ_INVALID_SOCKET)
+	pj_sock_close(new_sock);
+    PJ_PERROR(1,(THIS_FILE, status, "Error replacing socket"));
+    pj_lock_release(h->ioqueue->lock);
+    return status;
+}
+#endif
+
+
+/*
+ * pj_ioqueue_poll()
+ *
+ * Few things worth written:
+ *
+ *  - we used to do only one callback called per poll, but it didn't go
+ *    very well. The reason is because on some situation, the write 
+ *    callback gets called all the time, thus doesn't give the read
+ *    callback to get called. This happens, for example, when user
+ *    submit write operation inside the write callback.
+ *    As the result, we changed the behaviour so that now multiple
+ *    callbacks are called in a single poll. It should be fast too,
+ *    just that we need to be carefull with the ioqueue data structs.
+ *
+ *  - to guarantee preemptiveness etc, the poll function must strictly
+ *    work on fd_set copy of the ioqueue (not the original one).
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
+{
+    pj_fd_set_t rfdset, wfdset, xfdset;
+    int count, i, counter;
+    pj_ioqueue_key_t *h;
+    struct event
+    {
+        pj_ioqueue_key_t	*key;
+        enum ioqueue_event_type  event_type;
+    } event[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL];
+
+    PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL);
+
+    /* Lock ioqueue before making fd_set copies */
+    pj_lock_acquire(ioqueue->lock);
+
+    /* We will only do select() when there are sockets to be polled.
+     * Otherwise select() will return error.
+     */
+    if (PJ_FD_COUNT(&ioqueue->rfdset)==0 &&
+        PJ_FD_COUNT(&ioqueue->wfdset)==0 
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP!=0
+        && PJ_FD_COUNT(&ioqueue->xfdset)==0
+#endif
+	)
+    {
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	scan_closing_keys(ioqueue);
+#endif
+	pj_lock_release(ioqueue->lock);
+	TRACE__((THIS_FILE, "     poll: no fd is set"));
+        if (timeout)
+            pj_thread_sleep(PJ_TIME_VAL_MSEC(*timeout));
+        return 0;
+    }
+
+    /* Copy ioqueue's pj_fd_set_t to local variables. */
+    pj_memcpy(&rfdset, &ioqueue->rfdset, sizeof(pj_fd_set_t));
+    pj_memcpy(&wfdset, &ioqueue->wfdset, sizeof(pj_fd_set_t));
+#if PJ_HAS_TCP
+    pj_memcpy(&xfdset, &ioqueue->xfdset, sizeof(pj_fd_set_t));
+#else
+    PJ_FD_ZERO(&xfdset);
+#endif
+
+#if VALIDATE_FD_SET
+    validate_sets(ioqueue, &rfdset, &wfdset, &xfdset);
+#endif
+
+    /* Unlock ioqueue before select(). */
+    pj_lock_release(ioqueue->lock);
+
+    count = pj_sock_select(ioqueue->nfds+1, &rfdset, &wfdset, &xfdset, 
+			   timeout);
+    
+    if (count == 0)
+	return 0;
+    else if (count < 0)
+	return -pj_get_netos_error();
+    else if (count > PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL)
+        count = PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL;
+
+    /* Scan descriptor sets for event and add the events in the event
+     * array to be processed later in this function. We do this so that
+     * events can be processed in parallel without holding ioqueue lock.
+     */
+    pj_lock_acquire(ioqueue->lock);
+
+    counter = 0;
+
+    /* Scan for writable sockets first to handle piggy-back data
+     * coming with accept().
+     */
+    h = ioqueue->active_list.next;
+    for ( ; h!=&ioqueue->active_list && counter<count; h = h->next) {
+
+	if ( (key_has_pending_write(h) || key_has_pending_connect(h))
+	     && PJ_FD_ISSET(h->fd, &wfdset) && !IS_CLOSING(h))
+        {
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	    increment_counter(h);
+#endif
+            event[counter].key = h;
+            event[counter].event_type = WRITEABLE_EVENT;
+            ++counter;
+        }
+
+        /* Scan for readable socket. */
+	if ((key_has_pending_read(h) || key_has_pending_accept(h))
+            && PJ_FD_ISSET(h->fd, &rfdset) && !IS_CLOSING(h) &&
+	    counter<count)
+        {
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	    increment_counter(h);
+#endif
+            event[counter].key = h;
+            event[counter].event_type = READABLE_EVENT;
+            ++counter;
+	}
+
+#if PJ_HAS_TCP
+        if (key_has_pending_connect(h) && PJ_FD_ISSET(h->fd, &xfdset) &&
+	    !IS_CLOSING(h) && counter<count) 
+	{
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	    increment_counter(h);
+#endif
+            event[counter].key = h;
+            event[counter].event_type = EXCEPTION_EVENT;
+            ++counter;
+        }
+#endif
+    }
+
+    for (i=0; i<counter; ++i) {
+	if (event[i].key->grp_lock)
+	    pj_grp_lock_add_ref_dbg(event[i].key->grp_lock, "ioqueue", 0);
+    }
+
+    PJ_RACE_ME(5);
+
+    pj_lock_release(ioqueue->lock);
+
+    PJ_RACE_ME(5);
+
+    count = counter;
+
+    /* Now process all events. The dispatch functions will take care
+     * of locking in each of the key
+     */
+    for (counter=0; counter<count; ++counter) {
+        switch (event[counter].event_type) {
+        case READABLE_EVENT:
+            ioqueue_dispatch_read_event(ioqueue, event[counter].key);
+            break;
+        case WRITEABLE_EVENT:
+            ioqueue_dispatch_write_event(ioqueue, event[counter].key);
+            break;
+        case EXCEPTION_EVENT:
+            ioqueue_dispatch_exception_event(ioqueue, event[counter].key);
+            break;
+        case NO_EVENT:
+            pj_assert(!"Invalid event!");
+            break;
+        }
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	decrement_counter(event[counter].key);
+#endif
+
+	if (event[counter].key->grp_lock)
+	    pj_grp_lock_dec_ref_dbg(event[counter].key->grp_lock,
+	                            "ioqueue", 0);
+    }
+
+
+    return count;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ioqueue_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/ioqueue_symbian.cpp
new file mode 100644
index 0000000..0b310d1
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ioqueue_symbian.cpp
@@ -0,0 +1,866 @@
+/* $Id: ioqueue_symbian.cpp 4374 2013-02-27 07:15:57Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ioqueue.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/list.h>
+#include <pj/lock.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#include "os_symbian.h"
+
+class CIoqueueCallback;
+
+/*
+ * IO Queue structure.
+ */
+struct pj_ioqueue_t
+{
+    int		     eventCount;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Class to encapsulate asynchronous socket operation.
+//
+class CIoqueueCallback : public CActive
+{
+public:
+    static CIoqueueCallback* NewL(pj_ioqueue_t *ioqueue,
+				  pj_ioqueue_key_t *key, 
+				  pj_sock_t sock, 
+				  const pj_ioqueue_callback *cb, 
+				  void *user_data);
+
+    //
+    // Start asynchronous recv() operation
+    //
+    pj_status_t StartRead(pj_ioqueue_op_key_t *op_key, 
+			  void *buf, pj_ssize_t *size, unsigned flags,
+			  pj_sockaddr_t *addr, int *addrlen);
+
+    //
+    // Start asynchronous accept() operation.
+    //
+    pj_status_t StartAccept(pj_ioqueue_op_key_t *op_key,
+			    pj_sock_t *new_sock,
+			    pj_sockaddr_t *local,
+			    pj_sockaddr_t *remote,
+			    int *addrlen );
+
+    //
+    // Completion callback.
+    //
+    void RunL();
+
+    //
+    // CActive's DoCancel()
+    //
+    void DoCancel();
+
+    //
+    // Cancel operation and call callback.
+    //
+    void CancelOperation(pj_ioqueue_op_key_t *op_key, 
+			 pj_ssize_t bytes_status);
+
+    //
+    // Accessors
+    //
+    void* get_user_data() const
+    {
+	return user_data_;
+    }
+    void set_user_data(void *user_data)
+    {
+	user_data_ = user_data;
+    }
+    pj_ioqueue_op_key_t *get_op_key() const
+    {
+	return pending_data_.common_.op_key_;
+    }
+    CPjSocket* get_pj_socket()
+    {
+	return sock_;
+    }
+
+private:
+    // Type of pending operation.
+    enum Type {
+	TYPE_NONE,
+	TYPE_READ,
+	TYPE_ACCEPT,
+    };
+
+    // Static data.
+    pj_ioqueue_t		*ioqueue_;
+    pj_ioqueue_key_t		*key_;
+    CPjSocket			*sock_;
+    pj_ioqueue_callback		 cb_;
+    void			*user_data_;
+
+    // Symbian data.
+    TPtr8			 aBufferPtr_;
+    TInetAddr			 aAddress_;
+
+    // Application data.
+    Type			 type_;
+
+    union Pending_Data
+    {
+	struct Common
+	{
+	    pj_ioqueue_op_key_t	*op_key_;
+	} common_;
+
+	struct Pending_Read
+	{
+	    pj_ioqueue_op_key_t	*op_key_;
+	    pj_sockaddr_t	*addr_;
+	    int			*addrlen_;
+	} read_;
+
+	struct Pending_Accept
+	{
+	    pj_ioqueue_op_key_t *op_key_;
+	    pj_sock_t		*new_sock_;
+	    pj_sockaddr_t	*local_;
+	    pj_sockaddr_t	*remote_;
+	    int			*addrlen_;
+	} accept_;
+    };
+
+    union Pending_Data		 pending_data_;
+    RSocket			blank_sock_;
+
+    CIoqueueCallback(pj_ioqueue_t *ioqueue,
+		     pj_ioqueue_key_t *key, pj_sock_t sock, 
+		     const pj_ioqueue_callback *cb, void *user_data)
+    : CActive(CActive::EPriorityStandard),
+	  ioqueue_(ioqueue), key_(key), sock_((CPjSocket*)sock), 
+	  user_data_(user_data), aBufferPtr_(NULL, 0), type_(TYPE_NONE)
+    {
+    	pj_memcpy(&cb_, cb, sizeof(*cb));
+    }
+
+
+    void ConstructL()
+    {
+	CActiveScheduler::Add(this);
+    }
+    
+    void HandleReadCompletion();
+    CPjSocket *HandleAcceptCompletion();
+};
+
+
+CIoqueueCallback* CIoqueueCallback::NewL(pj_ioqueue_t *ioqueue,
+					 pj_ioqueue_key_t *key, 
+					 pj_sock_t sock, 
+					 const pj_ioqueue_callback *cb, 
+					 void *user_data)
+{
+    CIoqueueCallback *self = new CIoqueueCallback(ioqueue, key, sock, 
+						  cb, user_data);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+
+    return self;
+}
+
+
+//
+// Start asynchronous recv() operation
+//
+pj_status_t CIoqueueCallback::StartRead(pj_ioqueue_op_key_t *op_key, 
+					void *buf, pj_ssize_t *size, 
+					unsigned flags,
+					pj_sockaddr_t *addr, int *addrlen)
+{
+    PJ_ASSERT_RETURN(IsActive()==false, PJ_EBUSY);
+    PJ_ASSERT_RETURN(pending_data_.common_.op_key_==NULL, PJ_EBUSY);
+
+    flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
+
+    pending_data_.read_.op_key_ = op_key;
+    pending_data_.read_.addr_ = addr;
+    pending_data_.read_.addrlen_ = addrlen;
+
+    aBufferPtr_.Set((TUint8*)buf, 0, (TInt)*size);
+
+    type_ = TYPE_READ;
+    if (addr && addrlen) {
+	sock_->Socket().RecvFrom(aBufferPtr_, aAddress_, flags, iStatus);
+    } else {
+	aAddress_.SetAddress(0);
+	aAddress_.SetPort(0);
+
+	if (sock_->IsDatagram()) {
+	    sock_->Socket().Recv(aBufferPtr_, flags, iStatus);
+	} else {
+	    // Using static like this is not pretty, but we don't need to use
+	    // the value anyway, hence doing it like this is probably most
+	    // optimal.
+	    static TSockXfrLength len;
+	    sock_->Socket().RecvOneOrMore(aBufferPtr_, flags, iStatus, len);
+	}
+    }
+
+    SetActive();
+    return PJ_EPENDING;
+}
+
+
+//
+// Start asynchronous accept() operation.
+//
+pj_status_t CIoqueueCallback::StartAccept(pj_ioqueue_op_key_t *op_key,
+					  pj_sock_t *new_sock,
+					  pj_sockaddr_t *local,
+					  pj_sockaddr_t *remote,
+					  int *addrlen )
+{
+    PJ_ASSERT_RETURN(IsActive()==false, PJ_EBUSY);
+    PJ_ASSERT_RETURN(pending_data_.common_.op_key_==NULL, PJ_EBUSY);
+
+    // addrlen must be specified if local or remote is specified
+    PJ_ASSERT_RETURN((!local && !remote) ||
+    		     (addrlen && *addrlen), PJ_EINVAL);
+    
+    pending_data_.accept_.op_key_ = op_key;
+    pending_data_.accept_.new_sock_ = new_sock;
+    pending_data_.accept_.local_ = local;
+    pending_data_.accept_.remote_ = remote;
+    pending_data_.accept_.addrlen_ = addrlen;
+
+    // Create blank socket
+    blank_sock_.Open(PjSymbianOS::Instance()->SocketServ());
+
+    type_ = TYPE_ACCEPT;
+    sock_->Socket().Accept(blank_sock_, iStatus);
+
+    SetActive();
+    return PJ_EPENDING;
+}
+
+
+//
+// Handle asynchronous RecvFrom() completion
+//
+void CIoqueueCallback::HandleReadCompletion() 
+{
+    if (pending_data_.read_.addr_ && pending_data_.read_.addrlen_) {
+	PjSymbianOS::Addr2pj(aAddress_, 
+			     *(pj_sockaddr*)pending_data_.read_.addr_,
+			     pending_data_.read_.addrlen_);
+	pending_data_.read_.addr_ = NULL;
+	pending_data_.read_.addrlen_ = NULL;
+    }
+	
+    pending_data_.read_.op_key_ = NULL;
+}
+
+
+//
+// Handle asynchronous Accept() completion.
+//
+CPjSocket *CIoqueueCallback::HandleAcceptCompletion() 
+{
+	CPjSocket *pjNewSock = new CPjSocket(get_pj_socket()->GetAf(), 
+					     get_pj_socket()->GetSockType(),
+					     blank_sock_);
+	int addrlen = 0;
+	
+	if (pending_data_.accept_.new_sock_) {
+	    *pending_data_.accept_.new_sock_ = (pj_sock_t)pjNewSock;
+	    pending_data_.accept_.new_sock_ = NULL;
+	}
+
+	if (pending_data_.accept_.local_) {
+	    TInetAddr aAddr;
+	    pj_sockaddr *ptr_sockaddr;
+	    
+	    blank_sock_.LocalName(aAddr);
+	    ptr_sockaddr = (pj_sockaddr*)pending_data_.accept_.local_;
+	    addrlen = *pending_data_.accept_.addrlen_;
+	    PjSymbianOS::Addr2pj(aAddr, *ptr_sockaddr, &addrlen);
+	    pending_data_.accept_.local_ = NULL;
+	}
+
+	if (pending_data_.accept_.remote_) {
+	    TInetAddr aAddr;
+	    pj_sockaddr *ptr_sockaddr;
+
+	    blank_sock_.RemoteName(aAddr);
+	    ptr_sockaddr = (pj_sockaddr*)pending_data_.accept_.remote_;
+	    addrlen = *pending_data_.accept_.addrlen_;
+	    PjSymbianOS::Addr2pj(aAddr, *ptr_sockaddr, &addrlen);
+	    pending_data_.accept_.remote_ = NULL;
+	}
+
+	if (pending_data_.accept_.addrlen_) {
+	    if (addrlen == 0) {
+	    	if (pjNewSock->GetAf() == PJ_AF_INET)
+	    	    addrlen = sizeof(pj_sockaddr_in);
+	    	else if (pjNewSock->GetAf() == PJ_AF_INET6)
+	    	    addrlen = sizeof(pj_sockaddr_in6);
+	    	else {
+	    	    pj_assert(!"Unsupported address family");
+	    	}
+	    }
+	    *pending_data_.accept_.addrlen_ = addrlen;
+	    pending_data_.accept_.addrlen_ = NULL;
+	}
+	
+	return pjNewSock;
+}
+
+
+//
+// Completion callback.
+//
+void CIoqueueCallback::RunL()
+{
+    pj_ioqueue_t *ioq = ioqueue_;
+    Type cur_type = type_;
+
+    type_ = TYPE_NONE;
+
+    if (cur_type == TYPE_READ) {
+	//
+	// Completion of asynchronous RecvFrom()
+	//
+
+	/* Clear op_key (save it to temp variable first!) */
+	pj_ioqueue_op_key_t	*op_key = pending_data_.read_.op_key_;
+	pending_data_.read_.op_key_ = NULL;
+
+	// Handle failure condition
+	if (iStatus != KErrNone) {
+	    if (cb_.on_read_complete) {
+	    	cb_.on_read_complete( key_, op_key, 
+				      -PJ_RETURN_OS_ERROR(iStatus.Int()));
+	    }
+	    return;
+	}
+
+	HandleReadCompletion();
+
+	/* Call callback */
+	if (cb_.on_read_complete) {
+	    cb_.on_read_complete(key_, op_key, aBufferPtr_.Length());
+	}
+
+    } else if (cur_type == TYPE_ACCEPT) {
+	//
+	// Completion of asynchronous Accept()
+	//
+	
+	/* Clear op_key (save it to temp variable first!) */
+	pj_ioqueue_op_key_t	*op_key = pending_data_.read_.op_key_;
+	pending_data_.read_.op_key_ = NULL;
+
+	// Handle failure condition
+	if (iStatus != KErrNone) {
+	    if (pending_data_.accept_.new_sock_)
+		*pending_data_.accept_.new_sock_ = PJ_INVALID_SOCKET;
+	    
+	    if (cb_.on_accept_complete) {
+	    	cb_.on_accept_complete( key_, op_key, PJ_INVALID_SOCKET,
+				        -PJ_RETURN_OS_ERROR(iStatus.Int()));
+	    }
+	    return;
+	}
+
+	CPjSocket *pjNewSock = HandleAcceptCompletion();
+	
+	// Call callback.
+	if (cb_.on_accept_complete) {
+	    cb_.on_accept_complete( key_, op_key, (pj_sock_t)pjNewSock, 
+				    PJ_SUCCESS);
+	}
+    }
+
+    ioq->eventCount++;
+}
+
+//
+// CActive's DoCancel()
+//
+void CIoqueueCallback::DoCancel()
+{
+    if (type_ == TYPE_READ)
+	sock_->Socket().CancelRecv();
+    else if (type_ == TYPE_ACCEPT)
+	sock_->Socket().CancelAccept();
+
+    type_ = TYPE_NONE;
+    pending_data_.common_.op_key_ = NULL;
+}
+
+//
+// Cancel operation and call callback.
+//
+void CIoqueueCallback::CancelOperation(pj_ioqueue_op_key_t *op_key, 
+				       pj_ssize_t bytes_status)
+{
+    Type cur_type = type_;
+
+    pj_assert(op_key == pending_data_.common_.op_key_);
+
+    Cancel();
+
+    if (cur_type == TYPE_READ) {
+    	if (cb_.on_read_complete)
+    	    cb_.on_read_complete(key_, op_key, bytes_status);
+    } else if (cur_type == TYPE_ACCEPT)
+	;    
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/*
+ * IO Queue key structure.
+ */
+struct pj_ioqueue_key_t
+{
+    CIoqueueCallback	*cbObj;
+};
+
+
+/*
+ * Return the name of the ioqueue implementation.
+ */
+PJ_DEF(const char*) pj_ioqueue_name(void)
+{
+    return "ioqueue-symbian";
+}
+
+
+/*
+ * Create a new I/O Queue framework.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create(	pj_pool_t *pool, 
+					pj_size_t max_fd,
+					pj_ioqueue_t **p_ioqueue)
+{
+    pj_ioqueue_t *ioq;
+
+    PJ_UNUSED_ARG(max_fd);
+
+    ioq = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_t);
+    *p_ioqueue = ioq;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy the I/O queue.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioq )
+{
+    PJ_UNUSED_ARG(ioq);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Set the lock object to be used by the I/O Queue. 
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioq, 
+					 pj_lock_t *lock,
+					 pj_bool_t auto_delete )
+{
+    /* Don't really need lock for now */
+    PJ_UNUSED_ARG(ioq);
+    
+    if (auto_delete) {
+	pj_lock_destroy(lock);
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency(pj_ioqueue_t *ioqueue,
+													   pj_bool_t allow)
+{
+	/* Not supported, just return PJ_SUCCESS silently */
+	PJ_UNUSED_ARG(ioqueue);
+	PJ_UNUSED_ARG(allow);
+	return PJ_SUCCESS;
+}
+
+/*
+ * Register a socket to the I/O queue framework. 
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+					      pj_ioqueue_t *ioq,
+					      pj_sock_t sock,
+					      void *user_data,
+					      const pj_ioqueue_callback *cb,
+                                              pj_ioqueue_key_t **p_key )
+{
+    pj_ioqueue_key_t *key;
+
+    key = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_key_t);
+    key->cbObj = CIoqueueCallback::NewL(ioq, key, sock, cb, user_data);
+
+    *p_key = key;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool,
+					      pj_ioqueue_t *ioqueue,
+					      pj_sock_t sock,
+					      pj_grp_lock_t *grp_lock,
+					      void *user_data,
+					      const pj_ioqueue_callback *cb,
+                                              pj_ioqueue_key_t **p_key)
+{
+    PJ_UNUSED_ARG(grp_lock);
+
+    return pj_ioqueue_register_sock(pool, ioqueue, sock, user_data, cb, p_key);
+}
+
+/*
+ * Unregister from the I/O Queue framework. 
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key )
+{
+    if (key == NULL || key->cbObj == NULL)
+	return PJ_SUCCESS;
+
+    // Cancel pending async object
+    if (key->cbObj) {
+	key->cbObj->Cancel();
+    }
+
+    // Close socket.
+    key->cbObj->get_pj_socket()->Socket().Close();
+    delete key->cbObj->get_pj_socket();
+
+    // Delete async object.
+    if (key->cbObj) {
+	delete key->cbObj;
+	key->cbObj = NULL;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get user data associated with an ioqueue key.
+ */
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+    return key->cbObj->get_user_data();
+}
+
+
+/*
+ * Set or change the user data to be associated with the file descriptor or
+ * handle or socket descriptor.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
+                                              void *user_data,
+                                              void **old_data)
+{
+    if (old_data)
+	*old_data = key->cbObj->get_user_data();
+    key->cbObj->set_user_data(user_data);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Initialize operation key.
+ */
+PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
+				     pj_size_t size )
+{
+    pj_bzero(op_key, size);
+}
+
+
+/*
+ * Check if operation is pending on the specified operation key.
+ */
+PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key,
+                                         pj_ioqueue_op_key_t *op_key )
+{
+    return key->cbObj->get_op_key()==op_key &&
+	   key->cbObj->IsActive();
+}
+
+
+/*
+ * Post completion status to the specified operation key and call the
+ * appropriate callback. 
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
+                                                pj_ioqueue_op_key_t *op_key,
+                                                pj_ssize_t bytes_status )
+{
+    if (pj_ioqueue_is_pending(key, op_key)) {
+	key->cbObj->CancelOperation(op_key, bytes_status);
+    }
+    return PJ_SUCCESS;
+}
+
+
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+/**
+ * Instruct I/O Queue to accept incoming connection on the specified 
+ * listening socket.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
+                                       pj_ioqueue_op_key_t *op_key,
+				       pj_sock_t *new_sock,
+				       pj_sockaddr_t *local,
+				       pj_sockaddr_t *remote,
+				       int *addrlen )
+{
+    
+    return key->cbObj->StartAccept(op_key, new_sock, local, remote, addrlen);
+}
+
+
+/*
+ * Initiate non-blocking socket connect.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
+					const pj_sockaddr_t *addr,
+					int addrlen )
+{
+    pj_status_t status;
+    
+    RSocket &rSock = key->cbObj->get_pj_socket()->Socket();
+    TInetAddr inetAddr;
+    TRequestStatus reqStatus;
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    // Convert address
+    status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen, 
+    				  inetAddr);
+    if (status != PJ_SUCCESS)
+    	return status;
+    
+    // We don't support async connect for now.
+    PJ_TODO(IOQUEUE_SUPPORT_ASYNC_CONNECT);
+
+    rSock.Connect(inetAddr, reqStatus);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus == KErrNone)
+	return PJ_SUCCESS;
+
+    return PJ_RETURN_OS_ERROR(reqStatus.Int());
+}
+
+
+#endif	/* PJ_HAS_TCP */
+
+/*
+ * Poll the I/O Queue for completed events.
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioq,
+			     const pj_time_val *timeout)
+{
+    /* Polling is not necessary on Symbian, since all async activities
+     * are registered to active scheduler.
+     */
+    PJ_UNUSED_ARG(ioq);
+    PJ_UNUSED_ARG(timeout);
+    return 0;
+}
+
+
+/*
+ * Instruct the I/O Queue to read from the specified handle.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
+                                     pj_ioqueue_op_key_t *op_key,
+				     void *buffer,
+				     pj_ssize_t *length,
+				     pj_uint32_t flags )
+{
+    // If socket has reader, delete it.
+    if (key->cbObj->get_pj_socket()->Reader())
+    	key->cbObj->get_pj_socket()->DestroyReader();
+    
+    // Clear flag
+    flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
+    return key->cbObj->StartRead(op_key, buffer, length, flags, NULL, NULL);
+}
+
+
+/*
+ * This function behaves similarly as #pj_ioqueue_recv(), except that it is
+ * normally called for socket, and the remote address will also be returned
+ * along with the data.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
+                                         pj_ioqueue_op_key_t *op_key,
+					 void *buffer,
+					 pj_ssize_t *length,
+                                         pj_uint32_t flags,
+					 pj_sockaddr_t *addr,
+					 int *addrlen)
+{
+    CPjSocket *sock = key->cbObj->get_pj_socket();
+    
+    // If address is specified, check that the length match the
+    // address family
+    if (addr || addrlen) {
+    	PJ_ASSERT_RETURN(addr && addrlen && *addrlen, PJ_EINVAL);
+    	if (sock->GetAf() == PJ_AF_INET) {
+    	    PJ_ASSERT_RETURN(*addrlen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+    	} else if (sock->GetAf() == PJ_AF_INET6) {
+    	    PJ_ASSERT_RETURN(*addrlen>=(int)sizeof(pj_sockaddr_in6), PJ_EINVAL);
+    	}
+    }
+    
+    // If socket has reader, delete it.
+    if (sock->Reader())
+    	sock->DestroyReader();
+    
+    if (key->cbObj->IsActive())
+	return PJ_EBUSY;
+
+    // Clear flag
+    flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
+    return key->cbObj->StartRead(op_key, buffer, length, flags, addr, addrlen);
+}
+
+
+/*
+ * Instruct the I/O Queue to write to the handle.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
+                                     pj_ioqueue_op_key_t *op_key,
+				     const void *data,
+				     pj_ssize_t *length,
+				     pj_uint32_t flags )
+{
+    TRequestStatus reqStatus;
+    TPtrC8 aBuffer((const TUint8*)data, (TInt)*length);
+    TSockXfrLength aLen;
+    
+    PJ_UNUSED_ARG(op_key);
+
+    // Forcing pending operation is not supported.
+    PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL);
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+
+    // Clear flag
+    flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
+
+    key->cbObj->get_pj_socket()->Socket().Send(aBuffer, flags, reqStatus, aLen);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus.Int() != KErrNone)
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+
+    //At least in UIQ Emulator, aLen.Length() reports incorrect length
+    //for UDP (some newlc.com users seem to have reported this too).
+    //*length = aLen.Length();
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Instruct the I/O Queue to write to the handle.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
+                                       pj_ioqueue_op_key_t *op_key,
+				       const void *data,
+				       pj_ssize_t *length,
+                                       pj_uint32_t flags,
+				       const pj_sockaddr_t *addr,
+				       int addrlen)
+{
+    TRequestStatus reqStatus;
+    TPtrC8 aBuffer;
+    TInetAddr inetAddr;
+    TSockXfrLength aLen;
+    pj_status_t status;
+    
+    PJ_UNUSED_ARG(op_key);
+
+    // Forcing pending operation is not supported.
+    PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL);
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+
+    // Convert address
+    status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen, 
+    				  inetAddr);
+    if (status != PJ_SUCCESS)
+    	return status;
+    
+    // Clear flag
+    flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
+
+    aBuffer.Set((const TUint8*)data, (TInt)*length);
+    CPjSocket *pjSock = key->cbObj->get_pj_socket();
+
+    pjSock->Socket().SendTo(aBuffer, inetAddr, flags, reqStatus, aLen);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus.Int() != KErrNone)
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+
+    //At least in UIQ Emulator, aLen.Length() reports incorrect length
+    //for UDP (some newlc.com users seem to have reported this too).
+    //*length = aLen.Length();
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key,
+											   pj_bool_t allow)
+{
+	/* Not supported, just return PJ_SUCCESS silently */
+	PJ_UNUSED_ARG(key);
+	PJ_UNUSED_ARG(allow);
+	return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key)
+{
+	/* Not supported, just return PJ_SUCCESS silently */
+	PJ_UNUSED_ARG(key);
+	return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key)
+{
+	/* Not supported, just return PJ_SUCCESS silently */
+	PJ_UNUSED_ARG(key);
+	return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/ioqueue_winnt.c b/jni/pjproject-android/pjlib/src/pj/ioqueue_winnt.c
new file mode 100644
index 0000000..fbe8ab8
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ioqueue_winnt.c
@@ -0,0 +1,1443 @@
+/* $Id: ioqueue_winnt.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ioqueue.h>
+#include <pj/os.h>
+#include <pj/lock.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/array.h>
+#include <pj/log.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/compat/socket.h>
+
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+#  include <winsock2.h>
+#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+#  include <winsock.h>
+#endif
+
+#if defined(PJ_HAS_MSWSOCK_H) && PJ_HAS_MSWSOCK_H != 0
+#  include <mswsock.h>
+#endif
+
+
+/* The address specified in AcceptEx() must be 16 more than the size of
+ * SOCKADDR (source: MSDN).
+ */
+#define ACCEPT_ADDR_LEN	    (sizeof(pj_sockaddr_in)+16)
+
+typedef struct generic_overlapped
+{
+    WSAOVERLAPPED	   overlapped;
+    pj_ioqueue_operation_e operation;
+} generic_overlapped;
+
+/*
+ * OVERLAPPPED structure for send and receive.
+ */
+typedef struct ioqueue_overlapped
+{
+    WSAOVERLAPPED	   overlapped;
+    pj_ioqueue_operation_e operation;
+    WSABUF		   wsabuf;
+    pj_sockaddr_in         dummy_addr;
+    int                    dummy_addrlen;
+} ioqueue_overlapped;
+
+#if PJ_HAS_TCP
+/*
+ * OVERLAP structure for accept.
+ */
+typedef struct ioqueue_accept_rec
+{
+    WSAOVERLAPPED	    overlapped;
+    pj_ioqueue_operation_e  operation;
+    pj_sock_t		    newsock;
+    pj_sock_t		   *newsock_ptr;
+    int			   *addrlen;
+    void		   *remote;
+    void		   *local;
+    char		    accept_buf[2 * ACCEPT_ADDR_LEN];
+} ioqueue_accept_rec;
+#endif
+
+/*
+ * Structure to hold pending operation key.
+ */
+union operation_key
+{
+    generic_overlapped      generic;
+    ioqueue_overlapped      overlapped;
+#if PJ_HAS_TCP
+    ioqueue_accept_rec      accept;
+#endif
+};
+
+/* Type of handle in the key. */
+enum handle_type
+{
+    HND_IS_UNKNOWN,
+    HND_IS_FILE,
+    HND_IS_SOCKET,
+};
+
+enum { POST_QUIT_LEN = 0xFFFFDEADUL };
+
+/*
+ * Structure for individual socket.
+ */
+struct pj_ioqueue_key_t
+{
+    PJ_DECL_LIST_MEMBER(struct pj_ioqueue_key_t);
+
+    pj_ioqueue_t       *ioqueue;
+    HANDLE		hnd;
+    void	       *user_data;
+    enum handle_type    hnd_type;
+    pj_ioqueue_callback	cb;
+    pj_bool_t		allow_concurrent;
+
+#if PJ_HAS_TCP
+    int			connecting;
+#endif
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    pj_atomic_t	       *ref_count;
+    pj_bool_t		closing;
+    pj_time_val		free_time;
+    pj_mutex_t	       *mutex;
+#endif
+
+};
+
+/*
+ * IO Queue structure.
+ */
+struct pj_ioqueue_t
+{
+    HANDLE	      iocp;
+    pj_lock_t        *lock;
+    pj_bool_t         auto_delete_lock;
+    pj_bool_t	      default_concurrency;
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    pj_ioqueue_key_t  active_list;
+    pj_ioqueue_key_t  free_list;
+    pj_ioqueue_key_t  closing_list;
+#endif
+
+    /* These are to keep track of connecting sockets */
+#if PJ_HAS_TCP
+    unsigned	      event_count;
+    HANDLE	      event_pool[MAXIMUM_WAIT_OBJECTS+1];
+    unsigned	      connecting_count;
+    HANDLE	      connecting_handles[MAXIMUM_WAIT_OBJECTS+1];
+    pj_ioqueue_key_t *connecting_keys[MAXIMUM_WAIT_OBJECTS+1];
+#endif
+};
+
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Prototype */
+static void scan_closing_keys(pj_ioqueue_t *ioqueue);
+#endif
+
+
+#if PJ_HAS_TCP
+/*
+ * Process the socket when the overlapped accept() completed.
+ */
+static void ioqueue_on_accept_complete(pj_ioqueue_key_t *key,
+                                       ioqueue_accept_rec *accept_overlapped)
+{
+    struct sockaddr *local;
+    struct sockaddr *remote;
+    int locallen, remotelen;
+    pj_status_t status;
+
+    PJ_CHECK_STACK();
+
+    /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket 
+     * addresses can be obtained with getsockname() and getpeername().
+     */
+    status = setsockopt(accept_overlapped->newsock, SOL_SOCKET,
+                        SO_UPDATE_ACCEPT_CONTEXT, 
+                        (char*)&key->hnd, 
+                        sizeof(SOCKET));
+    /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later.
+     * So ignore the error status.
+     */
+
+    /* Operation complete immediately. */
+    if (accept_overlapped->addrlen) {
+	GetAcceptExSockaddrs( accept_overlapped->accept_buf,
+			      0, 
+			      ACCEPT_ADDR_LEN,
+			      ACCEPT_ADDR_LEN,
+			      &local,
+			      &locallen,
+			      &remote,
+			      &remotelen);
+	if (*accept_overlapped->addrlen >= locallen) {
+	    if (accept_overlapped->local)
+		pj_memcpy(accept_overlapped->local, local, locallen);
+	    if (accept_overlapped->remote)
+		pj_memcpy(accept_overlapped->remote, remote, locallen);
+	} else {
+	    if (accept_overlapped->local)
+		pj_bzero(accept_overlapped->local, 
+			 *accept_overlapped->addrlen);
+	    if (accept_overlapped->remote)
+		pj_bzero(accept_overlapped->remote, 
+			 *accept_overlapped->addrlen);
+	}
+
+	*accept_overlapped->addrlen = locallen;
+    }
+    if (accept_overlapped->newsock_ptr)
+        *accept_overlapped->newsock_ptr = accept_overlapped->newsock;
+    accept_overlapped->operation = 0;
+}
+
+static void erase_connecting_socket( pj_ioqueue_t *ioqueue, unsigned pos)
+{
+    pj_ioqueue_key_t *key = ioqueue->connecting_keys[pos];
+    HANDLE hEvent = ioqueue->connecting_handles[pos];
+
+    /* Remove key from array of connecting handles. */
+    pj_array_erase(ioqueue->connecting_keys, sizeof(key),
+		   ioqueue->connecting_count, pos);
+    pj_array_erase(ioqueue->connecting_handles, sizeof(HANDLE),
+		   ioqueue->connecting_count, pos);
+    --ioqueue->connecting_count;
+
+    /* Disassociate the socket from the event. */
+    WSAEventSelect((pj_sock_t)key->hnd, hEvent, 0);
+
+    /* Put event object to pool. */
+    if (ioqueue->event_count < MAXIMUM_WAIT_OBJECTS) {
+	ioqueue->event_pool[ioqueue->event_count++] = hEvent;
+    } else {
+	/* Shouldn't happen. There should be no more pending connections
+	 * than max. 
+	 */
+	pj_assert(0);
+	CloseHandle(hEvent);
+    }
+
+}
+
+/*
+ * Poll for the completion of non-blocking connect().
+ * If there's a completion, the function return the key of the completed
+ * socket, and 'result' argument contains the connect() result. If connect()
+ * succeeded, 'result' will have value zero, otherwise will have the error
+ * code.
+ */
+static int check_connecting( pj_ioqueue_t *ioqueue )
+{
+    if (ioqueue->connecting_count) {
+	int i, count;
+	struct 
+	{
+	    pj_ioqueue_key_t *key;
+	    pj_status_t	      status;
+	} events[PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL-1];
+
+	pj_lock_acquire(ioqueue->lock);
+	for (count=0; count<PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL-1; ++count) {
+	    DWORD result;
+
+	    result = WaitForMultipleObjects(ioqueue->connecting_count,
+					    ioqueue->connecting_handles,
+					    FALSE, 0);
+	    if (result >= WAIT_OBJECT_0 && 
+		result < WAIT_OBJECT_0+ioqueue->connecting_count) 
+	    {
+		WSANETWORKEVENTS net_events;
+
+		/* Got completed connect(). */
+		unsigned pos = result - WAIT_OBJECT_0;
+		events[count].key = ioqueue->connecting_keys[pos];
+
+		/* See whether connect has succeeded. */
+		WSAEnumNetworkEvents((pj_sock_t)events[count].key->hnd, 
+				     ioqueue->connecting_handles[pos], 
+				     &net_events);
+		events[count].status = 
+		    PJ_STATUS_FROM_OS(net_events.iErrorCode[FD_CONNECT_BIT]);
+
+		/* Erase socket from pending connect. */
+		erase_connecting_socket(ioqueue, pos);
+	    } else {
+		/* No more events */
+		break;
+	    }
+	}
+	pj_lock_release(ioqueue->lock);
+
+	/* Call callbacks. */
+	for (i=0; i<count; ++i) {
+	    if (events[i].key->cb.on_connect_complete) {
+		events[i].key->cb.on_connect_complete(events[i].key, 
+						      events[i].status);
+	    }
+	}
+
+	return count;
+    }
+
+    return 0;
+    
+}
+#endif
+
+/*
+ * pj_ioqueue_name()
+ */
+PJ_DEF(const char*) pj_ioqueue_name(void)
+{
+    return "iocp";
+}
+
+/*
+ * pj_ioqueue_create()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool, 
+				       pj_size_t max_fd,
+				       pj_ioqueue_t **p_ioqueue)
+{
+    pj_ioqueue_t *ioqueue;
+    unsigned i;
+    pj_status_t rc;
+
+    PJ_UNUSED_ARG(max_fd);
+    PJ_ASSERT_RETURN(pool && p_ioqueue, PJ_EINVAL);
+
+    rc = sizeof(union operation_key);
+
+    /* Check that sizeof(pj_ioqueue_op_key_t) makes sense. */
+    PJ_ASSERT_RETURN(sizeof(pj_ioqueue_op_key_t)-sizeof(void*) >= 
+                     sizeof(union operation_key), PJ_EBUG);
+
+    /* Create IOCP */
+    ioqueue = pj_pool_zalloc(pool, sizeof(*ioqueue));
+    ioqueue->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+    if (ioqueue->iocp == NULL)
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    /* Create IOCP mutex */
+    rc = pj_lock_create_recursive_mutex(pool, NULL, &ioqueue->lock);
+    if (rc != PJ_SUCCESS) {
+	CloseHandle(ioqueue->iocp);
+	return rc;
+    }
+
+    ioqueue->auto_delete_lock = PJ_TRUE;
+    ioqueue->default_concurrency = PJ_IOQUEUE_DEFAULT_ALLOW_CONCURRENCY;
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /*
+     * Create and initialize key pools.
+     */
+    pj_list_init(&ioqueue->active_list);
+    pj_list_init(&ioqueue->free_list);
+    pj_list_init(&ioqueue->closing_list);
+
+    /* Preallocate keys according to max_fd setting, and put them
+     * in free_list.
+     */
+    for (i=0; i<max_fd; ++i) {
+	pj_ioqueue_key_t *key;
+
+	key = pj_pool_alloc(pool, sizeof(pj_ioqueue_key_t));
+
+	rc = pj_atomic_create(pool, 0, &key->ref_count);
+	if (rc != PJ_SUCCESS) {
+	    key = ioqueue->free_list.next;
+	    while (key != &ioqueue->free_list) {
+		pj_atomic_destroy(key->ref_count);
+		pj_mutex_destroy(key->mutex);
+		key = key->next;
+	    }
+	    CloseHandle(ioqueue->iocp);
+	    return rc;
+	}
+
+	rc = pj_mutex_create_recursive(pool, "ioqkey", &key->mutex);
+	if (rc != PJ_SUCCESS) {
+	    pj_atomic_destroy(key->ref_count);
+	    key = ioqueue->free_list.next;
+	    while (key != &ioqueue->free_list) {
+		pj_atomic_destroy(key->ref_count);
+		pj_mutex_destroy(key->mutex);
+		key = key->next;
+	    }
+	    CloseHandle(ioqueue->iocp);
+	    return rc;
+	}
+
+	pj_list_push_back(&ioqueue->free_list, key);
+    }
+#endif
+
+    *p_ioqueue = ioqueue;
+
+    PJ_LOG(4, ("pjlib", "WinNT IOCP I/O Queue created (%p)", ioqueue));
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_destroy()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioqueue )
+{
+#if PJ_HAS_TCP
+    unsigned i;
+#endif
+    pj_ioqueue_key_t *key;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(ioqueue, PJ_EINVAL);
+
+    pj_lock_acquire(ioqueue->lock);
+
+#if PJ_HAS_TCP
+    /* Destroy events in the pool */
+    for (i=0; i<ioqueue->event_count; ++i) {
+	CloseHandle(ioqueue->event_pool[i]);
+    }
+    ioqueue->event_count = 0;
+#endif
+
+    if (CloseHandle(ioqueue->iocp) != TRUE)
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Destroy reference counters */
+    key = ioqueue->active_list.next;
+    while (key != &ioqueue->active_list) {
+	pj_atomic_destroy(key->ref_count);
+	pj_mutex_destroy(key->mutex);
+	key = key->next;
+    }
+
+    key = ioqueue->closing_list.next;
+    while (key != &ioqueue->closing_list) {
+	pj_atomic_destroy(key->ref_count);
+	pj_mutex_destroy(key->mutex);
+	key = key->next;
+    }
+
+    key = ioqueue->free_list.next;
+    while (key != &ioqueue->free_list) {
+	pj_atomic_destroy(key->ref_count);
+	pj_mutex_destroy(key->mutex);
+	key = key->next;
+    }
+#endif
+
+    if (ioqueue->auto_delete_lock)
+        pj_lock_destroy(ioqueue->lock);
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency(pj_ioqueue_t *ioqueue,
+						       pj_bool_t allow)
+{
+    PJ_ASSERT_RETURN(ioqueue != NULL, PJ_EINVAL);
+    ioqueue->default_concurrency = allow;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_set_lock()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioqueue, 
+					 pj_lock_t *lock,
+					 pj_bool_t auto_delete )
+{
+    PJ_ASSERT_RETURN(ioqueue && lock, PJ_EINVAL);
+
+    if (ioqueue->auto_delete_lock) {
+        pj_lock_destroy(ioqueue->lock);
+    }
+
+    ioqueue->lock = lock;
+    ioqueue->auto_delete_lock = auto_delete;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_ioqueue_register_sock()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
+					      pj_ioqueue_t *ioqueue,
+					      pj_sock_t sock,
+					      void *user_data,
+					      const pj_ioqueue_callback *cb,
+					      pj_ioqueue_key_t **key )
+{
+    HANDLE hioq;
+    pj_ioqueue_key_t *rec;
+    u_long value;
+    int rc;
+
+    PJ_ASSERT_RETURN(pool && ioqueue && cb && key, PJ_EINVAL);
+
+    pj_lock_acquire(ioqueue->lock);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Scan closing list first to release unused keys.
+     * Must do this with lock acquired.
+     */
+    scan_closing_keys(ioqueue);
+
+    /* If safe unregistration is used, then get the key record from
+     * the free list.
+     */
+    if (pj_list_empty(&ioqueue->free_list)) {
+	pj_lock_release(ioqueue->lock);
+	return PJ_ETOOMANY;
+    }
+
+    rec = ioqueue->free_list.next;
+    pj_list_erase(rec);
+
+    /* Set initial reference count to 1 */
+    pj_assert(pj_atomic_get(rec->ref_count) == 0);
+    pj_atomic_inc(rec->ref_count);
+
+    rec->closing = 0;
+
+#else
+    rec = pj_pool_zalloc(pool, sizeof(pj_ioqueue_key_t));
+#endif
+
+    /* Build the key for this socket. */
+    rec->ioqueue = ioqueue;
+    rec->hnd = (HANDLE)sock;
+    rec->hnd_type = HND_IS_SOCKET;
+    rec->user_data = user_data;
+    pj_memcpy(&rec->cb, cb, sizeof(pj_ioqueue_callback));
+
+    /* Set concurrency for this handle */
+    rc = pj_ioqueue_set_concurrency(rec, ioqueue->default_concurrency);
+    if (rc != PJ_SUCCESS) {
+	pj_lock_release(ioqueue->lock);
+	return rc;
+    }
+
+#if PJ_HAS_TCP
+    rec->connecting = 0;
+#endif
+
+    /* Set socket to nonblocking. */
+    value = 1;
+    rc = ioctlsocket(sock, FIONBIO, &value);
+    if (rc != 0) {
+	pj_lock_release(ioqueue->lock);
+        return PJ_RETURN_OS_ERROR(WSAGetLastError());
+    }
+
+    /* Associate with IOCP */
+    hioq = CreateIoCompletionPort((HANDLE)sock, ioqueue->iocp, (DWORD)rec, 0);
+    if (!hioq) {
+	pj_lock_release(ioqueue->lock);
+	return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+
+    *key = rec;
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    pj_list_push_back(&ioqueue->active_list, rec);
+#endif
+
+    pj_lock_release(ioqueue->lock);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_ioqueue_get_user_data()
+ */
+PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
+{
+    PJ_ASSERT_RETURN(key, NULL);
+    return key->user_data;
+}
+
+/*
+ * pj_ioqueue_set_user_data()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
+                                              void *user_data,
+                                              void **old_data )
+{
+    PJ_ASSERT_RETURN(key, PJ_EINVAL);
+    
+    if (old_data)
+        *old_data = key->user_data;
+
+    key->user_data = user_data;
+    return PJ_SUCCESS;
+}
+
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Decrement the key's reference counter, and when the counter reach zero,
+ * destroy the key.
+ */
+static void decrement_counter(pj_ioqueue_key_t *key)
+{
+    if (pj_atomic_dec_and_get(key->ref_count) == 0) {
+
+	pj_lock_acquire(key->ioqueue->lock);
+
+	pj_assert(key->closing == 1);
+	pj_gettickcount(&key->free_time);
+	key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY;
+	pj_time_val_normalize(&key->free_time);
+
+	pj_list_erase(key);
+	pj_list_push_back(&key->ioqueue->closing_list, key);
+
+	pj_lock_release(key->ioqueue->lock);
+    }
+}
+#endif
+
+/*
+ * Poll the I/O Completion Port, execute callback, 
+ * and return the key and bytes transfered of the last operation.
+ */
+static pj_bool_t poll_iocp( HANDLE hIocp, DWORD dwTimeout, 
+			    pj_ssize_t *p_bytes, pj_ioqueue_key_t **p_key )
+{
+    DWORD dwBytesTransfered, dwKey;
+    generic_overlapped *pOv;
+    pj_ioqueue_key_t *key;
+    pj_ssize_t size_status = -1;
+    BOOL rcGetQueued;
+
+    /* Poll for completion status. */
+    rcGetQueued = GetQueuedCompletionStatus(hIocp, &dwBytesTransfered,
+					    &dwKey, (OVERLAPPED**)&pOv, 
+					    dwTimeout);
+
+    /* The return value is:
+     * - nonzero if event was dequeued.
+     * - zero and pOv==NULL if no event was dequeued.
+     * - zero and pOv!=NULL if event for failed I/O was dequeued.
+     */
+    if (pOv) {
+	pj_bool_t has_lock;
+
+	/* Event was dequeued for either successfull or failed I/O */
+	key = (pj_ioqueue_key_t*)dwKey;
+	size_status = dwBytesTransfered;
+
+	/* Report to caller regardless */
+	if (p_bytes)
+	    *p_bytes = size_status;
+	if (p_key)
+	    *p_key = key;
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	/* We shouldn't call callbacks if key is quitting. */
+	if (key->closing)
+	    return PJ_TRUE;
+
+	/* If concurrency is disabled, lock the key 
+	 * (and save the lock status to local var since app may change
+	 * concurrency setting while in the callback) */
+	if (key->allow_concurrent == PJ_FALSE) {
+	    pj_mutex_lock(key->mutex);
+	    has_lock = PJ_TRUE;
+	} else {
+	    has_lock = PJ_FALSE;
+	}
+
+	/* Now that we get the lock, check again that key is not closing */
+	if (key->closing) {
+	    if (has_lock) {
+		pj_mutex_unlock(key->mutex);
+	    }
+	    return PJ_TRUE;
+	}
+
+	/* Increment reference counter to prevent this key from being
+	 * deleted
+	 */
+	pj_atomic_inc(key->ref_count);
+#else
+	PJ_UNUSED_ARG(has_lock);
+#endif
+
+	/* Carry out the callback */
+	switch (pOv->operation) {
+	case PJ_IOQUEUE_OP_READ:
+	case PJ_IOQUEUE_OP_RECV:
+	case PJ_IOQUEUE_OP_RECV_FROM:
+            pOv->operation = 0;
+            if (key->cb.on_read_complete)
+	        key->cb.on_read_complete(key, (pj_ioqueue_op_key_t*)pOv, 
+                                         size_status);
+	    break;
+	case PJ_IOQUEUE_OP_WRITE:
+	case PJ_IOQUEUE_OP_SEND:
+	case PJ_IOQUEUE_OP_SEND_TO:
+            pOv->operation = 0;
+            if (key->cb.on_write_complete)
+	        key->cb.on_write_complete(key, (pj_ioqueue_op_key_t*)pOv, 
+                                                size_status);
+	    break;
+#if PJ_HAS_TCP
+	case PJ_IOQUEUE_OP_ACCEPT:
+	    /* special case for accept. */
+	    ioqueue_on_accept_complete(key, (ioqueue_accept_rec*)pOv);
+            if (key->cb.on_accept_complete) {
+                ioqueue_accept_rec *accept_rec = (ioqueue_accept_rec*)pOv;
+		pj_status_t status = PJ_SUCCESS;
+		pj_sock_t newsock;
+
+		newsock = accept_rec->newsock;
+		accept_rec->newsock = PJ_INVALID_SOCKET;
+
+		if (newsock == PJ_INVALID_SOCKET) {
+		    int dwError = WSAGetLastError();
+		    if (dwError == 0) dwError = OSERR_ENOTCONN;
+		    status = PJ_RETURN_OS_ERROR(dwError);
+		}
+
+	        key->cb.on_accept_complete(key, (pj_ioqueue_op_key_t*)pOv,
+                                           newsock, status);
+		
+            }
+	    break;
+	case PJ_IOQUEUE_OP_CONNECT:
+#endif
+	case PJ_IOQUEUE_OP_NONE:
+	    pj_assert(0);
+	    break;
+	}
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+	decrement_counter(key);
+	if (has_lock)
+	    pj_mutex_unlock(key->mutex);
+#endif
+
+	return PJ_TRUE;
+    }
+
+    /* No event was queued. */
+    return PJ_FALSE;
+}
+
+/*
+ * pj_ioqueue_unregister()
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key )
+{
+    unsigned i;
+    pj_bool_t has_lock;
+    enum { RETRY = 10 };
+
+    PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+#if PJ_HAS_TCP
+    if (key->connecting) {
+	unsigned pos;
+        pj_ioqueue_t *ioqueue;
+
+        ioqueue = key->ioqueue;
+
+	/* Erase from connecting_handles */
+	pj_lock_acquire(ioqueue->lock);
+	for (pos=0; pos < ioqueue->connecting_count; ++pos) {
+	    if (ioqueue->connecting_keys[pos] == key) {
+		erase_connecting_socket(ioqueue, pos);
+		break;
+	    }
+	}
+	key->connecting = 0;
+	pj_lock_release(ioqueue->lock);
+    }
+#endif
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Mark key as closing before closing handle. */
+    key->closing = 1;
+
+    /* If concurrency is disabled, wait until the key has finished
+     * processing the callback
+     */
+    if (key->allow_concurrent == PJ_FALSE) {
+	pj_mutex_lock(key->mutex);
+	has_lock = PJ_TRUE;
+    } else {
+	has_lock = PJ_FALSE;
+    }
+#else
+    PJ_UNUSED_ARG(has_lock);
+#endif
+    
+    /* Close handle (the only way to disassociate handle from IOCP). 
+     * We also need to close handle to make sure that no further events
+     * will come to the handle.
+     */
+    /* Update 2008/07/18 (http://trac.pjsip.org/repos/ticket/575):
+     *  - It seems that CloseHandle() in itself does not actually close
+     *    the socket (i.e. it will still appear in "netstat" output). Also
+     *    if we only use CloseHandle(), an "Invalid Handle" exception will
+     *    be raised in WSACleanup().
+     *  - MSDN documentation says that CloseHandle() must be called after 
+     *    closesocket() call (see
+     *    http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx).
+     *    But turns out that this will raise "Invalid Handle" exception
+     *    in debug mode.
+     *  So because of this, we replaced CloseHandle() with closesocket()
+     *  instead. These was tested on WinXP SP2.
+     */
+    //CloseHandle(key->hnd);
+    pj_sock_close((pj_sock_t)key->hnd);
+
+    /* Reset callbacks */
+    key->cb.on_accept_complete = NULL;
+    key->cb.on_connect_complete = NULL;
+    key->cb.on_read_complete = NULL;
+    key->cb.on_write_complete = NULL;
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Even after handle is closed, I suspect that IOCP may still try to
+     * do something with the handle, causing memory corruption when pool
+     * debugging is enabled.
+     *
+     * Forcing context switch seems to have fixed that, but this is quite
+     * an ugly solution..
+     *
+     * Update 2008/02/13:
+     *	This should not happen if concurrency is disallowed for the key.
+     *  So at least application has a solution for this (i.e. by disallowing
+     *  concurrency in the key).
+     */
+    //This will loop forever if unregistration is done on the callback.
+    //Doing this with RETRY I think should solve the IOCP setting the 
+    //socket signalled, without causing the deadlock.
+    //while (pj_atomic_get(key->ref_count) != 1)
+    //	pj_thread_sleep(0);
+    for (i=0; pj_atomic_get(key->ref_count) != 1 && i<RETRY; ++i)
+	pj_thread_sleep(0);
+
+    /* Decrement reference counter to destroy the key. */
+    decrement_counter(key);
+
+    if (has_lock)
+	pj_mutex_unlock(key->mutex);
+#endif
+
+    return PJ_SUCCESS;
+}
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+/* Scan the closing list, and put pending closing keys to free list.
+ * Must do this with ioqueue mutex held.
+ */
+static void scan_closing_keys(pj_ioqueue_t *ioqueue)
+{
+    if (!pj_list_empty(&ioqueue->closing_list)) {
+	pj_time_val now;
+	pj_ioqueue_key_t *key;
+
+	pj_gettickcount(&now);
+	
+	/* Move closing keys to free list when they've finished the closing
+	 * idle time.
+	 */
+	key = ioqueue->closing_list.next;
+	while (key != &ioqueue->closing_list) {
+	    pj_ioqueue_key_t *next = key->next;
+
+	    pj_assert(key->closing != 0);
+
+	    if (PJ_TIME_VAL_GTE(now, key->free_time)) {
+		pj_list_erase(key);
+		pj_list_push_back(&ioqueue->free_list, key);
+	    }
+	    key = next;
+	}
+    }
+}
+#endif
+
+/*
+ * pj_ioqueue_poll()
+ *
+ * Poll for events.
+ */
+PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioqueue, const pj_time_val *timeout)
+{
+    DWORD dwMsec;
+#if PJ_HAS_TCP
+    int connect_count = 0;
+#endif
+    int event_count = 0;
+
+    PJ_ASSERT_RETURN(ioqueue, -PJ_EINVAL);
+
+    /* Calculate miliseconds timeout for GetQueuedCompletionStatus */
+    dwMsec = timeout ? timeout->sec*1000 + timeout->msec : INFINITE;
+
+    /* Poll for completion status. */
+    event_count = poll_iocp(ioqueue->iocp, dwMsec, NULL, NULL);
+
+#if PJ_HAS_TCP
+    /* Check the connecting array, only when there's no activity. */
+    if (event_count == 0) {
+	connect_count = check_connecting(ioqueue);
+	if (connect_count > 0)
+	    event_count += connect_count;
+    }
+#endif
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Check the closing keys only when there's no activity and when there are
+     * pending closing keys.
+     */
+    if (event_count == 0 && !pj_list_empty(&ioqueue->closing_list)) {
+	pj_lock_acquire(ioqueue->lock);
+	scan_closing_keys(ioqueue);
+	pj_lock_release(ioqueue->lock);
+    }
+#endif
+
+    /* Return number of events. */
+    return event_count;
+}
+
+/*
+ * pj_ioqueue_recv()
+ *
+ * Initiate overlapped WSARecv() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recv(  pj_ioqueue_key_t *key,
+                                      pj_ioqueue_op_key_t *op_key,
+				      void *buffer,
+				      pj_ssize_t *length,
+				      pj_uint32_t flags )
+{
+    /*
+     * Ideally we should just call pj_ioqueue_recvfrom() with NULL addr and
+     * addrlen here. But unfortunately it generates EINVAL... :-(
+     *  -bennylp
+     */
+    int rc;
+    DWORD bytesRead;
+    DWORD dwFlags = 0;
+    union operation_key *op_key_rec;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(key && op_key && buffer && length, PJ_EINVAL);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Check key is not closing */
+    if (key->closing)
+	return PJ_ECANCELLED;
+#endif
+
+    op_key_rec = (union operation_key*)op_key->internal__;
+    op_key_rec->overlapped.wsabuf.buf = buffer;
+    op_key_rec->overlapped.wsabuf.len = *length;
+
+    dwFlags = flags;
+    
+    /* Try non-overlapped received first to see if data is
+     * immediately available.
+     */
+    if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
+	rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+		     &bytesRead, &dwFlags, NULL, NULL);
+	if (rc == 0) {
+	    *length = bytesRead;
+	    return PJ_SUCCESS;
+	} else {
+	    DWORD dwError = WSAGetLastError();
+	    if (dwError != WSAEWOULDBLOCK) {
+		*length = -1;
+		return PJ_RETURN_OS_ERROR(dwError);
+	    }
+	}
+    }
+
+    dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
+
+    /*
+     * No immediate data available.
+     * Register overlapped Recv() operation.
+     */
+    pj_bzero( &op_key_rec->overlapped.overlapped, 
+              sizeof(op_key_rec->overlapped.overlapped));
+    op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV;
+
+    rc = WSARecv((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, 
+                  &bytesRead, &dwFlags, 
+		  &op_key_rec->overlapped.overlapped, NULL);
+    if (rc == SOCKET_ERROR) {
+	DWORD dwStatus = WSAGetLastError();
+        if (dwStatus!=WSA_IO_PENDING) {
+            *length = -1;
+            return PJ_STATUS_FROM_OS(dwStatus);
+        }
+    }
+
+    /* Pending operation has been scheduled. */
+    return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_recvfrom()
+ *
+ * Initiate overlapped RecvFrom() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
+                                         pj_ioqueue_op_key_t *op_key,
+					 void *buffer,
+					 pj_ssize_t *length,
+                                         pj_uint32_t flags,
+					 pj_sockaddr_t *addr,
+					 int *addrlen)
+{
+    int rc;
+    DWORD bytesRead;
+    DWORD dwFlags = 0;
+    union operation_key *op_key_rec;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(key && op_key && buffer, PJ_EINVAL);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Check key is not closing */
+    if (key->closing)
+	return PJ_ECANCELLED;
+#endif
+
+    op_key_rec = (union operation_key*)op_key->internal__;
+    op_key_rec->overlapped.wsabuf.buf = buffer;
+    op_key_rec->overlapped.wsabuf.len = *length;
+
+    dwFlags = flags;
+    
+    /* Try non-overlapped received first to see if data is
+     * immediately available.
+     */
+    if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
+	rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+			 &bytesRead, &dwFlags, addr, addrlen, NULL, NULL);
+	if (rc == 0) {
+	    *length = bytesRead;
+	    return PJ_SUCCESS;
+	} else {
+	    DWORD dwError = WSAGetLastError();
+	    if (dwError != WSAEWOULDBLOCK) {
+		*length = -1;
+		return PJ_RETURN_OS_ERROR(dwError);
+	    }
+	}
+    }
+
+    dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
+
+    /*
+     * No immediate data available.
+     * Register overlapped Recv() operation.
+     */
+    pj_bzero( &op_key_rec->overlapped.overlapped, 
+              sizeof(op_key_rec->overlapped.overlapped));
+    op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_RECV;
+
+    rc = WSARecvFrom((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1, 
+                     &bytesRead, &dwFlags, addr, addrlen,
+		     &op_key_rec->overlapped.overlapped, NULL);
+    if (rc == SOCKET_ERROR) {
+	DWORD dwStatus = WSAGetLastError();
+        if (dwStatus!=WSA_IO_PENDING) {
+            *length = -1;
+            return PJ_STATUS_FROM_OS(dwStatus);
+        }
+    } 
+    
+    /* Pending operation has been scheduled. */
+    return PJ_EPENDING;
+}
+
+/*
+ * pj_ioqueue_send()
+ *
+ * Initiate overlapped Send operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_send(  pj_ioqueue_key_t *key,
+                                      pj_ioqueue_op_key_t *op_key,
+				      const void *data,
+				      pj_ssize_t *length,
+				      pj_uint32_t flags )
+{
+    return pj_ioqueue_sendto(key, op_key, data, length, flags, NULL, 0);
+}
+
+
+/*
+ * pj_ioqueue_sendto()
+ *
+ * Initiate overlapped SendTo operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
+                                       pj_ioqueue_op_key_t *op_key,
+				       const void *data,
+				       pj_ssize_t *length,
+                                       pj_uint32_t flags,
+				       const pj_sockaddr_t *addr,
+				       int addrlen)
+{
+    int rc;
+    DWORD bytesWritten;
+    DWORD dwFlags;
+    union operation_key *op_key_rec;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(key && op_key && data, PJ_EINVAL);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Check key is not closing */
+    if (key->closing)
+	return PJ_ECANCELLED;
+#endif
+
+    op_key_rec = (union operation_key*)op_key->internal__;
+
+    /*
+     * First try blocking write.
+     */
+    op_key_rec->overlapped.wsabuf.buf = (void*)data;
+    op_key_rec->overlapped.wsabuf.len = *length;
+
+    dwFlags = flags;
+
+    if ((flags & PJ_IOQUEUE_ALWAYS_ASYNC) == 0) {
+	rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+		       &bytesWritten, dwFlags, addr, addrlen,
+		       NULL, NULL);
+	if (rc == 0) {
+	    *length = bytesWritten;
+	    return PJ_SUCCESS;
+	} else {
+	    DWORD dwStatus = WSAGetLastError();
+	    if (dwStatus != WSAEWOULDBLOCK) {
+		*length = -1;
+		return PJ_RETURN_OS_ERROR(dwStatus);
+	    }
+	}
+    }
+
+    dwFlags &= ~(PJ_IOQUEUE_ALWAYS_ASYNC);
+
+    /*
+     * Data can't be sent immediately.
+     * Schedule asynchronous WSASend().
+     */
+    pj_bzero( &op_key_rec->overlapped.overlapped, 
+              sizeof(op_key_rec->overlapped.overlapped));
+    op_key_rec->overlapped.operation = PJ_IOQUEUE_OP_SEND;
+
+    rc = WSASendTo((SOCKET)key->hnd, &op_key_rec->overlapped.wsabuf, 1,
+                   &bytesWritten,  dwFlags, addr, addrlen,
+		   &op_key_rec->overlapped.overlapped, NULL);
+    if (rc == SOCKET_ERROR) {
+	DWORD dwStatus = WSAGetLastError();
+        if (dwStatus!=WSA_IO_PENDING)
+            return PJ_STATUS_FROM_OS(dwStatus);
+    }
+
+    /* Asynchronous operation successfully submitted. */
+    return PJ_EPENDING;
+}
+
+#if PJ_HAS_TCP
+
+/*
+ * pj_ioqueue_accept()
+ *
+ * Initiate overlapped accept() operation.
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
+                                       pj_ioqueue_op_key_t *op_key,
+			               pj_sock_t *new_sock,
+			               pj_sockaddr_t *local,
+			               pj_sockaddr_t *remote,
+			               int *addrlen)
+{
+    BOOL rc;
+    DWORD bytesReceived;
+    pj_status_t status;
+    union operation_key *op_key_rec;
+    SOCKET sock;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(key && op_key && new_sock, PJ_EINVAL);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Check key is not closing */
+    if (key->closing)
+	return PJ_ECANCELLED;
+#endif
+
+    /*
+     * See if there is a new connection immediately available.
+     */
+    sock = WSAAccept((SOCKET)key->hnd, remote, addrlen, NULL, 0);
+    if (sock != INVALID_SOCKET) {
+        /* Yes! New socket is available! */
+	if (local && addrlen) {
+	    int status;
+
+	    /* On WinXP or later, use SO_UPDATE_ACCEPT_CONTEXT so that socket 
+	     * addresses can be obtained with getsockname() and getpeername().
+	     */
+	    status = setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
+				(char*)&key->hnd, sizeof(SOCKET));
+	    /* SO_UPDATE_ACCEPT_CONTEXT is for WinXP or later.
+	     * So ignore the error status.
+	     */
+
+	    status = getsockname(sock, local, addrlen);
+	    if (status != 0) {
+		DWORD dwError = WSAGetLastError();
+		closesocket(sock);
+		return PJ_RETURN_OS_ERROR(dwError);
+	    }
+	}
+
+        *new_sock = sock;
+        return PJ_SUCCESS;
+
+    } else {
+        DWORD dwError = WSAGetLastError();
+        if (dwError != WSAEWOULDBLOCK) {
+            return PJ_RETURN_OS_ERROR(dwError);
+        }
+    }
+
+    /*
+     * No connection is immediately available.
+     * Must schedule an asynchronous operation.
+     */
+    op_key_rec = (union operation_key*)op_key->internal__;
+    
+    status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, 
+                            &op_key_rec->accept.newsock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    op_key_rec->accept.operation = PJ_IOQUEUE_OP_ACCEPT;
+    op_key_rec->accept.addrlen = addrlen;
+    op_key_rec->accept.local = local;
+    op_key_rec->accept.remote = remote;
+    op_key_rec->accept.newsock_ptr = new_sock;
+    pj_bzero( &op_key_rec->accept.overlapped, 
+	      sizeof(op_key_rec->accept.overlapped));
+
+    rc = AcceptEx( (SOCKET)key->hnd, (SOCKET)op_key_rec->accept.newsock,
+		   op_key_rec->accept.accept_buf,
+		   0, ACCEPT_ADDR_LEN, ACCEPT_ADDR_LEN,
+		   &bytesReceived,
+		   &op_key_rec->accept.overlapped );
+
+    if (rc == TRUE) {
+	ioqueue_on_accept_complete(key, &op_key_rec->accept);
+	return PJ_SUCCESS;
+    } else {
+	DWORD dwStatus = WSAGetLastError();
+	if (dwStatus!=WSA_IO_PENDING)
+            return PJ_STATUS_FROM_OS(dwStatus);
+    }
+
+    /* Asynchronous Accept() has been submitted. */
+    return PJ_EPENDING;
+}
+
+
+/*
+ * pj_ioqueue_connect()
+ *
+ * Initiate overlapped connect() operation (well, it's non-blocking actually,
+ * since there's no overlapped version of connect()).
+ */
+PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
+					const pj_sockaddr_t *addr,
+					int addrlen )
+{
+    HANDLE hEvent;
+    pj_ioqueue_t *ioqueue;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(key && addr && addrlen, PJ_EINVAL);
+
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    /* Check key is not closing */
+    if (key->closing)
+	return PJ_ECANCELLED;
+#endif
+
+    /* Initiate connect() */
+    if (connect((pj_sock_t)key->hnd, addr, addrlen) != 0) {
+	DWORD dwStatus;
+	dwStatus = WSAGetLastError();
+        if (dwStatus != WSAEWOULDBLOCK) {
+	    return PJ_RETURN_OS_ERROR(dwStatus);
+	}
+    } else {
+	/* Connect has completed immediately! */
+	return PJ_SUCCESS;
+    }
+
+    ioqueue = key->ioqueue;
+
+    /* Add to the array of connecting socket to be polled */
+    pj_lock_acquire(ioqueue->lock);
+
+    if (ioqueue->connecting_count >= MAXIMUM_WAIT_OBJECTS) {
+	pj_lock_release(ioqueue->lock);
+	return PJ_ETOOMANYCONN;
+    }
+
+    /* Get or create event object. */
+    if (ioqueue->event_count) {
+	hEvent = ioqueue->event_pool[ioqueue->event_count - 1];
+	--ioqueue->event_count;
+    } else {
+	hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (hEvent == NULL) {
+	    DWORD dwStatus = GetLastError();
+	    pj_lock_release(ioqueue->lock);
+	    return PJ_STATUS_FROM_OS(dwStatus);
+	}
+    }
+
+    /* Mark key as connecting.
+     * We can't use array index since key can be removed dynamically. 
+     */
+    key->connecting = 1;
+
+    /* Associate socket events to the event object. */
+    if (WSAEventSelect((pj_sock_t)key->hnd, hEvent, FD_CONNECT) != 0) {
+	CloseHandle(hEvent);
+	pj_lock_release(ioqueue->lock);
+	return PJ_RETURN_OS_ERROR(WSAGetLastError());
+    }
+
+    /* Add to array. */
+    ioqueue->connecting_keys[ ioqueue->connecting_count ] = key;
+    ioqueue->connecting_handles[ ioqueue->connecting_count ] = hEvent;
+    ioqueue->connecting_count++;
+
+    pj_lock_release(ioqueue->lock);
+
+    return PJ_EPENDING;
+}
+#endif	/* #if PJ_HAS_TCP */
+
+
+PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
+				     pj_size_t size )
+{
+    pj_bzero(op_key, size);
+}
+
+PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key,
+                                         pj_ioqueue_op_key_t *op_key )
+{
+    BOOL rc;
+    DWORD bytesTransfered;
+
+    rc = GetOverlappedResult( key->hnd, (LPOVERLAPPED)op_key,
+                              &bytesTransfered, FALSE );
+
+    if (rc == FALSE) {
+        return GetLastError()==ERROR_IO_INCOMPLETE;
+    }
+
+    return FALSE;
+}
+
+
+PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
+                                                pj_ioqueue_op_key_t *op_key,
+                                                pj_ssize_t bytes_status )
+{
+    BOOL rc;
+
+    rc = PostQueuedCompletionStatus(key->ioqueue->iocp, bytes_status,
+                                    (long)key, (OVERLAPPED*)op_key );
+    if (rc == FALSE) {
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key,
+					       pj_bool_t allow)
+{
+    PJ_ASSERT_RETURN(key, PJ_EINVAL);
+
+    /* PJ_IOQUEUE_HAS_SAFE_UNREG must be enabled if concurrency is
+     * disabled.
+     */
+    PJ_ASSERT_RETURN(allow || PJ_IOQUEUE_HAS_SAFE_UNREG, PJ_EINVAL);
+
+    key->allow_concurrent = allow;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key)
+{
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    return pj_mutex_lock(key->mutex);
+#else
+    PJ_ASSERT_RETURN(!"PJ_IOQUEUE_HAS_SAFE_UNREG is disabled", PJ_EINVALIDOP);
+#endif
+}
+
+PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key)
+{
+#if PJ_IOQUEUE_HAS_SAFE_UNREG
+    return pj_mutex_unlock(key->mutex);
+#else
+    PJ_ASSERT_RETURN(!"PJ_IOQUEUE_HAS_SAFE_UNREG is disabled", PJ_EINVALIDOP);
+#endif
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ip_helper_generic.c b/jni/pjproject-android/pjlib/src/pj/ip_helper_generic.c
new file mode 100644
index 0000000..94d5a1f
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ip_helper_generic.c
@@ -0,0 +1,409 @@
+/* $Id: ip_helper_generic.c 4355 2013-02-19 16:27:37Z bennylp $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ip_helper.h>
+#include <pj/addr_resolv.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/compat/socket.h>
+#include <pj/sock.h>
+
+/* Set to 1 to enable tracing */
+#if 0
+#   include <pj/log.h>
+#   define THIS_FILE	"ip_helper_generic.c"
+#   define TRACE_(exp)	PJ_LOG(5,exp)
+    static const char *get_os_errmsg(void)
+    {
+	static char errmsg[PJ_ERR_MSG_SIZE];
+	pj_strerror(pj_get_os_error(), errmsg, sizeof(errmsg));
+	return errmsg;
+    }
+    static const char *get_addr(void *addr)
+    {
+	static char txt[PJ_INET6_ADDRSTRLEN];
+	struct sockaddr *ad = (struct sockaddr*)addr;
+	if (ad->sa_family != PJ_AF_INET && ad->sa_family != PJ_AF_INET6)
+	    return "?";
+	return pj_inet_ntop2(ad->sa_family, pj_sockaddr_get_addr(ad), 
+			     txt, sizeof(txt));
+    }
+#else
+#   define TRACE_(exp)
+#endif
+
+
+#if 0
+    /* dummy */
+
+#elif defined(PJ_HAS_IFADDRS_H) && PJ_HAS_IFADDRS_H != 0 && \
+      defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0
+/* Using getifaddrs() is preferred since it can work with both IPv4 and IPv6 */
+static pj_status_t if_enum_by_af(int af,
+				 unsigned *p_cnt,
+				 pj_sockaddr ifs[])
+{
+    struct ifaddrs *ifap = NULL, *it;
+    unsigned max;
+
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL);
+    
+    TRACE_((THIS_FILE, "Starting interface enum with getifaddrs() for af=%d",
+	    af));
+
+    if (getifaddrs(&ifap) != 0) {
+	TRACE_((THIS_FILE, " getifarrds() failed: %s", get_os_errmsg()));
+	return PJ_RETURN_OS_ERROR(pj_get_netos_error());
+    }
+
+    it = ifap;
+    max = *p_cnt;
+    *p_cnt = 0;
+    for (; it!=NULL && *p_cnt < max; it = it->ifa_next) {
+	struct sockaddr *ad = it->ifa_addr;
+
+	TRACE_((THIS_FILE, " checking %s", it->ifa_name));
+
+	if ((it->ifa_flags & IFF_UP)==0) {
+	    TRACE_((THIS_FILE, "  interface is down"));
+	    continue; /* Skip when interface is down */
+	}
+
+#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
+	if (it->ifa_flags & IFF_LOOPBACK) {
+	    TRACE_((THIS_FILE, "  loopback interface"));
+	    continue; /* Skip loopback interface */
+	}
+#endif
+
+	if (ad==NULL) {
+	    TRACE_((THIS_FILE, "  NULL address ignored"));
+	    continue; /* reported to happen on Linux 2.6.25.9 
+			 with ppp interface */
+	}
+
+	if (ad->sa_family != af) {
+	    TRACE_((THIS_FILE, "  address %s ignored (af=%d)", 
+		    get_addr(ad), ad->sa_family));
+	    continue; /* Skip when interface is down */
+	}
+
+	/* Ignore 0.0.0.0/8 address. This is a special address
+	 * which doesn't seem to have practical use.
+	 */
+	if (af==pj_AF_INET() &&
+	    (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0)
+	{
+	    TRACE_((THIS_FILE, "  address %s ignored (0.0.0.0/8 class)", 
+		    get_addr(ad), ad->sa_family));
+	    continue;
+	}
+
+	TRACE_((THIS_FILE, "  address %s (af=%d) added at index %d", 
+		get_addr(ad), ad->sa_family, *p_cnt));
+
+	pj_bzero(&ifs[*p_cnt], sizeof(ifs[0]));
+	pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad));
+	PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]);
+	(*p_cnt)++;
+    }
+
+    freeifaddrs(ifap);
+    TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt));
+    return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+#elif defined(SIOCGIFCONF) && \
+      defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0
+
+/* Note: this does not work with IPv6 */
+static pj_status_t if_enum_by_af(int af,
+				 unsigned *p_cnt,
+				 pj_sockaddr ifs[])
+{
+    pj_sock_t sock;
+    char buf[512];
+    struct ifconf ifc;
+    struct ifreq *ifr;
+    int i, count;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL);
+    
+    TRACE_((THIS_FILE, "Starting interface enum with SIOCGIFCONF for af=%d",
+	    af));
+
+    status = pj_sock_socket(af, PJ_SOCK_DGRAM, 0, &sock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Query available interfaces */
+    ifc.ifc_len = sizeof(buf);
+    ifc.ifc_buf = buf;
+
+    if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
+	int oserr = pj_get_netos_error();
+	TRACE_((THIS_FILE, " ioctl(SIOCGIFCONF) failed: %s", get_os_errmsg()));
+	pj_sock_close(sock);
+	return PJ_RETURN_OS_ERROR(oserr);
+    }
+
+    /* Interface interfaces */
+    ifr = (struct ifreq*) ifc.ifc_req;
+    count = ifc.ifc_len / sizeof(struct ifreq);
+    if (count > *p_cnt)
+	count = *p_cnt;
+
+    *p_cnt = 0;
+    for (i=0; i<count; ++i) {
+	struct ifreq *itf = &ifr[i];
+        struct ifreq iff = *itf;
+	struct sockaddr *ad = &itf->ifr_addr;
+	
+	TRACE_((THIS_FILE, " checking interface %s", itf->ifr_name));
+
+	/* Skip address with different family */
+	if (ad->sa_family != af) {
+	    TRACE_((THIS_FILE, "  address %s (af=%d) ignored",
+		    get_addr(ad), (int)ad->sa_family));
+	    continue;
+	}
+
+        if (ioctl(sock, SIOCGIFFLAGS, &iff) != 0) {
+	    TRACE_((THIS_FILE, "  ioctl(SIOCGIFFLAGS) failed: %s",
+		    get_os_errmsg()));
+	    continue;	/* Failed to get flags, continue */
+	}
+
+	if ((iff.ifr_flags & IFF_UP)==0) {
+	    TRACE_((THIS_FILE, "  interface is down"));
+	    continue; /* Skip when interface is down */
+	}
+
+#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
+	if (iff.ifr_flags & IFF_LOOPBACK) {
+	    TRACE_((THIS_FILE, "  loopback interface"));
+	    continue; /* Skip loopback interface */
+	}
+#endif
+
+	/* Ignore 0.0.0.0/8 address. This is a special address
+	 * which doesn't seem to have practical use.
+	 */
+	if (af==pj_AF_INET() &&
+	    (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0)
+	{
+	    TRACE_((THIS_FILE, "  address %s ignored (0.0.0.0/8 class)", 
+		    get_addr(ad), ad->sa_family));
+	    continue;
+	}
+
+	TRACE_((THIS_FILE, "  address %s (af=%d) added at index %d", 
+		get_addr(ad), ad->sa_family, *p_cnt));
+
+	pj_bzero(&ifs[*p_cnt], sizeof(ifs[0]));
+	pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad));
+	PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]);
+	(*p_cnt)++;
+    }
+
+    /* Done with socket */
+    pj_sock_close(sock);
+
+    TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt));
+    return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+
+#elif defined(PJ_HAS_NET_IF_H) && PJ_HAS_NET_IF_H != 0
+/* Note: this does not work with IPv6 */
+static pj_status_t if_enum_by_af(int af, unsigned *p_cnt, pj_sockaddr ifs[])
+{
+    struct if_nameindex *if_list;
+    struct ifreq ifreq;
+    pj_sock_t sock;
+    unsigned i, max_count;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL);
+
+    TRACE_((THIS_FILE, "Starting if_nameindex() for af=%d", af));
+
+    status = pj_sock_socket(af, PJ_SOCK_DGRAM, 0, &sock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if_list = if_nameindex();
+    if (if_list == NULL)
+	return PJ_ENOTFOUND;
+
+    max_count = *p_cnt;
+    *p_cnt = 0;
+    for (i=0; if_list[i].if_index && *p_cnt<max_count; ++i) {
+	struct sockaddr *ad;
+	int rc;
+
+	strncpy(ifreq.ifr_name, if_list[i].if_name, IFNAMSIZ);
+
+	TRACE_((THIS_FILE, " checking interface %s", ifreq.ifr_name));
+
+	if ((rc=ioctl(sock, SIOCGIFFLAGS, &ifreq)) != 0) {
+	    TRACE_((THIS_FILE, "  ioctl(SIOCGIFFLAGS) failed: %s",
+		    get_os_errmsg()));
+	    continue;	/* Failed to get flags, continue */
+	}
+
+	if ((ifreq.ifr_flags & IFF_UP)==0) {
+	    TRACE_((THIS_FILE, "  interface is down"));
+	    continue; /* Skip when interface is down */
+	}
+
+#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
+	if (ifreq.ifr_flags & IFF_LOOPBACK) {
+	    TRACE_((THIS_FILE, "  loopback interface"));
+	    continue; /* Skip loopback interface */
+	}
+#endif
+
+	/* Note: SIOCGIFADDR does not work for IPv6! */
+	if ((rc=ioctl(sock, SIOCGIFADDR, &ifreq)) != 0) {
+	    TRACE_((THIS_FILE, "  ioctl(SIOCGIFADDR) failed: %s",
+		    get_os_errmsg()));
+	    continue;	/* Failed to get address, continue */
+	}
+
+	ad = (struct sockaddr*) &ifreq.ifr_addr;
+
+	if (ad->sa_family != af) {
+	    TRACE_((THIS_FILE, "  address %s family %d ignored", 
+			       get_addr(&ifreq.ifr_addr),
+			       ifreq.ifr_addr.sa_family));
+	    continue;	/* Not address family that we want, continue */
+	}
+
+	/* Ignore 0.0.0.0/8 address. This is a special address
+	 * which doesn't seem to have practical use.
+	 */
+	if (af==pj_AF_INET() &&
+	    (pj_ntohl(((pj_sockaddr_in*)ad)->sin_addr.s_addr) >> 24) == 0)
+	{
+	    TRACE_((THIS_FILE, "  address %s ignored (0.0.0.0/8 class)", 
+		    get_addr(ad), ad->sa_family));
+	    continue;
+	}
+
+	/* Got an address ! */
+	TRACE_((THIS_FILE, "  address %s (af=%d) added at index %d", 
+		get_addr(ad), ad->sa_family, *p_cnt));
+
+	pj_bzero(&ifs[*p_cnt], sizeof(ifs[0]));
+	pj_memcpy(&ifs[*p_cnt], ad, pj_sockaddr_get_len(ad));
+	PJ_SOCKADDR_RESET_LEN(&ifs[*p_cnt]);
+	(*p_cnt)++;
+    }
+
+    if_freenameindex(if_list);
+    pj_sock_close(sock);
+
+    TRACE_((THIS_FILE, "done, found %d address(es)", *p_cnt));
+    return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+#else
+static pj_status_t if_enum_by_af(int af,
+				 unsigned *p_cnt,
+				 pj_sockaddr ifs[])
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && ifs, PJ_EINVAL);
+
+    pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt));
+
+    /* Just get one default route */
+    status = pj_getdefaultipinterface(af, &ifs[0]);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    *p_cnt = 1;
+    return PJ_SUCCESS;
+}
+#endif /* SIOCGIFCONF */
+
+/*
+ * Enumerate the local IP interface currently active in the host.
+ */
+PJ_DEF(pj_status_t) pj_enum_ip_interface(int af,
+					 unsigned *p_cnt,
+					 pj_sockaddr ifs[])
+{
+    unsigned start;
+    pj_status_t status;
+
+    start = 0;
+    if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) {
+	unsigned max = *p_cnt;
+	status = if_enum_by_af(PJ_AF_INET6, &max, &ifs[start]);
+	if (status == PJ_SUCCESS) {
+	    start += max;
+	    (*p_cnt) -= max;
+	}
+    }
+
+    if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) {
+	unsigned max = *p_cnt;
+	status = if_enum_by_af(PJ_AF_INET, &max, &ifs[start]);
+	if (status == PJ_SUCCESS) {
+	    start += max;
+	    (*p_cnt) -= max;
+	}
+    }
+
+    *p_cnt = start;
+
+    return (*p_cnt != 0) ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+/*
+ * Enumerate the IP routing table for this host.
+ */
+PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt,
+				     pj_ip_route_entry routes[])
+{
+    pj_sockaddr itf;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && routes, PJ_EINVAL);
+
+    pj_bzero(routes, sizeof(routes[0]) * (*p_cnt));
+
+    /* Just get one default route */
+    status = pj_getdefaultipinterface(PJ_AF_INET, &itf);
+    if (status != PJ_SUCCESS)
+	return status;
+    
+    routes[0].ipv4.if_addr.s_addr = itf.ipv4.sin_addr.s_addr;
+    routes[0].ipv4.dst_addr.s_addr = 0;
+    routes[0].ipv4.mask.s_addr = 0;
+    *p_cnt = 1;
+
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ip_helper_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/ip_helper_symbian.cpp
new file mode 100644
index 0000000..fb751ad
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ip_helper_symbian.cpp
@@ -0,0 +1,150 @@
+/* $Id: ip_helper_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ip_helper.h>
+#include <pj/addr_resolv.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/compat/socket.h>
+
+
+#include "os_symbian.h"
+
+#define THIS_FILE	"ip_helper_symbian.cpp"
+#define TRACE_ME	0
+
+static pj_status_t rsock_enum_interface(int af,
+					unsigned *p_cnt,
+					pj_sockaddr ifs[]) 
+{
+    TInt rc;
+    RSocket rSock;
+    TPckgBuf<TSoInetInterfaceInfo> info;
+    unsigned i;
+    
+    if (PjSymbianOS::Instance()->Connection()) {
+    	
+    	rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), 
+    			af, PJ_SOCK_DGRAM, KProtocolInetUdp,
+    			*PjSymbianOS::Instance()->Connection());
+    } else {
+    	
+    	rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), 
+    			af, PJ_SOCK_DGRAM, KProtocolInetUdp);
+    			
+    }
+        
+    if (rc != KErrNone)
+	return PJ_RETURN_OS_ERROR(rc);
+    
+    rSock.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl);
+    
+    for (i=0; i<*p_cnt &&
+    		rSock.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, 
+    		             info) == KErrNone; ) 
+    {
+    	TInetAddr &iAddress = info().iAddress;
+    	int namelen;
+
+#if TRACE_ME
+		if (1) {
+			pj_sockaddr a;
+			char ipaddr[PJ_INET6_ADDRSTRLEN+2];
+			
+			namelen = sizeof(pj_sockaddr);
+			if (PjSymbianOS::Addr2pj(iAddress, a, &namelen, 
+									 PJ_FALSE) == PJ_SUCCESS) 
+			{
+				PJ_LOG(5,(THIS_FILE, "Enum: found address %s", 
+						pj_sockaddr_print(&a, ipaddr, sizeof(ipaddr), 2)));
+			}
+		}
+#endif
+    	
+    	namelen = sizeof(ifs[i]);
+    	if (PjSymbianOS::Addr2pj(iAddress, ifs[i], &namelen, 
+    							 PJ_TRUE) != PJ_SUCCESS)
+    	{
+    	    continue;
+    	}
+
+    	if (ifs[i].addr.sa_family != af)
+		    continue;
+    	
+    	++i;
+    }
+    
+    rSock.Close();
+    
+    // Done
+    *p_cnt = i;
+    
+    return PJ_SUCCESS;
+}
+					
+/*
+ * Enumerate the local IP interface currently active in the host.
+ */
+PJ_DEF(pj_status_t) pj_enum_ip_interface(int af,
+					 unsigned *p_cnt,
+					 pj_sockaddr ifs[])
+{
+    unsigned start;
+    pj_status_t status = PJ_SUCCESS;
+
+    start = 0;
+    	    
+    /* Get IPv6 interface first. */
+    if (af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) {
+    	unsigned max = *p_cnt;
+    	status = rsock_enum_interface(PJ_AF_INET6, &max, &ifs[start]);
+    	if (status == PJ_SUCCESS) {
+    	    (*p_cnt) -= max;
+    	    start += max;
+    	}
+    }
+    
+    /* Get IPv4 interface. */
+    if (af==PJ_AF_INET || af==PJ_AF_UNSPEC) {
+    	unsigned max = *p_cnt;
+    	status = rsock_enum_interface(PJ_AF_INET, &max, &ifs[start]);
+    	if (status == PJ_SUCCESS) {
+    	    (*p_cnt) -= max;
+    	    start += max;
+    	}
+    }
+    
+    *p_cnt = start;
+    
+    return start ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+/*
+ * Enumerate the IP routing table for this host.
+ */
+PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt,
+				     pj_ip_route_entry routes[])
+{
+    PJ_ASSERT_RETURN(p_cnt && *p_cnt > 0 && routes, PJ_EINVAL);
+    *p_cnt = 0;
+    return PJ_ENOTSUP;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ip_helper_win32.c b/jni/pjproject-android/pjlib/src/pj/ip_helper_win32.c
new file mode 100644
index 0000000..a924606
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ip_helper_win32.c
@@ -0,0 +1,441 @@
+/* $Id: ip_helper_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/config.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+/* PMIB_ICMP_EX is not declared in VC6, causing error.
+ * But EVC4, which also claims to be VC6, does have it! 
+ */
+#if defined(_MSC_VER) && _MSC_VER==1200 && !defined(PJ_WIN32_WINCE)
+#   define PMIB_ICMP_EX void*
+#endif
+#include <winsock2.h>
+
+/* If you encounter error "Cannot open include file: 'Iphlpapi.h' here,
+ * you need to install newer Platform SDK. Presumably you're using
+ * Microsoft Visual Studio 6?
+ */
+#include <Iphlpapi.h>
+
+#include <pj/ip_helper.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+
+/* Dealing with Unicode quirks:
+
+ There seems to be a difference with GetProcAddress() API signature between
+ Windows (i.e. Win32) and Windows CE (e.g. Windows Mobile). On Windows, the
+ API is declared as:
+
+   FARPROC GetProcAddress(
+     HMODULE hModule,
+     LPCSTR lpProcName);
+ 
+ while on Windows CE:
+
+   FARPROC GetProcAddress(
+     HMODULE hModule,
+     LPCWSTR lpProcName);
+
+ Notice the difference with lpProcName argument type. This means that on 
+ Windows, even on Unicode Windows, the lpProcName always takes ANSI format, 
+ while on Windows CE, the argument follows the UNICODE setting.
+
+ Because of this, we use a different Unicode treatment here than the usual
+ PJ_NATIVE_STRING_IS_UNICODE PJLIB setting (<pj/unicode.h>):
+   - GPA_TEXT macro: convert literal string to platform's native literal 
+         string
+   - gpa_char: the platform native character type
+
+ Note that "GPA" and "gpa" are abbreviations for GetProcAddress.
+*/
+#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
+    /* on CE, follow the PJLIB Unicode setting */
+#   define GPA_TEXT(x)	PJ_T(x)
+#   define gpa_char	pj_char_t
+#else
+    /* on non-CE, always use ANSI format */
+#   define GPA_TEXT(x)	x
+#   define gpa_char	char
+#endif
+
+
+typedef DWORD (WINAPI *PFN_GetIpAddrTable)(PMIB_IPADDRTABLE pIpAddrTable, 
+					   PULONG pdwSize, 
+					   BOOL bOrder);
+typedef DWORD (WINAPI *PFN_GetAdapterAddresses)(ULONG Family,
+					        ULONG Flags,
+					        PVOID Reserved,
+					        PIP_ADAPTER_ADDRESSES AdapterAddresses,
+					        PULONG SizePointer);
+typedef DWORD (WINAPI *PFN_GetIpForwardTable)(PMIB_IPFORWARDTABLE pIpForwardTable,
+					      PULONG pdwSize, 
+					      BOOL bOrder);
+typedef DWORD (WINAPI *PFN_GetIfEntry)(PMIB_IFROW pIfRow);
+
+static HANDLE s_hDLL;
+static PFN_GetIpAddrTable s_pfnGetIpAddrTable;
+static PFN_GetAdapterAddresses s_pfnGetAdapterAddresses;
+static PFN_GetIpForwardTable s_pfnGetIpForwardTable;
+static PFN_GetIfEntry s_pfnGetIfEntry;
+
+
+static void unload_iphlp_module(void)
+{
+    FreeLibrary(s_hDLL);
+    s_hDLL = NULL;
+    s_pfnGetIpAddrTable = NULL;
+    s_pfnGetIpForwardTable = NULL;
+    s_pfnGetIfEntry = NULL;
+    s_pfnGetAdapterAddresses = NULL;
+}
+
+static FARPROC GetIpHlpApiProc(gpa_char *lpProcName)
+{
+    if(NULL == s_hDLL) {
+	s_hDLL = LoadLibrary(PJ_T("IpHlpApi"));
+	if(NULL != s_hDLL) {
+	    pj_atexit(&unload_iphlp_module);
+	}
+    }
+	
+    if(NULL != s_hDLL)
+	return GetProcAddress(s_hDLL, lpProcName);
+    
+    return NULL;
+}
+
+static DWORD MyGetIpAddrTable(PMIB_IPADDRTABLE pIpAddrTable, 
+			      PULONG pdwSize, 
+			      BOOL bOrder)
+{
+    if(NULL == s_pfnGetIpAddrTable) {
+	s_pfnGetIpAddrTable = (PFN_GetIpAddrTable) 
+	    GetIpHlpApiProc(GPA_TEXT("GetIpAddrTable"));
+    }
+    
+    if(NULL != s_pfnGetIpAddrTable) {
+	return s_pfnGetIpAddrTable(pIpAddrTable, pdwSize, bOrder);
+    }
+    
+    return ERROR_NOT_SUPPORTED;
+}
+
+static DWORD MyGetAdapterAddresses(ULONG Family,
+				   ULONG Flags,
+				   PVOID Reserved,
+				   PIP_ADAPTER_ADDRESSES AdapterAddresses,
+				   PULONG SizePointer)
+{
+    if(NULL == s_pfnGetAdapterAddresses) {
+	s_pfnGetAdapterAddresses = (PFN_GetAdapterAddresses) 
+	    GetIpHlpApiProc(GPA_TEXT("GetAdaptersAddresses"));
+    }
+    
+    if(NULL != s_pfnGetAdapterAddresses) {
+	return s_pfnGetAdapterAddresses(Family, Flags, Reserved,
+					AdapterAddresses, SizePointer);
+    }
+    
+    return ERROR_NOT_SUPPORTED;
+}
+
+#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
+static DWORD MyGetIfEntry(MIB_IFROW *pIfRow)
+{
+    if(NULL == s_pfnGetIfEntry) {
+	s_pfnGetIfEntry = (PFN_GetIfEntry) 
+	    GetIpHlpApiProc(GPA_TEXT("GetIfEntry"));
+    }
+    
+    if(NULL != s_pfnGetIfEntry) {
+	return s_pfnGetIfEntry(pIfRow);
+    }
+    
+    return ERROR_NOT_SUPPORTED;
+}
+#endif
+
+
+static DWORD MyGetIpForwardTable(PMIB_IPFORWARDTABLE pIpForwardTable, 
+				 PULONG pdwSize, 
+				 BOOL bOrder)
+{
+    if(NULL == s_pfnGetIpForwardTable) {
+	s_pfnGetIpForwardTable = (PFN_GetIpForwardTable) 
+	    GetIpHlpApiProc(GPA_TEXT("GetIpForwardTable"));
+    }
+    
+    if(NULL != s_pfnGetIpForwardTable) {
+	return s_pfnGetIpForwardTable(pIpForwardTable, pdwSize, bOrder);
+    }
+    
+    return ERROR_NOT_SUPPORTED;
+}
+
+/* Enumerate local IP interface using GetIpAddrTable()
+ * for IPv4 addresses only.
+ */
+static pj_status_t enum_ipv4_interface(unsigned *p_cnt,
+				       pj_sockaddr ifs[])
+{
+    char ipTabBuff[512];
+    MIB_IPADDRTABLE *pTab = (MIB_IPADDRTABLE*)ipTabBuff;
+    ULONG tabSize = sizeof(ipTabBuff);
+    unsigned i, count;
+    DWORD rc = NO_ERROR;
+
+    PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
+
+    /* Get IP address table */
+    rc = MyGetIpAddrTable(pTab, &tabSize, FALSE);
+    if (rc != NO_ERROR) {
+	if (rc == ERROR_INSUFFICIENT_BUFFER) {
+	    /* Retry with larger buffer */
+	    pTab = (MIB_IPADDRTABLE*)malloc(tabSize);
+	    if (pTab)
+		rc = MyGetIpAddrTable(pTab, &tabSize, FALSE);
+	}
+
+	if (rc != NO_ERROR) {
+	    if (pTab != (MIB_IPADDRTABLE*)ipTabBuff)
+		free(pTab);
+	    return PJ_RETURN_OS_ERROR(rc);
+	}
+    }
+
+    /* Reset result */
+    pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt));
+
+    /* Now fill out the entries */
+    count = (pTab->dwNumEntries < *p_cnt) ? pTab->dwNumEntries : *p_cnt;
+    *p_cnt = 0;
+    for (i=0; i<count; ++i) {
+	MIB_IFROW ifRow;
+
+	/* Ignore 0.0.0.0 address (interface is down?) */
+	if (pTab->table[i].dwAddr == 0)
+	    continue;
+
+	/* Ignore 0.0.0.0/8 address. This is a special address
+	 * which doesn't seem to have practical use.
+	 */
+	if ((pj_ntohl(pTab->table[i].dwAddr) >> 24) == 0)
+	    continue;
+
+#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
+	/* Investigate the type of this interface */
+	pj_bzero(&ifRow, sizeof(ifRow));
+	ifRow.dwIndex = pTab->table[i].dwIndex;
+	if (MyGetIfEntry(&ifRow) != 0)
+	    continue;
+
+	if (ifRow.dwType == MIB_IF_TYPE_LOOPBACK)
+	    continue;
+#endif
+
+	ifs[*p_cnt].ipv4.sin_family = PJ_AF_INET;
+	ifs[*p_cnt].ipv4.sin_addr.s_addr = pTab->table[i].dwAddr;
+	(*p_cnt)++;
+    }
+
+    if (pTab != (MIB_IPADDRTABLE*)ipTabBuff)
+	free(pTab);
+
+    return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+/* Enumerate local IP interface using GetAdapterAddresses(),
+ * which works for both IPv4 and IPv6.
+ */
+static pj_status_t enum_ipv4_ipv6_interface(int af,
+					    unsigned *p_cnt,
+					    pj_sockaddr ifs[])
+{
+    pj_uint8_t buffer[600];
+    IP_ADAPTER_ADDRESSES *adapter = (IP_ADAPTER_ADDRESSES*)buffer;
+    void *adapterBuf = NULL;
+    ULONG size = sizeof(buffer);
+    ULONG flags;
+    unsigned i;
+    DWORD rc;
+
+    flags = GAA_FLAG_SKIP_FRIENDLY_NAME |
+	    GAA_FLAG_SKIP_DNS_SERVER |
+	    GAA_FLAG_SKIP_MULTICAST;
+
+    rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size);
+    if (rc != ERROR_SUCCESS) {
+	if (rc == ERROR_BUFFER_OVERFLOW) {
+	    /* Retry with larger memory size */
+	    adapterBuf = malloc(size);
+	    adapter = (IP_ADAPTER_ADDRESSES*) adapterBuf;
+	    if (adapter != NULL)
+		rc = MyGetAdapterAddresses(af, flags, NULL, adapter, &size);
+	} 
+
+	if (rc != ERROR_SUCCESS) {
+	    if (adapterBuf)
+		free(adapterBuf);
+	    return PJ_RETURN_OS_ERROR(rc);
+	}
+    }
+
+    /* Reset result */
+    pj_bzero(ifs, sizeof(ifs[0]) * (*p_cnt));
+
+    /* Enumerate interface */
+    for (i=0; i<*p_cnt && adapter; adapter = adapter->Next) {
+	if (adapter->FirstUnicastAddress) {
+	    SOCKET_ADDRESS *pAddr = &adapter->FirstUnicastAddress->Address;
+
+	    /* Ignore address family which we didn't request, just in case */
+	    if (pAddr->lpSockaddr->sa_family != PJ_AF_INET &&
+		pAddr->lpSockaddr->sa_family != PJ_AF_INET6)
+	    {
+		continue;
+	    }
+
+	    /* Apply some filtering to known IPv4 unusable addresses */
+	    if (pAddr->lpSockaddr->sa_family == PJ_AF_INET) {
+		const pj_sockaddr_in *addr_in = 
+		    (const pj_sockaddr_in*)pAddr->lpSockaddr;
+
+		/* Ignore 0.0.0.0 address (interface is down?) */
+		if (addr_in->sin_addr.s_addr == 0)
+		    continue;
+
+		/* Ignore 0.0.0.0/8 address. This is a special address
+		 * which doesn't seem to have practical use.
+		 */
+		if ((pj_ntohl(addr_in->sin_addr.s_addr) >> 24) == 0)
+		    continue;
+	    }
+
+#if PJ_IP_HELPER_IGNORE_LOOPBACK_IF
+	    /* Ignore loopback interfaces */
+	    /* This should have been IF_TYPE_SOFTWARE_LOOPBACK according to
+	     * MSDN, and this macro should have been declared in Ipifcons.h, 
+	     * but some SDK versions don't have it.
+	     */
+	    if (adapter->IfType == MIB_IF_TYPE_LOOPBACK)
+		continue;
+#endif
+
+	    /* Ignore down interface */
+	    if (adapter->OperStatus != IfOperStatusUp)
+		continue;
+
+	    ifs[i].addr.sa_family = pAddr->lpSockaddr->sa_family;
+	    pj_memcpy(&ifs[i], pAddr->lpSockaddr, pAddr->iSockaddrLength);
+	    ++i;
+	}
+    }
+
+    if (adapterBuf)
+	free(adapterBuf);
+
+    *p_cnt = i;
+    return (*p_cnt) ? PJ_SUCCESS : PJ_ENOTFOUND;
+}
+
+
+/*
+ * Enumerate the local IP interface currently active in the host.
+ */
+PJ_DEF(pj_status_t) pj_enum_ip_interface(int af,
+					 unsigned *p_cnt,
+					 pj_sockaddr ifs[])
+{
+    pj_status_t status = -1;
+
+    PJ_ASSERT_RETURN(p_cnt && ifs, PJ_EINVAL);
+    PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC || af==PJ_AF_INET || af==PJ_AF_INET6,
+		     PJ_EAFNOTSUP);
+
+    status = enum_ipv4_ipv6_interface(af, p_cnt, ifs);
+    if (status != PJ_SUCCESS && (af==PJ_AF_INET || af==PJ_AF_UNSPEC))
+	status = enum_ipv4_interface(p_cnt, ifs);
+    return status;
+}
+
+/*
+ * Enumerate the IP routing table for this host.
+ */
+PJ_DEF(pj_status_t) pj_enum_ip_route(unsigned *p_cnt,
+				     pj_ip_route_entry routes[])
+{
+    char ipTabBuff[1024];
+    MIB_IPADDRTABLE *pIpTab;
+    char rtabBuff[1024];
+    MIB_IPFORWARDTABLE *prTab;
+    ULONG tabSize;
+    unsigned i, count;
+    DWORD rc = NO_ERROR;
+
+    PJ_ASSERT_RETURN(p_cnt && routes, PJ_EINVAL);
+
+    pIpTab = (MIB_IPADDRTABLE *)ipTabBuff;
+    prTab = (MIB_IPFORWARDTABLE *)rtabBuff;
+
+    /* First get IP address table */
+    tabSize = sizeof(ipTabBuff);
+    rc = MyGetIpAddrTable(pIpTab, &tabSize, FALSE);
+    if (rc != NO_ERROR)
+	return PJ_RETURN_OS_ERROR(rc);
+
+    /* Next get IP route table */
+    tabSize = sizeof(rtabBuff);
+
+    rc = MyGetIpForwardTable(prTab, &tabSize, 1);
+    if (rc != NO_ERROR)
+	return PJ_RETURN_OS_ERROR(rc);
+
+    /* Reset routes */
+    pj_bzero(routes, sizeof(routes[0]) * (*p_cnt));
+
+    /* Now fill out the route entries */
+    count = (prTab->dwNumEntries < *p_cnt) ? prTab->dwNumEntries : *p_cnt;
+    *p_cnt = 0;
+    for (i=0; i<count; ++i) {
+	unsigned j;
+
+	/* Find interface entry */
+	for (j=0; j<pIpTab->dwNumEntries; ++j) {
+	    if (pIpTab->table[j].dwIndex == prTab->table[i].dwForwardIfIndex)
+		break;
+	}
+
+	if (j==pIpTab->dwNumEntries)
+	    continue;	/* Interface not found */
+
+	routes[*p_cnt].ipv4.if_addr.s_addr = pIpTab->table[j].dwAddr;
+	routes[*p_cnt].ipv4.dst_addr.s_addr = prTab->table[i].dwForwardDest;
+	routes[*p_cnt].ipv4.mask.s_addr = prTab->table[i].dwForwardMask;
+
+	(*p_cnt)++;
+    }
+
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/list.c b/jni/pjproject-android/pjlib/src/pj/list.c
new file mode 100644
index 0000000..6ff8d20
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/list.c
@@ -0,0 +1,26 @@
+/* $Id: list.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/list.h>
+
+#if !PJ_FUNCTIONS_ARE_INLINED
+#  include <pj/list_i.h>
+#endif
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/lock.c b/jni/pjproject-android/pjlib/src/pj/lock.c
new file mode 100644
index 0000000..34e2d1e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/lock.c
@@ -0,0 +1,715 @@
+/* $Id: lock.c 4412 2013-03-05 03:12:32Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/lock.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/errno.h>
+
+#define THIS_FILE	"lock.c"
+
+typedef void LOCK_OBJ;
+
+/*
+ * Lock structure.
+ */
+struct pj_lock_t
+{
+    LOCK_OBJ *lock_object;
+
+    pj_status_t	(*acquire)	(LOCK_OBJ*);
+    pj_status_t	(*tryacquire)	(LOCK_OBJ*);
+    pj_status_t	(*release)	(LOCK_OBJ*);
+    pj_status_t	(*destroy)	(LOCK_OBJ*);
+};
+
+typedef pj_status_t (*FPTR)(LOCK_OBJ*);
+
+/******************************************************************************
+ * Implementation of lock object with mutex.
+ */
+static pj_lock_t mutex_lock_template = 
+{
+    NULL,
+    (FPTR) &pj_mutex_lock,
+    (FPTR) &pj_mutex_trylock,
+    (FPTR) &pj_mutex_unlock,
+    (FPTR) &pj_mutex_destroy
+};
+
+static pj_status_t create_mutex_lock( pj_pool_t *pool,
+				      const char *name,
+				      int type,
+				      pj_lock_t **lock )
+{
+    pj_lock_t *p_lock;
+    pj_mutex_t *mutex;
+    pj_status_t rc;
+
+    PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
+
+    p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t);
+    if (!p_lock)
+	return PJ_ENOMEM;
+
+    pj_memcpy(p_lock, &mutex_lock_template, sizeof(pj_lock_t));
+    rc = pj_mutex_create(pool, name, type, &mutex);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    p_lock->lock_object = mutex;
+    *lock = p_lock;
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_lock_create_simple_mutex( pj_pool_t *pool,
+						 const char *name,
+						 pj_lock_t **lock )
+{
+    return create_mutex_lock(pool, name, PJ_MUTEX_SIMPLE, lock);
+}
+
+PJ_DEF(pj_status_t) pj_lock_create_recursive_mutex( pj_pool_t *pool,
+						    const char *name,
+						    pj_lock_t **lock )
+{
+    return create_mutex_lock(pool, name, PJ_MUTEX_RECURSE, lock);
+}
+
+
+/******************************************************************************
+ * Implementation of NULL lock object.
+ */
+static pj_status_t null_op(void *arg)
+{
+    PJ_UNUSED_ARG(arg);
+    return PJ_SUCCESS;
+}
+
+static pj_lock_t null_lock_template = 
+{
+    NULL,
+    &null_op,
+    &null_op,
+    &null_op,
+    &null_op
+};
+
+PJ_DEF(pj_status_t) pj_lock_create_null_mutex( pj_pool_t *pool,
+					       const char *name,
+					       pj_lock_t **lock )
+{
+    PJ_UNUSED_ARG(name);
+    PJ_UNUSED_ARG(pool);
+
+    PJ_ASSERT_RETURN(lock, PJ_EINVAL);
+
+    *lock = &null_lock_template;
+    return PJ_SUCCESS;
+}
+
+
+/******************************************************************************
+ * Implementation of semaphore lock object.
+ */
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+static pj_lock_t sem_lock_template = 
+{
+    NULL,
+    (FPTR) &pj_sem_wait,
+    (FPTR) &pj_sem_trywait,
+    (FPTR) &pj_sem_post,
+    (FPTR) &pj_sem_destroy
+};
+
+PJ_DEF(pj_status_t) pj_lock_create_semaphore(  pj_pool_t *pool,
+					       const char *name,
+					       unsigned initial,
+					       unsigned max,
+					       pj_lock_t **lock )
+{
+    pj_lock_t *p_lock;
+    pj_sem_t *sem;
+    pj_status_t rc;
+
+    PJ_ASSERT_RETURN(pool && lock, PJ_EINVAL);
+
+    p_lock = PJ_POOL_ALLOC_T(pool, pj_lock_t);
+    if (!p_lock)
+	return PJ_ENOMEM;
+
+    pj_memcpy(p_lock, &sem_lock_template, sizeof(pj_lock_t));
+    rc = pj_sem_create( pool, name, initial, max, &sem);
+    if (rc != PJ_SUCCESS)
+        return rc;
+
+    p_lock->lock_object = sem;
+    *lock = p_lock;
+
+    return PJ_SUCCESS;
+}
+
+
+#endif	/* PJ_HAS_SEMAPHORE */
+
+
+PJ_DEF(pj_status_t) pj_lock_acquire( pj_lock_t *lock )
+{
+    PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+    return (*lock->acquire)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_tryacquire( pj_lock_t *lock )
+{
+    PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+    return (*lock->tryacquire)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_release( pj_lock_t *lock )
+{
+    PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+    return (*lock->release)(lock->lock_object);
+}
+
+PJ_DEF(pj_status_t) pj_lock_destroy( pj_lock_t *lock )
+{
+    PJ_ASSERT_RETURN(lock != NULL, PJ_EINVAL);
+    return (*lock->destroy)(lock->lock_object);
+}
+
+
+/******************************************************************************
+ * Group lock
+ */
+
+/* Individual lock in the group lock */
+typedef struct grp_lock_item
+{
+    PJ_DECL_LIST_MEMBER(struct grp_lock_item);
+    int		 prio;
+    pj_lock_t	*lock;
+
+} grp_lock_item;
+
+/* Destroy callbacks */
+typedef struct grp_destroy_callback
+{
+    PJ_DECL_LIST_MEMBER(struct grp_destroy_callback);
+    void	*comp;
+    void	(*handler)(void*);
+} grp_destroy_callback;
+
+#if PJ_GRP_LOCK_DEBUG
+/* Store each add_ref caller */
+typedef struct grp_lock_ref
+{
+    PJ_DECL_LIST_MEMBER(struct grp_lock_ref);
+    const char	*file;
+    int		 line;
+} grp_lock_ref;
+#endif
+
+/* The group lock */
+struct pj_grp_lock_t
+{
+    pj_lock_t	 	 base;
+
+    pj_pool_t		*pool;
+    pj_atomic_t		*ref_cnt;
+    pj_lock_t		*own_lock;
+
+    pj_thread_t		*owner;
+    int			 owner_cnt;
+
+    grp_lock_item	 lock_list;
+    grp_destroy_callback destroy_list;
+
+#if PJ_GRP_LOCK_DEBUG
+    grp_lock_ref	 ref_list;
+    grp_lock_ref	 ref_free_list;
+#endif
+};
+
+
+PJ_DEF(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg)
+{
+    pj_bzero(cfg, sizeof(*cfg));
+}
+
+static void grp_lock_set_owner_thread(pj_grp_lock_t *glock)
+{
+    if (!glock->owner) {
+	glock->owner = pj_thread_this();
+	glock->owner_cnt = 1;
+    } else {
+	pj_assert(glock->owner == pj_thread_this());
+	glock->owner_cnt++;
+    }
+}
+
+static void grp_lock_unset_owner_thread(pj_grp_lock_t *glock)
+{
+    pj_assert(glock->owner == pj_thread_this());
+    pj_assert(glock->owner_cnt > 0);
+    if (--glock->owner_cnt <= 0) {
+	glock->owner = NULL;
+	glock->owner_cnt = 0;
+    }
+}
+
+static pj_status_t grp_lock_acquire(LOCK_OBJ *p)
+{
+    pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
+    grp_lock_item *lck;
+
+    pj_assert(pj_atomic_get(glock->ref_cnt) > 0);
+
+    lck = glock->lock_list.next;
+    while (lck != &glock->lock_list) {
+	pj_lock_acquire(lck->lock);
+	lck = lck->next;
+    }
+    grp_lock_set_owner_thread(glock);
+    pj_grp_lock_add_ref(glock);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t grp_lock_tryacquire(LOCK_OBJ *p)
+{
+    pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
+    grp_lock_item *lck;
+
+    pj_assert(pj_atomic_get(glock->ref_cnt) > 0);
+
+    lck = glock->lock_list.next;
+    while (lck != &glock->lock_list) {
+	pj_status_t status = pj_lock_tryacquire(lck->lock);
+	if (status != PJ_SUCCESS) {
+	    lck = lck->prev;
+	    while (lck != &glock->lock_list) {
+		pj_lock_release(lck->lock);
+		lck = lck->prev;
+	    }
+	    return status;
+	}
+	lck = lck->next;
+    }
+    grp_lock_set_owner_thread(glock);
+    pj_grp_lock_add_ref(glock);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t grp_lock_release(LOCK_OBJ *p)
+{
+    pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
+    grp_lock_item *lck;
+
+    grp_lock_unset_owner_thread(glock);
+
+    lck = glock->lock_list.prev;
+    while (lck != &glock->lock_list) {
+	pj_lock_release(lck->lock);
+	lck = lck->prev;
+    }
+    return pj_grp_lock_dec_ref(glock);
+}
+
+static pj_status_t grp_lock_destroy(LOCK_OBJ *p)
+{
+    pj_grp_lock_t *glock = (pj_grp_lock_t*)p;
+    pj_pool_t *pool = glock->pool;
+    grp_lock_item *lck;
+    grp_destroy_callback *cb;
+
+    if (!glock->pool) {
+	/* already destroyed?! */
+	return PJ_EINVAL;
+    }
+
+    /* Release all chained locks */
+    lck = glock->lock_list.next;
+    while (lck != &glock->lock_list) {
+	if (lck->lock != glock->own_lock) {
+	    int i;
+	    for (i=0; i<glock->owner_cnt; ++i)
+		pj_lock_release(lck->lock);
+	}
+	lck = lck->next;
+    }
+
+    /* Call callbacks */
+    cb = glock->destroy_list.next;
+    while (cb != &glock->destroy_list) {
+	grp_destroy_callback *next = cb->next;
+	cb->handler(cb->comp);
+	cb = next;
+    }
+
+    pj_lock_destroy(glock->own_lock);
+    pj_atomic_destroy(glock->ref_cnt);
+    glock->pool = NULL;
+    pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_grp_lock_create( pj_pool_t *pool,
+                                        const pj_grp_lock_config *cfg,
+                                        pj_grp_lock_t **p_grp_lock)
+{
+    pj_grp_lock_t *glock;
+    grp_lock_item *own_lock;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && p_grp_lock, PJ_EINVAL);
+
+    PJ_UNUSED_ARG(cfg);
+
+    pool = pj_pool_create(pool->factory, "glck%p", 512, 512, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    glock = PJ_POOL_ZALLOC_T(pool, pj_grp_lock_t);
+    glock->base.lock_object = glock;
+    glock->base.acquire = &grp_lock_acquire;
+    glock->base.tryacquire = &grp_lock_tryacquire;
+    glock->base.release = &grp_lock_release;
+    glock->base.destroy = &grp_lock_destroy;
+
+    glock->pool = pool;
+    pj_list_init(&glock->lock_list);
+    pj_list_init(&glock->destroy_list);
+#if PJ_GRP_LOCK_DEBUG
+    pj_list_init(&glock->ref_list);
+    pj_list_init(&glock->ref_free_list);
+#endif
+
+    status = pj_atomic_create(pool, 0, &glock->ref_cnt);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
+                                            &glock->own_lock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    own_lock = PJ_POOL_ZALLOC_T(pool, grp_lock_item);
+    own_lock->lock = glock->own_lock;
+    pj_list_push_back(&glock->lock_list, own_lock);
+
+    *p_grp_lock = glock;
+    return PJ_SUCCESS;
+
+on_error:
+    grp_lock_destroy(glock);
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock)
+{
+    return grp_lock_destroy(grp_lock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock)
+{
+    return grp_lock_acquire(grp_lock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock)
+{
+    return grp_lock_tryacquire(grp_lock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock)
+{
+    return grp_lock_release(grp_lock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_replace( pj_grp_lock_t *old_lock,
+                                         pj_grp_lock_t *new_lock)
+{
+    grp_destroy_callback *ocb;
+
+    /* Move handlers from old to new */
+    ocb = old_lock->destroy_list.next;
+    while (ocb != &old_lock->destroy_list) {
+	grp_destroy_callback *ncb;
+
+	ncb = PJ_POOL_ALLOC_T(new_lock->pool, grp_destroy_callback);
+	ncb->comp = ocb->comp;
+	ncb->handler = ocb->handler;
+	pj_list_push_back(&new_lock->destroy_list, ncb);
+
+	ocb = ocb->next;
+    }
+
+    pj_list_init(&old_lock->destroy_list);
+
+    grp_lock_destroy(old_lock);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_add_handler( pj_grp_lock_t *glock,
+                                             pj_pool_t *pool,
+                                             void *comp,
+                                             void (*destroy)(void *comp))
+{
+    grp_destroy_callback *cb;
+
+    grp_lock_acquire(glock);
+
+    if (pool == NULL)
+	pool = glock->pool;
+
+    cb = PJ_POOL_ZALLOC_T(pool, grp_destroy_callback);
+    cb->comp = comp;
+    cb->handler = destroy;
+    pj_list_push_back(&glock->destroy_list, cb);
+
+    grp_lock_release(glock);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_del_handler( pj_grp_lock_t *glock,
+                                             void *comp,
+                                             void (*destroy)(void *comp))
+{
+    grp_destroy_callback *cb;
+
+    grp_lock_acquire(glock);
+
+    cb = glock->destroy_list.next;
+    while (cb != &glock->destroy_list) {
+	if (cb->comp == comp && cb->handler == destroy)
+	    break;
+	cb = cb->next;
+    }
+
+    if (cb != &glock->destroy_list)
+	pj_list_erase(cb);
+
+    grp_lock_release(glock);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t grp_lock_add_ref(pj_grp_lock_t *glock)
+{
+    pj_atomic_inc(glock->ref_cnt);
+    return PJ_SUCCESS;
+}
+
+static pj_status_t grp_lock_dec_ref(pj_grp_lock_t *glock)
+{
+    int cnt; /* for debugging */
+    if ((cnt=pj_atomic_dec_and_get(glock->ref_cnt)) == 0) {
+	grp_lock_destroy(glock);
+	return PJ_EGONE;
+    }
+    pj_assert(cnt > 0);
+    pj_grp_lock_dump(glock);
+    return PJ_SUCCESS;
+}
+
+#if PJ_GRP_LOCK_DEBUG
+PJ_DEF(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *glock,
+                                            const char *file,
+                                            int line)
+{
+    grp_lock_ref *ref;
+    pj_status_t status;
+
+    pj_enter_critical_section();
+    if (!pj_list_empty(&glock->ref_free_list)) {
+	ref = glock->ref_free_list.next;
+	pj_list_erase(ref);
+    } else {
+	ref = PJ_POOL_ALLOC_T(glock->pool, grp_lock_ref);
+    }
+
+    ref->file = file;
+    ref->line = line;
+    pj_list_push_back(&glock->ref_list, ref);
+
+    pj_leave_critical_section();
+
+    status = grp_lock_add_ref(glock);
+
+    if (status != PJ_SUCCESS) {
+	pj_enter_critical_section();
+	pj_list_erase(ref);
+	pj_list_push_back(&glock->ref_free_list, ref);
+	pj_leave_critical_section();
+    }
+
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *glock,
+                                            const char *file,
+                                            int line)
+{
+    grp_lock_ref *ref;
+
+    pj_enter_critical_section();
+    /* Find the same source file */
+    ref = glock->ref_list.next;
+    while (ref != &glock->ref_list) {
+	if (strcmp(ref->file, file) == 0) {
+	    pj_list_erase(ref);
+	    pj_list_push_back(&glock->ref_free_list, ref);
+	    break;
+	}
+	ref = ref->next;
+    }
+    pj_leave_critical_section();
+
+    if (ref == &glock->ref_list) {
+	PJ_LOG(2,(THIS_FILE, "pj_grp_lock_dec_ref_dbg() could not find "
+			      "matching ref for %s", file));
+    }
+
+    return grp_lock_dec_ref(glock);
+}
+#else
+PJ_DEF(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *glock)
+{
+    return grp_lock_add_ref(glock);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *glock)
+{
+    return grp_lock_dec_ref(glock);
+}
+#endif
+
+PJ_DEF(int) pj_grp_lock_get_ref(pj_grp_lock_t *glock)
+{
+    return pj_atomic_get(glock->ref_cnt);
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_chain_lock( pj_grp_lock_t *glock,
+                                            pj_lock_t *lock,
+                                            int pos)
+{
+    grp_lock_item *lck, *new_lck;
+    int i;
+
+    grp_lock_acquire(glock);
+
+    for (i=0; i<glock->owner_cnt; ++i)
+	pj_lock_acquire(lock);
+
+    lck = glock->lock_list.next;
+    while (lck != &glock->lock_list) {
+	if (lck->prio >= pos)
+	    break;
+	lck = lck->next;
+    }
+
+    new_lck = PJ_POOL_ZALLOC_T(glock->pool, grp_lock_item);
+    new_lck->prio = pos;
+    new_lck->lock = lock;
+    pj_list_insert_before(lck, new_lck);
+
+    /* this will also release the new lock */
+    grp_lock_release(glock);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_grp_lock_unchain_lock( pj_grp_lock_t *glock,
+                                              pj_lock_t *lock)
+{
+    grp_lock_item *lck;
+
+    grp_lock_acquire(glock);
+
+    lck = glock->lock_list.next;
+    while (lck != &glock->lock_list) {
+	if (lck->lock == lock)
+	    break;
+	lck = lck->next;
+    }
+
+    if (lck != &glock->lock_list) {
+	int i;
+
+	pj_list_erase(lck);
+	for (i=0; i<glock->owner_cnt; ++i)
+	    pj_lock_release(lck->lock);
+    }
+
+    grp_lock_release(glock);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock)
+{
+#if PJ_GRP_LOCK_DEBUG
+    grp_lock_ref *ref = grp_lock->ref_list.next;
+    char info_buf[1000];
+    pj_str_t info;
+
+    info.ptr = info_buf;
+    info.slen = 0;
+
+    pj_grp_lock_acquire(grp_lock);
+    pj_enter_critical_section();
+
+    while (ref != &grp_lock->ref_list && info.slen < sizeof(info_buf)) {
+	char *start = info.ptr + info.slen;
+	int max_len = sizeof(info_buf) - info.slen;
+	int len;
+
+	len = pj_ansi_snprintf(start, max_len, "%s:%d ", ref->file, ref->line);
+	if (len < 1 || len > max_len) {
+	    len = strlen(ref->file);
+	    if (len > max_len - 1)
+		len = max_len - 1;
+
+	    memcpy(start, ref->file, len);
+	    start[len++] = ' ';
+	}
+
+	info.slen += len;
+
+	ref = ref->next;
+    }
+
+    if (ref != &grp_lock->ref_list) {
+	int i;
+	for (i=0; i<4; ++i)
+	    info_buf[sizeof(info_buf)-i-1] = '.';
+    }
+    info.ptr[info.slen-1] = '\0';
+
+    pj_leave_critical_section();
+    pj_grp_lock_release(grp_lock);
+
+    PJ_LOG(4,(THIS_FILE, "Group lock %p, ref_cnt=%d. Reference holders: %s",
+	       grp_lock, pj_grp_lock_get_ref(grp_lock), info.ptr));
+#else
+    PJ_UNUSED_ARG(grp_lock);
+#endif
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/log.c b/jni/pjproject-android/pjlib/src/pj/log.c
new file mode 100644
index 0000000..f0889b2
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/log.c
@@ -0,0 +1,545 @@
+/* $Id: log.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/types.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/compat/stdarg.h>
+
+#if PJ_LOG_MAX_LEVEL >= 1
+
+#if 0
+PJ_DEF_DATA(int) pj_log_max_level = PJ_LOG_MAX_LEVEL;
+#else
+static int pj_log_max_level = PJ_LOG_MAX_LEVEL;
+#endif
+
+static void *g_last_thread;
+
+#if PJ_HAS_THREADS
+static long thread_suspended_tls_id = -1;
+#  if PJ_LOG_ENABLE_INDENT
+static long thread_indent_tls_id = -1;
+#  endif
+#endif
+
+#if !PJ_LOG_ENABLE_INDENT || !PJ_HAS_THREADS
+static int log_indent;
+#endif
+
+static pj_log_func *log_writer = &pj_log_write;
+static unsigned log_decor = PJ_LOG_HAS_TIME | PJ_LOG_HAS_MICRO_SEC |
+			    PJ_LOG_HAS_SENDER | PJ_LOG_HAS_NEWLINE |
+			    PJ_LOG_HAS_SPACE | PJ_LOG_HAS_THREAD_SWC |
+			    PJ_LOG_HAS_INDENT
+#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
+    (defined(PJ_WIN64) && PJ_WIN64!=0)
+			    | PJ_LOG_HAS_COLOR
+#endif
+			    ;
+
+static pj_color_t PJ_LOG_COLOR_0 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
+static pj_color_t PJ_LOG_COLOR_1 = PJ_TERM_COLOR_BRIGHT | PJ_TERM_COLOR_R;
+static pj_color_t PJ_LOG_COLOR_2 = PJ_TERM_COLOR_BRIGHT | 
+				   PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G;
+static pj_color_t PJ_LOG_COLOR_3 = PJ_TERM_COLOR_BRIGHT | 
+				   PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G | 
+				   PJ_TERM_COLOR_B;
+static pj_color_t PJ_LOG_COLOR_4 = PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G | 
+				   PJ_TERM_COLOR_B;
+static pj_color_t PJ_LOG_COLOR_5 = PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G | 
+				   PJ_TERM_COLOR_B;
+static pj_color_t PJ_LOG_COLOR_6 = PJ_TERM_COLOR_R | 
+				   PJ_TERM_COLOR_G | 
+				   PJ_TERM_COLOR_B;
+/* Default terminal color */
+static pj_color_t PJ_LOG_COLOR_77 = PJ_TERM_COLOR_R | 
+				    PJ_TERM_COLOR_G | 
+				    PJ_TERM_COLOR_B;
+
+#if PJ_LOG_USE_STACK_BUFFER==0
+static char log_buffer[PJ_LOG_MAX_SIZE];
+#endif
+
+#define LOG_MAX_INDENT		80
+
+#if PJ_HAS_THREADS
+static void logging_shutdown(void)
+{
+    if (thread_suspended_tls_id != -1) {
+	pj_thread_local_free(thread_suspended_tls_id);
+	thread_suspended_tls_id = -1;
+    }
+#  if PJ_LOG_ENABLE_INDENT
+    if (thread_indent_tls_id != -1) {
+	pj_thread_local_free(thread_indent_tls_id);
+	thread_indent_tls_id = -1;
+    }
+#  endif
+}
+#endif	/* PJ_HAS_THREADS */
+
+#if PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS
+static void log_set_indent(int indent)
+{
+    if (indent < 0) indent = 0;
+    pj_thread_local_set(thread_indent_tls_id, (void*)(pj_ssize_t)indent);
+}
+
+static int log_get_raw_indent()
+{
+    return (long)(pj_ssize_t)pj_thread_local_get(thread_indent_tls_id);
+}
+
+#else
+static void log_set_indent(int indent)
+{
+    log_indent = indent;
+    if (log_indent < 0) log_indent = 0;
+}
+
+static int log_get_raw_indent()
+{
+    return log_indent;
+}
+#endif	/* PJ_LOG_ENABLE_INDENT && PJ_HAS_THREADS */
+
+static int log_get_indent()
+{
+    int indent = log_get_raw_indent();
+    return indent > LOG_MAX_INDENT ? LOG_MAX_INDENT : indent;
+}
+
+PJ_DEF(void) pj_log_add_indent(int indent)
+{
+    log_set_indent(log_get_raw_indent() + indent);
+}
+
+PJ_DEF(void) pj_log_push_indent(void)
+{
+    pj_log_add_indent(PJ_LOG_INDENT_SIZE);
+}
+
+PJ_DEF(void) pj_log_pop_indent(void)
+{
+    pj_log_add_indent(-PJ_LOG_INDENT_SIZE);
+}
+
+pj_status_t pj_log_init(void)
+{
+#if PJ_HAS_THREADS
+    if (thread_suspended_tls_id == -1) {
+	pj_status_t status;
+	status = pj_thread_local_alloc(&thread_suspended_tls_id);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+#  if PJ_LOG_ENABLE_INDENT
+	status = pj_thread_local_alloc(&thread_indent_tls_id);
+	if (status != PJ_SUCCESS) {
+	    pj_thread_local_free(thread_suspended_tls_id);
+	    thread_suspended_tls_id = -1;
+	    return status;
+	}
+#  endif
+	pj_atexit(&logging_shutdown);
+    }
+#endif
+    g_last_thread = NULL;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_log_set_decor(unsigned decor)
+{
+    log_decor = decor;
+}
+
+PJ_DEF(unsigned) pj_log_get_decor(void)
+{
+    return log_decor;
+}
+
+PJ_DEF(void) pj_log_set_color(int level, pj_color_t color)
+{
+    switch (level) 
+    {
+	case 0: PJ_LOG_COLOR_0 = color; 
+	    break;
+	case 1: PJ_LOG_COLOR_1 = color; 
+	    break;
+	case 2: PJ_LOG_COLOR_2 = color; 
+	    break;
+	case 3: PJ_LOG_COLOR_3 = color; 
+	    break;
+	case 4: PJ_LOG_COLOR_4 = color; 
+	    break;
+	case 5: PJ_LOG_COLOR_5 = color; 
+	    break;
+	case 6: PJ_LOG_COLOR_6 = color; 
+	    break;
+	/* Default terminal color */
+	case 77: PJ_LOG_COLOR_77 = color; 
+	    break;
+	default:
+	    /* Do nothing */
+	    break;
+    }
+}
+
+PJ_DEF(pj_color_t) pj_log_get_color(int level)
+{
+    switch (level) {
+	case 0:
+	    return PJ_LOG_COLOR_0;
+	case 1:
+	    return PJ_LOG_COLOR_1;
+	case 2:
+	    return PJ_LOG_COLOR_2;
+	case 3:
+	    return PJ_LOG_COLOR_3;
+	case 4:
+	    return PJ_LOG_COLOR_4;
+	case 5:
+	    return PJ_LOG_COLOR_5;
+	case 6:
+	    return PJ_LOG_COLOR_6;
+	default:
+	    /* Return default terminal color */
+	    return PJ_LOG_COLOR_77;
+    }
+}
+
+PJ_DEF(void) pj_log_set_level(int level)
+{
+    pj_log_max_level = level;
+}
+
+#if 1
+PJ_DEF(int) pj_log_get_level(void)
+{
+    return pj_log_max_level;
+}
+#endif
+
+PJ_DEF(void) pj_log_set_log_func( pj_log_func *func )
+{
+    log_writer = func;
+}
+
+PJ_DEF(pj_log_func*) pj_log_get_log_func(void)
+{
+    return log_writer;
+}
+
+/* Temporarily suspend logging facility for this thread.
+ * If thread local storage/variable is not used or not initialized, then
+ * we can only suspend the logging globally across all threads. This may
+ * happen e.g. when log function is called before PJLIB is fully initialized
+ * or after PJLIB is shutdown.
+ */
+static void suspend_logging(int *saved_level)
+{
+	/* Save the level regardless, just in case PJLIB is shutdown
+	 * between suspend and resume.
+	 */
+	*saved_level = pj_log_max_level;
+
+#if PJ_HAS_THREADS
+    if (thread_suspended_tls_id != -1) 
+    {
+	pj_thread_local_set(thread_suspended_tls_id, 
+			    (void*)(pj_ssize_t)PJ_TRUE);
+    } 
+    else
+#endif
+    {
+	pj_log_max_level = 0;
+    }
+}
+
+/* Resume logging facility for this thread */
+static void resume_logging(int *saved_level)
+{
+#if PJ_HAS_THREADS
+    if (thread_suspended_tls_id != -1) 
+    {
+	pj_thread_local_set(thread_suspended_tls_id, (void*)PJ_FALSE);
+    }
+    else
+#endif
+    {
+	/* Only revert the level if application doesn't change the
+	 * logging level between suspend and resume.
+	 */
+	if (pj_log_max_level==0 && *saved_level)
+	    pj_log_max_level = *saved_level;
+    }
+}
+
+/* Is logging facility suspended for this thread? */
+static pj_bool_t is_logging_suspended(void)
+{
+#if PJ_HAS_THREADS
+    if (thread_suspended_tls_id != -1) 
+    {
+	return pj_thread_local_get(thread_suspended_tls_id) != NULL;
+    }
+    else
+#endif
+    {
+	return pj_log_max_level == 0;
+    }
+}
+
+PJ_DEF(void) pj_log( const char *sender, int level, 
+		     const char *format, va_list marker)
+{
+    pj_time_val now;
+    pj_parsed_time ptime;
+    char *pre;
+#if PJ_LOG_USE_STACK_BUFFER
+    char log_buffer[PJ_LOG_MAX_SIZE];
+#endif
+    int saved_level, len, print_len, indent;
+
+    PJ_CHECK_STACK();
+
+    if (level > pj_log_max_level)
+	return;
+
+    if (is_logging_suspended())
+	return;
+
+    /* Temporarily disable logging for this thread. Some of PJLIB APIs that
+     * this function calls below will recursively call the logging function 
+     * back, hence it will cause infinite recursive calls if we allow that.
+     */
+    suspend_logging(&saved_level);
+
+    /* Get current date/time. */
+    pj_gettimeofday(&now);
+    pj_time_decode(&now, &ptime);
+
+    pre = log_buffer;
+    if (log_decor & PJ_LOG_HAS_LEVEL_TEXT) {
+	static const char *ltexts[] = { "FATAL:", "ERROR:", " WARN:", 
+			      " INFO:", "DEBUG:", "TRACE:", "DETRC:"};
+	pj_ansi_strcpy(pre, ltexts[level]);
+	pre += 6;
+    }
+    if (log_decor & PJ_LOG_HAS_DAY_NAME) {
+	static const char *wdays[] = { "Sun", "Mon", "Tue", "Wed",
+				       "Thu", "Fri", "Sat"};
+	pj_ansi_strcpy(pre, wdays[ptime.wday]);
+	pre += 3;
+    }
+    if (log_decor & PJ_LOG_HAS_YEAR) {
+	if (pre!=log_buffer) *pre++ = ' ';
+	pre += pj_utoa(ptime.year, pre);
+    }
+    if (log_decor & PJ_LOG_HAS_MONTH) {
+	*pre++ = '-';
+	pre += pj_utoa_pad(ptime.mon+1, pre, 2, '0');
+    }
+    if (log_decor & PJ_LOG_HAS_DAY_OF_MON) {
+	*pre++ = '-';
+	pre += pj_utoa_pad(ptime.day, pre, 2, '0');
+    }
+    if (log_decor & PJ_LOG_HAS_TIME) {
+	if (pre!=log_buffer) *pre++ = ' ';
+	pre += pj_utoa_pad(ptime.hour, pre, 2, '0');
+	*pre++ = ':';
+	pre += pj_utoa_pad(ptime.min, pre, 2, '0');
+	*pre++ = ':';
+	pre += pj_utoa_pad(ptime.sec, pre, 2, '0');
+    }
+    if (log_decor & PJ_LOG_HAS_MICRO_SEC) {
+	*pre++ = '.';
+	pre += pj_utoa_pad(ptime.msec, pre, 3, '0');
+    }
+    if (log_decor & PJ_LOG_HAS_SENDER) {
+	enum { SENDER_WIDTH = 14 };
+	pj_size_t sender_len = strlen(sender);
+	if (pre!=log_buffer) *pre++ = ' ';
+	if (sender_len <= SENDER_WIDTH) {
+	    while (sender_len < SENDER_WIDTH)
+		*pre++ = ' ', ++sender_len;
+	    while (*sender)
+		*pre++ = *sender++;
+	} else {
+	    int i;
+	    for (i=0; i<SENDER_WIDTH; ++i)
+		*pre++ = *sender++;
+	}
+    }
+    if (log_decor & PJ_LOG_HAS_THREAD_ID) {
+	enum { THREAD_WIDTH = 12 };
+	const char *thread_name = pj_thread_get_name(pj_thread_this());
+	pj_size_t thread_len = strlen(thread_name);
+	*pre++ = ' ';
+	if (thread_len <= THREAD_WIDTH) {
+	    while (thread_len < THREAD_WIDTH)
+		*pre++ = ' ', ++thread_len;
+	    while (*thread_name)
+		*pre++ = *thread_name++;
+	} else {
+	    int i;
+	    for (i=0; i<THREAD_WIDTH; ++i)
+		*pre++ = *thread_name++;
+	}
+    }
+
+    if (log_decor != 0 && log_decor != PJ_LOG_HAS_NEWLINE)
+	*pre++ = ' ';
+
+    if (log_decor & PJ_LOG_HAS_THREAD_SWC) {
+	void *current_thread = (void*)pj_thread_this();
+	if (current_thread != g_last_thread) {
+	    *pre++ = '!';
+	    g_last_thread = current_thread;
+	} else {
+	    *pre++ = ' ';
+	}
+    } else if (log_decor & PJ_LOG_HAS_SPACE) {
+	*pre++ = ' ';
+    }
+
+#if PJ_LOG_ENABLE_INDENT
+    if (log_decor & PJ_LOG_HAS_INDENT) {
+	indent = log_get_indent();
+	if (indent > 0) {
+	    pj_memset(pre, PJ_LOG_INDENT_CHAR, indent);
+	    pre += indent;
+	}
+    }
+#endif
+
+    len = (int)(pre - log_buffer);
+
+    /* Print the whole message to the string log_buffer. */
+    print_len = pj_ansi_vsnprintf(pre, sizeof(log_buffer)-len, format, 
+				  marker);
+    if (print_len < 0) {
+	level = 1;
+	print_len = pj_ansi_snprintf(pre, sizeof(log_buffer)-len, 
+				     "<logging error: msg too long>");
+    }
+    len = len + print_len;
+    if (len > 0 && len < (int)sizeof(log_buffer)-2) {
+	if (log_decor & PJ_LOG_HAS_CR) {
+	    log_buffer[len++] = '\r';
+	}
+	if (log_decor & PJ_LOG_HAS_NEWLINE) {
+	    log_buffer[len++] = '\n';
+	}
+	log_buffer[len] = '\0';
+    } else {
+	len = sizeof(log_buffer)-1;
+	if (log_decor & PJ_LOG_HAS_CR) {
+	    log_buffer[sizeof(log_buffer)-3] = '\r';
+	}
+	if (log_decor & PJ_LOG_HAS_NEWLINE) {
+	    log_buffer[sizeof(log_buffer)-2] = '\n';
+	}
+	log_buffer[sizeof(log_buffer)-1] = '\0';
+    }
+
+    /* It should be safe to resume logging at this point. Application can
+     * recursively call the logging function inside the callback.
+     */
+    resume_logging(&saved_level);
+
+    if (log_writer)
+	(*log_writer)(level, log_buffer, len);
+}
+
+/*
+PJ_DEF(void) pj_log_0(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 0, format, arg);
+    va_end(arg);
+}
+*/
+
+PJ_DEF(void) pj_log_1(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 1, format, arg);
+    va_end(arg);
+}
+#endif	/* PJ_LOG_MAX_LEVEL >= 1 */
+
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_DEF(void) pj_log_2(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 2, format, arg);
+    va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_DEF(void) pj_log_3(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 3, format, arg);
+    va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_DEF(void) pj_log_4(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 4, format, arg);
+    va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_DEF(void) pj_log_5(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 5, format, arg);
+    va_end(arg);
+}
+#endif
+
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_DEF(void) pj_log_6(const char *obj, const char *format, ...)
+{
+    va_list arg;
+    va_start(arg, format);
+    pj_log(obj, 6, format, arg);
+    va_end(arg);
+}
+#endif
+
diff --git a/jni/pjproject-android/pjlib/src/pj/log_writer_printk.c b/jni/pjproject-android/pjlib/src/pj/log_writer_printk.c
new file mode 100644
index 0000000..c75fb67
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/log_writer_printk.c
@@ -0,0 +1,28 @@
+/* $Id: log_writer_printk.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/log.h>
+#include <pj/os.h>
+
+PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
+{
+    PJ_CHECK_STACK();
+    printk(KERN_INFO "%s", buffer);
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/log_writer_stdout.c b/jni/pjproject-android/pjlib/src/pj/log_writer_stdout.c
new file mode 100644
index 0000000..b1e5482
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/log_writer_stdout.c
@@ -0,0 +1,57 @@
+/* $Id: log_writer_stdout.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/compat/stdfileio.h>
+
+
+static void term_set_color(int level)
+{
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+    pj_term_set_color(pj_log_get_color(level));
+#else
+    PJ_UNUSED_ARG(level);
+#endif
+}
+
+static void term_restore_color(void)
+{
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+    /* Set terminal to its default color */
+    pj_term_set_color(pj_log_get_color(77));
+#endif
+}
+
+
+PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
+{
+    PJ_CHECK_STACK();
+    PJ_UNUSED_ARG(len);
+
+    /* Copy to terminal/file. */
+    if (pj_log_get_decor() & PJ_LOG_HAS_COLOR) {
+	term_set_color(level);
+	printf("%s", buffer);
+	term_restore_color();
+    } else {
+	printf("%s", buffer);
+    }
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/log_writer_symbian_console.cpp b/jni/pjproject-android/pjlib/src/pj/log_writer_symbian_console.cpp
new file mode 100644
index 0000000..525394e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/log_writer_symbian_console.cpp
@@ -0,0 +1,44 @@
+/* $Id: log_writer_symbian_console.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/unicode.h>
+
+#include "os_symbian.h"
+#include <e32cons.h>
+
+PJ_DEF(void) pj_log_write(int level, const char *buffer, int len)
+{
+#if 0
+    wchar_t wbuffer[PJ_LOG_MAX_SIZE];
+    CConsoleBase *cons = PjSymbianOS::Instance->Console();
+
+    pj_ansi_to_unicode(buffer, len, wbuffer, PJ_ARRAY_SIZE(wbuffer));
+
+    
+    TPtrC16 aPtr((TUint16*)wbuffer, len);
+    console->Write(aPtr);
+#else
+    PJ_UNUSED_ARG(level);
+    PJ_UNUSED_ARG(buffer);
+    PJ_UNUSED_ARG(len);
+#endif
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_core_darwin.m b/jni/pjproject-android/pjlib/src/pj/os_core_darwin.m
new file mode 100644
index 0000000..f93f425
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_core_darwin.m
@@ -0,0 +1,100 @@
+/* $Id: os_core_darwin.m 3670 2011-07-20 03:00:48Z ming $ */
+/* 
+ * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include "TargetConditionals.h"
+
+#if TARGET_OS_IPHONE
+
+PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
+                       unsigned flags)
+{
+    return (*main_func)(argc, argv);
+}
+
+#else
+
+#include <pthread.h>
+#include <AppKit/AppKit.h>
+#include <CoreFoundation/CFRunLoop.h>
+#include <Foundation/Foundation.h>
+
+#define THIS_FILE   "os_core_darwin.m"
+
+typedef struct run_app_t {
+    pj_main_func_ptr  main_func;
+    int               argc;
+    char            **argv;
+    int               retval;
+} run_app_t;
+
+@interface DeadThread: NSObject { ;; }
++ (void)enterMultiThreadedMode;
++ (void)emptyThreadMethod:(id)obj;
+@end
+
+@implementation DeadThread
++ (void)enterMultiThreadedMode
+{
+    [NSThread detachNewThreadSelector:@selector(emptyThreadMethod:)
+              toTarget:[DeadThread class] withObject:nil];
+}
+
++ (void)emptyThreadMethod:(id)obj { ; }
+@end
+
+static void* main_thread(void *data)
+{
+    run_app_t *param = (run_app_t *)data;
+    
+    param->retval = (*param->main_func)(param->argc, param->argv);
+    CFRunLoopStop(CFRunLoopGetMain());
+    
+    return NULL;
+}
+
+/*
+ * pj_run_app()
+ * This function has to be called from the main thread. The purpose of
+ * this function is to initialize the application's memory pool, event
+ * loop management, and multi-threading environment.
+ */
+PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
+                       unsigned flags)
+{
+    pthread_t thread;
+    run_app_t param;
+    NSAutoreleasePool *pool;
+    
+    pool = [[NSAutoreleasePool alloc] init];
+    [NSApplication sharedApplication];
+    [DeadThread enterMultiThreadedMode];
+
+    param.argc = argc;
+    param.argv = (char **)argv;
+    param.main_func = main_func;
+    if (pthread_create(&thread, NULL, &main_thread, &param) == 0) {
+        CFRunLoopRun();
+    }
+    
+    PJ_UNUSED_ARG(pool);
+    
+    return param.retval;
+}
+
+#endif
diff --git a/jni/pjproject-android/pjlib/src/pj/os_core_linux_kernel.c b/jni/pjproject-android/pjlib/src/pj/os_core_linux_kernel.c
new file mode 100644
index 0000000..c1865ef
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_core_linux_kernel.c
@@ -0,0 +1,698 @@
+/* $Id: os_core_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/except.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/compat/high_precision.h>
+#include <pj/compat/sprintf.h>
+
+#include <linux/config.h>
+#include <linux/version.h>
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/sched.h>
+//#include <linux/tqueue.h>
+#include <linux/wait.h>
+#include <linux/signal.h>
+
+#include <asm/atomic.h>
+#include <asm/unistd.h>
+#include <asm/semaphore.h>
+
+#define THIS_FILE   "oslinuxkern"
+
+struct pj_thread_t
+{
+    /** Thread's name. */
+    char obj_name[PJ_MAX_OBJ_NAME];
+
+    /** Linux task structure for thread. */
+    struct task_struct *thread;	
+
+    /** Flags (specified in pj_thread_create) */
+    unsigned flags;
+
+    /** Task queue needed to launch thread. */
+    //struct tq_struct	tq;	
+
+    /** Semaphore needed to control thread startup. */
+    struct semaphore	startstop_sem;
+
+    /** Semaphore to suspend thread during startup. */
+    struct semaphore	suspend_sem;
+
+    /** Queue thread is waiting on. Gets initialized by
+        thread_initialize, can be used by thread itself.
+     */
+    wait_queue_head_t	queue;
+
+    /** Flag to tell thread whether to die or not.
+        When the thread receives a signal, it must check
+        the value of terminate and call thread_deinitialize and terminate
+        if set.
+     */
+    int terminate;    
+
+    /** Thread's entry. */
+    pj_thread_proc *func;
+
+    /** Argument. */
+    void *arg;
+};
+
+struct pj_atomic_t
+{
+    atomic_t atom;
+};
+
+struct pj_mutex_t
+{
+    struct semaphore sem;
+    pj_bool_t	     recursive;
+    pj_thread_t	    *owner;
+    int		     own_count;
+};
+
+struct pj_sem_t
+{
+    struct semaphore sem;
+};
+
+/*
+ * Static global variables.
+ */
+#define MAX_TLS_ID  32
+static void *tls_values[MAX_TLS_ID];
+static int tls_id;
+static long thread_tls_id;
+static spinlock_t critical_section = SPIN_LOCK_UNLOCKED;
+static unsigned long spinlock_flags;
+static pj_thread_t main_thread;
+
+/* private functions */
+//#define TRACE_(expr)	PJ_LOG(3,expr)
+#define TRACE_(x)
+
+
+/* This must be called in the context of the new thread. */
+static void thread_initialize( pj_thread_t *thread )
+{
+    TRACE_((THIS_FILE, "---new thread initializing..."));
+
+    /* Set TLS */
+    pj_thread_local_set(thread_tls_id, thread);
+
+    /* fill in thread structure */
+    thread->thread = current;
+    pj_assert(thread->thread != NULL);
+
+    /* set signal mask to what we want to respond */
+    siginitsetinv(&current->blocked, 
+		  sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM));
+
+    /* initialise wait queue */
+    init_waitqueue_head(&thread->queue);
+
+    /* initialise termination flag */
+    thread->terminate = 0;
+
+    /* set name of this process (making sure obj_name is null 
+     * terminated first) 
+     */
+    thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    sprintf(current->comm, thread->obj_name);
+        
+    /* tell the creator that we are ready and let him continue */
+    up(&thread->startstop_sem);	
+}
+
+/* cleanup of thread. Called by the exiting thread. */
+static void thread_deinitialize(pj_thread_t *thread)
+{
+    /* we are terminating */
+
+    /* lock the kernel, the exit will unlock it */
+    thread->thread = NULL;
+    mb();
+
+    /* notify the stop_kthread() routine that we are terminating. */
+    up(&thread->startstop_sem);
+
+    /* the kernel_thread that called clone() does a do_exit here. */
+
+    /* there is no race here between execution of the "killer" and 
+       real termination of the thread (race window between up and do_exit), 
+       since both the thread and the "killer" function are running with 
+       the kernel lock held.
+       The kernel lock will be freed after the thread exited, so the code
+       is really not executed anymore as soon as the unload functions gets
+       the kernel lock back.
+       The init process may not have made the cleanup of the process here,
+       but the cleanup can be done safely with the module unloaded.
+    */
+
+}
+
+static int thread_proc(void *arg)
+{
+    pj_thread_t *thread = arg;
+
+    TRACE_((THIS_FILE, "---new thread starting!"));
+
+    /* Initialize thread. */
+    thread_initialize( thread );
+
+    /* Wait if created suspended. */
+    if (thread->flags & PJ_THREAD_SUSPENDED) {
+	TRACE_((THIS_FILE, "---new thread suspended..."));
+	down(&thread->suspend_sem);
+    }
+
+    TRACE_((THIS_FILE, "---new thread running..."));
+
+    pj_assert(thread->func != NULL);
+
+    /* Call thread's entry. */
+    (*thread->func)(thread->arg);
+
+    TRACE_((THIS_FILE, "---thread exiting..."));
+
+    /* Cleanup thread. */
+    thread_deinitialize(thread);
+
+    return 0;
+}
+
+/* The very task entry. */
+static void kthread_launcher(void *arg)
+{
+    TRACE_((THIS_FILE, "...launching thread!..."));
+    kernel_thread(&thread_proc, arg, 0);
+}
+
+PJ_DEF(pj_status_t) pj_init(void)
+{
+    pj_status_t rc;
+
+    PJ_LOG(5, ("pj_init", "Initializing PJ Library.."));
+
+    rc = pj_thread_init();
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    /* Initialize exception ID for the pool. 
+     * Must do so after critical section is configured.
+     */
+    rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+    if (rc != PJ_SUCCESS)
+        return rc;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+    return 1;
+}
+
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+					 pj_thread_desc desc,
+					 pj_thread_t **ptr_thread)
+{
+    char stack_ptr;
+    pj_thread_t *thread = (pj_thread_t *)desc;
+    pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+    /* Size sanity check. */
+    if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+	pj_assert(!"Not enough pj_thread_desc size!");
+	return PJ_EBUG;
+    }
+
+    /* If a thread descriptor has been registered before, just return it. */
+    if (pj_thread_local_get (thread_tls_id) != 0) {
+	// 2006-02-26 bennylp:
+	//  This wouldn't work in all cases!.
+	//  If thread is created by external module (e.g. sound thread),
+	//  thread may be reused while the pool used for the thread descriptor
+	//  has been deleted by application.
+	//*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+        //return PJ_SUCCESS;
+    }
+
+    /* Initialize and set the thread entry. */
+    pj_bzero(desc, sizeof(struct pj_thread_t));
+
+    if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+	pj_sprintf(thread->obj_name, cstr_thread_name, thread->thread);
+    else
+	pj_snprintf(thread->obj_name, sizeof(thread->obj_name), 
+		    "thr%p", (void*)thread->thread);
+    
+    /* Initialize. */
+    thread_initialize(thread);
+
+    /* Eat semaphore. */
+    down(&thread->startstop_sem);
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    thread->stk_start = &stack_ptr;
+    thread->stk_size = 0xFFFFFFFFUL;
+    thread->stk_max_usage = 0;
+#else
+    stack_ptr = '\0';
+#endif
+
+    *ptr_thread = thread;
+    return PJ_SUCCESS;
+}
+
+
+pj_status_t pj_thread_init(void)
+{
+    pj_status_t rc;
+    pj_thread_t *dummy;
+    
+    rc = pj_thread_local_alloc(&thread_tls_id);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    return pj_thread_register("pjlib-main", (long*)&main_thread, &dummy);
+}
+
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, const char *thread_name,
+				      pj_thread_proc *proc, void *arg,
+				      pj_size_t stack_size, unsigned flags,
+				      pj_thread_t **ptr_thread)
+{
+    pj_thread_t *thread;
+
+    TRACE_((THIS_FILE, "pj_thread_create()"));
+    
+    PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
+
+    thread = pj_pool_zalloc(pool, sizeof(pj_thread_t));
+    if (!thread)
+	return PJ_ENOMEM;
+
+    PJ_UNUSED_ARG(stack_size);
+
+    /* Thread name. */
+    if (!thread_name) 
+	thread_name = "thr%p";
+    
+    if (strchr(thread_name, '%')) {
+	pj_snprintf(thread->obj_name, PJ_MAX_OBJ_NAME, thread_name, thread);
+    } else {
+	strncpy(thread->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+	thread->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+    
+    /* Init thread's semaphore. */
+    TRACE_((THIS_FILE, "...init semaphores..."));
+    init_MUTEX_LOCKED(&thread->startstop_sem);
+    init_MUTEX_LOCKED(&thread->suspend_sem);
+
+    thread->flags = flags;
+
+    if ((flags & PJ_THREAD_SUSPENDED) == 0) {
+	up(&thread->suspend_sem);
+    }
+
+    /* Store the functions and argument. */
+    thread->func = proc;
+    thread->arg = arg;
+    
+    /* Save return value. */
+    *ptr_thread = thread;
+    
+    /* Create the new thread by running a task through keventd. */
+
+#if 0
+    /* Initialize the task queue struct. */
+    thread->tq.sync = 0;
+    INIT_LIST_HEAD(&thread->tq.list);
+    thread->tq.routine = kthread_launcher;
+    thread->tq.data = thread;
+
+    /* and schedule it for execution. */
+    schedule_task(&thread->tq);
+#endif
+    kthread_launcher(thread);
+
+    /* Wait until thread has reached the setup_thread routine. */
+    TRACE_((THIS_FILE, "...wait for the new thread..."));
+    down(&thread->startstop_sem);
+
+    TRACE_((THIS_FILE, "...main thread resumed..."));
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *thread)
+{
+    return thread->obj_name;
+}
+
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *thread)
+{
+    up(&thread->suspend_sem);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+    return (pj_thread_t*)pj_thread_local_get(thread_tls_id);
+}
+
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+    TRACE_((THIS_FILE, "pj_thread_join()"));
+    down(&p->startstop_sem);
+    TRACE_((THIS_FILE, "  joined!"));
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *thread)
+{
+    PJ_ASSERT_RETURN(thread != NULL, PJ_EINVALIDOP);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+    pj_highprec_t ticks;
+    pj_thread_t *thread = pj_thread_this();
+
+    PJ_ASSERT_RETURN(thread != NULL, PJ_EBUG);
+    
+    /* Use high precision calculation to make sure we don't
+     * crop values:
+     *
+     *	ticks = HZ * msec / 1000
+     */
+    ticks = HZ;
+    pj_highprec_mul(ticks, msec);
+    pj_highprec_div(ticks, 1000);
+
+    TRACE_((THIS_FILE, "this thread will sleep for %u ticks", ticks));
+    interruptible_sleep_on_timeout( &thread->queue, ticks);
+    return PJ_SUCCESS;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, 
+				      pj_atomic_value_t value,
+				      pj_atomic_t **ptr_var)
+{
+    pj_atomic_t *t = pj_pool_calloc(pool, 1, sizeof(pj_atomic_t));
+    if (!t) return PJ_ENOMEM;
+
+    atomic_set(&t->atom, value);
+    *ptr_var = t;
+    
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+{
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_atomic_set(pj_atomic_t *var, pj_atomic_value_t value)
+{
+    atomic_set(&var->atom, value);
+}
+
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *var)
+{
+    return atomic_read(&var->atom);
+}
+
+PJ_DEF(void) pj_atomic_inc(pj_atomic_t *var)
+{
+    atomic_inc(&var->atom);
+}
+
+PJ_DEF(void) pj_atomic_dec(pj_atomic_t *var)
+{
+    atomic_dec(&var->atom);
+}
+
+PJ_DEF(void) pj_atomic_add( pj_atomic_t *var, pj_atomic_value_t value )
+{
+    atomic_add(value, &var->atom);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
+{
+    if (tls_id >= MAX_TLS_ID)
+	return PJ_ETOOMANY;
+    
+    *index = tls_id++;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+    pj_assert(index >= 0 && index < MAX_TLS_ID);
+}
+
+PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
+{
+    pj_assert(index >= 0 && index < MAX_TLS_ID);
+    tls_values[index] = value;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+    pj_assert(index >= 0 && index < MAX_TLS_ID);
+    return tls_values[index];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+    spin_lock_irqsave(&critical_section, spinlock_flags);
+}
+
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+    spin_unlock_irqrestore(&critical_section, spinlock_flags);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, 
+				     const char *name, 
+				     int type,
+				     pj_mutex_t **ptr_mutex)
+{
+    pj_mutex_t *mutex;
+    
+    PJ_UNUSED_ARG(name);
+
+    mutex = pj_pool_alloc(pool, sizeof(pj_mutex_t));
+    if (!mutex)
+	return PJ_ENOMEM;
+
+    init_MUTEX(&mutex->sem);
+
+    mutex->recursive = (type == PJ_MUTEX_RECURSE);
+    mutex->owner = NULL;
+    mutex->own_count = 0;
+    
+    /* Done. */
+    *ptr_mutex = mutex;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, const char *name,
+					    pj_mutex_t **mutex )
+{
+    return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+					       const char *name,
+					       pj_mutex_t **mutex )
+{
+    return pj_mutex_create( pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    if (mutex->recursive) {
+	pj_thread_t *this_thread = pj_thread_this();
+	if (mutex->owner == this_thread) {
+	    ++mutex->own_count;
+	} else {
+	    down(&mutex->sem);
+	    pj_assert(mutex->own_count == 0);
+	    mutex->owner = this_thread;
+	    mutex->own_count = 1;
+	}
+    } else {
+	down(&mutex->sem);
+    }
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+    long rc;
+
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    if (mutex->recursive) {
+	pj_thread_t *this_thread = pj_thread_this();
+	if (mutex->owner == this_thread) {
+	    ++mutex->own_count;
+	} else {
+	    rc = down_interruptible(&mutex->sem);
+	    if (rc != 0)
+		return PJ_RETURN_OS_ERROR(-rc);
+	    pj_assert(mutex->own_count == 0);
+	    mutex->owner = this_thread;
+	    mutex->own_count = 1;
+	}
+    } else {
+	int rc = down_trylock(&mutex->sem);
+	if (rc != 0)
+	    return PJ_RETURN_OS_ERROR(-rc);
+    }
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    if (mutex->recursive) {
+	pj_thread_t *this_thread = pj_thread_this();
+	if (mutex->owner == this_thread) {
+	    pj_assert(mutex->own_count > 0);
+	    --mutex->own_count;
+	    if (mutex->own_count == 0) {
+		mutex->owner = NULL;
+		up(&mutex->sem);
+	    }
+	} else {
+	    pj_assert(!"Not owner!");
+	    return PJ_EINVALIDOP;
+	}
+    } else {
+	up(&mutex->sem);
+    }
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+    PJ_ASSERT_RETURN(mutex != NULL, PJ_EINVAL);
+
+    return PJ_SUCCESS;
+}
+
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+    if (mutex->recursive)
+	return mutex->owner == pj_thread_this();
+    else
+	return 1;
+}
+#endif	/* PJ_DEBUG */
+
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+PJ_DEF(pj_status_t) pj_sem_create(  pj_pool_t *pool, 
+                                    const char *name,
+				    unsigned initial, 
+                                    unsigned max,
+				    pj_sem_t **sem)
+{
+    pj_sem_t *sem;
+
+    PJ_UNUSED_ARG(max);
+
+    PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+    
+    sem = pj_pool_alloc(pool, sizeof(pj_sem_t));
+    sema_init(&sem->sem, initial);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+    PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+    down(&sem->sem);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+    int rc;
+
+    PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+    rc = down_trylock(&sem->sem);
+    if (rc != 0) {
+	return PJ_RETURN_OS_ERROR(-rc);
+    } else {
+	return PJ_SUCCESS;
+    }
+}
+
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+    PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+    up(&sem->sem);
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+    PJ_ASSERT_RETURN(pool && sem, PJ_EINVAL);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJ_HAS_SEMAPHORE */
+
+
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_core_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/os_core_symbian.cpp
new file mode 100644
index 0000000..f9f5d7d
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_core_symbian.cpp
@@ -0,0 +1,1063 @@
+/* $Id: os_core_symbian.cpp 3999 2012-03-30 07:10:13Z bennylp $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/except.h>
+#include <pj/errno.h>
+
+#include "os_symbian.h"
+
+
+#define PJ_MAX_TLS	    32
+#define DUMMY_MUTEX	    ((pj_mutex_t*)101)
+#define DUMMY_SEMAPHORE	    ((pj_sem_t*)102)
+#define THIS_FILE	    "os_core_symbian.c"
+
+/* Default message slot number for RSocketServ::Connect().
+ * Increase it to 32 from the default 8 (KESockDefaultMessageSlots)
+ */
+#ifndef PJ_SYMBIAN_SOCK_MSG_SLOTS
+#  define PJ_SYMBIAN_SOCK_MSG_SLOTS  32
+#endif
+
+/*
+ * Note:
+ *
+ * The Symbian implementation does not support threading!
+ */ 
+
+struct pj_thread_t
+{
+    char	    obj_name[PJ_MAX_OBJ_NAME];
+    void	   *tls_values[PJ_MAX_TLS];
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    pj_uint32_t	    stk_size;
+    pj_uint32_t	    stk_max_usage;
+    char	   *stk_start;
+    const char	   *caller_file;
+    int		    caller_line;
+#endif
+
+} main_thread;
+
+struct pj_atomic_t
+{
+    pj_atomic_value_t	value;
+};
+
+struct pj_sem_t
+{
+    int value;
+    int max;
+};
+
+/* Flag and reference counter for PJLIB instance */
+static int initialized;
+
+/* Flags to indicate which TLS variables have been used */
+static int tls_vars[PJ_MAX_TLS];
+
+/* atexit handlers */
+static unsigned atexit_count;
+static void (*atexit_func[32])(void);
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// CPjTimeoutTimer implementation
+//
+
+CPjTimeoutTimer::CPjTimeoutTimer()
+: CActive(PJ_SYMBIAN_TIMER_PRIORITY), hasTimedOut_(PJ_FALSE)
+{
+}
+
+CPjTimeoutTimer::~CPjTimeoutTimer()
+{
+    Cancel();
+    timer_.Close();
+}
+
+void CPjTimeoutTimer::ConstructL()
+{
+    hasTimedOut_ = PJ_FALSE;
+    timer_.CreateLocal();
+    CActiveScheduler::Add(this);
+}
+
+CPjTimeoutTimer *CPjTimeoutTimer::NewL()
+{
+    CPjTimeoutTimer *self = new CPjTimeoutTimer;
+    CleanupStack::PushL(self);
+
+    self->ConstructL();
+
+    CleanupStack::Pop(self);
+    return self;
+
+}
+
+void CPjTimeoutTimer::StartTimer(TUint miliSeconds)
+{
+    Cancel();
+
+    hasTimedOut_ = PJ_FALSE;
+    timer_.After(iStatus, miliSeconds * 1000);
+    SetActive();
+}
+
+bool CPjTimeoutTimer::HasTimedOut() const
+{
+    return hasTimedOut_ != 0;
+}
+
+void CPjTimeoutTimer::RunL()
+{
+    hasTimedOut_ = PJ_TRUE;
+}
+
+void CPjTimeoutTimer::DoCancel()
+{
+    timer_.Cancel();
+}
+
+TInt CPjTimeoutTimer::RunError(TInt aError)
+{
+    PJ_UNUSED_ARG(aError);
+    return KErrNone;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// PjSymbianOS implementation
+//
+
+PjSymbianOS::PjSymbianOS()
+: isConnectionUp_(false),
+  isSocketServInitialized_(false), isResolverInitialized_(false),
+  console_(NULL), selectTimeoutTimer_(NULL),
+  appSocketServ_(NULL), appConnection_(NULL), appHostResolver_(NULL),
+  appHostResolver6_(NULL)
+{
+}
+
+// Set parameters
+void PjSymbianOS::SetParameters(pj_symbianos_params *params) 
+{
+    appSocketServ_ = (RSocketServ*) params->rsocketserv;
+    appConnection_ = (RConnection*) params->rconnection;
+    appHostResolver_ = (RHostResolver*) params->rhostresolver;
+    appHostResolver6_ = (RHostResolver*) params->rhostresolver6;
+}
+
+// Get PjSymbianOS instance
+PjSymbianOS *PjSymbianOS::Instance()
+{
+    static PjSymbianOS instance_;
+    return &instance_;
+}
+
+
+// Initialize
+TInt PjSymbianOS::Initialize()
+{
+    TInt err;
+
+    selectTimeoutTimer_ = CPjTimeoutTimer::NewL();
+
+#if 0
+    pj_assert(console_ == NULL);
+    TRAPD(err, console_ = Console::NewL(_L("PJLIB"), 
+				        TSize(KConsFullScreen,KConsFullScreen)));
+    return err;
+#endif
+
+    /* Only create RSocketServ if application doesn't specify it
+     * in the parameters
+     */
+    if (!isSocketServInitialized_ && appSocketServ_ == NULL) {
+	err = socketServ_.Connect(PJ_SYMBIAN_SOCK_MSG_SLOTS);
+	if (err != KErrNone)
+	    goto on_error;
+
+	isSocketServInitialized_ = true;
+    }
+
+    if (!isResolverInitialized_) {
+    	if (appHostResolver_ == NULL) {
+    	    if (Connection())
+    	    	err = hostResolver_.Open(SocketServ(), KAfInet, KSockStream,
+    	    			     	 *Connection());
+    	    else
+	    	err = hostResolver_.Open(SocketServ(), KAfInet, KSockStream);
+    	
+	    if (err != KErrNone)
+	    	goto on_error;
+    	}
+    	
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
+    	if (appHostResolver6_ == NULL) {
+    	    if (Connection())
+    	    	err = hostResolver6_.Open(SocketServ(), KAfInet6, KSockStream,
+    	    			     	  *Connection());
+    	    else
+	    	err = hostResolver6_.Open(SocketServ(), KAfInet6, KSockStream);
+    	
+	    if (err != KErrNone)
+	    	goto on_error;
+    	}
+#endif
+    	
+    	
+	isResolverInitialized_ = true;
+    }
+
+    isConnectionUp_ = true;
+    
+    return KErrNone;
+
+on_error:
+    Shutdown();
+    return err;
+}
+
+// Shutdown
+void PjSymbianOS::Shutdown()
+{
+    isConnectionUp_ = false;
+    
+    if (isResolverInitialized_) {
+		hostResolver_.Close();
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
+    	hostResolver6_.Close();
+#endif
+    	isResolverInitialized_ = false;
+    }
+
+    if (isSocketServInitialized_) {
+	socketServ_.Close();
+	isSocketServInitialized_ = false;
+    }
+
+    delete console_;
+    console_ = NULL;
+
+    delete selectTimeoutTimer_;
+    selectTimeoutTimer_ = NULL;
+    
+    appSocketServ_ = NULL;
+    appConnection_ = NULL;
+    appHostResolver_ = NULL;
+    appHostResolver6_ = NULL;
+}
+
+// Convert to Unicode
+TInt PjSymbianOS::ConvertToUnicode(TDes16 &aUnicode, const TDesC8 &aForeign)
+{
+#if 0
+    pj_assert(conv_ != NULL);
+    return conv_->ConvertToUnicode(aUnicode, aForeign, convToUnicodeState_);
+#else
+    return CnvUtfConverter::ConvertToUnicodeFromUtf8(aUnicode, aForeign);
+#endif
+}
+
+// Convert from Unicode
+TInt PjSymbianOS::ConvertFromUnicode(TDes8 &aForeign, const TDesC16 &aUnicode)
+{
+#if 0
+    pj_assert(conv_ != NULL);
+    return conv_->ConvertFromUnicode(aForeign, aUnicode, convToAnsiState_);
+#else
+    return CnvUtfConverter::ConvertFromUnicodeToUtf8(aForeign, aUnicode);
+#endif
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// PJLIB os.h implementation
+//
+
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+    return 0;
+}
+
+
+/* Set Symbian specific parameters */
+PJ_DEF(pj_status_t) pj_symbianos_set_params(pj_symbianos_params *prm) 
+{
+    PJ_ASSERT_RETURN(prm != NULL, PJ_EINVAL);
+    PjSymbianOS::Instance()->SetParameters(prm);
+    return PJ_SUCCESS;
+}
+
+
+/* Set connection status */
+PJ_DEF(void) pj_symbianos_set_connection_status(pj_bool_t up)
+{
+    PjSymbianOS::Instance()->SetConnectionStatus(up != 0);
+}
+
+
+/*
+ * pj_init(void).
+ * Init PJLIB!
+ */
+PJ_DEF(pj_status_t) pj_init(void)
+{
+	char stack_ptr;
+    pj_status_t status;
+    
+    /* Check if PJLIB have been initialized */
+    if (initialized) {
+	++initialized;
+	return PJ_SUCCESS;
+    }
+
+    pj_ansi_strcpy(main_thread.obj_name, "pjthread");
+
+    // Init main thread
+    pj_memset(&main_thread, 0, sizeof(main_thread));
+
+    // Initialize PjSymbianOS instance
+    PjSymbianOS *os = PjSymbianOS::Instance();
+
+    PJ_LOG(4,(THIS_FILE, "Initializing PJLIB for Symbian OS.."));
+
+    TInt err; 
+    err = os->Initialize();
+    if (err != KErrNone)
+    	return PJ_RETURN_OS_ERROR(err);
+
+    /* Init logging */
+    pj_log_init();
+
+    /* Initialize exception ID for the pool. 
+     * Must do so after critical section is configured.
+     */ 
+    status = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+    if (status != PJ_SUCCESS)
+        goto on_error;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    main_thread.stk_start = &stack_ptr;
+    main_thread.stk_size = 0xFFFFFFFFUL;
+    main_thread.stk_max_usage = 0;
+#else
+    stack_ptr = '\0';
+#endif
+
+    /* Flag PJLIB as initialized */
+    ++initialized;
+    pj_assert(initialized == 1);
+
+    PJ_LOG(5,(THIS_FILE, "PJLIB initialized."));
+    return PJ_SUCCESS;
+
+on_error:
+    pj_shutdown();
+    return PJ_RETURN_OS_ERROR(err);
+}
+
+
+PJ_DEF(pj_status_t) pj_atexit(pj_exit_callback func)
+{
+    if (atexit_count >= PJ_ARRAY_SIZE(atexit_func))
+	return PJ_ETOOMANY;
+
+    atexit_func[atexit_count++] = func;
+    return PJ_SUCCESS;
+}
+
+
+
+PJ_DEF(void) pj_shutdown(void)
+{
+    /* Only perform shutdown operation when 'initialized' reaches zero */
+    pj_assert(initialized > 0);
+    if (--initialized != 0)
+	return;
+
+    /* Call atexit() functions */
+    while (atexit_count > 0) {
+	(*atexit_func[atexit_count-1])();
+	--atexit_count;
+    }
+
+    /* Free exception ID */
+    if (PJ_NO_MEMORY_EXCEPTION != -1) {
+	pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION);
+	PJ_NO_MEMORY_EXCEPTION = -1;
+    }
+
+    /* Clear static variables */
+    pj_errno_clear_handlers();
+
+    PjSymbianOS *os = PjSymbianOS::Instance();
+    os->Shutdown();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+class CPollTimeoutTimer : public CActive 
+{
+public:
+    static CPollTimeoutTimer* NewL(int msec, TInt prio);
+    ~CPollTimeoutTimer();
+    
+    virtual void RunL();
+    virtual void DoCancel();
+
+private:	
+    RTimer	     rtimer_;
+    
+    explicit CPollTimeoutTimer(TInt prio);
+    void ConstructL(int msec);
+};
+
+CPollTimeoutTimer::CPollTimeoutTimer(TInt prio)
+: CActive(prio)
+{
+}
+
+
+CPollTimeoutTimer::~CPollTimeoutTimer() 
+{
+    rtimer_.Close();
+}
+
+void CPollTimeoutTimer::ConstructL(int msec) 
+{
+    rtimer_.CreateLocal();
+    CActiveScheduler::Add(this);
+    rtimer_.After(iStatus, msec*1000);
+    SetActive();
+}
+
+CPollTimeoutTimer* CPollTimeoutTimer::NewL(int msec, TInt prio) 
+{
+    CPollTimeoutTimer *self = new CPollTimeoutTimer(prio);
+    CleanupStack::PushL(self);
+    self->ConstructL(msec);    
+    CleanupStack::Pop(self);
+
+    return self;
+}
+
+void CPollTimeoutTimer::RunL() 
+{
+}
+
+void CPollTimeoutTimer::DoCancel() 
+{
+     rtimer_.Cancel();
+}
+
+
+/*
+ * Wait the completion of any Symbian active objects. 
+ */
+PJ_DEF(pj_bool_t) pj_symbianos_poll(int priority, int ms_timeout)
+{
+    CPollTimeoutTimer *timer = NULL;
+    
+    if (priority==-1)
+    	priority = EPriorityNull;
+    
+    if (ms_timeout >= 0) {
+    	timer = CPollTimeoutTimer::NewL(ms_timeout, priority);
+    }
+    
+    PjSymbianOS::Instance()->WaitForActiveObjects(priority);
+    
+    if (timer) {
+        bool timer_is_active = timer->IsActive();
+    
+	timer->Cancel();
+        
+        delete timer;
+        
+    	return timer_is_active ? PJ_TRUE : PJ_FALSE;
+    	
+    } else {
+    	return PJ_TRUE;
+    }
+}
+
+
+/*
+ * pj_thread_is_registered()
+ */
+PJ_DEF(pj_bool_t) pj_thread_is_registered(void)
+{
+    return PJ_FALSE;
+}
+
+
+/*
+ * Get thread priority value for the thread.
+ */
+PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread)
+{
+    PJ_UNUSED_ARG(thread);
+    return 1;
+}
+
+
+/*
+ * Set the thread priority.
+ */
+PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread,  int prio)
+{
+    PJ_UNUSED_ARG(thread);
+    PJ_UNUSED_ARG(prio);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the lowest priority value available on this system.
+ */
+PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread)
+{
+    PJ_UNUSED_ARG(thread);
+    return 1;
+}
+
+
+/*
+ * Get the highest priority value available on this system.
+ */
+PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread)
+{
+    PJ_UNUSED_ARG(thread);
+    return 1;
+}
+
+
+/*
+ * pj_thread_get_os_handle()
+ */
+PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) 
+{
+    PJ_UNUSED_ARG(thread);
+    return NULL;
+}
+
+/*
+ * pj_thread_register(..)
+ */
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+					 pj_thread_desc desc,
+                                         pj_thread_t **thread_ptr)
+{
+    PJ_UNUSED_ARG(cstr_thread_name);
+    PJ_UNUSED_ARG(desc);
+    PJ_UNUSED_ARG(thread_ptr);
+    return PJ_EINVALIDOP;
+}
+
+
+/*
+ * pj_thread_create(...)
+ */
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, 
+				      const char *thread_name,
+				      pj_thread_proc *proc, 
+				      void *arg,
+				      pj_size_t stack_size, 
+				      unsigned flags,
+				      pj_thread_t **ptr_thread)
+{
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(thread_name);
+    PJ_UNUSED_ARG(proc);
+    PJ_UNUSED_ARG(arg);
+    PJ_UNUSED_ARG(stack_size);
+    PJ_UNUSED_ARG(flags);
+    PJ_UNUSED_ARG(ptr_thread);
+
+    /* Sorry mate, we don't support threading */
+    return PJ_ENOTSUP;
+}
+
+/*
+ * pj_thread-get_name()
+ */
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+    pj_assert(p == &main_thread);
+    return p->obj_name;
+}
+
+/*
+ * pj_thread_resume()
+ */
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+    PJ_UNUSED_ARG(p);
+    return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_thread_this()
+ */
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+    return &main_thread;
+}
+
+/*
+ * pj_thread_join()
+ */
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *rec)
+{
+    PJ_UNUSED_ARG(rec);
+    return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_thread_destroy()
+ */
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *rec)
+{
+    PJ_UNUSED_ARG(rec);
+    return PJ_EINVALIDOP;
+}
+
+/*
+ * pj_thread_sleep()
+ */
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+    User::After(msec*1000);
+
+    return PJ_SUCCESS;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_thread_local_alloc()
+ */
+
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
+{
+    unsigned i;
+
+    /* Find unused TLS variable */
+    for (i=0; i<PJ_ARRAY_SIZE(tls_vars); ++i) {
+	if (tls_vars[i] == 0)
+	    break;
+    }
+
+    if (i == PJ_ARRAY_SIZE(tls_vars))
+	return PJ_ETOOMANY;
+
+    tls_vars[i] = 1;
+    *index = i;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_local_free()
+ */
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+    PJ_ASSERT_ON_FAIL(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) &&
+		     tls_vars[index] != 0, return);
+
+    tls_vars[index] = 0;
+}
+
+
+/*
+ * pj_thread_local_set()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
+{
+    pj_thread_t *rec = pj_thread_this();
+
+    PJ_ASSERT_RETURN(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) &&
+		     tls_vars[index] != 0, PJ_EINVAL);
+
+    rec->tls_values[index] = value;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_local_get()
+ */
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+    pj_thread_t *rec = pj_thread_this();
+
+    PJ_ASSERT_RETURN(index >= 0 && index < (int)PJ_ARRAY_SIZE(tls_vars) &&
+		     tls_vars[index] != 0, NULL);
+
+    return rec->tls_values[index];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Create atomic variable.
+ */
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, 
+				      pj_atomic_value_t initial,
+				      pj_atomic_t **atomic )
+{
+    *atomic = (pj_atomic_t*)pj_pool_alloc(pool, sizeof(struct pj_atomic_t));
+    (*atomic)->value = initial;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy atomic variable.
+ */
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+{
+    PJ_UNUSED_ARG(atomic_var);
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Set the value of an atomic type, and return the previous value.
+ */
+PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, 
+			    pj_atomic_value_t value)
+{
+    atomic_var->value = value;
+}
+
+
+/*
+ * Get the value of an atomic type.
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+    return atomic_var->value;
+}
+
+
+/*
+ * Increment the value of an atomic type.
+ */
+PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+{
+    ++atomic_var->value;
+}
+
+
+/*
+ * Increment the value of an atomic type and get the result.
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
+{
+    return ++atomic_var->value;
+}
+
+
+/*
+ * Decrement the value of an atomic type.
+ */
+PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+{
+    --atomic_var->value;
+}	
+
+
+/*
+ * Decrement the value of an atomic type and get the result.
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
+{
+    return --atomic_var->value;
+}
+
+
+/*
+ * Add a value to an atomic type.
+ */
+PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
+			    pj_atomic_value_t value)
+{
+    atomic_var->value += value;
+}
+
+
+/*
+ * Add a value to an atomic type and get the result.
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
+			                         pj_atomic_value_t value)
+{
+    atomic_var->value += value;
+    return atomic_var->value;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_mutex_create( pj_pool_t *pool, 
+                                     const char *name,
+				     int type, 
+                                     pj_mutex_t **mutex)
+{
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(name);
+    PJ_UNUSED_ARG(type);
+
+    *mutex = DUMMY_MUTEX;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_create_simple()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, 
+                                            const char *name,
+					    pj_mutex_t **mutex )
+{
+    return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+					       const char *name,
+					       pj_mutex_t **mutex )
+{
+    return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+
+/*
+ * pj_mutex_lock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+    pj_assert(mutex == DUMMY_MUTEX);
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_trylock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+    pj_assert(mutex == DUMMY_MUTEX);
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_unlock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+    pj_assert(mutex == DUMMY_MUTEX);
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_destroy()
+ */
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+    pj_assert(mutex == DUMMY_MUTEX);
+    return PJ_SUCCESS;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/*
+ * RW Mutex
+ */
+#include "os_rwmutex.c"
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Enter critical section.
+ */
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+    /* Nothing to do */
+}
+
+
+/*
+ * Leave critical section.
+ */
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+    /* Nothing to do */
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Create semaphore.
+ */
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, 
+                                   const char *name,
+				   unsigned initial, 
+                                   unsigned max,
+				   pj_sem_t **p_sem)
+{
+    pj_sem_t *sem;
+ 
+    PJ_UNUSED_ARG(name);
+
+    sem = (pj_sem_t*) pj_pool_zalloc(pool, sizeof(pj_sem_t));
+    sem->value = initial;
+    sem->max = max;
+
+    *p_sem = sem;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Wait for semaphore.
+ */
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+    if (sem->value > 0) {
+	sem->value--;
+	return PJ_SUCCESS;
+    } else {
+	pj_assert(!"Unexpected!");
+	return PJ_EINVALIDOP;
+    }
+}
+
+
+/*
+ * Try wait for semaphore.
+ */
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+    if (sem->value > 0) {
+	sem->value--;
+	return PJ_SUCCESS;
+    } else {
+	pj_assert(!"Unexpected!");
+	return PJ_EINVALIDOP;
+    }
+}
+
+
+/*
+ * Release semaphore.
+ */
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+    sem->value++;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Destroy semaphore.
+ */
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+    PJ_UNUSED_ARG(sem);
+    return PJ_SUCCESS;
+}
+
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
+/*
+ * The implementation of stack checking. 
+ */
+PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
+{
+    char stk_ptr;
+    pj_uint32_t usage;
+    pj_thread_t *thread = pj_thread_this();
+
+    pj_assert(thread);
+
+    /* Calculate current usage. */
+    usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
+		thread->stk_start - &stk_ptr;
+
+    /* Assert if stack usage is dangerously high. */
+    pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
+
+    /* Keep statistic. */
+    if (usage > thread->stk_max_usage) {
+	thread->stk_max_usage = usage;
+	thread->caller_file = file;
+	thread->caller_line = line;
+    }
+}
+
+/*
+ * Get maximum stack usage statistic. 
+ */
+PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
+{
+    return thread->stk_max_usage;
+}
+
+/*
+ * Dump thread stack status. 
+ */
+PJ_DEF(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread,
+					     const char **file,
+					     int *line)
+{
+    pj_assert(thread);
+
+    *file = thread->caller_file;
+    *line = thread->caller_line;
+    return 0;
+}
+
+#endif	/* PJ_OS_HAS_CHECK_STACK */
+
+/*
+ * pj_run_app()
+ */
+PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
+                       unsigned flags)
+{
+    return (*main_func)(argc, argv);
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/os_core_unix.c b/jni/pjproject-android/pjlib/src/pj/os_core_unix.c
new file mode 100644
index 0000000..ff8ba68
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_core_unix.c
@@ -0,0 +1,1932 @@
+/* $Id: os_core_unix.c 4359 2013-02-21 11:18:36Z bennylp $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+/*
+ * Contributors:
+ * - Thanks for Zetron, Inc. (Phil Torre, ptorre@zetron.com) for donating
+ *   the RTEMS port.
+ */
+#ifndef _GNU_SOURCE
+#   define _GNU_SOURCE
+#endif
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/except.h>
+#include <pj/errno.h>
+
+#if defined(PJ_HAS_SEMAPHORE_H) && PJ_HAS_SEMAPHORE_H != 0
+#  include <semaphore.h>
+#endif
+
+#include <unistd.h>	    // getpid()
+#include <errno.h>	    // errno
+
+#include <pthread.h>
+
+#define THIS_FILE   "os_core_unix.c"
+
+#define SIGNATURE1  0xDEAFBEEF
+#define SIGNATURE2  0xDEADC0DE
+
+struct pj_thread_t
+{
+    char	    obj_name[PJ_MAX_OBJ_NAME];
+    pthread_t	    thread;
+    pj_thread_proc *proc;
+    void	   *arg;
+    pj_uint32_t	    signature1;
+    pj_uint32_t	    signature2;
+
+    pj_mutex_t	   *suspended_mutex;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    pj_uint32_t	    stk_size;
+    pj_uint32_t	    stk_max_usage;
+    char	   *stk_start;
+    const char	   *caller_file;
+    int		    caller_line;
+#endif
+};
+
+struct pj_atomic_t
+{
+    pj_mutex_t	       *mutex;
+    pj_atomic_value_t	value;
+};
+
+struct pj_mutex_t
+{
+    pthread_mutex_t     mutex;
+    char		obj_name[PJ_MAX_OBJ_NAME];
+#if PJ_DEBUG
+    int		        nesting_level;
+    pj_thread_t	       *owner;
+    char		owner_name[PJ_MAX_OBJ_NAME];
+#endif
+};
+
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+struct pj_sem_t
+{
+    sem_t	       *sem;
+    char		obj_name[PJ_MAX_OBJ_NAME];
+};
+#endif /* PJ_HAS_SEMAPHORE */
+
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+struct pj_event_t
+{
+    enum event_state {
+	EV_STATE_OFF,
+	EV_STATE_SET,
+	EV_STATE_PULSED
+    } state;
+
+    pj_mutex_t		mutex;
+    pthread_cond_t	cond;
+
+    pj_bool_t		auto_reset;
+    unsigned		threads_waiting;
+    unsigned		threads_to_release;
+};
+#endif	/* PJ_HAS_EVENT_OBJ */
+
+
+/*
+ * Flag and reference counter for PJLIB instance.
+ */
+static int initialized;
+
+#if PJ_HAS_THREADS
+    static pj_thread_t main_thread;
+    static long thread_tls_id;
+    static pj_mutex_t critical_section;
+#else
+#   define MAX_THREADS 32
+    static int tls_flag[MAX_THREADS];
+    static void *tls[MAX_THREADS];
+#endif
+
+static unsigned atexit_count;
+static void (*atexit_func[32])(void);
+
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type);
+
+/*
+ * pj_init(void).
+ * Init PJLIB!
+ */
+PJ_DEF(pj_status_t) pj_init(void)
+{
+    char dummy_guid[PJ_GUID_MAX_LENGTH];
+    pj_str_t guid;
+    pj_status_t rc;
+
+    /* Check if PJLIB have been initialized */
+    if (initialized) {
+	++initialized;
+	return PJ_SUCCESS;
+    }
+
+#if PJ_HAS_THREADS
+    /* Init this thread's TLS. */
+    if ((rc=pj_thread_init()) != 0) {
+	return rc;
+    }
+
+    /* Critical section. */
+    if ((rc=init_mutex(&critical_section, "critsec", PJ_MUTEX_RECURSE)) != 0)
+	return rc;
+
+#endif
+
+    /* Init logging */
+    pj_log_init();
+
+    /* Initialize exception ID for the pool. 
+     * Must do so after critical section is configured.
+     */
+    rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+    if (rc != PJ_SUCCESS)
+        return rc;
+    
+    /* Init random seed. */
+    /* Or probably not. Let application in charge of this */
+    /* pj_srand( clock() ); */
+
+    /* Startup GUID. */
+    guid.ptr = dummy_guid;
+    pj_generate_unique_string( &guid );
+
+    /* Startup timestamp */
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+    {
+	pj_timestamp dummy_ts;
+	if ((rc=pj_get_timestamp(&dummy_ts)) != 0) {
+	    return rc;
+	}
+    }
+#endif   
+
+    /* Flag PJLIB as initialized */
+    ++initialized;
+    pj_assert(initialized == 1);
+
+    PJ_LOG(4,(THIS_FILE, "pjlib %s for POSIX initialized",
+	      PJ_VERSION));
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_atexit()
+ */
+PJ_DEF(pj_status_t) pj_atexit(void (*func)(void))
+{
+    if (atexit_count >= PJ_ARRAY_SIZE(atexit_func))
+	return PJ_ETOOMANY;
+
+    atexit_func[atexit_count++] = func;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_shutdown(void)
+ */
+PJ_DEF(void) pj_shutdown()
+{
+    int i;
+
+    /* Only perform shutdown operation when 'initialized' reaches zero */
+    pj_assert(initialized > 0);
+    if (--initialized != 0)
+	return;
+
+    /* Call atexit() functions */
+    for (i=atexit_count-1; i>=0; --i) {
+	(*atexit_func[i])();
+    }
+    atexit_count = 0;
+
+    /* Free exception ID */
+    if (PJ_NO_MEMORY_EXCEPTION != -1) {
+	pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION);
+	PJ_NO_MEMORY_EXCEPTION = -1;
+    }
+
+#if PJ_HAS_THREADS
+    /* Destroy PJLIB critical section */
+    pj_mutex_destroy(&critical_section);
+
+    /* Free PJLIB TLS */
+    if (thread_tls_id != -1) {
+	pj_thread_local_free(thread_tls_id);
+	thread_tls_id = -1;
+    }
+
+    /* Ticket #1132: Assertion when (re)starting PJLIB on different thread */
+    pj_bzero(&main_thread, sizeof(main_thread));
+#endif
+
+    /* Clear static variables */
+    pj_errno_clear_handlers();
+}
+
+
+/*
+ * pj_getpid(void)
+ */
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+    PJ_CHECK_STACK();
+    return getpid();
+}
+
+/*
+ * Check if this thread has been registered to PJLIB.
+ */
+PJ_DEF(pj_bool_t) pj_thread_is_registered(void)
+{
+#if PJ_HAS_THREADS
+    return pj_thread_local_get(thread_tls_id) != 0;
+#else
+    pj_assert("pj_thread_is_registered() called in non-threading mode!");
+    return PJ_TRUE;
+#endif
+}
+
+
+/*
+ * Get thread priority value for the thread.
+ */
+PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread)
+{
+#if PJ_HAS_THREADS
+    struct sched_param param;
+    int policy;
+    int rc;
+
+    rc = pthread_getschedparam (thread->thread, &policy, &param);
+    if (rc != 0)
+	return -1;
+
+    return param.sched_priority;
+#else
+    PJ_UNUSED_ARG(thread);
+    return 1;
+#endif
+}
+
+
+/*
+ * Set the thread priority.
+ */
+PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread,  int prio)
+{
+#if PJ_HAS_THREADS
+    struct sched_param param;
+    int policy;
+    int rc;
+
+    rc = pthread_getschedparam (thread->thread, &policy, &param);
+    if (rc != 0)
+	return PJ_RETURN_OS_ERROR(rc);
+
+    param.sched_priority = prio;
+
+    rc = pthread_setschedparam(thread->thread, policy, &param);
+    if (rc != 0)
+	return PJ_RETURN_OS_ERROR(rc);
+
+    return PJ_SUCCESS;
+#else
+    PJ_UNUSED_ARG(thread);
+    PJ_UNUSED_ARG(prio);
+    pj_assert("pj_thread_set_prio() called in non-threading mode!");
+    return 1;
+#endif
+}
+
+
+/*
+ * Get the lowest priority value available on this system.
+ */
+PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread)
+{
+    struct sched_param param;
+    int policy;
+    int rc;
+
+    rc = pthread_getschedparam(thread->thread, &policy, &param);
+    if (rc != 0)
+	return -1;
+
+#if defined(_POSIX_PRIORITY_SCHEDULING)
+    return sched_get_priority_min(policy);
+#elif defined __OpenBSD__
+    /* Thread prio min/max are declared in OpenBSD private hdr */
+    return 0;
+#else
+    pj_assert("pj_thread_get_prio_min() not supported!");
+    return 0;
+#endif
+}
+
+
+/*
+ * Get the highest priority value available on this system.
+ */
+PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread)
+{
+    struct sched_param param;
+    int policy;
+    int rc;
+
+    rc = pthread_getschedparam(thread->thread, &policy, &param);
+    if (rc != 0)
+	return -1;
+
+#if defined(_POSIX_PRIORITY_SCHEDULING)
+    return sched_get_priority_max(policy);
+#elif defined __OpenBSD__
+    /* Thread prio min/max are declared in OpenBSD private hdr */
+    return 31;
+#else
+    pj_assert("pj_thread_get_prio_max() not supported!");
+    return 0;
+#endif
+}
+
+
+/*
+ * Get native thread handle
+ */
+PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) 
+{
+    PJ_ASSERT_RETURN(thread, NULL);
+
+#if PJ_HAS_THREADS
+    return &thread->thread;
+#else
+    pj_assert("pj_thread_is_registered() called in non-threading mode!");
+    return NULL;
+#endif
+}
+
+/*
+ * pj_thread_register(..)
+ */
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+					 pj_thread_desc desc,
+					 pj_thread_t **ptr_thread)
+{
+#if PJ_HAS_THREADS
+    char stack_ptr;
+    pj_status_t rc;
+    pj_thread_t *thread = (pj_thread_t *)desc;
+    pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+    /* Size sanity check. */
+    if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+	pj_assert(!"Not enough pj_thread_desc size!");
+	return PJ_EBUG;
+    }
+
+    /* Warn if this thread has been registered before */
+    if (pj_thread_local_get (thread_tls_id) != 0) {
+	// 2006-02-26 bennylp:
+	//  This wouldn't work in all cases!.
+	//  If thread is created by external module (e.g. sound thread),
+	//  thread may be reused while the pool used for the thread descriptor
+	//  has been deleted by application.
+	//*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+        //return PJ_SUCCESS;
+	PJ_LOG(4,(THIS_FILE, "Info: possibly re-registering existing "
+			     "thread"));
+    }
+
+    /* On the other hand, also warn if the thread descriptor buffer seem to
+     * have been used to register other threads.
+     */
+    pj_assert(thread->signature1 != SIGNATURE1 ||
+	      thread->signature2 != SIGNATURE2 ||
+	      (thread->thread == pthread_self()));
+
+    /* Initialize and set the thread entry. */
+    pj_bzero(desc, sizeof(struct pj_thread_t));
+    thread->thread = pthread_self();
+    thread->signature1 = SIGNATURE1;
+    thread->signature2 = SIGNATURE2;
+
+    if(cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+	pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), 
+			 cstr_thread_name, thread->thread);
+    else
+	pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), 
+			 "thr%p", (void*)thread->thread);
+    
+    rc = pj_thread_local_set(thread_tls_id, thread);
+    if (rc != PJ_SUCCESS) {
+	pj_bzero(desc, sizeof(struct pj_thread_t));
+	return rc;
+    }
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    thread->stk_start = &stack_ptr;
+    thread->stk_size = 0xFFFFFFFFUL;
+    thread->stk_max_usage = 0;
+#else
+    stack_ptr = '\0';
+#endif
+
+    *ptr_thread = thread;
+    return PJ_SUCCESS;
+#else
+    pj_thread_t *thread = (pj_thread_t*)desc;
+    *ptr_thread = thread;
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_thread_init(void)
+ */
+pj_status_t pj_thread_init(void)
+{
+#if PJ_HAS_THREADS
+    pj_status_t rc;
+    pj_thread_t *dummy;
+
+    rc = pj_thread_local_alloc(&thread_tls_id );
+    if (rc != PJ_SUCCESS) {
+	return rc;
+    }
+    return pj_thread_register("thr%p", (long*)&main_thread, &dummy);
+#else
+    PJ_LOG(2,(THIS_FILE, "Thread init error. Threading is not enabled!"));
+    return PJ_EINVALIDOP;
+#endif
+}
+
+#if PJ_HAS_THREADS
+/*
+ * thread_main()
+ *
+ * This is the main entry for all threads.
+ */
+static void *thread_main(void *param)
+{
+    pj_thread_t *rec = (pj_thread_t*)param;
+    void *result;
+    pj_status_t rc;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    rec->stk_start = (char*)&rec;
+#endif
+
+    /* Set current thread id. */
+    rc = pj_thread_local_set(thread_tls_id, rec);
+    if (rc != PJ_SUCCESS) {
+	pj_assert(!"Thread TLS ID is not set (pj_init() error?)");
+    }
+
+    /* Check if suspension is required. */
+    if (rec->suspended_mutex) {
+	pj_mutex_lock(rec->suspended_mutex);
+	pj_mutex_unlock(rec->suspended_mutex);
+    }
+
+    PJ_LOG(6,(rec->obj_name, "Thread started"));
+
+    /* Call user's entry! */
+    result = (void*)(long)(*rec->proc)(rec->arg);
+
+    /* Done. */
+    PJ_LOG(6,(rec->obj_name, "Thread quitting"));
+
+    return result;
+}
+#endif
+
+/*
+ * pj_thread_create(...)
+ */
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, 
+				      const char *thread_name,
+				      pj_thread_proc *proc, 
+				      void *arg,
+				      pj_size_t stack_size, 
+				      unsigned flags,
+				      pj_thread_t **ptr_thread)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *rec;
+    pthread_attr_t thread_attr;
+    void *stack_addr;
+    int rc;
+
+    PJ_UNUSED_ARG(stack_addr);
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(pool && proc && ptr_thread, PJ_EINVAL);
+
+    /* Create thread record and assign name for the thread */
+    rec = (struct pj_thread_t*) pj_pool_zalloc(pool, sizeof(pj_thread_t));
+    PJ_ASSERT_RETURN(rec, PJ_ENOMEM);
+    
+    /* Set name. */
+    if (!thread_name) 
+	thread_name = "thr%p";
+    
+    if (strchr(thread_name, '%')) {
+	pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
+    } else {
+	strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+	rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    /* Set default stack size */
+    if (stack_size == 0)
+	stack_size = PJ_THREAD_DEFAULT_STACK_SIZE;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    rec->stk_size = stack_size;
+    rec->stk_max_usage = 0;
+#endif
+
+    /* Emulate suspended thread with mutex. */
+    if (flags & PJ_THREAD_SUSPENDED) {
+	rc = pj_mutex_create_simple(pool, NULL, &rec->suspended_mutex);
+	if (rc != PJ_SUCCESS) {
+	    return rc;
+	}
+
+	pj_mutex_lock(rec->suspended_mutex);
+    } else {
+	pj_assert(rec->suspended_mutex == NULL);
+    }
+    
+
+    /* Init thread attributes */
+    pthread_attr_init(&thread_attr);
+
+#if defined(PJ_THREAD_SET_STACK_SIZE) && PJ_THREAD_SET_STACK_SIZE!=0
+    /* Set thread's stack size */
+    rc = pthread_attr_setstacksize(&thread_attr, stack_size);
+    if (rc != 0)
+	return PJ_RETURN_OS_ERROR(rc);
+#endif	/* PJ_THREAD_SET_STACK_SIZE */
+
+
+#if defined(PJ_THREAD_ALLOCATE_STACK) && PJ_THREAD_ALLOCATE_STACK!=0
+    /* Allocate memory for the stack */
+    stack_addr = pj_pool_alloc(pool, stack_size);
+    PJ_ASSERT_RETURN(stack_addr, PJ_ENOMEM);
+
+    rc = pthread_attr_setstackaddr(&thread_attr, stack_addr);
+    if (rc != 0)
+	return PJ_RETURN_OS_ERROR(rc);
+#endif	/* PJ_THREAD_ALLOCATE_STACK */
+
+
+    /* Create the thread. */
+    rec->proc = proc;
+    rec->arg = arg;
+    rc = pthread_create( &rec->thread, &thread_attr, &thread_main, rec);
+    if (rc != 0) {
+	return PJ_RETURN_OS_ERROR(rc);
+    }
+
+    *ptr_thread = rec;
+
+    PJ_LOG(6, (rec->obj_name, "Thread created"));
+    return PJ_SUCCESS;
+#else
+    pj_assert(!"Threading is disabled!");
+    return PJ_EINVALIDOP;
+#endif
+}
+
+/*
+ * pj_thread-get_name()
+ */
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *rec = (pj_thread_t*)p;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(p, "");
+
+    return rec->obj_name;
+#else
+    return "";
+#endif
+}
+
+/*
+ * pj_thread_resume()
+ */
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+    pj_status_t rc;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+    rc = pj_mutex_unlock(p->suspended_mutex);
+
+    return rc;
+}
+
+/*
+ * pj_thread_this()
+ */
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *rec = (pj_thread_t*)pj_thread_local_get(thread_tls_id);
+    
+    if (rec == NULL) {
+	pj_assert(!"Calling pjlib from unknown/external thread. You must "
+		   "register external threads with pj_thread_register() "
+		   "before calling any pjlib functions.");
+    }
+
+    /*
+     * MUST NOT check stack because this function is called
+     * by PJ_CHECK_STACK() itself!!!
+     *
+     */
+
+    return rec;
+#else
+    pj_assert(!"Threading is not enabled!");
+    return NULL;
+#endif
+}
+
+/*
+ * pj_thread_join()
+ */
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+#if PJ_HAS_THREADS
+    pj_thread_t *rec = (pj_thread_t *)p;
+    void *ret;
+    int result;
+
+    PJ_CHECK_STACK();
+
+    PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
+    result = pthread_join( rec->thread, &ret);
+
+    if (result == 0)
+	return PJ_SUCCESS;
+    else {
+	/* Calling pthread_join() on a thread that no longer exists and 
+	 * getting back ESRCH isn't an error (in this context). 
+	 * Thanks Phil Torre <ptorre@zetron.com>.
+	 */
+	return result==ESRCH ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(result);
+    }
+#else
+    PJ_CHECK_STACK();
+    pj_assert(!"No multithreading support!");
+    return PJ_EINVALIDOP;
+#endif
+}
+
+/*
+ * pj_thread_destroy()
+ */
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
+{
+    PJ_CHECK_STACK();
+
+    /* Destroy mutex used to suspend thread */
+    if (p->suspended_mutex) {
+	pj_mutex_destroy(p->suspended_mutex);
+	p->suspended_mutex = NULL;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_sleep()
+ */
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+/* TODO: should change this to something like PJ_OS_HAS_NANOSLEEP */
+#if defined(PJ_RTEMS) && PJ_RTEMS!=0
+    enum { NANOSEC_PER_MSEC = 1000000 };
+    struct timespec req;
+
+    PJ_CHECK_STACK();
+    req.tv_sec = msec / 1000;
+    req.tv_nsec = (msec % 1000) * NANOSEC_PER_MSEC;
+
+    if (nanosleep(&req, NULL) == 0)
+	return PJ_SUCCESS;
+
+    return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+    PJ_CHECK_STACK();
+
+    pj_set_os_error(0);
+
+    usleep(msec * 1000);
+
+    /* MacOS X (reported on 10.5) seems to always set errno to ETIMEDOUT.
+     * It does so because usleep() is declared to return int, and we're
+     * supposed to check for errno only when usleep() returns non-zero. 
+     * Unfortunately, usleep() is declared to return void in other platforms
+     * so it's not possible to always check for the return value (unless 
+     * we add a detection routine in autoconf).
+     *
+     * As a workaround, here we check if ETIMEDOUT is returned and
+     * return successfully if it is.
+     */
+    if (pj_get_native_os_error() == ETIMEDOUT)
+	return PJ_SUCCESS;
+
+    return pj_get_os_error();
+
+#endif	/* PJ_RTEMS */
+}
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+/*
+ * pj_thread_check_stack()
+ * Implementation for PJ_CHECK_STACK()
+ */
+PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
+{
+    char stk_ptr;
+    pj_uint32_t usage;
+    pj_thread_t *thread = pj_thread_this();
+
+    /* Calculate current usage. */
+    usage = (&stk_ptr > thread->stk_start) ? &stk_ptr - thread->stk_start :
+		thread->stk_start - &stk_ptr;
+
+    /* Assert if stack usage is dangerously high. */
+    pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
+
+    /* Keep statistic. */
+    if (usage > thread->stk_max_usage) {
+	thread->stk_max_usage = usage;
+	thread->caller_file = file;
+	thread->caller_line = line;
+    }
+}
+
+/*
+ * pj_thread_get_stack_max_usage()
+ */
+PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
+{
+    return thread->stk_max_usage;
+}
+
+/*
+ * pj_thread_get_stack_info()
+ */
+PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
+					      const char **file,
+					      int *line )
+{
+    pj_assert(thread);
+
+    *file = thread->caller_file;
+    *line = thread->caller_line;
+    return 0;
+}
+
+#endif	/* PJ_OS_HAS_CHECK_STACK */
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_atomic_create()
+ */
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, 
+				      pj_atomic_value_t initial,
+				      pj_atomic_t **ptr_atomic)
+{
+    pj_status_t rc;
+    pj_atomic_t *atomic_var;
+
+    atomic_var = PJ_POOL_ZALLOC_T(pool, pj_atomic_t);
+
+    PJ_ASSERT_RETURN(atomic_var, PJ_ENOMEM);
+    
+#if PJ_HAS_THREADS
+    rc = pj_mutex_create(pool, "atm%p", PJ_MUTEX_SIMPLE, &atomic_var->mutex);
+    if (rc != PJ_SUCCESS)
+	return rc;
+#endif
+    atomic_var->value = initial;
+
+    *ptr_atomic = atomic_var;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_atomic_destroy()
+ */
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *atomic_var )
+{
+    PJ_ASSERT_RETURN(atomic_var, PJ_EINVAL);
+#if PJ_HAS_THREADS
+    return pj_mutex_destroy( atomic_var->mutex );
+#else
+    return 0;
+#endif
+}
+
+/*
+ * pj_atomic_set()
+ */
+PJ_DEF(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value)
+{
+    PJ_CHECK_STACK();
+
+#if PJ_HAS_THREADS
+    pj_mutex_lock( atomic_var->mutex );
+#endif
+    atomic_var->value = value;
+#if PJ_HAS_THREADS
+    pj_mutex_unlock( atomic_var->mutex);
+#endif 
+}
+
+/*
+ * pj_atomic_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+    pj_atomic_value_t oldval;
+    
+    PJ_CHECK_STACK();
+
+#if PJ_HAS_THREADS
+    pj_mutex_lock( atomic_var->mutex );
+#endif
+    oldval = atomic_var->value;
+#if PJ_HAS_THREADS
+    pj_mutex_unlock( atomic_var->mutex);
+#endif
+    return oldval;
+}
+
+/*
+ * pj_atomic_inc_and_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
+{
+    pj_atomic_value_t new_value;
+
+    PJ_CHECK_STACK();
+
+#if PJ_HAS_THREADS
+    pj_mutex_lock( atomic_var->mutex );
+#endif
+    new_value = ++atomic_var->value;
+#if PJ_HAS_THREADS
+    pj_mutex_unlock( atomic_var->mutex);
+#endif
+
+    return new_value;
+}
+/*
+ * pj_atomic_inc()
+ */
+PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+{
+    pj_atomic_inc_and_get(atomic_var);
+}
+
+/*
+ * pj_atomic_dec_and_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
+{
+    pj_atomic_value_t new_value;
+
+    PJ_CHECK_STACK();
+
+#if PJ_HAS_THREADS
+    pj_mutex_lock( atomic_var->mutex );
+#endif
+    new_value = --atomic_var->value;
+#if PJ_HAS_THREADS
+    pj_mutex_unlock( atomic_var->mutex);
+#endif
+
+    return new_value;
+}
+
+/*
+ * pj_atomic_dec()
+ */
+PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+{
+    pj_atomic_dec_and_get(atomic_var);
+}
+
+/*
+ * pj_atomic_add_and_get()
+ */ 
+PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var, 
+                                                 pj_atomic_value_t value )
+{
+    pj_atomic_value_t new_value;
+
+#if PJ_HAS_THREADS
+    pj_mutex_lock(atomic_var->mutex);
+#endif
+    
+    atomic_var->value += value;
+    new_value = atomic_var->value;
+
+#if PJ_HAS_THREADS
+    pj_mutex_unlock(atomic_var->mutex);
+#endif
+
+    return new_value;
+}
+
+/*
+ * pj_atomic_add()
+ */ 
+PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var, 
+                            pj_atomic_value_t value )
+{
+    pj_atomic_add_and_get(atomic_var, value);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_thread_local_alloc()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *p_index)
+{
+#if PJ_HAS_THREADS
+    pthread_key_t key;
+    int rc;
+
+    PJ_ASSERT_RETURN(p_index != NULL, PJ_EINVAL);
+
+    pj_assert( sizeof(pthread_key_t) <= sizeof(long));
+    if ((rc=pthread_key_create(&key, NULL)) != 0)
+	return PJ_RETURN_OS_ERROR(rc);
+
+    *p_index = key;
+    return PJ_SUCCESS;
+#else
+    int i;
+    for (i=0; i<MAX_THREADS; ++i) {
+	if (tls_flag[i] == 0)
+	    break;
+    }
+    if (i == MAX_THREADS) 
+	return PJ_ETOOMANY;
+    
+    tls_flag[i] = 1;
+    tls[i] = NULL;
+
+    *p_index = i;
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_thread_local_free()
+ */
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+    PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+    pthread_key_delete(index);
+#else
+    tls_flag[index] = 0;
+#endif
+}
+
+/*
+ * pj_thread_local_set()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
+{
+    //Can't check stack because this function is called in the
+    //beginning before main thread is initialized.
+    //PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+    int rc=pthread_setspecific(index, value);
+    return rc==0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc);
+#else
+    pj_assert(index >= 0 && index < MAX_THREADS);
+    tls[index] = value;
+    return PJ_SUCCESS;
+#endif
+}
+
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+    //Can't check stack because this function is called
+    //by PJ_CHECK_STACK() itself!!!
+    //PJ_CHECK_STACK();
+#if PJ_HAS_THREADS
+    return pthread_getspecific(index);
+#else
+    pj_assert(index >= 0 && index < MAX_THREADS);
+    return tls[index];
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+#if PJ_HAS_THREADS
+    pj_mutex_lock(&critical_section);
+#endif
+}
+
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+#if PJ_HAS_THREADS
+    pj_mutex_unlock(&critical_section);
+#endif
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_LINUX) && PJ_LINUX!=0
+PJ_BEGIN_DECL
+PJ_DECL(int) pthread_mutexattr_settype(pthread_mutexattr_t*,int);
+PJ_END_DECL
+#endif
+
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name, int type)
+{
+#if PJ_HAS_THREADS
+    pthread_mutexattr_t attr;
+    int rc;
+
+    PJ_CHECK_STACK();
+
+    rc = pthread_mutexattr_init(&attr);
+    if (rc != 0)
+	return PJ_RETURN_OS_ERROR(rc);
+
+    if (type == PJ_MUTEX_SIMPLE) {
+#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \
+    defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE)
+	rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_FAST_NP);
+#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \
+       defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE)
+	/* Nothing to do, default is simple */
+#else
+	rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
+#endif
+    } else {
+#if (defined(PJ_LINUX) && PJ_LINUX!=0) || \
+     defined(PJ_HAS_PTHREAD_MUTEXATTR_SETTYPE)
+	rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
+#elif (defined(PJ_RTEMS) && PJ_RTEMS!=0) || \
+       defined(PJ_PTHREAD_MUTEXATTR_T_HAS_RECURSIVE)
+	// Phil Torre <ptorre@zetron.com>:
+	// The RTEMS implementation of POSIX mutexes doesn't include 
+	// pthread_mutexattr_settype(), so what follows is a hack
+	// until I get RTEMS patched to support the set/get functions.
+	//
+	// More info:
+	//   newlib's pthread also lacks pthread_mutexattr_settype(),
+	//   but it seems to have mutexattr.recursive.
+	PJ_TODO(FIX_RTEMS_RECURSIVE_MUTEX_TYPE)
+	attr.recursive = 1;
+#else
+	rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+#endif
+    }
+    
+    if (rc != 0) {
+	return PJ_RETURN_OS_ERROR(rc);
+    }
+
+    rc = pthread_mutex_init(&mutex->mutex, &attr);
+    if (rc != 0) {
+	return PJ_RETURN_OS_ERROR(rc);
+    }
+    
+    rc = pthread_mutexattr_destroy(&attr);
+    if (rc != 0) {
+	pj_status_t status = PJ_RETURN_OS_ERROR(rc);
+	pthread_mutex_destroy(&mutex->mutex);
+	return status;
+    }
+
+#if PJ_DEBUG
+    /* Set owner. */
+    mutex->nesting_level = 0;
+    mutex->owner = NULL;
+#endif
+
+    /* Set name. */
+    if (!name) {
+	name = "mtx%p";
+    }
+    if (strchr(name, '%')) {
+	pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
+    } else {
+	strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
+	mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (mutex->obj_name, "Mutex created"));
+    return PJ_SUCCESS;
+#else /* PJ_HAS_THREADS */
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_create()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, 
+				    const char *name, 
+				    int type,
+				    pj_mutex_t **ptr_mutex)
+{
+#if PJ_HAS_THREADS
+    pj_status_t rc;
+    pj_mutex_t *mutex;
+
+    PJ_ASSERT_RETURN(pool && ptr_mutex, PJ_EINVAL);
+
+    mutex = PJ_POOL_ALLOC_T(pool, pj_mutex_t);
+    PJ_ASSERT_RETURN(mutex, PJ_ENOMEM);
+
+    if ((rc=init_mutex(mutex, name, type)) != PJ_SUCCESS)
+	return rc;
+    
+    *ptr_mutex = mutex;
+    return PJ_SUCCESS;
+#else /* PJ_HAS_THREADS */
+    *ptr_mutex = (pj_mutex_t*)1;
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_create_simple()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, 
+                                            const char *name,
+					    pj_mutex_t **mutex )
+{
+    return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+/*
+ * pj_mutex_create_recursive()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+					       const char *name,
+					       pj_mutex_t **mutex )
+{
+    return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+/*
+ * pj_mutex_lock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    pj_status_t status;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_DEBUG
+    PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting (mutex owner=%s)", 
+				pj_thread_this()->obj_name,
+				mutex->owner_name));
+#else
+    PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is waiting", 
+				pj_thread_this()->obj_name));
+#endif
+
+    status = pthread_mutex_lock( &mutex->mutex );
+
+
+#if PJ_DEBUG
+    if (status == PJ_SUCCESS) {
+	mutex->owner = pj_thread_this();
+	pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name);
+	++mutex->nesting_level;
+    }
+
+    PJ_LOG(6,(mutex->obj_name, 
+	      (status==0 ? 
+		"Mutex acquired by thread %s (level=%d)" : 
+		"Mutex acquisition FAILED by %s (level=%d)"),
+	      pj_thread_this()->obj_name,
+	      mutex->nesting_level));
+#else
+    PJ_LOG(6,(mutex->obj_name, 
+	      (status==0 ? "Mutex acquired by thread %s" : "FAILED by %s"),
+	      pj_thread_this()->obj_name));
+#endif
+
+    if (status == 0)
+	return PJ_SUCCESS;
+    else
+	return PJ_RETURN_OS_ERROR(status);
+#else	/* PJ_HAS_THREADS */
+    pj_assert( mutex == (pj_mutex_t*)1 );
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_unlock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    pj_status_t status;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_DEBUG
+    pj_assert(mutex->owner == pj_thread_this());
+    if (--mutex->nesting_level == 0) {
+	mutex->owner = NULL;
+	mutex->owner_name[0] = '\0';
+    }
+
+    PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s (level=%d)", 
+				pj_thread_this()->obj_name, 
+				mutex->nesting_level));
+#else
+    PJ_LOG(6,(mutex->obj_name, "Mutex released by thread %s", 
+				pj_thread_this()->obj_name));
+#endif
+
+    status = pthread_mutex_unlock( &mutex->mutex );
+    if (status == 0)
+	return PJ_SUCCESS;
+    else
+	return PJ_RETURN_OS_ERROR(status);
+
+#else /* PJ_HAS_THREADS */
+    pj_assert( mutex == (pj_mutex_t*)1 );
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_trylock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    int status;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s is trying", 
+				pj_thread_this()->obj_name));
+
+    status = pthread_mutex_trylock( &mutex->mutex );
+
+    if (status==0) {
+#if PJ_DEBUG
+	mutex->owner = pj_thread_this();
+	pj_ansi_strcpy(mutex->owner_name, mutex->owner->obj_name);
+	++mutex->nesting_level;
+
+	PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s (level=%d)", 
+				   pj_thread_this()->obj_name,
+				   mutex->nesting_level));
+#else
+	PJ_LOG(6,(mutex->obj_name, "Mutex acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+#endif
+    } else {
+	PJ_LOG(6,(mutex->obj_name, "Mutex: thread %s's trylock() failed", 
+				    pj_thread_this()->obj_name));
+    }
+    
+    if (status==0)
+	return PJ_SUCCESS;
+    else
+	return PJ_RETURN_OS_ERROR(status);
+#else	/* PJ_HAS_THREADS */
+    pj_assert( mutex == (pj_mutex_t*)1);
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_mutex_destroy()
+ */
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+    enum { RETRY = 4 };
+    int status = 0;
+    unsigned retry;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_HAS_THREADS
+    PJ_LOG(6,(mutex->obj_name, "Mutex destroyed by thread %s",
+			       pj_thread_this()->obj_name));
+
+    for (retry=0; retry<RETRY; ++retry) {
+	status = pthread_mutex_destroy( &mutex->mutex );
+	if (status == PJ_SUCCESS)
+	    break;
+	else if (retry<RETRY-1 && status == EBUSY)
+	    pthread_mutex_unlock(&mutex->mutex);
+    }
+
+    if (status == 0)
+	return PJ_SUCCESS;
+    else {
+	return PJ_RETURN_OS_ERROR(status);
+    }
+#else
+    pj_assert( mutex == (pj_mutex_t*)1 );
+    status = PJ_SUCCESS;
+    return status;
+#endif
+}
+
+#if PJ_DEBUG
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+#if PJ_HAS_THREADS
+    return mutex->owner == pj_thread_this();
+#else
+    return 1;
+#endif
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Include Read/Write mutex emulation for POSIX platforms that lack it (e.g.
+ * RTEMS). Otherwise use POSIX rwlock.
+ */
+#if defined(PJ_EMULATE_RWMUTEX) && PJ_EMULATE_RWMUTEX!=0
+    /* We need semaphore functionality to emulate rwmutex */
+#   if !defined(PJ_HAS_SEMAPHORE) || PJ_HAS_SEMAPHORE==0
+#	error "Semaphore support needs to be enabled to emulate rwmutex"
+#   endif
+#   include "os_rwmutex.c"
+#else
+struct pj_rwmutex_t
+{
+    pthread_rwlock_t rwlock;
+};
+
+PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name,
+				      pj_rwmutex_t **p_mutex)
+{
+    pj_rwmutex_t *rwm;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(name);
+    
+    rwm = PJ_POOL_ALLOC_T(pool, pj_rwmutex_t);
+    PJ_ASSERT_RETURN(rwm, PJ_ENOMEM);
+
+    status = pthread_rwlock_init(&rwm->rwlock, NULL);
+    if (status != 0)
+	return PJ_RETURN_OS_ERROR(status);
+
+    *p_mutex = rwm;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Lock the mutex for reading.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex)
+{
+    pj_status_t status;
+
+    status = pthread_rwlock_rdlock(&mutex->rwlock);
+    if (status != 0)
+	return PJ_RETURN_OS_ERROR(status);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Lock the mutex for writing.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex)
+{
+    pj_status_t status;
+
+    status = pthread_rwlock_wrlock(&mutex->rwlock);
+    if (status != 0)
+	return PJ_RETURN_OS_ERROR(status);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Release read lock.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex)
+{
+    return pj_rwmutex_unlock_write(mutex);
+}
+
+/*
+ * Release write lock.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex)
+{
+    pj_status_t status;
+
+    status = pthread_rwlock_unlock(&mutex->rwlock);
+    if (status != 0)
+	return PJ_RETURN_OS_ERROR(status);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Destroy reader/writer mutex.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex)
+{
+    pj_status_t status;
+
+    status = pthread_rwlock_destroy(&mutex->rwlock);
+    if (status != 0)
+	return PJ_RETURN_OS_ERROR(status);
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJ_EMULATE_RWMUTEX */
+
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+/*
+ * pj_sem_create()
+ */
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, 
+				   const char *name,
+				   unsigned initial, 
+				   unsigned max,
+				   pj_sem_t **ptr_sem)
+{
+#if PJ_HAS_THREADS
+    pj_sem_t *sem;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(pool != NULL && ptr_sem != NULL, PJ_EINVAL);
+
+    sem = PJ_POOL_ALLOC_T(pool, pj_sem_t);
+    PJ_ASSERT_RETURN(sem, PJ_ENOMEM);
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+    /* MacOS X doesn't support anonymous semaphore */
+    {
+	char sem_name[PJ_GUID_MAX_LENGTH+1];
+	pj_str_t nam;
+
+	/* We should use SEM_NAME_LEN, but this doesn't seem to be 
+	 * declared anywhere? The value here is just from trial and error
+	 * to get the longest name supported.
+	 */
+#	define MAX_SEM_NAME_LEN	23
+
+	/* Create a unique name for the semaphore. */
+	if (PJ_GUID_STRING_LENGTH <= MAX_SEM_NAME_LEN) {
+	    nam.ptr = sem_name;
+	    pj_generate_unique_string(&nam);
+	    sem_name[nam.slen] = '\0';
+	} else {
+	    pj_create_random_string(sem_name, MAX_SEM_NAME_LEN);
+	    sem_name[MAX_SEM_NAME_LEN] = '\0';
+	}
+
+	/* Create semaphore */
+	sem->sem = sem_open(sem_name, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR, 
+			    initial);
+	if (sem->sem == SEM_FAILED)
+	    return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+
+	/* And immediately release the name as we don't need it */
+	sem_unlink(sem_name);
+    }
+#else
+    sem->sem = PJ_POOL_ALLOC_T(pool, sem_t);
+    if (sem_init( sem->sem, 0, initial) != 0) 
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#endif
+    
+    /* Set name. */
+    if (!name) {
+	name = "sem%p";
+    }
+    if (strchr(name, '%')) {
+	pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
+    } else {
+	strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
+	sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (sem->obj_name, "Semaphore created"));
+
+    *ptr_sem = sem;
+    return PJ_SUCCESS;
+#else
+    *ptr_sem = (pj_sem_t*)1;
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_wait()
+ */
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+    int result;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+    PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s is waiting", 
+			      pj_thread_this()->obj_name));
+
+    result = sem_wait( sem->sem );
+    
+    if (result == 0) {
+	PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+    } else {
+	PJ_LOG(6, (sem->obj_name, "Semaphore: thread %s FAILED to acquire", 
+				  pj_thread_this()->obj_name));
+    }
+
+    if (result == 0)
+	return PJ_SUCCESS;
+    else
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+    pj_assert( sem == (pj_sem_t*) 1 );
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_trywait()
+ */
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+    int result;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+    result = sem_trywait( sem->sem );
+    
+    if (result == 0) {
+	PJ_LOG(6, (sem->obj_name, "Semaphore acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+    } 
+    if (result == 0)
+	return PJ_SUCCESS;
+    else
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+    pj_assert( sem == (pj_sem_t*)1 );
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_post()
+ */
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+    int result;
+    PJ_LOG(6, (sem->obj_name, "Semaphore released by thread %s",
+			      pj_thread_this()->obj_name));
+    result = sem_post( sem->sem );
+
+    if (result == 0)
+	return PJ_SUCCESS;
+    else
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+    pj_assert( sem == (pj_sem_t*) 1);
+    return PJ_SUCCESS;
+#endif
+}
+
+/*
+ * pj_sem_destroy()
+ */
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+#if PJ_HAS_THREADS
+    int result;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+    PJ_LOG(6, (sem->obj_name, "Semaphore destroyed by thread %s",
+			      pj_thread_this()->obj_name));
+#if defined(PJ_DARWINOS) && PJ_DARWINOS!=0
+    result = sem_close( sem->sem );
+#else
+    result = sem_destroy( sem->sem );
+#endif
+
+    if (result == 0)
+	return PJ_SUCCESS;
+    else
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+#else
+    pj_assert( sem == (pj_sem_t*) 1 );
+    return PJ_SUCCESS;
+#endif
+}
+
+#endif	/* PJ_HAS_SEMAPHORE */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+
+/*
+ * pj_event_create()
+ */
+PJ_DEF(pj_status_t) pj_event_create(pj_pool_t *pool, const char *name,
+				    pj_bool_t manual_reset, pj_bool_t initial,
+				    pj_event_t **ptr_event)
+{
+    pj_event_t *event;
+
+    event = PJ_POOL_ALLOC_T(pool, pj_event_t);
+
+    init_mutex(&event->mutex, name, PJ_MUTEX_SIMPLE);
+    pthread_cond_init(&event->cond, 0);
+    event->auto_reset = !manual_reset;
+    event->threads_waiting = 0;
+
+    if (initial) {
+	event->state = EV_STATE_SET;
+	event->threads_to_release = 1;
+    } else {
+	event->state = EV_STATE_OFF;
+	event->threads_to_release = 0;
+    }
+
+    *ptr_event = event;
+    return PJ_SUCCESS;
+}
+
+static void event_on_one_release(pj_event_t *event)
+{
+    if (event->state == EV_STATE_SET) {
+	if (event->auto_reset) {
+	    event->threads_to_release = 0;
+	    event->state = EV_STATE_OFF;
+	} else {
+	    /* Manual reset remains on */
+	}
+    } else {
+	if (event->auto_reset) {
+	    /* Only release one */
+	    event->threads_to_release = 0;
+	    event->state = EV_STATE_OFF;
+	} else {
+	    event->threads_to_release--;
+	    pj_assert(event->threads_to_release >= 0);
+	    if (event->threads_to_release==0)
+		event->state = EV_STATE_OFF;
+	}
+    }
+}
+
+/*
+ * pj_event_wait()
+ */
+PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
+{
+    pthread_mutex_lock(&event->mutex.mutex);
+    event->threads_waiting++;
+    while (event->state == EV_STATE_OFF)
+	pthread_cond_wait(&event->cond, &event->mutex.mutex);
+    event->threads_waiting--;
+    event_on_one_release(event);
+    pthread_mutex_unlock(&event->mutex.mutex);
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_event_trywait()
+ */
+PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
+{
+    pj_status_t status;
+
+    pthread_mutex_lock(&event->mutex.mutex);
+    status = event->state != EV_STATE_OFF ? PJ_SUCCESS : -1;
+    if (status==PJ_SUCCESS) {
+	event_on_one_release(event);
+    }
+    pthread_mutex_unlock(&event->mutex.mutex);
+
+    return status;
+}
+
+/*
+ * pj_event_set()
+ */
+PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
+{
+    pthread_mutex_lock(&event->mutex.mutex);
+    event->threads_to_release = 1;
+    event->state = EV_STATE_SET;
+    if (event->auto_reset)
+	pthread_cond_signal(&event->cond);
+    else
+	pthread_cond_broadcast(&event->cond);
+    pthread_mutex_unlock(&event->mutex.mutex);
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_event_pulse()
+ */
+PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
+{
+    pthread_mutex_lock(&event->mutex.mutex);
+    if (event->threads_waiting) {
+	event->threads_to_release = event->auto_reset ? 1 :
+					event->threads_waiting;
+	event->state = EV_STATE_PULSED;
+	if (event->threads_to_release==1)
+	    pthread_cond_signal(&event->cond);
+	else
+	    pthread_cond_broadcast(&event->cond);
+    }
+    pthread_mutex_unlock(&event->mutex.mutex);
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_event_reset()
+ */
+PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
+{
+    pthread_mutex_lock(&event->mutex.mutex);
+    event->state = EV_STATE_OFF;
+    event->threads_to_release = 0;
+    pthread_mutex_unlock(&event->mutex.mutex);
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_event_destroy()
+ */
+PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
+{
+    pj_mutex_destroy(&event->mutex);
+    pthread_cond_destroy(&event->cond);
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJ_HAS_EVENT_OBJ */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+/*
+ * Terminal
+ */
+
+/**
+ * Set terminal color.
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+    /* put bright prefix to ansi_color */
+    char ansi_color[12] = "\033[01;3";
+
+    if (color & PJ_TERM_COLOR_BRIGHT) {
+	color ^= PJ_TERM_COLOR_BRIGHT;
+    } else {
+	strcpy(ansi_color, "\033[00;3");
+    }
+
+    switch (color) {
+    case 0:
+	/* black color */
+	strcat(ansi_color, "0m");
+	break;
+    case PJ_TERM_COLOR_R:
+	/* red color */
+	strcat(ansi_color, "1m");
+	break;
+    case PJ_TERM_COLOR_G:
+	/* green color */
+	strcat(ansi_color, "2m");
+	break;
+    case PJ_TERM_COLOR_B:
+	/* blue color */
+	strcat(ansi_color, "4m");
+	break;
+    case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G:
+	/* yellow color */
+	strcat(ansi_color, "3m");
+	break;
+    case PJ_TERM_COLOR_R | PJ_TERM_COLOR_B:
+	/* magenta color */
+	strcat(ansi_color, "5m");
+	break;
+    case PJ_TERM_COLOR_G | PJ_TERM_COLOR_B:
+	/* cyan color */
+	strcat(ansi_color, "6m");
+	break;
+    case PJ_TERM_COLOR_R | PJ_TERM_COLOR_G | PJ_TERM_COLOR_B:
+	/* white color */
+	strcat(ansi_color, "7m");
+	break;
+    default:
+	/* default console color */
+	strcpy(ansi_color, "\033[00m");
+	break;
+    }
+
+    fputs(ansi_color, stdout);
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+    return 0;
+}
+
+#endif	/* PJ_TERM_HAS_COLOR */
+
+#if !defined(PJ_DARWINOS) || PJ_DARWINOS == 0
+/*
+ * pj_run_app()
+ */
+PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
+                       unsigned flags)
+{
+    return (*main_func)(argc, argv);
+}
+#endif
diff --git a/jni/pjproject-android/pjlib/src/pj/os_core_win32.c b/jni/pjproject-android/pjlib/src/pj/os_core_win32.c
new file mode 100644
index 0000000..927d789
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_core_win32.c
@@ -0,0 +1,1457 @@
+/* $Id: os_core_win32.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/rand.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/except.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#if defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+#  include <winsock.h>
+#endif
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+#  include <winsock2.h>
+#endif
+
+/* Activate mutex related logging if PJ_DEBUG_MUTEX is set, otherwise
+ * use default level 6 logging.
+ */
+#if defined(PJ_DEBUG_MUTEX) && PJ_DEBUG_MUTEX
+#   undef PJ_DEBUG
+#   define PJ_DEBUG	    1
+#   define LOG_MUTEX(expr)  PJ_LOG(5,expr)
+#else
+#   define LOG_MUTEX(expr)  PJ_LOG(6,expr)
+#endif
+
+#define THIS_FILE	"os_core_win32.c"
+
+/*
+ * Implementation of pj_thread_t.
+ */
+struct pj_thread_t
+{
+    char	    obj_name[PJ_MAX_OBJ_NAME];
+    HANDLE	    hthread;
+    DWORD	    idthread;
+    pj_thread_proc *proc;
+    void	   *arg;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    pj_uint32_t	    stk_size;
+    pj_uint32_t	    stk_max_usage;
+    char	   *stk_start;
+    const char	   *caller_file;
+    int		    caller_line;
+#endif
+};
+
+
+/*
+ * Implementation of pj_mutex_t.
+ */
+struct pj_mutex_t
+{
+#if PJ_WIN32_WINNT >= 0x0400
+    CRITICAL_SECTION	crit;
+#else
+    HANDLE		hMutex;
+#endif
+    char		obj_name[PJ_MAX_OBJ_NAME];
+#if PJ_DEBUG
+    int		        nesting_level;
+    pj_thread_t	       *owner;
+#endif
+};
+
+/*
+ * Implementation of pj_sem_t.
+ */
+typedef struct pj_sem_t
+{
+    HANDLE		hSemaphore;
+    char		obj_name[PJ_MAX_OBJ_NAME];
+} pj_mem_t;
+
+
+/*
+ * Implementation of pj_event_t.
+ */
+struct pj_event_t
+{
+    HANDLE		hEvent;
+    char		obj_name[PJ_MAX_OBJ_NAME];
+};
+
+/*
+ * Implementation of pj_atomic_t.
+ */
+struct pj_atomic_t
+{
+    long value;
+};
+
+/*
+ * Flag and reference counter for PJLIB instance.
+ */
+static int initialized;
+
+/*
+ * Static global variables.
+ */
+static pj_thread_desc main_thread;
+static long thread_tls_id = -1;
+static pj_mutex_t critical_section_mutex;
+static unsigned atexit_count;
+static void (*atexit_func[32])(void);
+
+/*
+ * Some static prototypes.
+ */
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name);
+
+
+/*
+ * pj_init(void).
+ * Init PJLIB!
+ */
+PJ_DEF(pj_status_t) pj_init(void)
+{
+    WSADATA wsa;
+    char dummy_guid[32]; /* use maximum GUID length */
+    pj_str_t guid;
+    pj_status_t rc;
+
+    /* Check if PJLIB have been initialized */
+    if (initialized) {
+	++initialized;
+	return PJ_SUCCESS;
+    }
+
+    /* Init Winsock.. */
+    if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
+	return PJ_RETURN_OS_ERROR(WSAGetLastError());
+    }
+
+    /* Init this thread's TLS. */
+    if ((rc=pj_thread_init()) != PJ_SUCCESS) {
+	return rc;
+    }
+    
+    /* Init logging */
+    pj_log_init();
+
+    /* Init random seed. */
+    /* Or probably not. Let application in charge of this */
+    /* pj_srand( GetCurrentProcessId() ); */
+
+    /* Initialize critical section. */
+    if ((rc=init_mutex(&critical_section_mutex, "pj%p")) != PJ_SUCCESS)
+	return rc;
+
+    /* Startup GUID. */
+    guid.ptr = dummy_guid;
+    pj_generate_unique_string( &guid );
+
+    /* Initialize exception ID for the pool. 
+     * Must do so after critical section is configured.
+     */
+    rc = pj_exception_id_alloc("PJLIB/No memory", &PJ_NO_MEMORY_EXCEPTION);
+    if (rc != PJ_SUCCESS)
+        return rc;
+
+    /* Startup timestamp */
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+    {
+	pj_timestamp dummy_ts;
+	if ((rc=pj_get_timestamp_freq(&dummy_ts)) != PJ_SUCCESS) {
+	    return rc;
+	}
+	if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) {
+	    return rc;
+	}
+    }
+#endif   
+
+    /* Flag PJLIB as initialized */
+    ++initialized;
+    pj_assert(initialized == 1);
+
+    PJ_LOG(4,(THIS_FILE, "pjlib %s for win32 initialized",
+	      PJ_VERSION));
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_atexit()
+ */
+PJ_DEF(pj_status_t) pj_atexit(void (*func)(void))
+{
+    if (atexit_count >= PJ_ARRAY_SIZE(atexit_func))
+	return PJ_ETOOMANY;
+
+    atexit_func[atexit_count++] = func;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * pj_shutdown(void)
+ */
+PJ_DEF(void) pj_shutdown()
+{
+    int i;
+
+    /* Only perform shutdown operation when 'initialized' reaches zero */
+    pj_assert(initialized > 0);
+    if (--initialized != 0)
+	return;
+
+    /* Display stack usage */
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    {
+	pj_thread_t *rec = (pj_thread_t*)main_thread;
+	PJ_LOG(5,(rec->obj_name, "Main thread stack max usage=%u by %s:%d", 
+		  rec->stk_max_usage, rec->caller_file, rec->caller_line));
+    }
+#endif
+
+    /* Call atexit() functions */
+    for (i=atexit_count-1; i>=0; --i) {
+	(*atexit_func[i])();
+    }
+    atexit_count = 0;
+
+    /* Free exception ID */
+    if (PJ_NO_MEMORY_EXCEPTION != -1) {
+	pj_exception_id_free(PJ_NO_MEMORY_EXCEPTION);
+	PJ_NO_MEMORY_EXCEPTION = -1;
+    }
+
+    /* Destroy PJLIB critical section */
+    pj_mutex_destroy(&critical_section_mutex);
+
+    /* Free PJLIB TLS */
+    if (thread_tls_id != -1) {
+	pj_thread_local_free(thread_tls_id);
+	thread_tls_id = -1;
+    }
+
+    /* Clear static variables */
+    pj_errno_clear_handlers();
+
+    /* Ticket #1132: Assertion when (re)starting PJLIB on different thread */
+    pj_bzero(main_thread, sizeof(main_thread));
+
+    /* Shutdown Winsock */
+    WSACleanup();
+}
+
+
+/*
+ * pj_getpid(void)
+ */
+PJ_DEF(pj_uint32_t) pj_getpid(void)
+{
+    PJ_CHECK_STACK();
+    return GetCurrentProcessId();
+}
+
+/*
+ * Check if this thread has been registered to PJLIB.
+ */
+PJ_DEF(pj_bool_t) pj_thread_is_registered(void)
+{
+    return pj_thread_local_get(thread_tls_id) != 0;
+}
+
+
+/*
+ * Get thread priority value for the thread.
+ */
+PJ_DEF(int) pj_thread_get_prio(pj_thread_t *thread)
+{
+    return GetThreadPriority(thread->hthread);
+}
+
+
+/*
+ * Set the thread priority.
+ */
+PJ_DEF(pj_status_t) pj_thread_set_prio(pj_thread_t *thread,  int prio)
+{
+#if PJ_HAS_THREADS
+    PJ_ASSERT_RETURN(thread, PJ_EINVAL);
+    PJ_ASSERT_RETURN(prio>=THREAD_PRIORITY_IDLE && 
+			prio<=THREAD_PRIORITY_TIME_CRITICAL,
+		     PJ_EINVAL);
+
+    if (SetThreadPriority(thread->hthread, prio) == FALSE)
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    return PJ_SUCCESS;
+
+#else
+    PJ_UNUSED_ARG(thread);
+    PJ_UNUSED_ARG(prio);
+    pj_assert("pj_thread_set_prio() called in non-threading mode!");
+    return PJ_EINVALIDOP;
+#endif
+}
+
+
+/*
+ * Get the lowest priority value available on this system.
+ */
+PJ_DEF(int) pj_thread_get_prio_min(pj_thread_t *thread)
+{
+    PJ_UNUSED_ARG(thread);
+    return THREAD_PRIORITY_IDLE;
+}
+
+
+/*
+ * Get the highest priority value available on this system.
+ */
+PJ_DEF(int) pj_thread_get_prio_max(pj_thread_t *thread)
+{
+    PJ_UNUSED_ARG(thread);
+    return THREAD_PRIORITY_TIME_CRITICAL;
+}
+
+
+/*
+ * Get native thread handle
+ */
+PJ_DEF(void*) pj_thread_get_os_handle(pj_thread_t *thread) 
+{
+    PJ_ASSERT_RETURN(thread, NULL);
+
+#if PJ_HAS_THREADS
+    return thread->hthread;
+#else
+    pj_assert("pj_thread_is_registered() called in non-threading mode!");
+    return NULL;
+#endif
+}
+
+/*
+ * pj_thread_register(..)
+ */
+PJ_DEF(pj_status_t) pj_thread_register ( const char *cstr_thread_name,
+					 pj_thread_desc desc,
+                                         pj_thread_t **thread_ptr)
+{
+    char stack_ptr;
+    pj_status_t rc;
+    pj_thread_t *thread = (pj_thread_t *)desc;
+    pj_str_t thread_name = pj_str((char*)cstr_thread_name);
+
+    /* Size sanity check. */
+    if (sizeof(pj_thread_desc) < sizeof(pj_thread_t)) {
+	pj_assert(!"Not enough pj_thread_desc size!");
+	return PJ_EBUG;
+    }
+
+    /* If a thread descriptor has been registered before, just return it. */
+    if (pj_thread_local_get (thread_tls_id) != 0) {
+	// 2006-02-26 bennylp:
+	//  This wouldn't work in all cases!.
+	//  If thread is created by external module (e.g. sound thread),
+	//  thread may be reused while the pool used for the thread descriptor
+	//  has been deleted by application.
+	//*thread_ptr = (pj_thread_t*)pj_thread_local_get (thread_tls_id);
+        //return PJ_SUCCESS;
+    }
+
+    /* Initialize and set the thread entry. */
+    pj_bzero(desc, sizeof(struct pj_thread_t));
+    thread->hthread = GetCurrentThread();
+    thread->idthread = GetCurrentThreadId();
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    thread->stk_start = &stack_ptr;
+    thread->stk_size = 0xFFFFFFFFUL;
+    thread->stk_max_usage = 0;
+#else
+    stack_ptr = '\0';
+#endif
+
+    if (cstr_thread_name && pj_strlen(&thread_name) < sizeof(thread->obj_name)-1)
+	pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), 
+			 cstr_thread_name, thread->idthread);
+    else
+	pj_ansi_snprintf(thread->obj_name, sizeof(thread->obj_name), 
+		         "thr%p", (void*)(pj_ssize_t)thread->idthread);
+    
+    rc = pj_thread_local_set(thread_tls_id, thread);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    *thread_ptr = thread;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_init(void)
+ */
+pj_status_t pj_thread_init(void)
+{
+    pj_status_t rc;
+    pj_thread_t *thread;
+
+    rc = pj_thread_local_alloc(&thread_tls_id);
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    return pj_thread_register("thr%p", main_thread, &thread);
+}
+
+static DWORD WINAPI thread_main(void *param)
+{
+    pj_thread_t *rec = param;
+    DWORD result;
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    rec->stk_start = (char*)&rec;
+#endif
+
+    if (pj_thread_local_set(thread_tls_id, rec) != PJ_SUCCESS) {
+	pj_assert(!"TLS is not set (pj_init() error?)");
+    }
+
+    PJ_LOG(6,(rec->obj_name, "Thread started"));
+
+    result = (*rec->proc)(rec->arg);
+
+    PJ_LOG(6,(rec->obj_name, "Thread quitting"));
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    PJ_LOG(5,(rec->obj_name, "Thread stack max usage=%u by %s:%d", 
+	      rec->stk_max_usage, rec->caller_file, rec->caller_line));
+#endif
+
+    return (DWORD)result;
+}
+
+/*
+ * pj_thread_create(...)
+ */
+PJ_DEF(pj_status_t) pj_thread_create( pj_pool_t *pool, 
+                                      const char *thread_name,
+				      pj_thread_proc *proc, 
+                                      void *arg,
+				      pj_size_t stack_size, 
+				      unsigned flags,
+                                      pj_thread_t **thread_ptr)
+{
+    DWORD dwflags = 0;
+    pj_thread_t *rec;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(pool && proc && thread_ptr, PJ_EINVAL);
+
+    /* Set flags */
+    if (flags & PJ_THREAD_SUSPENDED)
+	dwflags |= CREATE_SUSPENDED;
+
+    /* Create thread record and assign name for the thread */
+    rec = (struct pj_thread_t*) pj_pool_calloc(pool, 1, sizeof(pj_thread_t));
+    if (!rec)
+	return PJ_ENOMEM;
+
+    /* Set name. */
+    if (!thread_name)
+	thread_name = "thr%p";
+
+    if (strchr(thread_name, '%')) {
+	pj_ansi_snprintf(rec->obj_name, PJ_MAX_OBJ_NAME, thread_name, rec);
+    } else {
+	pj_ansi_strncpy(rec->obj_name, thread_name, PJ_MAX_OBJ_NAME);
+	rec->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (rec->obj_name, "Thread created"));
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0
+    rec->stk_size = stack_size ? (pj_uint32_t)stack_size : 0xFFFFFFFFUL;
+    rec->stk_max_usage = 0;
+#endif
+
+    /* Create the thread. */
+    rec->proc = proc;
+    rec->arg = arg;
+    rec->hthread = CreateThread(NULL, stack_size, 
+				thread_main, rec,
+				dwflags, &rec->idthread);
+    if (rec->hthread == NULL)
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    /* Success! */
+    *thread_ptr = rec;
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread-get_name()
+ */
+PJ_DEF(const char*) pj_thread_get_name(pj_thread_t *p)
+{
+    pj_thread_t *rec = (pj_thread_t*)p;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(p, "");
+
+    return rec->obj_name;
+}
+
+/*
+ * pj_thread_resume()
+ */
+PJ_DEF(pj_status_t) pj_thread_resume(pj_thread_t *p)
+{
+    pj_thread_t *rec = (pj_thread_t*)p;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+    if (ResumeThread(rec->hthread) == (DWORD)-1)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    else
+        return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_this()
+ */
+PJ_DEF(pj_thread_t*) pj_thread_this(void)
+{
+    pj_thread_t *rec = pj_thread_local_get(thread_tls_id);
+
+    if (rec == NULL) {
+	pj_assert(!"Calling pjlib from unknown/external thread. You must "
+		   "register external threads with pj_thread_register() "
+		   "before calling any pjlib functions.");
+    }
+
+    /*
+     * MUST NOT check stack because this function is called
+     * by PJ_CHECK_STACK() itself!!!
+     *
+     */
+
+    return rec;
+}
+
+/*
+ * pj_thread_join()
+ */
+PJ_DEF(pj_status_t) pj_thread_join(pj_thread_t *p)
+{
+    pj_thread_t *rec = (pj_thread_t *)p;
+    DWORD rc;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+    if (p == pj_thread_this())
+	return PJ_ECANCELLED;
+
+    PJ_LOG(6, (pj_thread_this()->obj_name, "Joining thread %s", p->obj_name));
+
+    rc = WaitForSingleObject(rec->hthread, INFINITE);
+
+    if (rc==WAIT_OBJECT_0)
+        return PJ_SUCCESS;
+    else if (rc==WAIT_TIMEOUT)
+        return PJ_ETIMEDOUT;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_thread_destroy()
+ */
+PJ_DEF(pj_status_t) pj_thread_destroy(pj_thread_t *p)
+{
+    pj_thread_t *rec = (pj_thread_t *)p;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(p, PJ_EINVAL);
+
+    if (CloseHandle(rec->hthread) == TRUE)
+        return PJ_SUCCESS;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_thread_sleep()
+ */
+PJ_DEF(pj_status_t) pj_thread_sleep(unsigned msec)
+{
+    PJ_CHECK_STACK();
+    Sleep(msec);
+    return PJ_SUCCESS;
+}
+
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
+/*
+ * pj_thread_check_stack()
+ * Implementation for PJ_CHECK_STACK()
+ */
+PJ_DEF(void) pj_thread_check_stack(const char *file, int line)
+{
+    char stk_ptr;
+    pj_uint32_t usage;
+    pj_thread_t *thread = pj_thread_this();
+
+    pj_assert(thread);
+
+    /* Calculate current usage. */
+    usage = (&stk_ptr > thread->stk_start) ? 
+		(pj_uint32_t)(&stk_ptr - thread->stk_start) :
+		(pj_uint32_t)(thread->stk_start - &stk_ptr);
+
+    /* Assert if stack usage is dangerously high. */
+    pj_assert("STACK OVERFLOW!! " && (usage <= thread->stk_size - 128));
+
+    /* Keep statistic. */
+    if (usage > thread->stk_max_usage) {
+	thread->stk_max_usage = usage;
+	thread->caller_file = file;
+	thread->caller_line = line;
+    }
+
+}
+
+/*
+ * pj_thread_get_stack_max_usage()
+ */
+PJ_DEF(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread)
+{
+    return thread->stk_max_usage;
+}
+
+/*
+ * pj_thread_get_stack_info()
+ */
+PJ_DEF(pj_status_t) pj_thread_get_stack_info( pj_thread_t *thread,
+					      const char **file,
+					      int *line )
+{
+    pj_assert(thread);
+
+    *file = thread->caller_file;
+    *line = thread->caller_line;
+    return 0;
+}
+
+#endif	/* PJ_OS_HAS_CHECK_STACK */
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * pj_atomic_create()
+ */
+PJ_DEF(pj_status_t) pj_atomic_create( pj_pool_t *pool, 
+                                      pj_atomic_value_t initial,
+                                      pj_atomic_t **atomic_ptr)
+{
+    pj_atomic_t *atomic_var = pj_pool_alloc(pool, sizeof(pj_atomic_t));
+    if (!atomic_var)
+	return PJ_ENOMEM;
+
+    atomic_var->value = initial;
+    *atomic_ptr = atomic_var;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_atomic_destroy()
+ */
+PJ_DEF(pj_status_t) pj_atomic_destroy( pj_atomic_t *var )
+{
+    PJ_UNUSED_ARG(var);
+    PJ_ASSERT_RETURN(var, PJ_EINVAL);
+
+    return 0;
+}
+
+/*
+ * pj_atomic_set()
+ */
+PJ_DEF(void) pj_atomic_set( pj_atomic_t *atomic_var, pj_atomic_value_t value)
+{
+    PJ_CHECK_STACK();
+
+    InterlockedExchange(&atomic_var->value, value);
+}
+
+/*
+ * pj_atomic_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(atomic_var, 0);
+
+    return atomic_var->value;
+}
+
+/*
+ * pj_atomic_inc_and_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var)
+{
+    PJ_CHECK_STACK();
+
+#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+    return InterlockedIncrement(&atomic_var->value);
+#else
+    return InterlockedIncrement(&atomic_var->value);
+#endif
+}
+
+/*
+ * pj_atomic_inc()
+ */
+PJ_DEF(void) pj_atomic_inc(pj_atomic_t *atomic_var)
+{
+    pj_atomic_inc_and_get(atomic_var);
+}
+
+/*
+ * pj_atomic_dec_and_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var)
+{
+    PJ_CHECK_STACK();
+
+#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+    return InterlockedDecrement(&atomic_var->value);
+#else
+    return InterlockedDecrement(&atomic_var->value);
+#endif
+}
+
+/*
+ * pj_atomic_dec()
+ */
+PJ_DEF(void) pj_atomic_dec(pj_atomic_t *atomic_var)
+{
+    pj_atomic_dec_and_get(atomic_var);
+}
+
+/*
+ * pj_atomic_add()
+ */
+PJ_DEF(void) pj_atomic_add( pj_atomic_t *atomic_var,
+			    pj_atomic_value_t value )
+{
+#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+    InterlockedExchangeAdd( &atomic_var->value, value );
+#else
+    InterlockedExchangeAdd( &atomic_var->value, value );
+#endif
+}
+
+/*
+ * pj_atomic_add_and_get()
+ */
+PJ_DEF(pj_atomic_value_t) pj_atomic_add_and_get( pj_atomic_t *atomic_var,
+			                         pj_atomic_value_t value)
+{
+#if defined(PJ_WIN32_WINNT) && PJ_WIN32_WINNT >= 0x0400
+    long oldValue = InterlockedExchangeAdd( &atomic_var->value, value);
+    return oldValue + value;
+#else
+    long oldValue = InterlockedExchangeAdd( &atomic_var->value, value);
+    return oldValue + value;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_thread_local_alloc()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_alloc(long *index)
+{
+    PJ_ASSERT_RETURN(index != NULL, PJ_EINVAL);
+
+    //Can't check stack because this function is called in the
+    //beginning before main thread is initialized.
+    //PJ_CHECK_STACK();
+
+    *index = TlsAlloc();
+
+    if (*index == TLS_OUT_OF_INDEXES)
+        return PJ_RETURN_OS_ERROR(GetLastError());
+    else
+        return PJ_SUCCESS;
+}
+
+/*
+ * pj_thread_local_free()
+ */
+PJ_DEF(void) pj_thread_local_free(long index)
+{
+    PJ_CHECK_STACK();
+    TlsFree(index);
+}
+
+/*
+ * pj_thread_local_set()
+ */
+PJ_DEF(pj_status_t) pj_thread_local_set(long index, void *value)
+{
+    BOOL rc;
+
+    //Can't check stack because this function is called in the
+    //beginning before main thread is initialized.
+    //PJ_CHECK_STACK();
+    rc = TlsSetValue(index, value);
+    return rc!=0 ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_thread_local_get()
+ */
+PJ_DEF(void*) pj_thread_local_get(long index)
+{
+    //Can't check stack because this function is called
+    //by PJ_CHECK_STACK() itself!!!
+    //PJ_CHECK_STACK();
+    return TlsGetValue(index);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static pj_status_t init_mutex(pj_mutex_t *mutex, const char *name)
+{
+
+    PJ_CHECK_STACK();
+
+#if PJ_WIN32_WINNT >= 0x0400
+    InitializeCriticalSection(&mutex->crit);
+#else
+    mutex->hMutex = CreateMutex(NULL, FALSE, NULL);
+    if (!mutex->hMutex) {
+	return PJ_RETURN_OS_ERROR(GetLastError());
+    }
+#endif
+
+#if PJ_DEBUG
+    /* Set owner. */
+    mutex->nesting_level = 0;
+    mutex->owner = NULL;
+#endif
+
+    /* Set name. */
+    if (!name) {
+	name = "mtx%p";
+    }
+    if (strchr(name, '%')) {
+	pj_ansi_snprintf(mutex->obj_name, PJ_MAX_OBJ_NAME, name, mutex);
+    } else {
+	pj_ansi_strncpy(mutex->obj_name, name, PJ_MAX_OBJ_NAME);
+	mutex->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (mutex->obj_name, "Mutex created"));
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_create()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create(pj_pool_t *pool, 
+                                    const char *name, 
+                                    int type,
+                                    pj_mutex_t **mutex_ptr)
+{
+    pj_status_t rc;
+    pj_mutex_t *mutex;
+
+    PJ_UNUSED_ARG(type);
+    PJ_ASSERT_RETURN(pool && mutex_ptr, PJ_EINVAL);
+
+    mutex = pj_pool_alloc(pool, sizeof(*mutex));
+    if (!mutex)
+        return PJ_ENOMEM;
+
+    rc = init_mutex(mutex, name);
+    if (rc != PJ_SUCCESS)
+        return rc;
+
+    *mutex_ptr = mutex;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * pj_mutex_create_simple()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_simple( pj_pool_t *pool, 
+                                            const char *name,
+					    pj_mutex_t **mutex )
+{
+    return pj_mutex_create(pool, name, PJ_MUTEX_SIMPLE, mutex);
+}
+
+/*
+ * pj_mutex_create_recursive()
+ */
+PJ_DEF(pj_status_t) pj_mutex_create_recursive( pj_pool_t *pool,
+					       const char *name,
+					       pj_mutex_t **mutex )
+{
+    return pj_mutex_create(pool, name, PJ_MUTEX_RECURSE, mutex);
+}
+
+/*
+ * pj_mutex_lock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex)
+{
+    pj_status_t status;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is waiting", 
+				pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+    EnterCriticalSection(&mutex->crit);
+    status=PJ_SUCCESS;
+#else
+    if (WaitForSingleObject(mutex->hMutex, INFINITE)==WAIT_OBJECT_0)
+        status = PJ_SUCCESS;
+    else
+        status = PJ_STATUS_FROM_OS(GetLastError());
+
+#endif
+    LOG_MUTEX((mutex->obj_name, 
+	      (status==PJ_SUCCESS ? "Mutex acquired by thread %s" : "FAILED by %s"),
+	      pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+    if (status == PJ_SUCCESS) {
+	mutex->owner = pj_thread_this();
+	++mutex->nesting_level;
+    }
+#endif
+
+    return status;
+}
+
+/*
+ * pj_mutex_unlock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex)
+{
+    pj_status_t status;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+#if PJ_DEBUG
+    pj_assert(mutex->owner == pj_thread_this());
+    if (--mutex->nesting_level == 0) {
+	mutex->owner = NULL;
+    }
+#endif
+
+    LOG_MUTEX((mutex->obj_name, "Mutex released by thread %s", 
+				pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+    LeaveCriticalSection(&mutex->crit);
+    status=PJ_SUCCESS;
+#else
+    status = ReleaseMutex(mutex->hMutex) ? PJ_SUCCESS : 
+                PJ_STATUS_FROM_OS(GetLastError());
+#endif
+    return status;
+}
+
+/*
+ * pj_mutex_trylock()
+ */
+PJ_DEF(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex)
+{
+    pj_status_t status;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    LOG_MUTEX((mutex->obj_name, "Mutex: thread %s is trying", 
+				pj_thread_this()->obj_name));
+
+#if PJ_WIN32_WINNT >= 0x0400
+    status=TryEnterCriticalSection(&mutex->crit) ? PJ_SUCCESS : PJ_EUNKNOWN;
+#else
+    status = WaitForSingleObject(mutex->hMutex, 0)==WAIT_OBJECT_0 ? 
+                PJ_SUCCESS : PJ_ETIMEDOUT;
+#endif
+    if (status==PJ_SUCCESS) {
+	LOG_MUTEX((mutex->obj_name, "Mutex acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+
+#if PJ_DEBUG
+	mutex->owner = pj_thread_this();
+	++mutex->nesting_level;
+#endif
+    } else {
+	LOG_MUTEX((mutex->obj_name, "Mutex: thread %s's trylock() failed", 
+				    pj_thread_this()->obj_name));
+    }
+
+    return status;
+}
+
+/*
+ * pj_mutex_destroy()
+ */
+PJ_DEF(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    LOG_MUTEX((mutex->obj_name, "Mutex destroyed"));
+
+#if PJ_WIN32_WINNT >= 0x0400
+    DeleteCriticalSection(&mutex->crit);
+    return PJ_SUCCESS;
+#else
+    return CloseHandle(mutex->hMutex) ? PJ_SUCCESS : 
+            PJ_RETURN_OS_ERROR(GetLastError());
+#endif
+}
+
+/*
+ * pj_mutex_is_locked()
+ */
+PJ_DEF(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex)
+{
+#if PJ_DEBUG
+    return mutex->owner == pj_thread_this();
+#else
+    PJ_UNUSED_ARG(mutex);
+    pj_assert(!"PJ_DEBUG is not set!");
+    return 1;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Win32 lacks Read/Write mutex, so include the emulation.
+ */
+#include "os_rwmutex.c"
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * pj_enter_critical_section()
+ */
+PJ_DEF(void) pj_enter_critical_section(void)
+{
+    pj_mutex_lock(&critical_section_mutex);
+}
+
+
+/*
+ * pj_leave_critical_section()
+ */
+PJ_DEF(void) pj_leave_critical_section(void)
+{
+    pj_mutex_unlock(&critical_section_mutex);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+
+/*
+ * pj_sem_create()
+ */
+PJ_DEF(pj_status_t) pj_sem_create( pj_pool_t *pool, 
+                                   const char *name,
+				   unsigned initial, 
+                                   unsigned max,
+                                   pj_sem_t **sem_ptr)
+{
+    pj_sem_t *sem;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(pool && sem_ptr, PJ_EINVAL);
+
+    sem = pj_pool_alloc(pool, sizeof(*sem));    
+    sem->hSemaphore = CreateSemaphore(NULL, initial, max, NULL);
+    if (!sem->hSemaphore)
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    /* Set name. */
+    if (!name) {
+	name = "sem%p";
+    }
+    if (strchr(name, '%')) {
+	pj_ansi_snprintf(sem->obj_name, PJ_MAX_OBJ_NAME, name, sem);
+    } else {
+	pj_ansi_strncpy(sem->obj_name, name, PJ_MAX_OBJ_NAME);
+	sem->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    LOG_MUTEX((sem->obj_name, "Semaphore created"));
+
+    *sem_ptr = sem;
+    return PJ_SUCCESS;
+}
+
+static pj_status_t pj_sem_wait_for(pj_sem_t *sem, unsigned timeout)
+{
+    DWORD result;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+    LOG_MUTEX((sem->obj_name, "Semaphore: thread %s is waiting", 
+			      pj_thread_this()->obj_name));
+
+    result = WaitForSingleObject(sem->hSemaphore, timeout);
+    if (result == WAIT_OBJECT_0) {
+	LOG_MUTEX((sem->obj_name, "Semaphore acquired by thread %s", 
+				  pj_thread_this()->obj_name));
+    } else {
+	LOG_MUTEX((sem->obj_name, "Semaphore: thread %s FAILED to acquire", 
+				  pj_thread_this()->obj_name));
+    }
+
+    if (result==WAIT_OBJECT_0)
+        return PJ_SUCCESS;
+    else if (result==WAIT_TIMEOUT)
+        return PJ_ETIMEDOUT;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_sem_wait()
+ */
+PJ_DEF(pj_status_t) pj_sem_wait(pj_sem_t *sem)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+    return pj_sem_wait_for(sem, INFINITE);
+}
+
+/*
+ * pj_sem_trywait()
+ */
+PJ_DEF(pj_status_t) pj_sem_trywait(pj_sem_t *sem)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+    return pj_sem_wait_for(sem, 0);
+}
+
+/*
+ * pj_sem_post()
+ */
+PJ_DEF(pj_status_t) pj_sem_post(pj_sem_t *sem)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+    LOG_MUTEX((sem->obj_name, "Semaphore released by thread %s",
+			      pj_thread_this()->obj_name));
+
+    if (ReleaseSemaphore(sem->hSemaphore, 1, NULL))
+        return PJ_SUCCESS;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_sem_destroy()
+ */
+PJ_DEF(pj_status_t) pj_sem_destroy(pj_sem_t *sem)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sem, PJ_EINVAL);
+
+    LOG_MUTEX((sem->obj_name, "Semaphore destroyed by thread %s",
+			      pj_thread_this()->obj_name));
+
+    if (CloseHandle(sem->hSemaphore))
+        return PJ_SUCCESS;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+#endif	/* PJ_HAS_SEMAPHORE */
+///////////////////////////////////////////////////////////////////////////////
+
+
+#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
+
+/*
+ * pj_event_create()
+ */
+PJ_DEF(pj_status_t) pj_event_create( pj_pool_t *pool, 
+                                     const char *name,
+				     pj_bool_t manual_reset, 
+                                     pj_bool_t initial,
+                                     pj_event_t **event_ptr)
+{
+    pj_event_t *event;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(pool && event_ptr, PJ_EINVAL);
+
+    event = pj_pool_alloc(pool, sizeof(*event));
+    if (!event)
+        return PJ_ENOMEM;
+
+    event->hEvent = CreateEvent(NULL, manual_reset?TRUE:FALSE, 
+				initial?TRUE:FALSE, NULL);
+
+    if (!event->hEvent)
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    /* Set name. */
+    if (!name) {
+	name = "evt%p";
+    }
+    if (strchr(name, '%')) {
+	pj_ansi_snprintf(event->obj_name, PJ_MAX_OBJ_NAME, name, event);
+    } else {
+	pj_ansi_strncpy(event->obj_name, name, PJ_MAX_OBJ_NAME);
+	event->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+    }
+
+    PJ_LOG(6, (event->obj_name, "Event created"));
+
+    *event_ptr = event;
+    return PJ_SUCCESS;
+}
+
+static pj_status_t pj_event_wait_for(pj_event_t *event, unsigned timeout)
+{
+    DWORD result;
+
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+    PJ_LOG(6, (event->obj_name, "Event: thread %s is waiting", 
+			        pj_thread_this()->obj_name));
+
+    result = WaitForSingleObject(event->hEvent, timeout);
+    if (result == WAIT_OBJECT_0) {
+	PJ_LOG(6, (event->obj_name, "Event: thread %s is released", 
+				    pj_thread_this()->obj_name));
+    } else {
+	PJ_LOG(6, (event->obj_name, "Event: thread %s FAILED to acquire", 
+				    pj_thread_this()->obj_name));
+    }
+
+    if (result==WAIT_OBJECT_0)
+        return PJ_SUCCESS;
+    else if (result==WAIT_TIMEOUT)
+        return PJ_ETIMEDOUT;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_wait()
+ */
+PJ_DEF(pj_status_t) pj_event_wait(pj_event_t *event)
+{
+    PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+    return pj_event_wait_for(event, INFINITE);
+}
+
+/*
+ * pj_event_trywait()
+ */
+PJ_DEF(pj_status_t) pj_event_trywait(pj_event_t *event)
+{
+    PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+    return pj_event_wait_for(event, 0);
+}
+
+/*
+ * pj_event_set()
+ */
+PJ_DEF(pj_status_t) pj_event_set(pj_event_t *event)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+    PJ_LOG(6, (event->obj_name, "Setting event"));
+
+    if (SetEvent(event->hEvent))
+        return PJ_SUCCESS;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_pulse()
+ */
+PJ_DEF(pj_status_t) pj_event_pulse(pj_event_t *event)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+    PJ_LOG(6, (event->obj_name, "Pulsing event"));
+
+    if (PulseEvent(event->hEvent))
+        return PJ_SUCCESS;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_reset()
+ */
+PJ_DEF(pj_status_t) pj_event_reset(pj_event_t *event)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+    PJ_LOG(6, (event->obj_name, "Event is reset"));
+
+    if (ResetEvent(event->hEvent))
+        return PJ_SUCCESS;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_event_destroy()
+ */
+PJ_DEF(pj_status_t) pj_event_destroy(pj_event_t *event)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(event, PJ_EINVAL);
+
+    PJ_LOG(6, (event->obj_name, "Event is destroying"));
+
+    if (CloseHandle(event->hEvent))
+        return PJ_SUCCESS;
+    else
+        return PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+#endif	/* PJ_HAS_EVENT_OBJ */
+
+///////////////////////////////////////////////////////////////////////////////
+#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0
+/*
+ * Terminal color
+ */
+
+static WORD pj_color_to_os_attr(pj_color_t color)
+{
+    WORD attr = 0;
+
+    if (color & PJ_TERM_COLOR_R)
+	attr |= FOREGROUND_RED;
+    if (color & PJ_TERM_COLOR_G)
+	attr |= FOREGROUND_GREEN;
+    if (color & PJ_TERM_COLOR_B)
+	attr |= FOREGROUND_BLUE;
+    if (color & PJ_TERM_COLOR_BRIGHT)
+	attr |= FOREGROUND_INTENSITY;
+
+    return attr;
+}
+
+static pj_color_t os_attr_to_pj_color(WORD attr)
+{
+    int color = 0;
+
+    if (attr & FOREGROUND_RED)
+	color |= PJ_TERM_COLOR_R;
+    if (attr & FOREGROUND_GREEN)
+	color |= PJ_TERM_COLOR_G;
+    if (attr & FOREGROUND_BLUE)
+	color |= PJ_TERM_COLOR_B;
+    if (attr & FOREGROUND_INTENSITY)
+	color |= PJ_TERM_COLOR_BRIGHT;
+
+    return color;
+}
+
+
+/*
+ * pj_term_set_color()
+ */
+PJ_DEF(pj_status_t) pj_term_set_color(pj_color_t color)
+{
+    BOOL rc;
+    WORD attr = 0;
+
+    PJ_CHECK_STACK();
+
+    attr = pj_color_to_os_attr(color);
+    rc = SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), attr);
+    return rc ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(GetLastError());
+}
+
+/*
+ * pj_term_get_color()
+ * Get current terminal foreground color.
+ */
+PJ_DEF(pj_color_t) pj_term_get_color(void)
+{
+    CONSOLE_SCREEN_BUFFER_INFO info;
+
+    PJ_CHECK_STACK();
+
+    GetConsoleScreenBufferInfo( GetStdHandle(STD_OUTPUT_HANDLE), &info);
+    return os_attr_to_pj_color(info.wAttributes);
+}
+
+#endif	/* PJ_TERM_HAS_COLOR */
+
+/*
+ * pj_run_app()
+ */
+PJ_DEF(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[],
+                       unsigned flags)
+{
+    PJ_UNUSED_ARG(flags);
+    return (*main_func)(argc, argv);
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/os_error_linux_kernel.c b/jni/pjproject-android/pjlib/src/pj/os_error_linux_kernel.c
new file mode 100644
index 0000000..bd2ac90
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_error_linux_kernel.c
@@ -0,0 +1,81 @@
+/* $Id: os_error_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/string.h>
+#include <pj/compat/errno.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#if defined(MODVERSIONS)
+#include <linux/modversions.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+int kernel_errno;
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+    return errno;
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+    errno = code;
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+    return errno;
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+    errno = code;
+}
+
+/* 
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror() 
+ * in errno.c 
+ */
+int platform_strerror( pj_os_err_type os_errcode, 
+                       char *buf, pj_size_t bufsize)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+    int len;
+    
+    /* Handle EINVAL as special case so that it'll pass errno test. */
+    if (os_errcode==EINVAL)
+	strcpy(errmsg, "Invalid value");
+    else
+	snprintf(errmsg, sizeof(errmsg), "errno=%d", os_errcode);
+    
+    len = strlen(errmsg);
+
+    if (len >= bufsize)
+	len = bufsize-1;
+
+    pj_memcpy(buf, errmsg, len);
+    buf[len] = '\0';
+
+    return len;
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_error_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/os_error_symbian.cpp
new file mode 100644
index 0000000..607754c
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_error_symbian.cpp
@@ -0,0 +1,172 @@
+/* $Id: os_error_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/errno.h>
+#include <pj/assert.h>
+#include <pj/compat/stdarg.h>
+#include <pj/unicode.h>
+#include <pj/string.h>
+
+#include <e32err.h>
+#include <in_sock.h>
+
+
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0)
+static const struct {
+    pj_os_err_type code;
+    const char *msg;
+} gaErrorList[] = {
+    /*
+     * Generic error -1 to -46
+     */
+    PJ_BUILD_ERR( KErrNotFound,	    "Unable to find the specified object"),
+    PJ_BUILD_ERR( KErrGeneral,	    "General (unspecified) error"),
+    PJ_BUILD_ERR( KErrCancel,	    "The operation was cancelled"),
+    PJ_BUILD_ERR( KErrNoMemory,	    "Not enough memory"),
+    PJ_BUILD_ERR( KErrNotSupported, "The operation requested is not supported"),
+    PJ_BUILD_ERR( KErrArgument,	    "Bad request"),
+    PJ_BUILD_ERR( KErrTotalLossOfPrecision, "Total loss of precision"),
+    PJ_BUILD_ERR( KErrBadHandle,    "Bad object"),
+    PJ_BUILD_ERR( KErrOverflow,	    "Overflow"),
+    PJ_BUILD_ERR( KErrUnderflow,    "Underflow"),
+    PJ_BUILD_ERR( KErrAlreadyExists,"Already exists"),
+    PJ_BUILD_ERR( KErrPathNotFound, "Unable to find the specified folder"),
+    PJ_BUILD_ERR( KErrDied,	    "Closed"),
+    PJ_BUILD_ERR( KErrInUse,	    "The specified object is currently in use by another program"),
+    PJ_BUILD_ERR( KErrServerTerminated,	    "Server has closed"),
+    PJ_BUILD_ERR( KErrServerBusy,   "Server busy"),
+    PJ_BUILD_ERR( KErrCompletion,   "Completion error"),
+    PJ_BUILD_ERR( KErrNotReady,	    "Not ready"),
+    PJ_BUILD_ERR( KErrUnknown,	    "Unknown error"),
+    PJ_BUILD_ERR( KErrCorrupt,	    "Corrupt"),
+    PJ_BUILD_ERR( KErrAccessDenied, "Access denied"),
+    PJ_BUILD_ERR( KErrLocked,	    "Locked"),
+    PJ_BUILD_ERR( KErrWrite,	    "Failed to write"),
+    PJ_BUILD_ERR( KErrDisMounted,   "Wrong disk present"),
+    PJ_BUILD_ERR( KErrEof,	    "Unexpected end of file"),
+    PJ_BUILD_ERR( KErrDiskFull,	    "Disk full"),
+    PJ_BUILD_ERR( KErrBadDriver,    "Bad device driver"),
+    PJ_BUILD_ERR( KErrBadName,	    "Bad name"),
+    PJ_BUILD_ERR( KErrCommsLineFail,"Comms line failed"),
+    PJ_BUILD_ERR( KErrCommsFrame,   "Comms frame error"),
+    PJ_BUILD_ERR( KErrCommsOverrun, "Comms overrun error"),
+    PJ_BUILD_ERR( KErrCommsParity,  "Comms parity error"),
+    PJ_BUILD_ERR( KErrTimedOut,	    "Timed out"),
+    PJ_BUILD_ERR( KErrCouldNotConnect, "Failed to connect"),
+    PJ_BUILD_ERR( KErrCouldNotDisconnect, "Failed to disconnect"),
+    PJ_BUILD_ERR( KErrDisconnected, "Disconnected"),
+    PJ_BUILD_ERR( KErrBadLibraryEntryPoint, "Bad library entry point"),
+    PJ_BUILD_ERR( KErrBadDescriptor,"Bad descriptor"),
+    PJ_BUILD_ERR( KErrAbort,	    "Interrupted"),
+    PJ_BUILD_ERR( KErrTooBig,	    "Too big"),
+    PJ_BUILD_ERR( KErrDivideByZero, "Divide by zero"),
+    PJ_BUILD_ERR( KErrBadPower,	    "Batteries too low"),
+    PJ_BUILD_ERR( KErrDirFull,	    "Folder full"),
+    PJ_BUILD_ERR( KErrHardwareNotAvailable, ""),
+    PJ_BUILD_ERR( KErrSessionClosed,	    ""),
+    PJ_BUILD_ERR( KErrPermissionDenied,     ""),
+
+    /*
+     * Socket errors (-190 - -1000)
+     */
+    PJ_BUILD_ERR( KErrNetUnreach,   "Could not connect to the network. Currently unreachable"),
+    PJ_BUILD_ERR( KErrHostUnreach,  "Could not connect to the specified server"),
+    PJ_BUILD_ERR( KErrNoProtocolOpt,"The specified server refuses the selected protocol"),
+    PJ_BUILD_ERR( KErrUrgentData,   ""),
+    PJ_BUILD_ERR( KErrWouldBlock,   "Conflicts with KErrExtended, but cannot occur in practice"),
+
+    {0, NULL}
+};
+
+#endif	/* PJ_HAS_ERROR_STRING */
+
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+    return -1;
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+    PJ_UNUSED_ARG(code);
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+    return -1;
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+    PJ_UNUSED_ARG(code);
+}
+
+PJ_BEGIN_DECL
+
+    PJ_DECL(int) platform_strerror( pj_os_err_type os_errcode, 
+                       		    char *buf, pj_size_t bufsize);
+PJ_END_DECL
+
+/* 
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror() 
+ * in errno.c 
+ */
+PJ_DEF(int) platform_strerror( pj_os_err_type os_errcode, 
+			       char *buf, pj_size_t bufsize)
+{
+    int len = 0;
+
+    pj_assert(buf != NULL);
+    pj_assert(bufsize >= 0);
+
+    /*
+     * MUST NOT check stack here.
+     * This function might be called from PJ_CHECK_STACK() itself!
+       //PJ_CHECK_STACK();
+     */
+
+    if (!len) {
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0)
+	int i;
+        for (i = 0; gaErrorList[i].msg; ++i) {
+            if (gaErrorList[i].code == os_errcode) {
+                len = strlen(gaErrorList[i].msg);
+		if ((pj_size_t)len >= bufsize) {
+		    len = bufsize-1;
+		}
+		pj_memcpy(buf, gaErrorList[i].msg, len);
+		buf[len] = '\0';
+                break;
+            }
+        }
+#endif	/* PJ_HAS_ERROR_STRING */
+
+    }
+
+    if (!len) {
+	len = pj_ansi_snprintf( buf, bufsize, "Symbian native error %d", 
+				os_errcode);
+	buf[len] = '\0';
+    }
+
+    return len;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_error_unix.c b/jni/pjproject-android/pjlib/src/pj/os_error_unix.c
new file mode 100644
index 0000000..95c27b5
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_error_unix.c
@@ -0,0 +1,69 @@
+/* $Id: os_error_unix.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <errno.h>
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+    return PJ_STATUS_FROM_OS(errno);
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+    errno = PJ_STATUS_TO_OS(code);
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+    return PJ_STATUS_FROM_OS(errno);
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+    errno = PJ_STATUS_TO_OS(code);
+}
+
+PJ_BEGIN_DECL
+
+    PJ_DECL(int) platform_strerror(pj_os_err_type code, 
+                              	   char *buf, pj_size_t bufsize );
+PJ_END_DECL
+
+/* 
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror() 
+ * in errno.c 
+ */
+int platform_strerror( pj_os_err_type os_errcode, 
+                       char *buf, pj_size_t bufsize)
+{
+    const char *syserr = strerror(os_errcode);
+    pj_size_t len = syserr ? strlen(syserr) : 0;
+
+    if (len >= bufsize) len = bufsize - 1;
+    if (len > 0)
+	pj_memcpy(buf, syserr, len);
+    buf[len] = '\0';
+    return len;
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_error_win32.c b/jni/pjproject-android/pjlib/src/pj/os_error_win32.c
new file mode 100644
index 0000000..79df4f3
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_error_win32.c
@@ -0,0 +1,220 @@
+/* $Id: os_error_win32.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/errno.h>
+#include <pj/assert.h>
+#include <pj/compat/stdarg.h>
+#include <pj/unicode.h>
+#include <pj/string.h>
+
+
+#if defined(PJ_HAS_WINSOCK2_H) && PJ_HAS_WINSOCK2_H != 0
+#  include <winsock2.h>
+#elif defined(PJ_HAS_WINSOCK_H) && PJ_HAS_WINSOCK_H != 0
+#  include <winsock.h>
+#endif
+
+
+/*
+ * From Apache's APR:
+ */
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0)
+
+static const struct {
+    pj_os_err_type code;
+    const char *msg;
+} gaErrorList[] = {
+    PJ_BUILD_ERR( WSAEINTR,           "Interrupted system call"),
+    PJ_BUILD_ERR( WSAEBADF,           "Bad file number"),
+    PJ_BUILD_ERR( WSAEACCES,          "Permission denied"),
+    PJ_BUILD_ERR( WSAEFAULT,          "Bad address"),
+    PJ_BUILD_ERR( WSAEINVAL,          "Invalid argument"),
+    PJ_BUILD_ERR( WSAEMFILE,          "Too many open sockets"),
+    PJ_BUILD_ERR( WSAEWOULDBLOCK,     "Operation would block"),
+    PJ_BUILD_ERR( WSAEINPROGRESS,     "Operation now in progress"),
+    PJ_BUILD_ERR( WSAEALREADY,        "Operation already in progress"),
+    PJ_BUILD_ERR( WSAENOTSOCK,        "Socket operation on non-socket"),
+    PJ_BUILD_ERR( WSAEDESTADDRREQ,    "Destination address required"),
+    PJ_BUILD_ERR( WSAEMSGSIZE,        "Message too long"),
+    PJ_BUILD_ERR( WSAEPROTOTYPE,      "Protocol wrong type for socket"),
+    PJ_BUILD_ERR( WSAENOPROTOOPT,     "Bad protocol option"),
+    PJ_BUILD_ERR( WSAEPROTONOSUPPORT, "Protocol not supported"),
+    PJ_BUILD_ERR( WSAESOCKTNOSUPPORT, "Socket type not supported"),
+    PJ_BUILD_ERR( WSAEOPNOTSUPP,      "Operation not supported on socket"),
+    PJ_BUILD_ERR( WSAEPFNOSUPPORT,    "Protocol family not supported"),
+    PJ_BUILD_ERR( WSAEAFNOSUPPORT,    "Address family not supported"),
+    PJ_BUILD_ERR( WSAEADDRINUSE,      "Address already in use"),
+    PJ_BUILD_ERR( WSAEADDRNOTAVAIL,   "Can't assign requested address"),
+    PJ_BUILD_ERR( WSAENETDOWN,        "Network is down"),
+    PJ_BUILD_ERR( WSAENETUNREACH,     "Network is unreachable"),
+    PJ_BUILD_ERR( WSAENETRESET,       "Net connection reset"),
+    PJ_BUILD_ERR( WSAECONNABORTED,    "Software caused connection abort"),
+    PJ_BUILD_ERR( WSAECONNRESET,      "Connection reset by peer"),
+    PJ_BUILD_ERR( WSAENOBUFS,         "No buffer space available"),
+    PJ_BUILD_ERR( WSAEISCONN,         "Socket is already connected"),
+    PJ_BUILD_ERR( WSAENOTCONN,        "Socket is not connected"),
+    PJ_BUILD_ERR( WSAESHUTDOWN,       "Can't send after socket shutdown"),
+    PJ_BUILD_ERR( WSAETOOMANYREFS,    "Too many references, can't splice"),
+    PJ_BUILD_ERR( WSAETIMEDOUT,       "Connection timed out"),
+    PJ_BUILD_ERR( WSAECONNREFUSED,    "Connection refused"),
+    PJ_BUILD_ERR( WSAELOOP,           "Too many levels of symbolic links"),
+    PJ_BUILD_ERR( WSAENAMETOOLONG,    "File name too long"),
+    PJ_BUILD_ERR( WSAEHOSTDOWN,       "Host is down"),
+    PJ_BUILD_ERR( WSAEHOSTUNREACH,    "No route to host"),
+    PJ_BUILD_ERR( WSAENOTEMPTY,       "Directory not empty"),
+    PJ_BUILD_ERR( WSAEPROCLIM,        "Too many processes"),
+    PJ_BUILD_ERR( WSAEUSERS,          "Too many users"),
+    PJ_BUILD_ERR( WSAEDQUOT,          "Disc quota exceeded"),
+    PJ_BUILD_ERR( WSAESTALE,          "Stale NFS file handle"),
+    PJ_BUILD_ERR( WSAEREMOTE,         "Too many levels of remote in path"),
+    PJ_BUILD_ERR( WSASYSNOTREADY,     "Network system is unavailable"),
+    PJ_BUILD_ERR( WSAVERNOTSUPPORTED, "Winsock version out of range"),
+    PJ_BUILD_ERR( WSANOTINITIALISED,  "WSAStartup not yet called"),
+    PJ_BUILD_ERR( WSAEDISCON,         "Graceful shutdown in progress"),
+/*
+#define WSAENOMORE              (WSABASEERR+102)
+#define WSAECANCELLED           (WSABASEERR+103)
+#define WSAEINVALIDPROCTABLE    (WSABASEERR+104)
+#define WSAEINVALIDPROVIDER     (WSABASEERR+105)
+#define WSAEPROVIDERFAILEDINIT  (WSABASEERR+106)
+#define WSASYSCALLFAILURE       (WSABASEERR+107)
+#define WSASERVICE_NOT_FOUND    (WSABASEERR+108)
+#define WSATYPE_NOT_FOUND       (WSABASEERR+109)
+#define WSA_E_NO_MORE           (WSABASEERR+110)
+#define WSA_E_CANCELLED         (WSABASEERR+111)
+#define WSAEREFUSED             (WSABASEERR+112)
+ */
+    PJ_BUILD_ERR( WSAHOST_NOT_FOUND,  "Host not found"),
+/*
+#define WSATRY_AGAIN            (WSABASEERR+1002)
+#define WSANO_RECOVERY          (WSABASEERR+1003)
+ */
+    PJ_BUILD_ERR( WSANO_DATA,         "No host data of that type was found"),
+    {0, NULL}
+};
+
+#endif	/* PJ_HAS_ERROR_STRING */
+
+
+
+PJ_DEF(pj_status_t) pj_get_os_error(void)
+{
+    return PJ_STATUS_FROM_OS(GetLastError());
+}
+
+PJ_DEF(void) pj_set_os_error(pj_status_t code)
+{
+    SetLastError(PJ_STATUS_TO_OS(code));
+}
+
+PJ_DEF(pj_status_t) pj_get_netos_error(void)
+{
+    return PJ_STATUS_FROM_OS(WSAGetLastError());
+}
+
+PJ_DEF(void) pj_set_netos_error(pj_status_t code)
+{
+    WSASetLastError(PJ_STATUS_TO_OS(code));
+}
+
+/* 
+ * platform_strerror()
+ *
+ * Platform specific error message. This file is called by pj_strerror() 
+ * in errno.c 
+ */
+int platform_strerror( pj_os_err_type os_errcode, 
+                       char *buf, pj_size_t bufsize)
+{
+    pj_size_t len = 0;
+    PJ_DECL_UNICODE_TEMP_BUF(wbuf,128);
+
+    pj_assert(buf != NULL);
+    pj_assert(bufsize >= 0);
+
+    /*
+     * MUST NOT check stack here.
+     * This function might be called from PJ_CHECK_STACK() itself!
+       //PJ_CHECK_STACK();
+     */
+
+    if (!len) {
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING!=0)
+	int i;
+        for (i = 0; gaErrorList[i].msg; ++i) {
+            if (gaErrorList[i].code == os_errcode) {
+                len = strlen(gaErrorList[i].msg);
+		if ((pj_size_t)len >= bufsize) {
+		    len = bufsize-1;
+		}
+		pj_memcpy(buf, gaErrorList[i].msg, len);
+		buf[len] = '\0';
+                break;
+            }
+        }
+#endif	/* PJ_HAS_ERROR_STRING */
+
+    }
+
+
+    if (!len) {
+#if PJ_NATIVE_STRING_IS_UNICODE
+	len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM 
+			     | FORMAT_MESSAGE_IGNORE_INSERTS,
+			     NULL,
+			     os_errcode,
+			     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
+			     wbuf,
+			     sizeof(wbuf),
+			     NULL);
+	if (len) {
+	    pj_unicode_to_ansi(wbuf, len, buf, bufsize);
+	}
+#else
+	len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM 
+			     | FORMAT_MESSAGE_IGNORE_INSERTS,
+			     NULL,
+			     os_errcode,
+			     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
+			     buf,
+			     (int)bufsize,
+			     NULL);
+	buf[bufsize-1] = '\0';
+#endif
+
+	if (len) {
+	    /* Remove trailing newlines. */
+	    while (len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
+		buf[len-1] = '\0';
+		--len;
+	    }
+	}
+    }
+
+    if (!len) {
+	len = pj_ansi_snprintf( buf, bufsize, "Win32 error code %u", 
+				(unsigned)os_errcode);
+	if (len < 0 || len >= (int)bufsize)
+	    len = bufsize-1;
+	buf[len] = '\0';
+    }
+
+    return (int)len;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_info.c b/jni/pjproject-android/pjlib/src/pj/os_info.c
new file mode 100644
index 0000000..28ff856
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_info.c
@@ -0,0 +1,322 @@
+/* $Id: os_info.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/ctype.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+
+/*
+ * FYI these links contain useful infos about predefined macros across
+ * platforms:
+ *  - http://predef.sourceforge.net/preos.html
+ */
+
+#if defined(PJ_HAS_SYS_UTSNAME_H) && PJ_HAS_SYS_UTSNAME_H != 0
+/* For uname() */
+#   include <sys/utsname.h>
+#   include <stdlib.h>
+#   define PJ_HAS_UNAME		1
+#endif
+
+#if defined(PJ_HAS_LIMITS_H) && PJ_HAS_LIMITS_H != 0
+/* Include <limits.h> to get <features.h> to get various glibc macros.
+ * See http://predef.sourceforge.net/prelib.html
+ */
+#   include <limits.h>
+#endif
+
+#if defined(_MSC_VER)
+/* For all Windows including mobile */
+#   include <windows.h>
+#endif
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS != 0
+#   include "TargetConditionals.h"
+#endif
+
+#ifndef PJ_SYS_INFO_BUFFER_SIZE
+#   define PJ_SYS_INFO_BUFFER_SIZE	64
+#endif
+
+
+#if defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE
+    void pj_iphone_os_get_sys_info(pj_sys_info *si, pj_str_t *si_buffer);
+#endif
+    
+#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
+    PJ_BEGIN_DECL
+    unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size);
+    unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size);
+    void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver);
+    PJ_END_DECL
+#endif
+
+
+static char *ver_info(pj_uint32_t ver, char *buf)
+{
+    pj_size_t len;
+
+    if (ver == 0) {
+	*buf = '\0';
+	return buf;
+    }
+
+    sprintf(buf, "-%u.%u",
+	    (ver & 0xFF000000) >> 24,
+	    (ver & 0x00FF0000) >> 16);
+    len = strlen(buf);
+
+    if (ver & 0xFFFF) {
+	sprintf(buf+len, ".%u", (ver & 0xFF00) >> 8);
+	len = strlen(buf);
+
+	if (ver & 0x00FF) {
+	    sprintf(buf+len, ".%u", (ver & 0xFF));
+	}
+    }
+
+    return buf;
+}
+
+static pj_uint32_t parse_version(char *str)
+{
+    char *tok;
+    int i, maxtok;
+    pj_uint32_t version = 0;
+    
+    while (*str && !pj_isdigit(*str))
+	str++;
+
+    maxtok = 4;
+    for (tok = strtok(str, ".-"), i=0; tok && i<maxtok;
+	 ++i, tok=strtok(NULL, ".-"))
+    {
+	int n;
+
+	if (!pj_isdigit(*tok))
+	    break;
+	
+	n = atoi(tok);
+	version |= (n << ((3-i)*8));
+    }
+    
+    return version;
+}
+
+PJ_DEF(const pj_sys_info*) pj_get_sys_info(void)
+{
+    static char si_buffer[PJ_SYS_INFO_BUFFER_SIZE];
+    static pj_sys_info si;
+    static pj_bool_t si_initialized;
+    pj_size_t left = PJ_SYS_INFO_BUFFER_SIZE, len;
+
+    if (si_initialized)
+	return &si;
+
+    si.machine.ptr = si.os_name.ptr = si.sdk_name.ptr = si.info.ptr = "";
+
+#define ALLOC_CP_STR(str,field)	\
+	do { \
+	    len = pj_ansi_strlen(str); \
+	    if (len && left >= len+1) { \
+		si.field.ptr = si_buffer + PJ_SYS_INFO_BUFFER_SIZE - left; \
+		si.field.slen = len; \
+		pj_memcpy(si.field.ptr, str, len+1); \
+		left -= (len+1); \
+	    } \
+	} while (0)
+
+    /*
+     * Machine and OS info.
+     */
+#if defined(PJ_HAS_UNAME) && PJ_HAS_UNAME
+    #if defined(PJ_DARWINOS) && PJ_DARWINOS != 0 && TARGET_OS_IPHONE && \
+	(!defined TARGET_IPHONE_SIMULATOR || TARGET_IPHONE_SIMULATOR == 0)
+    {
+	pj_str_t buf = {si_buffer + PJ_SYS_INFO_BUFFER_SIZE - left, left};
+	pj_str_t machine = {"arm", 3};
+	pj_str_t sdk_name = {"iOS-SDK", 7};
+	char tmp[PJ_SYS_INFO_BUFFER_SIZE];
+	
+	pj_iphone_os_get_sys_info(&si, &buf);
+	left -= si.os_name.slen + 1;
+
+	si.os_ver = parse_version(si.machine.ptr);
+	
+	si.machine = machine;
+	si.sdk_name = sdk_name;
+
+	#ifdef PJ_SDK_NAME
+	pj_memcpy(tmp, PJ_SDK_NAME, pj_ansi_strlen(PJ_SDK_NAME) + 1);
+	si.sdk_ver = parse_version(tmp);
+	#endif
+    }
+    #else    
+    {
+	struct utsname u;
+
+	/* Successful uname() returns zero on Linux and positive value
+	 * on OpenSolaris.
+	 */
+	if (uname(&u) == -1)
+	    goto get_sdk_info;
+
+	ALLOC_CP_STR(u.machine, machine);
+	ALLOC_CP_STR(u.sysname, os_name);
+	
+	si.os_ver = parse_version(u.release);
+    }
+    #endif
+#elif defined(_MSC_VER)
+    {
+	OSVERSIONINFO ovi;
+
+	ovi.dwOSVersionInfoSize = sizeof(ovi);
+
+	if (GetVersionEx(&ovi) == FALSE)
+	    goto get_sdk_info;
+
+	si.os_ver = (ovi.dwMajorVersion << 24) |
+		    (ovi.dwMinorVersion << 16);
+	#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE
+	    si.os_name = pj_str("wince");
+	#else
+	    si.os_name = pj_str("win32");
+	#endif
+    }
+
+    {
+	SYSTEM_INFO wsi;
+
+	GetSystemInfo(&wsi);
+	switch (wsi.wProcessorArchitecture) {
+    #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE
+	case PROCESSOR_ARCHITECTURE_ARM:
+	    si.machine = pj_str("arm");
+	    break;
+	case PROCESSOR_ARCHITECTURE_SHX:
+	    si.machine = pj_str("shx");
+	    break;
+    #else
+	case PROCESSOR_ARCHITECTURE_AMD64:
+	    si.machine = pj_str("x86_64");
+	    break;
+	case PROCESSOR_ARCHITECTURE_IA64:
+	    si.machine = pj_str("ia64");
+	    break;
+	case PROCESSOR_ARCHITECTURE_INTEL:
+	    si.machine = pj_str("i386");
+	    break;
+    #endif	/* PJ_WIN32_WINCE */
+	}
+    }
+#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
+    {
+	pj_symbianos_get_model_info(si_buffer, sizeof(si_buffer));
+	ALLOC_CP_STR(si_buffer, machine);
+	
+	char *p = si_buffer + sizeof(si_buffer) - left;
+	unsigned plen;
+	plen = pj_symbianos_get_platform_info(p, left);
+	if (plen) {
+	    /* Output format will be "Series60vX.X" */
+	    si.os_name = pj_str("S60");
+	    si.os_ver  = parse_version(p+9);
+	} else {
+	    si.os_name = pj_str("Unknown");
+	}
+	
+	/* Avoid compile warning on Symbian. */
+	goto get_sdk_info;
+    }
+#endif
+
+    /*
+     * SDK info.
+     */
+get_sdk_info:
+
+#if defined(__GLIBC__)
+    si.sdk_ver = (__GLIBC__ << 24) |
+		 (__GLIBC_MINOR__ << 16);
+    si.sdk_name = pj_str("glibc");
+#elif defined(__GNU_LIBRARY__)
+    si.sdk_ver = (__GNU_LIBRARY__ << 24) |
+	         (__GNU_LIBRARY_MINOR__ << 16);
+    si.sdk_name = pj_str("libc");
+#elif defined(__UCLIBC__)
+    si.sdk_ver = (__UCLIBC_MAJOR__ << 24) |
+    	         (__UCLIBC_MINOR__ << 16);
+    si.sdk_name = pj_str("uclibc");
+#elif defined(_WIN32_WCE) && _WIN32_WCE
+    /* Old window mobile declares _WIN32_WCE as decimal (e.g. 300, 420, etc.),
+     * but then it was changed to use hex, e.g. 0x420, etc. See
+     * http://social.msdn.microsoft.com/forums/en-US/vssmartdevicesnative/thread/8a97c59f-5a1c-4bc6-99e6-427f065ff439/
+     */
+    #if _WIN32_WCE <= 500
+	si.sdk_ver = ( (_WIN32_WCE / 100) << 24) |
+		     ( ((_WIN32_WCE % 100) / 10) << 16) |
+		     ( (_WIN32_WCE % 10) << 8);
+    #else
+	si.sdk_ver = ( ((_WIN32_WCE & 0xFF00) >> 8) << 24) |
+		     ( ((_WIN32_WCE & 0x00F0) >> 4) << 16) |
+		     ( ((_WIN32_WCE & 0x000F) >> 0) << 8);
+    #endif
+    si.sdk_name = pj_str("cesdk");
+#elif defined(_MSC_VER)
+    /* No SDK info is easily obtainable for Visual C, so lets just use
+     * _MSC_VER. The _MSC_VER macro reports the major and minor versions
+     * of the compiler. For example, 1310 for Microsoft Visual C++ .NET 2003.
+     * 1310 represents version 13 and a 1.0 point release.
+     * The Visual C++ 2005 compiler version is 1400.
+     */
+    si.sdk_ver = ((_MSC_VER / 100) << 24) |
+    	         (((_MSC_VER % 100) / 10) << 16) |
+    	         ((_MSC_VER % 10) << 8);
+    si.sdk_name = pj_str("msvc");
+#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
+    pj_symbianos_get_sdk_info(&si.sdk_name, &si.sdk_ver);
+#endif
+
+    /*
+     * Build the info string.
+     */
+    {
+	char tmp[PJ_SYS_INFO_BUFFER_SIZE];
+	char os_ver[20], sdk_ver[20];
+	int cnt;
+
+	cnt = pj_ansi_snprintf(tmp, sizeof(tmp),
+			       "%s%s%s%s%s%s%s",
+			       si.os_name.ptr,
+			       ver_info(si.os_ver, os_ver),
+			       (si.machine.slen ? "/" : ""),
+			       si.machine.ptr,
+			       (si.sdk_name.slen ? "/" : ""),
+			       si.sdk_name.ptr,
+			       ver_info(si.sdk_ver, sdk_ver));
+	if (cnt > 0 && cnt < (int)sizeof(tmp)) {
+	    ALLOC_CP_STR(tmp, info);
+	}
+    }
+
+    si_initialized = PJ_TRUE;
+    return &si;
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/os_info_iphone.m b/jni/pjproject-android/pjlib/src/pj/os_info_iphone.m
new file mode 100644
index 0000000..b7315c1
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_info_iphone.m
@@ -0,0 +1,53 @@
+/* $Id: os_info_iphone.m 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "TargetConditionals.h"
+
+#if !defined TARGET_IPHONE_SIMULATOR || TARGET_IPHONE_SIMULATOR == 0
+
+#include <pj/os.h>
+#include <pj/string.h>
+
+#include <UIKit/UIDevice.h>
+
+void pj_iphone_os_get_sys_info(pj_sys_info *si, pj_str_t *si_buffer)
+{
+    unsigned buf_len = si_buffer->slen, left = si_buffer->slen, len;
+    UIDevice *device = [UIDevice currentDevice];
+    
+    if ([device respondsToSelector:@selector(isMultitaskingSupported)])
+	si->flags |= PJ_SYS_HAS_IOS_BG;
+    
+#define ALLOC_CP_STR(str,field)	\
+    do { \
+	len = [str length]; \
+	if (len && left >= len+1) { \
+	    si->field.ptr = si_buffer->ptr + buf_len - left; \
+	    si->field.slen = len; \
+	    [str getCString:si->field.ptr maxLength:len+1 \
+		 encoding:NSASCIIStringEncoding]; \
+	    left -= (len+1); \
+	} \
+    } while (0)
+
+    ALLOC_CP_STR([device systemName], os_name);
+    ALLOC_CP_STR([device systemVersion], machine);
+}
+
+#endif
diff --git a/jni/pjproject-android/pjlib/src/pj/os_info_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/os_info_symbian.cpp
new file mode 100644
index 0000000..9389f04
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_info_symbian.cpp
@@ -0,0 +1,190 @@
+/* $Id: os_info_symbian.cpp 3437 2011-03-08 06:30:34Z nanang $ */
+/* 
+ * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN == 0
+#   error This file is only for Symbian platform
+#endif
+
+#include <pj/ctype.h>
+#include <pj/string.h>
+
+#include <f32file.h>	/* link against efsrv.lib	*/
+#include <hal.h>	/* link against hal.lib		*/
+#include <utf.h>	/* link against charconv.lib	*/
+
+
+PJ_BEGIN_DECL
+unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size);
+unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size);
+void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver);
+PJ_END_DECL
+
+
+/* Get Symbian phone model info, returning length of model info */
+unsigned pj_symbianos_get_model_info(char *buf, unsigned buf_size)
+{
+    pj_str_t model_name;
+
+    /* Get machine UID */
+    TInt hal_val;
+    HAL::Get(HAL::EMachineUid, hal_val);
+    pj_ansi_snprintf(buf, buf_size, "0x%08X", hal_val);
+    pj_strset2(&model_name, buf);
+
+    /* Get model name */
+    const pj_str_t st_copyright = {"(C)", 3};
+    const pj_str_t st_nokia = {"Nokia", 5};
+    char tmp_buf[64];
+    pj_str_t tmp_str;
+
+    _LIT(KModelFilename,"Z:\\resource\\versions\\model.txt");
+    RFile file;
+    RFs fs;
+    TInt err;
+    
+    fs.Connect(1);
+    err = file.Open(fs, KModelFilename, EFileRead);
+    if (err == KErrNone) {
+	TFileText text;
+	text.Set(file);
+	TBuf16<64> ModelName16;
+	err = text.Read(ModelName16);
+	if (err == KErrNone) {
+	    TPtr8 ptr8((TUint8*)tmp_buf, sizeof(tmp_buf));
+	    ptr8.Copy(ModelName16);
+	    pj_strset(&tmp_str, tmp_buf, ptr8.Length());
+	    pj_strtrim(&tmp_str);
+	}
+	file.Close();
+    }
+    fs.Close();
+    if (err != KErrNone)
+	goto on_return;
+    
+    /* The retrieved model name is usually in long format, e.g: 
+     * "© Nokia N95 (01.01)", "(C) Nokia E52". As we need only
+     * the short version, let's clean it up.
+     */
+    
+    /* Remove preceding non-ASCII chars, e.g: "©" */
+    char *p = tmp_str.ptr;
+    while (!pj_isascii(*p)) { p++; }
+    pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr));
+    
+    /* Remove "(C)" */
+    p = pj_stristr(&tmp_str, &st_copyright);
+    if (p) {
+	p += st_copyright.slen;
+	pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr));
+    }
+
+    /* Remove "Nokia" */
+    p = pj_stristr(&tmp_str, &st_nokia);
+    if (p) {
+	p += st_nokia.slen;
+	pj_strset(&tmp_str, p, tmp_str.slen - (p - tmp_str.ptr));
+    }
+    
+    /* Remove language version, e.g: "(01.01)" */
+    p = pj_strchr(&tmp_str, '(');
+    if (p) {
+	tmp_str.slen = p - tmp_str.ptr;
+    }
+    
+    pj_strtrim(&tmp_str);
+    
+    if (tmp_str.slen == 0)
+	goto on_return;
+    
+    if ((unsigned)tmp_str.slen > buf_size - model_name.slen - 3)
+	tmp_str.slen = buf_size - model_name.slen - 3;
+    
+    pj_strcat2(&model_name, "(");
+    pj_strcat(&model_name, &tmp_str);
+    pj_strcat2(&model_name, ")");
+    
+    /* Zero terminate */
+    buf[model_name.slen] = '\0';
+    
+on_return:
+    return model_name.slen;
+}
+
+
+/* Get platform info, returned format will be "Series60vX.X" */
+unsigned pj_symbianos_get_platform_info(char *buf, unsigned buf_size)
+{
+    /* OS info */
+    _LIT(KS60ProductIDFile, "Series60v*.sis");
+    _LIT(KROMInstallDir, "z:\\system\\install\\");
+
+    RFs fs;
+    TFindFile ff(fs);
+    CDir* result;
+    pj_str_t plat_info = {NULL, 0};
+    TInt err;
+
+    fs.Connect(1);
+    err = ff.FindWildByDir(KS60ProductIDFile, KROMInstallDir, result);
+    if (err == KErrNone) {
+	err = result->Sort(ESortByName|EDescending);
+	if (err == KErrNone) {
+	    TPtr8 tmp_ptr8((TUint8*)buf, buf_size);
+	    const pj_str_t tmp_ext = {".sis", 4};
+	    char *p;
+	    
+	    tmp_ptr8.Copy((*result)[0].iName);
+	    pj_strset(&plat_info, buf, (pj_size_t)tmp_ptr8.Length());
+	    p = pj_stristr(&plat_info, &tmp_ext);
+	    if (p)
+		plat_info.slen -= (p - plat_info.ptr);
+	}
+	delete result;
+    }
+    fs.Close();
+    buf[plat_info.slen] = '\0';
+    
+    return plat_info.slen;
+}
+
+
+/* Get SDK info */
+void pj_symbianos_get_sdk_info(pj_str_t *name, pj_uint32_t *ver)
+{
+    const pj_str_t S60 = {"S60", 3};
+    #if defined(__SERIES60_30__)
+	*name = S60;
+	*ver  = (3 << 24);
+    #elif defined(__SERIES60_31__)
+	*name = S60;
+	*ver  = (3 << 24) | (1 << 16);
+    #elif defined(__S60_32__)
+	*name = S60;
+	*ver  = (3 << 24) | (2 << 16);
+    #elif defined(__S60_50__)
+	*name = S60;
+	*ver  = (5 << 24);
+    #elif defined(__NOKIA_N97__)
+	*name = pj_str("N97");
+	*ver  = (1 << 24);
+    #else
+	*name = pj_str("Unknown");
+	*ver  = 0;
+    #endif
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_rwmutex.c b/jni/pjproject-android/pjlib/src/pj/os_rwmutex.c
new file mode 100644
index 0000000..d392353
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_rwmutex.c
@@ -0,0 +1,163 @@
+/* $Id: os_rwmutex.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/* 
+ * Note: 
+ * 
+ * DO NOT BUILD THIS FILE DIRECTLY. THIS FILE WILL BE INCLUDED BY os_core_*.c
+ * WHEN MACRO PJ_EMULATE_RWMUTEX IS SET.
+ */
+
+/*
+ * os_rwmutex.c:
+ *
+ * Implementation of Read-Write mutex for platforms that lack it (e.g.
+ * Win32, RTEMS).
+ */
+
+
+struct pj_rwmutex_t
+{
+    pj_mutex_t *read_lock;
+    /* write_lock must use semaphore, because write_lock may be released
+     * by thread other than the thread that acquire the write_lock in the
+     * first place.
+     */
+    pj_sem_t   *write_lock;
+    pj_int32_t  reader_count;
+};
+
+/*
+ * Create reader/writer mutex.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name,
+				      pj_rwmutex_t **p_mutex)
+{
+    pj_status_t status;
+    pj_rwmutex_t *rwmutex;
+
+    PJ_ASSERT_RETURN(pool && p_mutex, PJ_EINVAL);
+
+    *p_mutex = NULL;
+    rwmutex = PJ_POOL_ALLOC_T(pool, pj_rwmutex_t);
+
+    status = pj_mutex_create_simple(pool, name, &rwmutex ->read_lock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pj_sem_create(pool, name, 1, 1, &rwmutex->write_lock);
+    if (status != PJ_SUCCESS) {
+	pj_mutex_destroy(rwmutex->read_lock);
+	return status;
+    }
+
+    rwmutex->reader_count = 0;
+    *p_mutex = rwmutex;
+    return PJ_SUCCESS;
+}
+
+/*
+ * Lock the mutex for reading.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    status = pj_mutex_lock(mutex->read_lock);
+    if (status != PJ_SUCCESS) {
+	pj_assert(!"This pretty much is unexpected");
+	return status;
+    }
+
+    mutex->reader_count++;
+
+    pj_assert(mutex->reader_count < 0x7FFFFFF0L);
+
+    if (mutex->reader_count == 1)
+	pj_sem_wait(mutex->write_lock);
+
+    status = pj_mutex_unlock(mutex->read_lock);
+    return status;
+}
+
+/*
+ * Lock the mutex for writing.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex)
+{
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+    return pj_sem_wait(mutex->write_lock);
+}
+
+/*
+ * Release read lock.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+
+    status = pj_mutex_lock(mutex->read_lock);
+    if (status != PJ_SUCCESS) {
+	pj_assert(!"This pretty much is unexpected");
+	return status;
+    }
+
+    pj_assert(mutex->reader_count >= 1);
+
+    --mutex->reader_count;
+    if (mutex->reader_count == 0)
+	pj_sem_post(mutex->write_lock);
+
+    status = pj_mutex_unlock(mutex->read_lock);
+    return status;
+}
+
+/*
+ * Release write lock.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex)
+{
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+    pj_assert(mutex->reader_count <= 1);
+    return pj_sem_post(mutex->write_lock);
+}
+
+
+/*
+ * Destroy reader/writer mutex.
+ *
+ */
+PJ_DEF(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex)
+{
+    PJ_ASSERT_RETURN(mutex, PJ_EINVAL);
+    pj_mutex_destroy(mutex->read_lock);
+    pj_sem_destroy(mutex->write_lock);
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_symbian.h b/jni/pjproject-android/pjlib/src/pj/os_symbian.h
new file mode 100644
index 0000000..9b3511b
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_symbian.h
@@ -0,0 +1,422 @@
+/* $Id: os_symbian.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __OS_SYMBIAN_H__
+#define __OS_SYMBIAN_H__
+
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/sock.h>
+#include <pj/os.h>
+#include <pj/string.h>
+
+#include <e32base.h>
+#include <e32cmn.h>
+#include <e32std.h>
+#include <es_sock.h>
+#include <in_sock.h>
+#include <charconv.h>
+#include <utf.h>
+#include <e32cons.h>
+
+// Forward declarations
+class CPjSocketReader;
+
+#ifndef PJ_SYMBIAN_TIMER_PRIORITY
+#    define PJ_SYMBIAN_TIMER_PRIORITY	EPriorityNormal
+#endif
+
+//
+// PJLIB Symbian's Socket
+//
+class CPjSocket
+{
+public:
+    enum
+    {
+	MAX_LEN = 1500,
+    };
+
+    // Construct CPjSocket
+    CPjSocket(int af, int sock_type, RSocket &sock)
+	: af_(af), sock_(sock), sock_type_(sock_type), connected_(false), 
+	  sockReader_(NULL)
+    { 
+    }
+
+    // Destroy CPjSocket
+    ~CPjSocket();
+
+    // Get address family
+    int GetAf() const 
+    {
+    	return af_;	
+    }
+    
+    // Get the internal RSocket
+    RSocket& Socket()
+    {
+	return sock_;
+    }
+
+    // Get socket connected flag.
+    bool IsConnected() const
+    {
+	return connected_;
+    }
+
+    // Set socket connected flag.
+    void SetConnected(bool connected)
+    {
+	connected_ = connected;
+    }
+
+    // Get socket type
+    int GetSockType() const
+    {
+	return sock_type_;
+    }
+    
+    // Returns true if socket is a datagram
+    bool IsDatagram() const
+    {
+	return sock_type_ == KSockDatagram;
+    }
+    
+    // Get socket reader, if any.
+    // May return NULL.
+    CPjSocketReader *Reader()
+    {
+	return sockReader_;
+    }
+
+    // Create socket reader.
+    CPjSocketReader *CreateReader(unsigned max_len=CPjSocket::MAX_LEN);
+
+    // Delete socket reader when it's not wanted.
+    void DestroyReader();
+    
+private:
+    int		     af_;
+    RSocket	     sock_;	    // Must not be reference, or otherwise
+				    // it may point to local variable!
+    unsigned   	     sock_type_;
+    
+    bool	     connected_;
+    CPjSocketReader *sockReader_;
+};
+
+
+//
+// Socket reader, used by select() and ioqueue abstraction
+//
+class CPjSocketReader : public CActive
+{
+public:
+    // Construct.
+    static CPjSocketReader *NewL(CPjSocket &sock, unsigned max_len=CPjSocket::MAX_LEN);
+
+    // Destroy;
+    ~CPjSocketReader();
+
+    // Start asynchronous read from the socket.
+    void StartRecv(void (*cb)(void *key)=NULL, 
+		   void *key=NULL, 
+		   TDes8 *aDesc = NULL,
+		   TUint flags = 0);
+
+    // Start asynchronous read from the socket.
+    void StartRecvFrom(void (*cb)(void *key)=NULL, 
+		       void *key=NULL, 
+		       TDes8 *aDesc = NULL,
+		       TUint flags = 0,
+		       TSockAddr *fromAddr = NULL);
+
+    // Cancel asynchronous read.
+    void DoCancel();
+
+    // Implementation: called when read has completed.
+    void RunL();
+
+    // Check if there's pending data.
+    bool HasData() const
+    {
+	return buffer_.Length() != 0;
+    }
+
+    // Append data to aDesc, up to aDesc's maximum size.
+    // If socket is datagram based, buffer_ will be clared.
+    void ReadData(TDes8 &aDesc, TInetAddr *addr=NULL);
+
+private:
+    CPjSocket	   &sock_;
+    bool	    isDatagram_;
+    TPtr8	    buffer_;
+    TInetAddr	    recvAddr_;
+
+    void	   (*readCb_)(void *key);
+    void	    *key_;
+
+    //
+    // Constructor
+    //
+    CPjSocketReader(CPjSocket &sock);
+    void ConstructL(unsigned max_len);
+};
+
+
+
+//
+// Time-out Timer Active Object
+//
+class CPjTimeoutTimer : public CActive
+{
+public:
+    static CPjTimeoutTimer *NewL();
+    ~CPjTimeoutTimer();
+
+    void StartTimer(TUint miliSeconds);
+    bool HasTimedOut() const;
+
+protected:
+    virtual void RunL();
+    virtual void DoCancel();
+    virtual TInt RunError(TInt aError);
+
+private:
+    RTimer	timer_;
+    pj_bool_t	hasTimedOut_;
+
+    CPjTimeoutTimer();
+    void ConstructL();
+};
+
+
+
+//
+// Symbian OS helper for PJLIB
+//
+class PjSymbianOS
+{
+public:
+    //
+    // Get the singleton instance of PjSymbianOS
+    //
+    static PjSymbianOS *Instance();
+
+    //
+    // Set parameters
+    //
+    void SetParameters(pj_symbianos_params *params);
+    
+    //
+    // Initialize.
+    //
+    TInt Initialize();
+
+    //
+    // Shutdown.
+    //
+    void Shutdown();
+
+
+    //
+    // Socket helper.
+    //
+
+    // Get RSocketServ instance to be used by all sockets.
+    RSocketServ &SocketServ()
+    {
+	return appSocketServ_ ? *appSocketServ_ : socketServ_;
+    }
+
+    // Get RConnection instance, if any.
+    RConnection *Connection() 
+    {
+    	return appConnection_;
+    }
+    
+    // Convert TInetAddr to pj_sockaddr_in
+    static inline pj_status_t Addr2pj(const TInetAddr & sym_addr,
+			       	      pj_sockaddr &pj_addr,
+			       	      int *addr_len,
+			       	      pj_bool_t convert_ipv4_mapped_addr = PJ_FALSE)
+    {
+    TUint fam = sym_addr.Family();
+	pj_bzero(&pj_addr, *addr_len);
+	if (fam == PJ_AF_INET || 
+			(convert_ipv4_mapped_addr && 
+			 fam == PJ_AF_INET6 && 
+			 sym_addr.IsV4Mapped())) 
+	{
+		pj_addr.addr.sa_family = PJ_AF_INET;
+	    PJ_ASSERT_RETURN(*addr_len>=(int)sizeof(pj_sockaddr_in), PJ_ETOOSMALL);
+	    pj_addr.ipv4.sin_addr.s_addr = pj_htonl(sym_addr.Address());
+	    pj_addr.ipv4.sin_port = pj_htons((pj_uint16_t) sym_addr.Port());
+	    *addr_len = sizeof(pj_sockaddr_in);
+	} else if (fam == PJ_AF_INET6) {
+	    PJ_ASSERT_RETURN(*addr_len>=(int)sizeof(pj_sockaddr_in6), PJ_ETOOSMALL);
+	    const TIp6Addr & ip6 = sym_addr.Ip6Address();
+	    pj_addr.addr.sa_family = PJ_AF_INET6;
+	    pj_memcpy(&pj_addr.ipv6.sin6_addr, ip6.u.iAddr8, 16);
+	    pj_addr.ipv6.sin6_port = pj_htons((pj_uint16_t) sym_addr.Port());
+	    pj_addr.ipv6.sin6_scope_id = pj_htonl(sym_addr.Scope());
+	    pj_addr.ipv6.sin6_flowinfo = pj_htonl(sym_addr.FlowLabel());
+	    *addr_len = sizeof(pj_sockaddr_in6);
+	} else {
+	    pj_assert(!"Unsupported address family");
+	    return PJ_EAFNOTSUP;
+	}
+	
+	return PJ_SUCCESS;
+    }
+
+
+    // Convert pj_sockaddr_in to TInetAddr
+    static inline pj_status_t pj2Addr(const pj_sockaddr &pj_addr,
+    				      int addrlen,
+			       	      TInetAddr & sym_addr)
+    {
+    	if (pj_addr.addr.sa_family == PJ_AF_INET) {
+    	    PJ_ASSERT_RETURN(addrlen >= (int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+	    sym_addr.Init(KAfInet);
+    	    sym_addr.SetAddress((TUint32)pj_ntohl(pj_addr.ipv4.sin_addr.s_addr));
+    	    sym_addr.SetPort(pj_ntohs(pj_addr.ipv4.sin_port));
+    	} else if (pj_addr.addr.sa_family == PJ_AF_INET6) {
+    	    TIp6Addr ip6;
+    	
+    	    PJ_ASSERT_RETURN(addrlen>=(int)sizeof(pj_sockaddr_in6), PJ_EINVAL);
+    	    pj_memcpy(ip6.u.iAddr8, &pj_addr.ipv6.sin6_addr, 16);
+    	    sym_addr.Init(KAfInet6);
+    	    sym_addr.SetAddress(ip6);
+    	    sym_addr.SetScope(pj_ntohl(pj_addr.ipv6.sin6_scope_id));
+    	    sym_addr.SetFlowLabel(pj_ntohl(pj_addr.ipv6.sin6_flowinfo));
+    	} else {
+    	    pj_assert(!"Unsupported address family");
+    	}
+    	return PJ_SUCCESS;
+    }
+
+
+    //
+    // Resolver helper
+    //
+
+    // Get RHostResolver instance
+    RHostResolver & GetResolver(int af)
+    {
+    	if (af==PJ_AF_INET6) {
+    	    return appHostResolver6_ ? *appHostResolver6_ : hostResolver6_;
+    	} else {
+    	    return appHostResolver_ ? *appHostResolver_ : hostResolver_;
+    	}
+    }
+
+    //
+    // Return true if the access point connection is up
+    //
+    bool IsConnectionUp() const
+    {
+	return isConnectionUp_;
+    }
+
+    //
+    // Set access point connection status
+    //
+    void SetConnectionStatus(bool up)
+    {
+	isConnectionUp_ = up;
+    }
+
+    //
+    // Unicode Converter
+    //
+
+    // Convert to Unicode
+    TInt ConvertToUnicode(TDes16 &aUnicode, const TDesC8 &aForeign);
+
+    // Convert from Unicode
+    TInt ConvertFromUnicode(TDes8 &aForeign, const TDesC16 &aUnicode);
+
+    //
+    // Get console
+    //
+    
+    // Get console
+    CConsoleBase *Console()
+    {
+	return console_;
+    }
+    
+    //
+    // Get select() timeout timer.
+    //
+    CPjTimeoutTimer *SelectTimeoutTimer()
+    {
+	return selectTimeoutTimer_;
+    }
+
+    //
+    // Wait for any active objects to run.
+    //
+    void WaitForActiveObjects(TInt aPriority = CActive::EPriorityStandard)
+    {
+	TInt aError;
+	CActiveScheduler::Current()->WaitForAnyRequest();
+	CActiveScheduler::RunIfReady(aError, aPriority);
+    }
+
+private:
+    bool isConnectionUp_;
+    
+    bool isSocketServInitialized_;
+    RSocketServ socketServ_;
+
+    bool isResolverInitialized_;
+    RHostResolver hostResolver_;
+    RHostResolver hostResolver6_;
+
+    CConsoleBase* console_;
+
+    CPjTimeoutTimer *selectTimeoutTimer_;
+
+    // App parameters
+    RSocketServ *appSocketServ_;
+    RConnection *appConnection_;
+    RHostResolver *appHostResolver_;
+    RHostResolver *appHostResolver6_;
+    
+private:
+    PjSymbianOS();
+};
+
+// This macro is used to check the access point connection status and return
+// failure if the AP connection is down or unusable. See the documentation
+// of pj_symbianos_set_connection_status() for more info
+#define PJ_SYMBIAN_CHECK_CONNECTION() \
+    PJ_SYMBIAN_CHECK_CONNECTION2(PJ_ECANCELLED)
+
+#define PJ_SYMBIAN_CHECK_CONNECTION2(retval) \
+    do { \
+	if (!PjSymbianOS::Instance()->IsConnectionUp()) \
+	    return retval; \
+    } while (0);
+
+#endif	/* __OS_SYMBIAN_H__ */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_time_bsd.c b/jni/pjproject-android/pjlib/src/pj/os_time_bsd.c
new file mode 100644
index 0000000..5240c9e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_time_bsd.c
@@ -0,0 +1,36 @@
+/* $Id: os_time_bsd.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/compat/time.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+    struct timeb tb;
+
+    PJ_CHECK_STACK();
+
+    ftime(&tb);
+    tv->sec = tb.time;
+    tv->msec = tb.millitm;
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_time_common.c b/jni/pjproject-android/pjlib/src/pj/os_time_common.c
new file mode 100644
index 0000000..cada701
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_time_common.c
@@ -0,0 +1,86 @@
+/* $Id: os_time_common.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/compat/time.h>
+#include <pj/errno.h>
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+    struct tm *local_time;
+
+    PJ_CHECK_STACK();
+
+    local_time = localtime((time_t*)&tv->sec);
+
+    pt->year = local_time->tm_year+1900;
+    pt->mon = local_time->tm_mon;
+    pt->day = local_time->tm_mday;
+    pt->hour = local_time->tm_hour;
+    pt->min = local_time->tm_min;
+    pt->sec = local_time->tm_sec;
+    pt->wday = local_time->tm_wday;
+    pt->msec = tv->msec;
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv)
+{
+    struct tm local_time;
+
+    local_time.tm_year = pt->year-1900;
+    local_time.tm_mon = pt->mon;
+    local_time.tm_mday = pt->day;
+    local_time.tm_hour = pt->hour;
+    local_time.tm_min = pt->min;
+    local_time.tm_sec = pt->sec;
+    local_time.tm_isdst = 0;
+    
+    tv->sec = mktime(&local_time);
+    tv->msec = pt->msec;
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv)
+{
+    PJ_UNUSED_ARG(tv);
+    return PJ_EBUG;
+}
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv)
+{
+    PJ_UNUSED_ARG(tv);
+    return PJ_EBUG;
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_time_linux_kernel.c b/jni/pjproject-android/pjlib/src/pj/os_time_linux_kernel.c
new file mode 100644
index 0000000..4c772a0
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_time_linux_kernel.c
@@ -0,0 +1,66 @@
+/* $Id: os_time_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <linux/time.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+    struct timeval tval;
+  
+    do_gettimeofday(&tval);
+    tv->sec = tval.tv_sec;
+    tv->msec = tval.tv_usec / 1000;
+
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+    pt->year = 2005;
+    pt->mon = 8;
+    pt->day = 20;
+    pt->hour = 16;
+    pt->min = 30;
+    pt->sec = 30;
+    pt->wday = 3;
+    pt->yday = 200;
+    pt->msec = 777;
+
+    return -1;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_time_unix.c b/jni/pjproject-android/pjlib/src/pj/os_time_unix.c
new file mode 100644
index 0000000..4f3139b
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_time_unix.c
@@ -0,0 +1,47 @@
+/* $Id: os_time_unix.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/errno.h>
+#include <pj/compat/time.h>
+
+#if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H!=0
+#    include <unistd.h>
+#endif
+
+#include <errno.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *p_tv)
+{
+    struct timeval the_time;
+    int rc;
+
+    PJ_CHECK_STACK();
+
+    rc = gettimeofday(&the_time, NULL);
+    if (rc != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+
+    p_tv->sec = the_time.tv_sec;
+    p_tv->msec = the_time.tv_usec / 1000;
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_time_win32.c b/jni/pjproject-android/pjlib/src/pj/os_time_win32.c
new file mode 100644
index 0000000..2143e2e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_time_win32.c
@@ -0,0 +1,303 @@
+/* $Id: os_time_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <windows.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define SECS_TO_FT_MULT 10000000
+
+static LARGE_INTEGER base_time;
+
+#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE
+#   define WINCE_TIME
+#endif
+
+#ifdef WINCE_TIME
+/* Note:
+ *  In Windows CE/Windows Mobile platforms, the availability of milliseconds
+ *  time resolution in SYSTEMTIME.wMilliseconds depends on the OEM, and most
+ *  likely it won't be available. When it's not available, the 
+ *  SYSTEMTIME.wMilliseconds will contain a constant arbitrary value.
+ *
+ *  Because of that, we need to emulate the milliseconds time resolution
+ *  using QueryPerformanceCounter() (via pj_get_timestamp() API). However 
+ *  there is limitation on using this, i.e. the time returned by 
+ *  pj_gettimeofday() may be off by up to plus/minus 999 msec (the second
+ *  part will be correct, however the msec part may be off), because we're 
+ *  not synchronizing the msec field with the change of value of the "second"
+ *  field of the system time.
+ *
+ *  Also there is other caveat which need to be handled (and they are 
+ *  handled by this implementation):
+ *   - user may change system time, so pj_gettimeofday() needs to periodically
+ *     checks if system time has changed. The period on which system time is
+ *     checked is controlled by PJ_WINCE_TIME_CHECK_INTERVAL macro.
+ */
+static LARGE_INTEGER g_start_time;  /* Time gettimeofday() is first called  */
+static pj_timestamp  g_start_tick;  /* TS gettimeofday() is first called  */
+static pj_timestamp  g_last_update; /* Last time check_system_time() is 
+				       called, to periodically synchronize
+				       with up-to-date system time (in case
+				       user changes system time).	    */
+static pj_uint64_t   g_update_period; /* Period (in TS) check_system_time()
+				         should be called.		    */
+
+/* Period on which check_system_time() is called, in seconds		    */
+#ifndef PJ_WINCE_TIME_CHECK_INTERVAL
+#   define PJ_WINCE_TIME_CHECK_INTERVAL (10)
+#endif
+
+#endif
+
+#ifdef WINCE_TIME
+static pj_status_t init_start_time(void)
+{
+    SYSTEMTIME st;
+    FILETIME ft;
+    pj_timestamp freq;
+    pj_status_t status;
+
+    GetLocalTime(&st);
+    SystemTimeToFileTime(&st, &ft);
+
+    g_start_time.LowPart = ft.dwLowDateTime;
+    g_start_time.HighPart = ft.dwHighDateTime;
+    g_start_time.QuadPart /= SECS_TO_FT_MULT;
+    g_start_time.QuadPart -= base_time.QuadPart;
+
+    status = pj_get_timestamp(&g_start_tick);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    g_last_update.u64 = g_start_tick.u64;
+
+    status = pj_get_timestamp_freq(&freq);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    g_update_period = PJ_WINCE_TIME_CHECK_INTERVAL * freq.u64;
+
+    PJ_LOG(4,("os_time_win32.c", "WinCE time (re)started"));
+
+    return PJ_SUCCESS;
+}
+
+static pj_status_t check_system_time(pj_uint64_t ts_elapsed)
+{
+    enum { MIS = 5 };
+    SYSTEMTIME st;
+    FILETIME ft;
+    LARGE_INTEGER cur, calc;
+    DWORD diff;
+    pj_timestamp freq;
+    pj_status_t status;
+
+    /* Get system's current time */
+    GetLocalTime(&st);
+    SystemTimeToFileTime(&st, &ft);
+    
+    cur.LowPart = ft.dwLowDateTime;
+    cur.HighPart = ft.dwHighDateTime;
+    cur.QuadPart /= SECS_TO_FT_MULT;
+    cur.QuadPart -= base_time.QuadPart;
+
+    /* Get our calculated system time */
+    status = pj_get_timestamp_freq(&freq);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    calc.QuadPart = g_start_time.QuadPart + ts_elapsed / freq.u64;
+
+    /* See the difference between calculated and actual system time */
+    if (calc.QuadPart >= cur.QuadPart) {
+	diff = (DWORD)(calc.QuadPart - cur.QuadPart);
+    } else {
+	diff = (DWORD)(cur.QuadPart - calc.QuadPart);
+    }
+
+    if (diff > MIS) {
+	/* System time has changed */
+	PJ_LOG(3,("os_time_win32.c", "WinCE system time changed detected "
+				      "(diff=%u)", diff));
+	status = init_start_time();
+    } else {
+	status = PJ_SUCCESS;
+    }
+
+    return status;
+}
+
+#endif
+
+// Find 1st Jan 1970 as a FILETIME 
+static pj_status_t get_base_time(void)
+{
+    SYSTEMTIME st;
+    FILETIME ft;
+    pj_status_t status = PJ_SUCCESS;
+
+    memset(&st,0,sizeof(st));
+    st.wYear=1970;
+    st.wMonth=1;
+    st.wDay=1;
+    SystemTimeToFileTime(&st, &ft);
+    
+    base_time.LowPart = ft.dwLowDateTime;
+    base_time.HighPart = ft.dwHighDateTime;
+    base_time.QuadPart /= SECS_TO_FT_MULT;
+
+#ifdef WINCE_TIME
+    pj_enter_critical_section();
+    status = init_start_time();
+    pj_leave_critical_section();
+#endif
+
+    return status;
+}
+
+PJ_DEF(pj_status_t) pj_gettimeofday(pj_time_val *tv)
+{
+#ifdef WINCE_TIME
+    pj_timestamp tick;
+    pj_uint64_t msec_elapsed;
+#else
+    SYSTEMTIME st;
+    FILETIME ft;
+    LARGE_INTEGER li;
+#endif
+    pj_status_t status;
+
+    if (base_time.QuadPart == 0) {
+	status = get_base_time();
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+#ifdef WINCE_TIME
+    do {
+	status = pj_get_timestamp(&tick);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	if (tick.u64 - g_last_update.u64 >= g_update_period) {
+	    pj_enter_critical_section();
+	    if (tick.u64 - g_last_update.u64 >= g_update_period) {
+		g_last_update.u64 = tick.u64;
+		check_system_time(tick.u64 - g_start_tick.u64);
+	    }
+	    pj_leave_critical_section();
+	} else {
+	    break;
+	}
+    } while (1);
+
+    msec_elapsed = pj_elapsed_msec64(&g_start_tick, &tick);
+
+    tv->sec = (long)(g_start_time.QuadPart + msec_elapsed/1000);
+    tv->msec = (long)(msec_elapsed % 1000);
+#else
+    /* Standard Win32 GetLocalTime */
+    GetLocalTime(&st);
+    SystemTimeToFileTime(&st, &ft);
+
+    li.LowPart = ft.dwLowDateTime;
+    li.HighPart = ft.dwHighDateTime;
+    li.QuadPart /= SECS_TO_FT_MULT;
+    li.QuadPart -= base_time.QuadPart;
+
+    tv->sec = li.LowPart;
+    tv->msec = st.wMilliseconds;
+#endif	/* WINCE_TIME */
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt)
+{
+    LARGE_INTEGER li;
+    FILETIME ft;
+    SYSTEMTIME st;
+
+    li.QuadPart = tv->sec;
+    li.QuadPart += base_time.QuadPart;
+    li.QuadPart *= SECS_TO_FT_MULT;
+
+    ft.dwLowDateTime = li.LowPart;
+    ft.dwHighDateTime = li.HighPart;
+    FileTimeToSystemTime(&ft, &st);
+
+    pt->year = st.wYear;
+    pt->mon = st.wMonth-1;
+    pt->day = st.wDay;
+    pt->wday = st.wDayOfWeek;
+
+    pt->hour = st.wHour;
+    pt->min = st.wMinute;
+    pt->sec = st.wSecond;
+    pt->msec = tv->msec;
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Encode parsed time to time value.
+ */
+PJ_DEF(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv)
+{
+    SYSTEMTIME st;
+    FILETIME ft;
+    LARGE_INTEGER li;
+
+    pj_bzero(&st, sizeof(st));
+    st.wYear = (pj_uint16_t) pt->year;
+    st.wMonth = (pj_uint16_t) (pt->mon + 1);
+    st.wDay = (pj_uint16_t) pt->day;
+    st.wHour = (pj_uint16_t) pt->hour;
+    st.wMinute = (pj_uint16_t) pt->min;
+    st.wSecond = (pj_uint16_t) pt->sec;
+    st.wMilliseconds = (pj_uint16_t) pt->msec;
+    
+    SystemTimeToFileTime(&st, &ft);
+
+    li.LowPart = ft.dwLowDateTime;
+    li.HighPart = ft.dwHighDateTime;
+    li.QuadPart /= SECS_TO_FT_MULT;
+    li.QuadPart -= base_time.QuadPart;
+
+    tv->sec = li.LowPart;
+    tv->msec = st.wMilliseconds;
+
+    return PJ_SUCCESS;
+}
+
+/**
+ * Convert local time to GMT.
+ */
+PJ_DEF(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);
+
+/**
+ * Convert GMT to local time.
+ */
+PJ_DEF(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_timestamp_common.c b/jni/pjproject-android/pjlib/src/pj/os_timestamp_common.c
new file mode 100644
index 0000000..af5e493
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_timestamp_common.c
@@ -0,0 +1,206 @@
+/* $Id: os_timestamp_common.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/compat/high_precision.h>
+
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+
+#define U32MAX  (0xFFFFFFFFUL)
+#define NANOSEC (1000000000UL)
+#define USEC    (1000000UL)
+#define MSEC    (1000)
+
+#define u64tohighprec(u64)	((pj_highprec_t)((pj_int64_t)(u64)))
+
+static pj_highprec_t get_elapsed( const pj_timestamp *start,
+                                  const pj_timestamp *stop )
+{
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+    return u64tohighprec(stop->u64 - start->u64);
+#else
+    pj_highprec_t elapsed_hi, elapsed_lo;
+
+    elapsed_hi = stop->u32.hi - start->u32.hi;
+    elapsed_lo = stop->u32.lo - start->u32.lo;
+
+    /* elapsed_hi = elapsed_hi * U32MAX */
+    pj_highprec_mul(elapsed_hi, U32MAX);
+
+    return elapsed_hi + elapsed_lo;
+#endif
+}
+
+static pj_highprec_t elapsed_msec( const pj_timestamp *start,
+                                   const pj_timestamp *stop )
+{
+    pj_timestamp ts_freq;
+    pj_highprec_t freq, elapsed;
+
+    if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
+        return 0;
+
+    /* Convert frequency timestamp */
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+    freq = u64tohighprec(ts_freq.u64);
+#else
+    freq = ts_freq.u32.hi;
+    pj_highprec_mul(freq, U32MAX);
+    freq += ts_freq.u32.lo;
+#endif
+
+    /* Avoid division by zero. */
+    if (freq == 0) freq = 1;
+
+    /* Get elapsed time in cycles. */
+    elapsed = get_elapsed(start, stop);
+
+    /* usec = elapsed * MSEC / freq */
+    pj_highprec_mul(elapsed, MSEC);
+    pj_highprec_div(elapsed, freq);
+
+    return elapsed;
+}
+
+static pj_highprec_t elapsed_usec( const pj_timestamp *start,
+                                   const pj_timestamp *stop )
+{
+    pj_timestamp ts_freq;
+    pj_highprec_t freq, elapsed;
+
+    if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
+        return 0;
+
+    /* Convert frequency timestamp */
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+    freq = u64tohighprec(ts_freq.u64);
+#else
+    freq = ts_freq.u32.hi;
+    pj_highprec_mul(freq, U32MAX);
+    freq += ts_freq.u32.lo;
+#endif
+
+    /* Avoid division by zero. */
+    if (freq == 0) freq = 1;
+
+    /* Get elapsed time in cycles. */
+    elapsed = get_elapsed(start, stop);
+
+    /* usec = elapsed * USEC / freq */
+    pj_highprec_mul(elapsed, USEC);
+    pj_highprec_div(elapsed, freq);
+
+    return elapsed;
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_nanosec( const pj_timestamp *start,
+                                        const pj_timestamp *stop )
+{
+    pj_timestamp ts_freq;
+    pj_highprec_t freq, elapsed;
+
+    if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
+        return 0;
+
+    /* Convert frequency timestamp */
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+    freq = u64tohighprec(ts_freq.u64);
+#else
+    freq = ts_freq.u32.hi;
+    pj_highprec_mul(freq, U32MAX);
+    freq += ts_freq.u32.lo;
+#endif
+
+    /* Avoid division by zero. */
+    if (freq == 0) freq = 1;
+
+    /* Get elapsed time in cycles. */
+    elapsed = get_elapsed(start, stop);
+
+    /* usec = elapsed * USEC / freq */
+    pj_highprec_mul(elapsed, NANOSEC);
+    pj_highprec_div(elapsed, freq);
+
+    return (pj_uint32_t)elapsed;
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_usec( const pj_timestamp *start,
+                                     const pj_timestamp *stop )
+{
+    return (pj_uint32_t)elapsed_usec(start, stop);
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start,
+                                     const pj_timestamp *stop )
+{
+    return (pj_uint32_t)elapsed_msec(start, stop);
+}
+
+PJ_DEF(pj_uint64_t) pj_elapsed_msec64(const pj_timestamp *start,
+                                      const pj_timestamp *stop )
+{
+    return (pj_uint64_t)elapsed_msec(start, stop);
+}
+
+PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
+                                     const pj_timestamp *stop )
+{
+    pj_highprec_t elapsed = elapsed_msec(start, stop);
+    pj_time_val tv_elapsed;
+
+    if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) {
+        tv_elapsed.sec = tv_elapsed.msec = 0;
+        return tv_elapsed;
+    } else {
+        pj_highprec_t sec, msec;
+
+        sec = elapsed;
+        pj_highprec_div(sec, MSEC);
+        tv_elapsed.sec = (long)sec;
+
+        msec = elapsed;
+        pj_highprec_mod(msec, MSEC);
+        tv_elapsed.msec = (long)msec;
+
+        return tv_elapsed;
+    }
+}
+
+PJ_DEF(pj_uint32_t) pj_elapsed_cycle( const pj_timestamp *start,
+                                      const pj_timestamp *stop )
+{
+    return stop->u32.lo - start->u32.lo;
+}
+
+PJ_DEF(pj_status_t) pj_gettickcount(pj_time_val *tv)
+{
+    pj_timestamp ts, start;
+    pj_status_t status;
+
+    if ((status = pj_get_timestamp(&ts)) != PJ_SUCCESS)
+        return status;
+
+    pj_set_timestamp32(&start, 0, 0);
+    *tv = pj_elapsed_time(&start, &ts);
+
+    return PJ_SUCCESS;
+}
+
+#endif  /* PJ_HAS_HIGH_RES_TIMER */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_timestamp_linux_kernel.c b/jni/pjproject-android/pjlib/src/pj/os_timestamp_linux_kernel.c
new file mode 100644
index 0000000..14609ea
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_timestamp_linux_kernel.c
@@ -0,0 +1,79 @@
+/* $Id: os_timestamp_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <linux/time.h>
+
+#if 0
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    ts->u32.hi = 0;
+    ts->u32.lo = jiffies;
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    freq->u32.hi = 0;
+    freq->u32.lo = HZ;
+    return 0;
+}
+#elif 0
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    struct timespec tv;
+    
+    tv = CURRENT_TIME;
+
+    ts->u64 = tv.tv_sec;
+    ts->u64 *= NSEC_PER_SEC;
+    ts->u64 += tv.tv_nsec;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    freq->u32.hi = 0;
+    freq->u32.lo = NSEC_PER_SEC;
+    return 0;
+}
+#else
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    struct timeval tv;
+    
+    do_gettimeofday(&tv);
+
+    ts->u64 = tv.tv_sec;
+    ts->u64 *= USEC_PER_SEC;
+    ts->u64 += tv.tv_usec;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    freq->u32.hi = 0;
+    freq->u32.lo = USEC_PER_SEC;
+    return 0;
+}
+
+#endif
+
diff --git a/jni/pjproject-android/pjlib/src/pj/os_timestamp_posix.c b/jni/pjproject-android/pjlib/src/pj/os_timestamp_posix.c
new file mode 100644
index 0000000..215589c
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_timestamp_posix.c
@@ -0,0 +1,220 @@
+/* $Id: os_timestamp_posix.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#if defined(PJ_HAS_UNISTD_H) && PJ_HAS_UNISTD_H != 0
+#   include <unistd.h>
+
+#   if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && \
+       defined(_POSIX_MONOTONIC_CLOCK)
+#       define USE_POSIX_TIMERS 1
+#   endif
+
+#endif
+
+#if defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 && \
+    defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \
+    defined(PJ_M_I386) && PJ_M_I386!=0 && \
+    defined(PJ_LINUX) && PJ_LINUX!=0
+static int machine_speed_mhz;
+static pj_timestamp machine_speed;
+
+static __inline__ unsigned long long int rdtsc()
+{
+    unsigned long long int x;
+    __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
+    return x;
+}
+
+/* Determine machine's CPU MHz to get the counter's frequency.
+ */
+static int get_machine_speed_mhz()
+{
+    FILE *strm;
+    char buf[512];
+    int len;
+    char *pos, *end;
+	
+    PJ_CHECK_STACK();
+	
+    /* Open /proc/cpuinfo and read the file */
+    strm = fopen("/proc/cpuinfo", "r");
+    if (!strm)
+        return -1;
+    len = fread(buf, 1, sizeof(buf), strm);
+    fclose(strm);
+    if (len < 1) {
+        return -1;
+    }
+    buf[len] = '\0';
+
+    /* Locate the MHz digit. */
+    pos = strstr(buf, "cpu MHz");
+    if (!pos)
+        return -1;
+    pos = strchr(pos, ':');
+    if (!pos)
+        return -1;
+    end = (pos += 2);
+    while (isdigit(*end)) ++end;
+    *end = '\0';
+
+    /* Return the Mhz part, and give it a +1. */
+    return atoi(pos)+1;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    if (machine_speed_mhz == 0) {
+	machine_speed_mhz = get_machine_speed_mhz();
+	if (machine_speed_mhz > 0) {
+	    machine_speed.u64 = machine_speed_mhz * 1000000.0;
+	}
+    }
+    
+    if (machine_speed_mhz == -1) {
+	ts->u64 = 0;
+	return -1;
+    } 
+    ts->u64 = rdtsc();
+    return 0;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    if (machine_speed_mhz == 0) {
+	machine_speed_mhz = get_machine_speed_mhz();
+	if (machine_speed_mhz > 0) {
+	    machine_speed.u64 = machine_speed_mhz * 1000000.0;
+	}
+    }
+    
+    if (machine_speed_mhz == -1) {
+	freq->u64 = 1;	/* return 1 to prevent division by zero in apps. */
+	return -1;
+    } 
+
+    freq->u64 = machine_speed.u64;
+    return 0;
+}
+
+#elif defined(PJ_DARWINOS) && PJ_DARWINOS != 0
+#include <mach/mach.h>
+#include <mach/clock.h>
+#include <errno.h>
+
+#define NSEC_PER_SEC	1000000000
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    mach_timespec_t tp;
+    int ret;
+    clock_serv_t serv;
+
+    ret = host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &serv);
+    if (ret != KERN_SUCCESS) {
+	return PJ_RETURN_OS_ERROR(EINVAL);
+    }
+
+    ret = clock_get_time(serv, &tp);
+    if (ret != KERN_SUCCESS) {
+	return PJ_RETURN_OS_ERROR(EINVAL);
+    }
+
+    ts->u64 = tp.tv_sec;
+    ts->u64 *= NSEC_PER_SEC;
+    ts->u64 += tp.tv_nsec;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    freq->u32.hi = 0;
+    freq->u32.lo = NSEC_PER_SEC;
+
+    return PJ_SUCCESS;
+}
+
+#elif defined(USE_POSIX_TIMERS) && USE_POSIX_TIMERS != 0
+#include <sys/time.h>
+#include <time.h>
+#include <errno.h>
+
+#define NSEC_PER_SEC	1000000000
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    struct timespec tp;
+
+    if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) {
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+    }
+
+    ts->u64 = tp.tv_sec;
+    ts->u64 *= NSEC_PER_SEC;
+    ts->u64 += tp.tv_nsec;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    freq->u32.hi = 0;
+    freq->u32.lo = NSEC_PER_SEC;
+
+    return PJ_SUCCESS;
+}
+
+#else
+#include <sys/time.h>
+#include <errno.h>
+
+#define USEC_PER_SEC	1000000
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    struct timeval tv;
+
+    if (gettimeofday(&tv, NULL) != 0) {
+	return PJ_RETURN_OS_ERROR(pj_get_native_os_error());
+    }
+
+    ts->u64 = tv.tv_sec;
+    ts->u64 *= USEC_PER_SEC;
+    ts->u64 += tv.tv_usec;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    freq->u32.hi = 0;
+    freq->u32.lo = USEC_PER_SEC;
+
+    return PJ_SUCCESS;
+}
+
+#endif
diff --git a/jni/pjproject-android/pjlib/src/pj/os_timestamp_win32.c b/jni/pjproject-android/pjlib/src/pj/os_timestamp_win32.c
new file mode 100644
index 0000000..7042ab1
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/os_timestamp_win32.c
@@ -0,0 +1,295 @@
+/* $Id: os_timestamp_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <windows.h>
+
+#define THIS_FILE   "os_timestamp_win32.c"
+
+
+#if 1
+#   define TRACE_(x)	    PJ_LOG(3,x)
+#else
+#   define TRACE_(x)	    ;
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \
+    defined(PJ_M_I386) && PJ_M_I386 != 0 && \
+    defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 && \
+    defined(_MSC_VER)
+
+/*
+ * Use rdtsc to get the OS timestamp.
+ */
+static LONG CpuMhz;
+static pj_int64_t CpuHz;
+ 
+static pj_status_t GetCpuHz(void)
+{
+    HKEY key;
+    LONG rc;
+    DWORD size;
+
+#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
+    rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+		      L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
+		      0, 0, &key);
+#else
+    rc = RegOpenKey( HKEY_LOCAL_MACHINE,
+		     "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
+		     &key);
+#endif
+
+    if (rc != ERROR_SUCCESS)
+	return PJ_RETURN_OS_ERROR(rc);
+
+    size = sizeof(CpuMhz);
+    rc = RegQueryValueEx(key, "~MHz", NULL, NULL, (BYTE*)&CpuMhz, &size);
+    RegCloseKey(key);
+
+    if (rc != ERROR_SUCCESS) {
+	return PJ_RETURN_OS_ERROR(rc);
+    }
+
+    CpuHz = CpuMhz;
+    CpuHz = CpuHz * 1000000;
+
+    return PJ_SUCCESS;
+}
+
+/* __int64 is nicely returned in EDX:EAX */
+__declspec(naked) __int64 rdtsc() 
+{
+    __asm 
+    {
+	RDTSC
+	RET
+    }
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    ts->u64 = rdtsc();
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    pj_status_t status;
+
+    if (CpuHz == 0) {
+	status = GetCpuHz();
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    freq->u64 = CpuHz;
+    return PJ_SUCCESS;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+#elif defined(PJ_TIMESTAMP_WIN32_USE_SAFE_QPC) && \
+         PJ_TIMESTAMP_WIN32_USE_SAFE_QPC!=0
+
+/* Use safe QueryPerformanceCounter.
+ * This implementation has some protection against bug in KB Q274323:
+ *   Performance counter value may unexpectedly leap forward
+ *   http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
+ *
+ * THIS SHOULD NOT BE USED YET AS IT DOESN'T HANDLE SYSTEM TIME
+ * CHANGE.
+ */
+
+static pj_timestamp g_ts_freq;
+static pj_timestamp g_ts_base;
+static pj_int64_t   g_time_base;
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    enum { MAX_RETRY = 10 };
+    unsigned i;
+
+
+    /* pj_get_timestamp_freq() must have been called before.
+     * This is done when application called pj_init().
+     */
+    pj_assert(g_ts_freq.u64 != 0);
+
+    /* Retry QueryPerformanceCounter() until we're sure that the
+     * value returned makes sense.
+     */
+    i = 0;
+    do {
+	LARGE_INTEGER val;
+	pj_int64_t counter64, time64, diff;
+	pj_time_val time_now;
+
+	/* Retrieve the counter */
+	if (!QueryPerformanceCounter(&val))
+	    return PJ_RETURN_OS_ERROR(GetLastError());
+
+	/* Regardless of the goodness of the value, we should put
+	 * the counter here, because normally application wouldn't
+	 * check the error result of this function.
+	 */
+	ts->u64 = val.QuadPart;
+
+	/* Retrieve time */
+	pj_gettimeofday(&time_now);
+
+	/* Get the counter elapsed time in miliseconds */
+	counter64 = (val.QuadPart - g_ts_base.u64) * 1000 / g_ts_freq.u64;
+	
+	/* Get the time elapsed in miliseconds. 
+	 * We don't want to use PJ_TIME_VAL_MSEC() since it's using
+	 * 32bit calculation, which limits the maximum elapsed time
+	 * to around 49 days only.
+	 */
+	time64 = time_now.sec;
+	time64 = time64 * 1000 + time_now.msec;
+	//time64 = GetTickCount();
+
+	/* It's good if the difference between two clocks are within
+	 * some compile time constant (default: 20ms, which to allow
+	 * context switch happen between QueryPerformanceCounter and
+	 * pj_gettimeofday()).
+	 */
+	diff = (time64 - g_time_base) - counter64;
+	if (diff >= -20 && diff <= 20) {
+	    /* It's good */
+	    return PJ_SUCCESS;
+	}
+
+	++i;
+
+    } while (i < MAX_RETRY);
+
+    TRACE_((THIS_FILE, "QueryPerformanceCounter returned bad value"));
+    return PJ_ETIMEDOUT;
+}
+
+static pj_status_t init_performance_counter(void)
+{
+    LARGE_INTEGER val;
+    pj_time_val time_base;
+    pj_status_t status;
+
+    /* Get the frequency */
+    if (!QueryPerformanceFrequency(&val))
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    g_ts_freq.u64 = val.QuadPart;
+
+    /* Get the base timestamp */
+    if (!QueryPerformanceCounter(&val))
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    g_ts_base.u64 = val.QuadPart;
+
+
+    /* Get the base time */
+    status = pj_gettimeofday(&time_base);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Convert time base to 64bit value in msec */
+    g_time_base = time_base.sec;
+    g_time_base  = g_time_base * 1000 + time_base.msec;
+    //g_time_base = GetTickCount();
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    if (g_ts_freq.u64 == 0) {
+	enum { MAX_REPEAT = 10 };
+	unsigned i;
+	pj_status_t status;
+
+	/* Make unellegant compiler happy */
+	status = 0;
+
+	/* Repeat initializing performance counter until we're sure
+	 * the base timing is correct. It is possible that the system
+	 * returns bad counter during this initialization!
+	 */
+	for (i=0; i<MAX_REPEAT; ++i) {
+
+	    pj_timestamp dummy;
+
+	    /* Init base time */
+	    status = init_performance_counter();
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    /* Try the base time */
+	    status = pj_get_timestamp(&dummy);
+	    if (status == PJ_SUCCESS)
+		break;
+	}
+
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    freq->u64 = g_ts_freq.u64;
+    return PJ_SUCCESS;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+#else
+
+/*
+ * Use QueryPerformanceCounter and QueryPerformanceFrequency.
+ * This should be the default implementation to be used on Windows.
+ */
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    LARGE_INTEGER val;
+
+    if (!QueryPerformanceCounter(&val))
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    ts->u64 = val.QuadPart;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    LARGE_INTEGER val;
+
+    if (!QueryPerformanceFrequency(&val))
+	return PJ_RETURN_OS_ERROR(GetLastError());
+
+    freq->u64 = val.QuadPart;
+    return PJ_SUCCESS;
+}
+
+
+#endif	/* PJ_TIMESTAMP_USE_RDTSC */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/pool.c b/jni/pjproject-android/pjlib/src/pj/pool.c
new file mode 100644
index 0000000..d324223
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/pool.c
@@ -0,0 +1,301 @@
+/* $Id: pool.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/except.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+
+#if !PJ_HAS_POOL_ALT_API
+
+
+/* Include inline definitions when inlining is disabled. */
+#if !PJ_FUNCTIONS_ARE_INLINED
+#  include <pj/pool_i.h>
+#endif
+
+#define LOG(expr)   		    PJ_LOG(6,expr)
+#define ALIGN_PTR(PTR,ALIGNMENT)    (PTR + (-(pj_ssize_t)(PTR) & (ALIGNMENT-1)))
+
+PJ_DEF_DATA(int) PJ_NO_MEMORY_EXCEPTION;
+
+PJ_DEF(int) pj_NO_MEMORY_EXCEPTION()
+{
+    return PJ_NO_MEMORY_EXCEPTION;
+}
+
+/*
+ * Create new block.
+ * Create a new big chunk of memory block, from which user allocation will be
+ * taken from.
+ */
+static pj_pool_block *pj_pool_create_block( pj_pool_t *pool, pj_size_t size)
+{
+    pj_pool_block *block;
+
+    PJ_CHECK_STACK();
+    pj_assert(size >= sizeof(pj_pool_block));
+
+    LOG((pool->obj_name, "create_block(sz=%u), cur.cap=%u, cur.used=%u", 
+	 size, pool->capacity, pj_pool_get_used_size(pool)));
+
+    /* Request memory from allocator. */
+    block = (pj_pool_block*) 
+	(*pool->factory->policy.block_alloc)(pool->factory, size);
+    if (block == NULL) {
+	(*pool->callback)(pool, size);
+	return NULL;
+    }
+
+    /* Add capacity. */
+    pool->capacity += size;
+
+    /* Set start and end of buffer. */
+    block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
+    block->end = ((unsigned char*)block) + size;
+
+    /* Set the start pointer, aligning it as needed */
+    block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT);
+
+    /* Insert in the front of the list. */
+    pj_list_insert_after(&pool->block_list, block);
+
+    LOG((pool->obj_name," block created, buffer=%p-%p",block->buf, block->end));
+
+    return block;
+}
+
+/*
+ * Allocate memory chunk for user from available blocks.
+ * This will iterate through block list to find space to allocate the chunk.
+ * If no space is available in all the blocks, a new block might be created
+ * (depending on whether the pool is allowed to resize).
+ */
+PJ_DEF(void*) pj_pool_allocate_find(pj_pool_t *pool, pj_size_t size)
+{
+    pj_pool_block *block = pool->block_list.next;
+    void *p;
+    pj_size_t block_size;
+
+    PJ_CHECK_STACK();
+
+    while (block != &pool->block_list) {
+	p = pj_pool_alloc_from_block(block, size);
+	if (p != NULL)
+	    return p;
+	block = block->next;
+    }
+    /* No available space in all blocks. */
+
+    /* If pool is configured NOT to expand, return error. */
+    if (pool->increment_size == 0) {
+	LOG((pool->obj_name, "Can't expand pool to allocate %u bytes "
+	     "(used=%u, cap=%u)",
+	     size, pj_pool_get_used_size(pool), pool->capacity));
+	(*pool->callback)(pool, size);
+	return NULL;
+    }
+
+    /* If pool is configured to expand, but the increment size
+     * is less than the required size, expand the pool by multiple
+     * increment size. Also count the size wasted due to aligning
+     * the block.
+     */
+    if (pool->increment_size < 
+	    size + sizeof(pj_pool_block) + PJ_POOL_ALIGNMENT) 
+    {
+        pj_size_t count;
+        count = (size + pool->increment_size + sizeof(pj_pool_block) +
+                 PJ_POOL_ALIGNMENT) / 
+                pool->increment_size;
+        block_size = count * pool->increment_size;
+
+    } else {
+        block_size = pool->increment_size;
+    }
+
+    LOG((pool->obj_name, 
+	 "%u bytes requested, resizing pool by %u bytes (used=%u, cap=%u)",
+	 size, block_size, pj_pool_get_used_size(pool), pool->capacity));
+
+    block = pj_pool_create_block(pool, block_size);
+    if (!block)
+	return NULL;
+
+    p = pj_pool_alloc_from_block(block, size);
+    pj_assert(p != NULL);
+#if PJ_DEBUG
+    if (p == NULL) {
+	p = p;
+    }
+#endif
+    return p;
+}
+
+/*
+ * Internal function to initialize pool.
+ */
+PJ_DEF(void) pj_pool_init_int(  pj_pool_t *pool, 
+				const char *name,
+				pj_size_t increment_size,
+				pj_pool_callback *callback)
+{
+    PJ_CHECK_STACK();
+
+    pool->increment_size = increment_size;
+    pool->callback = callback;
+
+    if (name) {
+	if (strchr(name, '%') != NULL) {
+	    pj_ansi_snprintf(pool->obj_name, sizeof(pool->obj_name), 
+			     name, pool);
+	} else {
+	    pj_ansi_strncpy(pool->obj_name, name, PJ_MAX_OBJ_NAME);
+	    pool->obj_name[PJ_MAX_OBJ_NAME-1] = '\0';
+	}
+    } else {
+	pool->obj_name[0] = '\0';
+    }
+}
+
+/*
+ * Create new memory pool.
+ */
+PJ_DEF(pj_pool_t*) pj_pool_create_int( pj_pool_factory *f, const char *name,
+				       pj_size_t initial_size, 
+				       pj_size_t increment_size,
+				       pj_pool_callback *callback)
+{
+    pj_pool_t *pool;
+    pj_pool_block *block;
+    pj_uint8_t *buffer;
+
+    PJ_CHECK_STACK();
+
+    /* Size must be at least sizeof(pj_pool)+sizeof(pj_pool_block) */
+    PJ_ASSERT_RETURN(initial_size >= sizeof(pj_pool_t)+sizeof(pj_pool_block),
+		     NULL);
+
+    /* If callback is NULL, set calback from the policy */
+    if (callback == NULL)
+	callback = f->policy.callback;
+
+    /* Allocate initial block */
+    buffer = (pj_uint8_t*) (*f->policy.block_alloc)(f, initial_size);
+    if (!buffer)
+	return NULL;
+
+    /* Set pool administrative data. */
+    pool = (pj_pool_t*)buffer;
+    pj_bzero(pool, sizeof(*pool));
+
+    pj_list_init(&pool->block_list);
+    pool->factory = f;
+
+    /* Create the first block from the memory. */
+    block = (pj_pool_block*) (buffer + sizeof(*pool));
+    block->buf = ((unsigned char*)block) + sizeof(pj_pool_block);
+    block->end = buffer + initial_size;
+
+    /* Set the start pointer, aligning it as needed */
+    block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT);
+
+    pj_list_insert_after(&pool->block_list, block);
+
+    pj_pool_init_int(pool, name, increment_size, callback);
+
+    /* Pool initial capacity and used size */
+    pool->capacity = initial_size;
+
+    LOG((pool->obj_name, "pool created, size=%u", pool->capacity));
+    return pool;
+}
+
+/*
+ * Reset the pool to the state when it was created.
+ * All blocks will be deallocated except the first block. All memory areas
+ * are marked as free.
+ */
+static void reset_pool(pj_pool_t *pool)
+{
+    pj_pool_block *block;
+
+    PJ_CHECK_STACK();
+
+    block = pool->block_list.prev;
+    if (block == &pool->block_list)
+	return;
+
+    /* Skip the first block because it is occupying the same memory
+       as the pool itself.
+    */
+    block = block->prev;
+    
+    while (block != &pool->block_list) {
+	pj_pool_block *prev = block->prev;
+	pj_list_erase(block);
+	(*pool->factory->policy.block_free)(pool->factory, block, 
+					    block->end - (unsigned char*)block);
+	block = prev;
+    }
+
+    block = pool->block_list.next;
+
+    /* Set the start pointer, aligning it as needed */
+    block->cur = ALIGN_PTR(block->buf, PJ_POOL_ALIGNMENT);
+
+    pool->capacity = block->end - (unsigned char*)pool;
+}
+
+/*
+ * The public function to reset pool.
+ */
+PJ_DEF(void) pj_pool_reset(pj_pool_t *pool)
+{
+    LOG((pool->obj_name, "reset(): cap=%d, used=%d(%d%%)", 
+	pool->capacity, pj_pool_get_used_size(pool), 
+	pj_pool_get_used_size(pool)*100/pool->capacity));
+
+    reset_pool(pool);
+}
+
+/*
+ * Destroy the pool.
+ */
+PJ_DEF(void) pj_pool_destroy_int(pj_pool_t *pool)
+{
+    pj_size_t initial_size;
+
+    LOG((pool->obj_name, "destroy(): cap=%d, used=%d(%d%%), block0=%p-%p", 
+	pool->capacity, pj_pool_get_used_size(pool), 
+	pj_pool_get_used_size(pool)*100/pool->capacity,
+	((pj_pool_block*)pool->block_list.next)->buf, 
+	((pj_pool_block*)pool->block_list.next)->end));
+
+    reset_pool(pool);
+    initial_size = ((pj_pool_block*)pool->block_list.next)->end - 
+		   (unsigned char*)pool;
+    if (pool->factory->policy.block_free)
+	(*pool->factory->policy.block_free)(pool->factory, pool, initial_size);
+}
+
+
+#endif	/* PJ_HAS_POOL_ALT_API */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/pool_buf.c b/jni/pjproject-android/pjlib/src/pj/pool_buf.c
new file mode 100644
index 0000000..94aaecb
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/pool_buf.c
@@ -0,0 +1,115 @@
+/* $Id: pool_buf.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/pool_buf.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+
+struct pj_pool_factory stack_based_factory;
+
+struct creation_param
+{
+    void	*stack_buf;
+    pj_size_t	 size;
+};
+
+static int is_initialized;
+static long tls = -1;
+static void* stack_alloc(pj_pool_factory *factory, pj_size_t size);
+
+static void pool_buf_cleanup(void)
+{
+    if (tls != -1) {
+	pj_thread_local_free(tls);
+	tls = -1;
+    }
+    if (is_initialized)
+	is_initialized = 0;
+}
+
+static pj_status_t pool_buf_initialize()
+{
+    pj_atexit(&pool_buf_cleanup);
+
+    stack_based_factory.policy.block_alloc = &stack_alloc;
+    return pj_thread_local_alloc(&tls);
+}
+
+static void* stack_alloc(pj_pool_factory *factory, pj_size_t size)
+{
+    struct creation_param *param;
+    void *buf;
+
+    PJ_UNUSED_ARG(factory);
+
+    param = (struct creation_param*) pj_thread_local_get(tls);
+    if (param == NULL) {
+	/* Don't assert(), this is normal no-memory situation */
+	return NULL;
+    }
+
+    pj_thread_local_set(tls, NULL);
+
+    PJ_ASSERT_RETURN(size <= param->size, NULL);
+
+    buf = param->stack_buf;
+
+    /* Prevent the buffer from being reused */
+    param->stack_buf = NULL;
+
+    return buf;
+}
+
+
+PJ_DEF(pj_pool_t*) pj_pool_create_on_buf(const char *name,
+					 void *buf,
+					 pj_size_t size)
+{
+#if PJ_HAS_POOL_ALT_API == 0
+    struct creation_param param;
+    pj_size_t align_diff;
+
+    PJ_ASSERT_RETURN(buf && size, NULL);
+
+    if (!is_initialized) {
+	if (pool_buf_initialize() != PJ_SUCCESS)
+	    return NULL;
+	is_initialized = 1;
+    }
+
+    /* Check and align buffer */
+    align_diff = (pj_size_t)buf;
+    if (align_diff & (PJ_POOL_ALIGNMENT-1)) {
+	align_diff &= (PJ_POOL_ALIGNMENT-1);
+	buf = (void*) (((char*)buf) + align_diff);
+	size -= align_diff;
+    }
+
+    param.stack_buf = buf;
+    param.size = size;
+    pj_thread_local_set(tls, &param);
+
+    return pj_pool_create_int(&stack_based_factory, name, size, 0, 
+			      pj_pool_factory_default_policy.callback);
+#else
+    PJ_UNUSED_ARG(buf);
+    return pj_pool_create(NULL, name, size, size, NULL);
+#endif
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/pool_caching.c b/jni/pjproject-android/pjlib/src/pj/pool_caching.c
new file mode 100644
index 0000000..14780e1
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/pool_caching.c
@@ -0,0 +1,338 @@
+/* $Id: pool_caching.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/lock.h>
+#include <pj/os.h>
+#include <pj/pool_buf.h>
+
+#if !PJ_HAS_POOL_ALT_API
+
+static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, 
+				    const char *name,
+				    pj_size_t initial_size, 
+				    pj_size_t increment_sz,
+				    pj_pool_callback *callback);
+static void cpool_release_pool(pj_pool_factory *pf, pj_pool_t *pool);
+static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail );
+static pj_bool_t cpool_on_block_alloc(pj_pool_factory *f, pj_size_t sz);
+static void cpool_on_block_free(pj_pool_factory *f, pj_size_t sz);
+
+
+static pj_size_t pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE] = 
+{
+    256, 512, 1024, 2048, 4096, 8192, 12288, 16384, 
+    20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536
+};
+
+/* Index where the search for size should begin.
+ * Start with pool_sizes[5], which is 8192.
+ */
+#define START_SIZE  5
+
+
+PJ_DEF(void) pj_caching_pool_init( pj_caching_pool *cp, 
+				   const pj_pool_factory_policy *policy,
+				   pj_size_t max_capacity)
+{
+    int i;
+    pj_pool_t *pool;
+
+    PJ_CHECK_STACK();
+
+    pj_bzero(cp, sizeof(*cp));
+    
+    cp->max_capacity = max_capacity;
+    pj_list_init(&cp->used_list);
+    for (i=0; i<PJ_CACHING_POOL_ARRAY_SIZE; ++i)
+	pj_list_init(&cp->free_list[i]);
+
+    if (policy == NULL) {
+    	policy = &pj_pool_factory_default_policy;
+    }
+    
+    pj_memcpy(&cp->factory.policy, policy, sizeof(pj_pool_factory_policy));
+    cp->factory.create_pool = &cpool_create_pool;
+    cp->factory.release_pool = &cpool_release_pool;
+    cp->factory.dump_status = &cpool_dump_status;
+    cp->factory.on_block_alloc = &cpool_on_block_alloc;
+    cp->factory.on_block_free = &cpool_on_block_free;
+
+    pool = pj_pool_create_on_buf("cachingpool", cp->pool_buf, sizeof(cp->pool_buf));
+    pj_lock_create_simple_mutex(pool, "cachingpool", &cp->lock);
+}
+
+PJ_DEF(void) pj_caching_pool_destroy( pj_caching_pool *cp )
+{
+    int i;
+    pj_pool_t *pool;
+
+    PJ_CHECK_STACK();
+
+    /* Delete all pool in free list */
+    for (i=0; i < PJ_CACHING_POOL_ARRAY_SIZE; ++i) {
+	pj_pool_t *pool = (pj_pool_t*) cp->free_list[i].next;
+	pj_pool_t *next;
+	for (; pool != (void*)&cp->free_list[i]; pool = next) {
+	    next = pool->next;
+	    pj_list_erase(pool);
+	    pj_pool_destroy_int(pool);
+	}
+    }
+
+    /* Delete all pools in used list */
+    pool = (pj_pool_t*) cp->used_list.next;
+    while (pool != (pj_pool_t*) &cp->used_list) {
+	pj_pool_t *next = pool->next;
+	pj_list_erase(pool);
+	PJ_LOG(4,(pool->obj_name, 
+		  "Pool is not released by application, releasing now"));
+	pj_pool_destroy_int(pool);
+	pool = next;
+    }
+
+    if (cp->lock) {
+	pj_lock_destroy(cp->lock);
+	pj_lock_create_null_mutex(NULL, "cachingpool", &cp->lock);
+    }
+}
+
+static pj_pool_t* cpool_create_pool(pj_pool_factory *pf, 
+					      const char *name, 
+					      pj_size_t initial_size, 
+					      pj_size_t increment_sz, 
+					      pj_pool_callback *callback)
+{
+    pj_caching_pool *cp = (pj_caching_pool*)pf;
+    pj_pool_t *pool;
+    int idx;
+
+    PJ_CHECK_STACK();
+
+    pj_lock_acquire(cp->lock);
+
+    /* Use pool factory's policy when callback is NULL */
+    if (callback == NULL) {
+	callback = pf->policy.callback;
+    }
+
+    /* Search the suitable size for the pool. 
+     * We'll just do linear search to the size array, as the array size itself
+     * is only a few elements. Binary search I suspect will be less efficient
+     * for this purpose.
+     */
+    if (initial_size <= pool_sizes[START_SIZE]) {
+	for (idx=START_SIZE-1; 
+	     idx >= 0 && pool_sizes[idx] >= initial_size;
+	     --idx)
+	    ;
+	++idx;
+    } else {
+	for (idx=START_SIZE+1; 
+	     idx < PJ_CACHING_POOL_ARRAY_SIZE && 
+		  pool_sizes[idx] < initial_size;
+	     ++idx)
+	    ;
+    }
+
+    /* Check whether there's a pool in the list. */
+    if (idx==PJ_CACHING_POOL_ARRAY_SIZE || pj_list_empty(&cp->free_list[idx])) {
+	/* No pool is available. */
+	/* Set minimum size. */
+	if (idx < PJ_CACHING_POOL_ARRAY_SIZE)
+	    initial_size =  pool_sizes[idx];
+
+	/* Create new pool */
+	pool = pj_pool_create_int(&cp->factory, name, initial_size, 
+				  increment_sz, callback);
+	if (!pool) {
+	    pj_lock_release(cp->lock);
+	    return NULL;
+	}
+
+    } else {
+	/* Get one pool from the list. */
+	pool = (pj_pool_t*) cp->free_list[idx].next;
+	pj_list_erase(pool);
+
+	/* Initialize the pool. */
+	pj_pool_init_int(pool, name, increment_sz, callback);
+
+	/* Update pool manager's free capacity. */
+	if (cp->capacity > pj_pool_get_capacity(pool)) {
+	    cp->capacity -= pj_pool_get_capacity(pool);
+	} else {
+	    cp->capacity = 0;
+	}
+
+	PJ_LOG(6, (pool->obj_name, "pool reused, size=%u", pool->capacity));
+    }
+
+    /* Put in used list. */
+    pj_list_insert_before( &cp->used_list, pool );
+
+    /* Mark factory data */
+    pool->factory_data = (void*) (pj_ssize_t) idx;
+
+    /* Increment used count. */
+    ++cp->used_count;
+
+    pj_lock_release(cp->lock);
+    return pool;
+}
+
+static void cpool_release_pool( pj_pool_factory *pf, pj_pool_t *pool)
+{
+    pj_caching_pool *cp = (pj_caching_pool*)pf;
+    pj_size_t pool_capacity;
+    unsigned i;
+
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_ON_FAIL(pf && pool, return);
+
+    pj_lock_acquire(cp->lock);
+
+#if PJ_SAFE_POOL
+    /* Make sure pool is still in our used list */
+    if (pj_list_find_node(&cp->used_list, pool) != pool) {
+	pj_assert(!"Attempt to destroy pool that has been destroyed before");
+	return;
+    }
+#endif
+
+    /* Erase from the used list. */
+    pj_list_erase(pool);
+
+    /* Decrement used count. */
+    --cp->used_count;
+
+    pool_capacity = pj_pool_get_capacity(pool);
+
+    /* Destroy the pool if the size is greater than our size or if the total
+     * capacity in our recycle list (plus the size of the pool) exceeds 
+     * maximum capacity.
+   . */
+    if (pool_capacity > pool_sizes[PJ_CACHING_POOL_ARRAY_SIZE-1] ||
+	cp->capacity + pool_capacity > cp->max_capacity)
+    {
+	pj_pool_destroy_int(pool);
+	pj_lock_release(cp->lock);
+	return;
+    }
+
+    /* Reset pool. */
+    PJ_LOG(6, (pool->obj_name, "recycle(): cap=%d, used=%d(%d%%)", 
+	       pool_capacity, pj_pool_get_used_size(pool), 
+	       pj_pool_get_used_size(pool)*100/pool_capacity));
+    pj_pool_reset(pool);
+
+    pool_capacity = pj_pool_get_capacity(pool);
+
+    /*
+     * Otherwise put the pool in our recycle list.
+     */
+    i = (unsigned) (unsigned long) (pj_ssize_t) pool->factory_data;
+
+    pj_assert(i<PJ_CACHING_POOL_ARRAY_SIZE);
+    if (i >= PJ_CACHING_POOL_ARRAY_SIZE ) {
+	/* Something has gone wrong with the pool. */
+	pj_pool_destroy_int(pool);
+	pj_lock_release(cp->lock);
+	return;
+    }
+
+    pj_list_insert_after(&cp->free_list[i], pool);
+    cp->capacity += pool_capacity;
+
+    pj_lock_release(cp->lock);
+}
+
+static void cpool_dump_status(pj_pool_factory *factory, pj_bool_t detail )
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+    pj_caching_pool *cp = (pj_caching_pool*)factory;
+
+    pj_lock_acquire(cp->lock);
+
+    PJ_LOG(3,("cachpool", " Dumping caching pool:"));
+    PJ_LOG(3,("cachpool", "   Capacity=%u, max_capacity=%u, used_cnt=%u", \
+			     cp->capacity, cp->max_capacity, cp->used_count));
+    if (detail) {
+	pj_pool_t *pool = (pj_pool_t*) cp->used_list.next;
+	pj_size_t total_used = 0, total_capacity = 0;
+        PJ_LOG(3,("cachpool", "  Dumping all active pools:"));
+	while (pool != (void*)&cp->used_list) {
+	    pj_size_t pool_capacity = pj_pool_get_capacity(pool);
+	    PJ_LOG(3,("cachpool", "   %16s: %8d of %8d (%d%%) used", 
+				  pj_pool_getobjname(pool), 
+				  pj_pool_get_used_size(pool), 
+				  pool_capacity,
+				  pj_pool_get_used_size(pool)*100/pool_capacity));
+	    total_used += pj_pool_get_used_size(pool);
+	    total_capacity += pool_capacity;
+	    pool = pool->next;
+	}
+	if (total_capacity) {
+	    PJ_LOG(3,("cachpool", "  Total %9d of %9d (%d %%) used!",
+				  total_used, total_capacity,
+				  total_used * 100 / total_capacity));
+	}
+    }
+
+    pj_lock_release(cp->lock);
+#else
+    PJ_UNUSED_ARG(factory);
+    PJ_UNUSED_ARG(detail);
+#endif
+}
+
+
+static pj_bool_t cpool_on_block_alloc(pj_pool_factory *f, pj_size_t sz)
+{
+    pj_caching_pool *cp = (pj_caching_pool*)f;
+
+    //Can't lock because mutex is not recursive
+    //if (cp->mutex) pj_mutex_lock(cp->mutex);
+
+    cp->used_size += sz;
+    if (cp->used_size > cp->peak_used_size)
+	cp->peak_used_size = cp->used_size;
+
+    //if (cp->mutex) pj_mutex_unlock(cp->mutex);
+
+    return PJ_TRUE;
+}
+
+
+static void cpool_on_block_free(pj_pool_factory *f, pj_size_t sz)
+{
+    pj_caching_pool *cp = (pj_caching_pool*)f;
+
+    //pj_mutex_lock(cp->mutex);
+    cp->used_size -= sz;
+    //pj_mutex_unlock(cp->mutex);
+}
+
+
+#endif	/* PJ_HAS_POOL_ALT_API */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/pool_dbg.c b/jni/pjproject-android/pjlib/src/pj/pool_dbg.c
new file mode 100644
index 0000000..21bd36e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/pool_dbg.c
@@ -0,0 +1,191 @@
+/* $Id: pool_dbg.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#if PJ_HAS_POOL_ALT_API
+
+#if PJ_HAS_MALLOC_H
+#   include <malloc.h>
+#endif
+
+
+#if PJ_HAS_STDLIB_H
+#   include <stdlib.h>
+#endif
+
+
+#if ((defined(PJ_WIN32) && PJ_WIN32!=0) || \
+     (defined(PJ_WIN64) && PJ_WIN64 != 0)) && \
+     defined(PJ_DEBUG) && PJ_DEBUG!=0 && !PJ_NATIVE_STRING_IS_UNICODE
+#   include <windows.h>
+#   define TRACE_(msg)	OutputDebugString(msg)
+#endif
+
+/* Uncomment this to enable TRACE_ */
+//#undef TRACE_
+
+
+
+int PJ_NO_MEMORY_EXCEPTION;
+
+
+PJ_DEF(int) pj_NO_MEMORY_EXCEPTION()
+{
+    return PJ_NO_MEMORY_EXCEPTION;
+}
+
+/* Create pool */
+PJ_DEF(pj_pool_t*) pj_pool_create_imp( const char *file, int line,
+				       void *factory,
+				       const char *name,
+				       pj_size_t initial_size,
+				       pj_size_t increment_size,
+				       pj_pool_callback *callback)
+{
+    pj_pool_t *pool;
+
+    PJ_UNUSED_ARG(file);
+    PJ_UNUSED_ARG(line);
+    PJ_UNUSED_ARG(factory);
+    PJ_UNUSED_ARG(initial_size);
+    PJ_UNUSED_ARG(increment_size);
+
+    pool = malloc(sizeof(struct pj_pool_t));
+    if (!pool)
+	return NULL;
+
+    if (name) {
+	pj_ansi_strncpy(pool->obj_name, name, sizeof(pool->obj_name));
+	pool->obj_name[sizeof(pool->obj_name)-1] = '\0';
+    } else {
+	strcpy(pool->obj_name, "altpool");
+    }
+
+    pool->factory = NULL;
+    pool->first_mem = NULL;
+    pool->used_size = 0;
+    pool->cb = callback;
+
+    return pool;
+}
+
+
+/* Release pool */
+PJ_DEF(void) pj_pool_release_imp(pj_pool_t *pool)
+{
+    pj_pool_reset(pool);
+    free(pool);
+}
+
+/* Get pool name */
+PJ_DEF(const char*) pj_pool_getobjname_imp(pj_pool_t *pool)
+{
+    PJ_UNUSED_ARG(pool);
+    return "pooldbg";
+}
+
+/* Reset pool */
+PJ_DEF(void) pj_pool_reset_imp(pj_pool_t *pool)
+{
+    struct pj_pool_mem *mem;
+
+    mem = pool->first_mem;
+    while (mem) {
+	struct pj_pool_mem *next = mem->next;
+	free(mem);
+	mem = next;
+    }
+
+    pool->first_mem = NULL;
+}
+
+/* Get capacity */
+PJ_DEF(pj_size_t) pj_pool_get_capacity_imp(pj_pool_t *pool)
+{
+    PJ_UNUSED_ARG(pool);
+
+    /* Unlimited capacity */
+    return 0x7FFFFFFFUL;
+}
+
+/* Get total used size */
+PJ_DEF(pj_size_t) pj_pool_get_used_size_imp(pj_pool_t *pool)
+{
+    return pool->used_size;
+}
+
+/* Allocate memory from the pool */
+PJ_DEF(void*) pj_pool_alloc_imp( const char *file, int line, 
+				 pj_pool_t *pool, pj_size_t sz)
+{
+    struct pj_pool_mem *mem;
+
+    PJ_UNUSED_ARG(file);
+    PJ_UNUSED_ARG(line);
+
+    mem = malloc(sz + sizeof(struct pj_pool_mem));
+    if (!mem) {
+	if (pool->cb)
+	    (*pool->cb)(pool, sz);
+	return NULL;
+    }
+
+    mem->next = pool->first_mem;
+    pool->first_mem = mem;
+
+#ifdef TRACE_
+    {
+	char msg[120];
+	pj_ansi_sprintf(msg, "Mem %X (%d+%d bytes) allocated by %s:%d\r\n",
+			mem, sz, sizeof(struct pj_pool_mem), 
+			file, line);
+	TRACE_(msg);
+    }
+#endif
+
+    return ((char*)mem) + sizeof(struct pj_pool_mem);
+}
+
+/* Allocate memory from the pool and zero the memory */
+PJ_DEF(void*) pj_pool_calloc_imp( const char *file, int line, 
+				  pj_pool_t *pool, unsigned cnt, 
+				  unsigned elemsz)
+{
+    void *mem;
+
+    mem = pj_pool_alloc_imp(file, line, pool, cnt*elemsz);
+    if (!mem)
+	return NULL;
+
+    pj_bzero(mem, cnt*elemsz);
+    return mem;
+}
+
+/* Allocate memory from the pool and zero the memory */
+PJ_DEF(void*) pj_pool_zalloc_imp( const char *file, int line, 
+				  pj_pool_t *pool, pj_size_t sz)
+{
+    return pj_pool_calloc_imp(file, line, pool, 1, sz); 
+}
+
+
+
+#endif	/* PJ_HAS_POOL_ALT_API */
diff --git a/jni/pjproject-android/pjlib/src/pj/pool_policy_kmalloc.c b/jni/pjproject-android/pjlib/src/pj/pool_policy_kmalloc.c
new file mode 100644
index 0000000..2b5ec70
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/pool_policy_kmalloc.c
@@ -0,0 +1,64 @@
+/* $Id: pool_policy_kmalloc.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/pool.h>
+#include <pj/except.h>
+#include <pj/os.h>
+
+
+static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
+{
+    PJ_CHECK_STACK();
+    PJ_UNUSED_ARG(factory);
+
+    return kmalloc(size, GFP_ATOMIC);
+}
+
+static void default_block_free(pj_pool_factory *factory, 
+			       void *mem, pj_size_t size)
+{
+    PJ_CHECK_STACK();
+    PJ_UNUSED_ARG(factory);
+    PJ_UNUSED_ARG(size);
+
+    kfree(mem);
+}
+
+static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
+{
+    PJ_CHECK_STACK();
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(size);
+
+    PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
+}
+
+pj_pool_factory_policy pj_pool_factory_default_policy = 
+{
+    &default_block_alloc,
+    &default_block_free,
+    &default_pool_callback,
+    0
+};
+
+PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void)
+{
+    return &pj_pool_factory_default_policy;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/pool_policy_malloc.c b/jni/pjproject-android/pjlib/src/pj/pool_policy_malloc.c
new file mode 100644
index 0000000..6bba91d
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/pool_policy_malloc.c
@@ -0,0 +1,104 @@
+/* $Id: pool_policy_malloc.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/pool.h>
+#include <pj/except.h>
+#include <pj/os.h>
+#include <pj/compat/malloc.h>
+
+#if !PJ_HAS_POOL_ALT_API
+
+/*
+ * This file contains pool default policy definition and implementation.
+ */
+#include "pool_signature.h"
+
+
+static void *default_block_alloc(pj_pool_factory *factory, pj_size_t size)
+{
+    void *p;
+
+    PJ_CHECK_STACK();
+
+    if (factory->on_block_alloc) {
+	int rc;
+	rc = factory->on_block_alloc(factory, size);
+	if (!rc)
+	    return NULL;
+    }
+
+    p = malloc(size+(SIG_SIZE << 1));
+
+    if (p == NULL) {
+	if (factory->on_block_free) 
+	    factory->on_block_free(factory, size);
+    } else {
+	/* Apply signature when PJ_SAFE_POOL is set. It will move
+	 * "p" pointer forward.
+	 */
+	APPLY_SIG(p, size);
+    }
+
+    return p;
+}
+
+static void default_block_free(pj_pool_factory *factory, void *mem, 
+			       pj_size_t size)
+{
+    PJ_CHECK_STACK();
+
+    if (factory->on_block_free) 
+        factory->on_block_free(factory, size);
+
+    /* Check and remove signature when PJ_SAFE_POOL is set. It will
+     * move "mem" pointer backward.
+     */
+    REMOVE_SIG(mem, size);
+
+    /* Note that when PJ_SAFE_POOL is set, the actual size of the block
+     * is size + SIG_SIZE*2.
+     */
+
+    free(mem);
+}
+
+static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
+{
+    PJ_CHECK_STACK();
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(size);
+
+    PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
+}
+
+PJ_DEF_DATA(pj_pool_factory_policy) pj_pool_factory_default_policy =
+{
+    &default_block_alloc,
+    &default_block_free,
+    &default_pool_callback,
+    0
+};
+
+PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void)
+{
+    return &pj_pool_factory_default_policy;
+}
+
+
+#endif	/* PJ_HAS_POOL_ALT_API */
diff --git a/jni/pjproject-android/pjlib/src/pj/pool_policy_new.cpp b/jni/pjproject-android/pjlib/src/pj/pool_policy_new.cpp
new file mode 100644
index 0000000..909eb9f
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/pool_policy_new.cpp
@@ -0,0 +1,102 @@
+/* $Id: pool_policy_new.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/pool.h>
+#include <pj/except.h>
+#include <pj/os.h>
+
+#if !PJ_HAS_POOL_ALT_API
+
+/*
+ * This file contains pool default policy definition and implementation.
+ */
+#include "pool_signature.h"
+ 
+
+static void *operator_new(pj_pool_factory *factory, pj_size_t size)
+{
+    void *mem;
+
+    PJ_CHECK_STACK();
+
+    if (factory->on_block_alloc) {
+		int rc;
+		rc = factory->on_block_alloc(factory, size);
+		if (!rc)
+		    return NULL;
+    }
+    
+    mem = (void*) new char[size+(SIG_SIZE << 1)];
+    
+    /* Exception for new operator may be disabled, so.. */
+    if (mem) {
+	/* Apply signature when PJ_SAFE_POOL is set. It will move
+	 * "mem" pointer forward.
+	 */
+	APPLY_SIG(mem, size);
+    }
+
+    return mem;
+}
+
+static void operator_delete(pj_pool_factory *factory, void *mem, pj_size_t size)
+{
+    PJ_CHECK_STACK();
+
+    if (factory->on_block_free) 
+        factory->on_block_free(factory, size);
+    
+    /* Check and remove signature when PJ_SAFE_POOL is set. It will
+     * move "mem" pointer backward.
+     */
+    REMOVE_SIG(mem, size);
+
+    /* Note that when PJ_SAFE_POOL is set, the actual size of the block
+     * is size + SIG_SIZE*2.
+     */
+
+    char *p = (char*)mem;
+    delete [] p;
+}
+
+static void default_pool_callback(pj_pool_t *pool, pj_size_t size)
+{
+    PJ_CHECK_STACK();
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(size);
+
+    PJ_THROW(PJ_NO_MEMORY_EXCEPTION);
+}
+
+PJ_DEF_DATA(pj_pool_factory_policy) pj_pool_factory_default_policy = 
+{
+    &operator_new,
+    &operator_delete,
+    &default_pool_callback,
+    0
+};
+
+PJ_DEF(const pj_pool_factory_policy*) pj_pool_factory_get_default_policy(void)
+{
+    return &pj_pool_factory_default_policy;
+}
+
+ 
+#endif	/* PJ_HAS_POOL_ALT_API */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/pool_signature.h b/jni/pjproject-android/pjlib/src/pj/pool_signature.h
new file mode 100644
index 0000000..6d0d432
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/pool_signature.h
@@ -0,0 +1,68 @@
+/* $Id: pool_signature.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/assert.h>
+#include <pj/string.h>
+
+#if PJ_SAFE_POOL
+#   define SIG_SIZE		sizeof(pj_uint32_t)
+
+static void apply_signature(void *p, pj_size_t size);
+static void check_pool_signature(void *p, pj_size_t size);
+
+#   define APPLY_SIG(p,sz)	apply_signature(p,sz), \
+				p=(void*)(((char*)p)+SIG_SIZE)
+#   define REMOVE_SIG(p,sz)	check_pool_signature(p,sz), \
+				p=(void*)(((char*)p)-SIG_SIZE)
+
+#   define SIG_BEGIN	    0x600DC0DE
+#   define SIG_END	    0x0BADC0DE
+
+static void apply_signature(void *p, pj_size_t size)
+{
+    pj_uint32_t sig;
+
+    sig = SIG_BEGIN;
+    pj_memcpy(p, &sig, SIG_SIZE);
+
+    sig = SIG_END;
+    pj_memcpy(((char*)p)+SIG_SIZE+size, &sig, SIG_SIZE);
+}
+
+static void check_pool_signature(void *p, pj_size_t size)
+{
+    pj_uint32_t sig;
+    pj_uint8_t *mem = (pj_uint8_t*)p;
+
+    /* Check that signature at the start of the block is still intact */
+    sig = SIG_BEGIN;
+    pj_assert(!pj_memcmp(mem-SIG_SIZE, &sig, SIG_SIZE));
+
+    /* Check that signature at the end of the block is still intact.
+     * Note that "mem" has been incremented by SIG_SIZE 
+     */
+    sig = SIG_END;
+    pj_assert(!pj_memcmp(mem+size, &sig, SIG_SIZE));
+}
+
+#else
+#   define SIG_SIZE	    0
+#   define APPLY_SIG(p,sz)
+#   define REMOVE_SIG(p,sz)
+#endif
diff --git a/jni/pjproject-android/pjlib/src/pj/rand.c b/jni/pjproject-android/pjlib/src/pj/rand.c
new file mode 100644
index 0000000..59bad5f
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/rand.c
@@ -0,0 +1,35 @@
+/* $Id: rand.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/rand.h>
+#include <pj/os.h>
+#include <pj/compat/rand.h>
+
+PJ_DEF(void) pj_srand(unsigned int seed)
+{
+    PJ_CHECK_STACK();
+    platform_srand(seed);
+}
+
+PJ_DEF(int) pj_rand(void)
+{
+    PJ_CHECK_STACK();
+    return platform_rand();
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/rbtree.c b/jni/pjproject-android/pjlib/src/pj/rbtree.c
new file mode 100644
index 0000000..37c977e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/rbtree.c
@@ -0,0 +1,428 @@
+/* $Id: rbtree.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/rbtree.h>
+#include <pj/os.h>
+
+static void left_rotate( pj_rbtree *tree, pj_rbtree_node *node ) 
+{
+    pj_rbtree_node *rnode, *parent;
+
+    PJ_CHECK_STACK();
+
+    rnode = node->right;
+    if (rnode == tree->null)
+        return;
+    
+    node->right = rnode->left;
+    if (rnode->left != tree->null)
+        rnode->left->parent = node;
+    parent = node->parent;
+    rnode->parent = parent;
+    if (parent != tree->null) {
+        if (parent->left == node)
+	   parent->left = rnode;
+        else
+	   parent->right = rnode;
+    } else {
+        tree->root = rnode;
+    }
+    rnode->left = node;
+    node->parent = rnode;
+}
+
+static void right_rotate( pj_rbtree *tree, pj_rbtree_node *node ) 
+{
+    pj_rbtree_node *lnode, *parent;
+
+    PJ_CHECK_STACK();
+
+    lnode = node->left;
+    if (lnode == tree->null)
+        return;
+
+    node->left = lnode->right;
+    if (lnode->right != tree->null)
+	lnode->right->parent = node;
+    parent = node->parent;
+    lnode->parent = parent;
+
+    if (parent != tree->null) {
+        if (parent->left == node)
+	    parent->left = lnode;
+	else
+	    parent->right = lnode;
+    } else {
+        tree->root = lnode;
+    }
+    lnode->right = node;
+    node->parent = lnode;
+}
+
+static void insert_fixup( pj_rbtree *tree, pj_rbtree_node *node ) 
+{
+    pj_rbtree_node *temp, *parent;
+
+    PJ_CHECK_STACK();
+
+    while (node != tree->root && node->parent->color == PJ_RBCOLOR_RED) {
+        parent = node->parent;
+        if (parent == parent->parent->left) {
+	    temp = parent->parent->right;
+	    if (temp->color == PJ_RBCOLOR_RED) {
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        node = parent;
+	        node->color = PJ_RBCOLOR_BLACK;
+	        node = node->parent;
+	        node->color = PJ_RBCOLOR_RED;
+	    } else {
+	        if (node == parent->right) {
+		   node = parent;
+		   left_rotate(tree, node);
+	        }
+	        temp = node->parent;
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        temp = temp->parent;
+	        temp->color = PJ_RBCOLOR_RED;
+	        right_rotate( tree, temp);
+	    }
+        } else {
+	    temp = parent->parent->left;
+	    if (temp->color == PJ_RBCOLOR_RED) {
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        node = parent;
+	        node->color = PJ_RBCOLOR_BLACK;
+	        node = node->parent;
+	        node->color = PJ_RBCOLOR_RED;
+	    } else {
+	        if (node == parent->left) {
+		    node = parent;
+		    right_rotate(tree, node);
+	        }
+	        temp = node->parent;
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        temp = temp->parent;
+	        temp->color = PJ_RBCOLOR_RED;
+	        left_rotate(tree, temp);
+	   }
+        }
+    }
+	
+    tree->root->color = PJ_RBCOLOR_BLACK;
+}
+
+
+static void delete_fixup( pj_rbtree *tree, pj_rbtree_node *node )
+{
+    pj_rbtree_node *temp;
+
+    PJ_CHECK_STACK();
+
+    while (node != tree->root && node->color == PJ_RBCOLOR_BLACK) {
+        if (node->parent->left == node) {
+	    temp = node->parent->right;
+	    if (temp->color == PJ_RBCOLOR_RED) {
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        node->parent->color = PJ_RBCOLOR_RED;
+	        left_rotate(tree, node->parent);
+	        temp = node->parent->right;
+	    }
+	    if (temp->left->color == PJ_RBCOLOR_BLACK && 
+	        temp->right->color == PJ_RBCOLOR_BLACK) 
+	    {
+	        temp->color = PJ_RBCOLOR_RED;
+	        node = node->parent;
+	    } else {
+	        if (temp->right->color == PJ_RBCOLOR_BLACK) {
+		    temp->left->color = PJ_RBCOLOR_BLACK;
+		    temp->color = PJ_RBCOLOR_RED;
+		    right_rotate( tree, temp);
+		    temp = node->parent->right;
+	        }
+	        temp->color = node->parent->color;
+	        temp->right->color = PJ_RBCOLOR_BLACK;
+	        node->parent->color = PJ_RBCOLOR_BLACK;
+	        left_rotate(tree, node->parent);
+	        node = tree->root;
+	    }
+        } else {
+	    temp = node->parent->left;
+	    if (temp->color == PJ_RBCOLOR_RED) {
+	        temp->color = PJ_RBCOLOR_BLACK;
+	        node->parent->color = PJ_RBCOLOR_RED;
+	        right_rotate( tree, node->parent);
+	        temp = node->parent->left;
+	    }
+	    if (temp->right->color == PJ_RBCOLOR_BLACK && 
+		temp->left->color == PJ_RBCOLOR_BLACK) 
+	    {
+	        temp->color = PJ_RBCOLOR_RED;
+	        node = node->parent;
+	    } else {
+	        if (temp->left->color == PJ_RBCOLOR_BLACK) {
+		    temp->right->color = PJ_RBCOLOR_BLACK;
+		    temp->color = PJ_RBCOLOR_RED;
+		    left_rotate( tree, temp);
+		    temp = node->parent->left;
+	        }
+	        temp->color = node->parent->color;
+	        node->parent->color = PJ_RBCOLOR_BLACK;
+	        temp->left->color = PJ_RBCOLOR_BLACK;
+	        right_rotate(tree, node->parent);
+	        node = tree->root;
+	    }
+        }
+    }
+	
+    node->color = PJ_RBCOLOR_BLACK;
+}
+
+
+PJ_DEF(void) pj_rbtree_init( pj_rbtree *tree, pj_rbtree_comp *comp )
+{
+    PJ_CHECK_STACK();
+
+    tree->null = tree->root = &tree->null_node;
+    tree->null->key = NULL;
+    tree->null->user_data = NULL;
+    tree->size = 0;
+    tree->null->left = tree->null->right = tree->null->parent = tree->null;
+    tree->null->color = PJ_RBCOLOR_BLACK;
+    tree->comp = comp;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_first( pj_rbtree *tree )
+{
+    register pj_rbtree_node *node = tree->root;
+    register pj_rbtree_node *null = tree->null;
+    
+    PJ_CHECK_STACK();
+
+    while (node->left != null)
+	node = node->left;
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_last( pj_rbtree *tree )
+{
+    register pj_rbtree_node *node = tree->root;
+    register pj_rbtree_node *null = tree->null;
+    
+    PJ_CHECK_STACK();
+
+    while (node->right != null)
+	node = node->right;
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_next( pj_rbtree *tree, 
+					register pj_rbtree_node *node )
+{
+    register pj_rbtree_node *null = tree->null;
+    
+    PJ_CHECK_STACK();
+
+    if (node->right != null) {
+	for (node=node->right; node->left!=null; node = node->left)
+	    /* void */;
+    } else {
+        register pj_rbtree_node *temp = node->parent;
+        while (temp!=null && temp->right==node) {
+	    node = temp;
+	    temp = temp->parent;
+	}
+	node = temp;
+    }    
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_prev( pj_rbtree *tree, 
+					register pj_rbtree_node *node )
+{
+    register pj_rbtree_node *null = tree->null;
+    
+    PJ_CHECK_STACK();
+
+    if (node->left != null) {
+        for (node=node->left; node->right!=null; node=node->right)
+	   /* void */;
+    } else {
+        register pj_rbtree_node *temp = node->parent;
+        while (temp!=null && temp->left==node) {
+	    node = temp;
+	    temp = temp->parent;
+        }
+        node = temp;
+    }    
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(int) pj_rbtree_insert( pj_rbtree *tree, 
+			      pj_rbtree_node *element )
+{
+    int rv = 0;
+    pj_rbtree_node *node, *parent = tree->null, 
+		   *null = tree->null;
+    pj_rbtree_comp *comp = tree->comp;
+	
+    PJ_CHECK_STACK();
+
+    node = tree->root;	
+    while (node != null) {
+        rv = (*comp)(element->key, node->key);
+        if (rv == 0) {
+	    /* found match, i.e. entry with equal key already exist */
+	    return -1;
+	}    
+	parent = node;
+        node = rv < 0 ? node->left : node->right;
+    }
+
+    element->color = PJ_RBCOLOR_RED;
+    element->left = element->right = null;
+
+    node = element;
+    if (parent != null) {
+        node->parent = parent;
+        if (rv < 0)
+	   parent->left = node;
+        else
+	   parent->right = node;
+        insert_fixup( tree, node);
+    } else {
+        tree->root = node;
+        node->parent = null;
+        node->color = PJ_RBCOLOR_BLACK;
+    }
+	
+    ++tree->size;
+    return 0;
+}
+
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_find( pj_rbtree *tree,
+					const void *key )
+{
+    int rv;
+    pj_rbtree_node *node = tree->root;
+    pj_rbtree_node *null = tree->null;
+    pj_rbtree_comp *comp = tree->comp;
+    
+    while (node != null) {
+        rv = (*comp)(key, node->key);
+        if (rv == 0)
+	    return node;
+        node = rv < 0 ? node->left : node->right;
+    }
+    return node != null ? node : NULL;
+}
+
+PJ_DEF(pj_rbtree_node*) pj_rbtree_erase( pj_rbtree *tree,
+					 pj_rbtree_node *node )
+{
+    pj_rbtree_node *succ;
+    pj_rbtree_node *null = tree->null;
+    pj_rbtree_node *child;
+    pj_rbtree_node *parent;
+    
+    PJ_CHECK_STACK();
+
+    if (node->left == null || node->right == null) {
+        succ = node;
+    } else {
+        for (succ=node->right; succ->left!=null; succ=succ->left)
+	   /* void */;
+    }
+
+    child = succ->left != null ? succ->left : succ->right;
+    parent = succ->parent;
+    child->parent = parent;
+    
+    if (parent != null) {
+	if (parent->left == succ)
+	    parent->left = child;
+        else
+	   parent->right = child;
+    } else
+        tree->root = child;
+
+    if (succ != node) {
+        succ->parent = node->parent;
+        succ->left = node->left;
+        succ->right = node->right;
+        succ->color = node->color;
+
+        parent = node->parent;
+        if (parent != null) {
+	   if (parent->left==node)
+	        parent->left=succ;
+	   else
+		parent->right=succ;
+        }
+        if (node->left != null)
+	   node->left->parent = succ;;
+        if (node->right != null)
+	    node->right->parent = succ;
+
+        if (tree->root == node)
+	   tree->root = succ;
+    }
+
+    if (succ->color == PJ_RBCOLOR_BLACK) {
+	if (child != null) 
+	    delete_fixup(tree, child);
+        tree->null->color = PJ_RBCOLOR_BLACK;
+    }
+
+    --tree->size;
+    return node;
+}
+
+
+PJ_DEF(unsigned) pj_rbtree_max_height( pj_rbtree *tree,
+				       pj_rbtree_node *node )
+{
+    unsigned l, r;
+    
+    PJ_CHECK_STACK();
+
+    if (node==NULL) 
+	node = tree->root;
+    
+    l = node->left != tree->null ? pj_rbtree_max_height(tree,node->left)+1 : 0;
+    r = node->right != tree->null ? pj_rbtree_max_height(tree,node->right)+1 : 0;
+    return l > r ? l : r;
+}
+
+PJ_DEF(unsigned) pj_rbtree_min_height( pj_rbtree *tree,
+				       pj_rbtree_node *node )
+{
+    unsigned l, r;
+    
+    PJ_CHECK_STACK();
+
+    if (node==NULL) 
+	node=tree->root;
+    
+    l = (node->left != tree->null) ? pj_rbtree_max_height(tree,node->left)+1 : 0;
+    r = (node->right != tree->null) ? pj_rbtree_max_height(tree,node->right)+1 : 0;
+    return l > r ? r : l;
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_bsd.c b/jni/pjproject-android/pjlib/src/pj/sock_bsd.c
new file mode 100644
index 0000000..2dd7d38
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_bsd.c
@@ -0,0 +1,850 @@
+/* $Id: sock_bsd.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/compat/socket.h>
+#include <pj/addr_resolv.h>
+#include <pj/errno.h>
+#include <pj/unicode.h>
+
+/*
+ * Address families conversion.
+ * The values here are indexed based on pj_addr_family.
+ */
+const pj_uint16_t PJ_AF_UNSPEC	= AF_UNSPEC;
+const pj_uint16_t PJ_AF_UNIX	= AF_UNIX;
+const pj_uint16_t PJ_AF_INET	= AF_INET;
+const pj_uint16_t PJ_AF_INET6	= AF_INET6;
+#ifdef AF_PACKET
+const pj_uint16_t PJ_AF_PACKET	= AF_PACKET;
+#else
+const pj_uint16_t PJ_AF_PACKET	= 0xFFFF;
+#endif
+#ifdef AF_IRDA
+const pj_uint16_t PJ_AF_IRDA	= AF_IRDA;
+#else
+const pj_uint16_t PJ_AF_IRDA	= 0xFFFF;
+#endif
+
+/*
+ * Socket types conversion.
+ * The values here are indexed based on pj_sock_type
+ */
+const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM;
+const pj_uint16_t PJ_SOCK_DGRAM	= SOCK_DGRAM;
+const pj_uint16_t PJ_SOCK_RAW	= SOCK_RAW;
+const pj_uint16_t PJ_SOCK_RDM	= SOCK_RDM;
+
+/*
+ * Socket level values.
+ */
+const pj_uint16_t PJ_SOL_SOCKET	= SOL_SOCKET;
+#ifdef SOL_IP
+const pj_uint16_t PJ_SOL_IP	= SOL_IP;
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64) 
+const pj_uint16_t PJ_SOL_IP	= IPPROTO_IP;
+#else
+const pj_uint16_t PJ_SOL_IP	= 0;
+#endif /* SOL_IP */
+
+#if defined(SOL_TCP)
+const pj_uint16_t PJ_SOL_TCP	= SOL_TCP;
+#elif defined(IPPROTO_TCP)
+const pj_uint16_t PJ_SOL_TCP	= IPPROTO_TCP;
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64)
+const pj_uint16_t PJ_SOL_TCP	= IPPROTO_TCP;
+#else
+const pj_uint16_t PJ_SOL_TCP	= 6;
+#endif /* SOL_TCP */
+
+#ifdef SOL_UDP
+const pj_uint16_t PJ_SOL_UDP	= SOL_UDP;
+#elif defined(IPPROTO_UDP)
+const pj_uint16_t PJ_SOL_UDP	= IPPROTO_UDP;
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64)
+const pj_uint16_t PJ_SOL_UDP	= IPPROTO_UDP;
+#else
+const pj_uint16_t PJ_SOL_UDP	= 17;
+#endif /* SOL_UDP */
+
+#ifdef SOL_IPV6
+const pj_uint16_t PJ_SOL_IPV6	= SOL_IPV6;
+#elif (defined(PJ_WIN32) && PJ_WIN32) || (defined(PJ_WIN64) && PJ_WIN64)
+#   if defined(IPPROTO_IPV6) || (_WIN32_WINNT >= 0x0501)
+	const pj_uint16_t PJ_SOL_IPV6	= IPPROTO_IPV6;
+#   else
+	const pj_uint16_t PJ_SOL_IPV6	= 41;
+#   endif
+#else
+const pj_uint16_t PJ_SOL_IPV6	= 41;
+#endif /* SOL_IPV6 */
+
+/* IP_TOS */
+#ifdef IP_TOS
+const pj_uint16_t PJ_IP_TOS	= IP_TOS;
+#else
+const pj_uint16_t PJ_IP_TOS	= 1;
+#endif
+
+
+/* TOS settings (declared in netinet/ip.h) */
+#ifdef IPTOS_LOWDELAY
+const pj_uint16_t PJ_IPTOS_LOWDELAY	= IPTOS_LOWDELAY;
+#else
+const pj_uint16_t PJ_IPTOS_LOWDELAY	= 0x10;
+#endif
+#ifdef IPTOS_THROUGHPUT
+const pj_uint16_t PJ_IPTOS_THROUGHPUT	= IPTOS_THROUGHPUT;
+#else
+const pj_uint16_t PJ_IPTOS_THROUGHPUT	= 0x08;
+#endif
+#ifdef IPTOS_RELIABILITY
+const pj_uint16_t PJ_IPTOS_RELIABILITY	= IPTOS_RELIABILITY;
+#else
+const pj_uint16_t PJ_IPTOS_RELIABILITY	= 0x04;
+#endif
+#ifdef IPTOS_MINCOST
+const pj_uint16_t PJ_IPTOS_MINCOST	= IPTOS_MINCOST;
+#else
+const pj_uint16_t PJ_IPTOS_MINCOST	= 0x02;
+#endif
+
+
+/* optname values. */
+const pj_uint16_t PJ_SO_TYPE    = SO_TYPE;
+const pj_uint16_t PJ_SO_RCVBUF  = SO_RCVBUF;
+const pj_uint16_t PJ_SO_SNDBUF  = SO_SNDBUF;
+const pj_uint16_t PJ_TCP_NODELAY= TCP_NODELAY;
+const pj_uint16_t PJ_SO_REUSEADDR= SO_REUSEADDR;
+#ifdef SO_NOSIGPIPE
+const pj_uint16_t PJ_SO_NOSIGPIPE = SO_NOSIGPIPE;
+#else
+const pj_uint16_t PJ_SO_NOSIGPIPE = 0xFFFF;
+#endif
+#if defined(SO_PRIORITY)
+const pj_uint16_t PJ_SO_PRIORITY = SO_PRIORITY;
+#else
+/* This is from Linux, YMMV */
+const pj_uint16_t PJ_SO_PRIORITY = 12;
+#endif
+
+/* Multicasting is not supported e.g. in PocketPC 2003 SDK */
+#ifdef IP_MULTICAST_IF
+const pj_uint16_t PJ_IP_MULTICAST_IF    = IP_MULTICAST_IF;
+const pj_uint16_t PJ_IP_MULTICAST_TTL   = IP_MULTICAST_TTL;
+const pj_uint16_t PJ_IP_MULTICAST_LOOP  = IP_MULTICAST_LOOP;
+const pj_uint16_t PJ_IP_ADD_MEMBERSHIP  = IP_ADD_MEMBERSHIP;
+const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = IP_DROP_MEMBERSHIP;
+#else
+const pj_uint16_t PJ_IP_MULTICAST_IF    = 0xFFFF;
+const pj_uint16_t PJ_IP_MULTICAST_TTL   = 0xFFFF;
+const pj_uint16_t PJ_IP_MULTICAST_LOOP  = 0xFFFF;
+const pj_uint16_t PJ_IP_ADD_MEMBERSHIP  = 0xFFFF;
+const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = 0xFFFF;
+#endif
+
+/* recv() and send() flags */
+const int PJ_MSG_OOB		= MSG_OOB;
+const int PJ_MSG_PEEK		= MSG_PEEK;
+const int PJ_MSG_DONTROUTE	= MSG_DONTROUTE;
+
+
+#if 0
+static void CHECK_ADDR_LEN(const pj_sockaddr *addr, int len)
+{
+    pj_sockaddr *a = (pj_sockaddr*)addr;
+    pj_assert((a->addr.sa_family==PJ_AF_INET && len==sizeof(pj_sockaddr_in)) ||
+	      (a->addr.sa_family==PJ_AF_INET6 && len==sizeof(pj_sockaddr_in6)));
+
+}
+#else
+#define CHECK_ADDR_LEN(addr,len)
+#endif
+
+/*
+ * Convert 16-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
+{
+    return ntohs(netshort);
+}
+
+/*
+ * Convert 16-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
+{
+    return htons(hostshort);
+}
+
+/*
+ * Convert 32-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
+{
+    return ntohl(netlong);
+}
+
+/*
+ * Convert 32-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
+{
+    return htonl(hostlong);
+}
+
+/*
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ */
+PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr)
+{
+#if !defined(PJ_LINUX) && !defined(PJ_LINUX_KERNEL)
+    return inet_ntoa(*(struct in_addr*)&inaddr);
+#else
+    struct in_addr addr;
+    addr.s_addr = inaddr.s_addr;
+    return inet_ntoa(addr);
+#endif
+}
+
+/*
+ * This function converts the Internet host address cp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to. 
+ */
+PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp)
+{
+    char tempaddr[PJ_INET_ADDRSTRLEN];
+
+    /* Initialize output with PJ_INADDR_NONE.
+     * Some apps relies on this instead of the return value
+     * (and anyway the return value is quite confusing!)
+     */
+    inp->s_addr = PJ_INADDR_NONE;
+
+    /* Caution:
+     *	this function might be called with cp->slen >= 16
+     *  (i.e. when called with hostname to check if it's an IP addr).
+     */
+    PJ_ASSERT_RETURN(cp && cp->slen && inp, 0);
+    if (cp->slen >= PJ_INET_ADDRSTRLEN) {
+	return 0;
+    }
+
+    pj_memcpy(tempaddr, cp->ptr, cp->slen);
+    tempaddr[cp->slen] = '\0';
+
+#if defined(PJ_SOCK_HAS_INET_ATON) && PJ_SOCK_HAS_INET_ATON != 0
+    return inet_aton(tempaddr, (struct in_addr*)inp);
+#else
+    inp->s_addr = inet_addr(tempaddr);
+    return inp->s_addr == PJ_INADDR_NONE ? 0 : 1;
+#endif
+}
+
+/*
+ * Convert text to IPv4/IPv6 address.
+ */
+PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst)
+{
+    char tempaddr[PJ_INET6_ADDRSTRLEN];
+
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP);
+    PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL);
+
+    /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be 
+     * compatible with pj_inet_aton()
+     */
+    if (af==PJ_AF_INET) {
+	((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE;
+    }
+
+    /* Caution:
+     *	this function might be called with cp->slen >= 46
+     *  (i.e. when called with hostname to check if it's an IP addr).
+     */
+    if (src->slen >= PJ_INET6_ADDRSTRLEN) {
+	return PJ_ENAMETOOLONG;
+    }
+
+    pj_memcpy(tempaddr, src->ptr, src->slen);
+    tempaddr[src->slen] = '\0';
+
+#if defined(PJ_SOCK_HAS_INET_PTON) && PJ_SOCK_HAS_INET_PTON != 0
+    /*
+     * Implementation using inet_pton()
+     */
+    if (inet_pton(af, tempaddr, dst) != 1) {
+	pj_status_t status = pj_get_netos_error();
+	if (status == PJ_SUCCESS)
+	    status = PJ_EUNKNOWN;
+
+	return status;
+    }
+
+    return PJ_SUCCESS;
+
+#elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE)
+    /*
+     * Implementation on Windows, using WSAStringToAddress().
+     * Should also work on Unicode systems.
+     */
+    {
+	PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN)
+	pj_sockaddr sock_addr;
+	int addr_len = sizeof(sock_addr);
+	int rc;
+
+	sock_addr.addr.sa_family = (pj_uint16_t)af;
+	rc = WSAStringToAddress(
+		PJ_STRING_TO_NATIVE(tempaddr,wtempaddr,sizeof(wtempaddr)), 
+		af, NULL, (LPSOCKADDR)&sock_addr, &addr_len);
+	if (rc != 0) {
+	    /* If you get rc 130022 Invalid argument (WSAEINVAL) with IPv6,
+	     * check that you have IPv6 enabled (install it in the network
+	     * adapter).
+	     */
+	    pj_status_t status = pj_get_netos_error();
+	    if (status == PJ_SUCCESS)
+		status = PJ_EUNKNOWN;
+
+	    return status;
+	}
+
+	if (sock_addr.addr.sa_family == PJ_AF_INET) {
+	    pj_memcpy(dst, &sock_addr.ipv4.sin_addr, 4);
+	    return PJ_SUCCESS;
+	} else if (sock_addr.addr.sa_family == PJ_AF_INET6) {
+	    pj_memcpy(dst, &sock_addr.ipv6.sin6_addr, 16);
+	    return PJ_SUCCESS;
+	} else {
+	    pj_assert(!"Shouldn't happen");
+	    return PJ_EBUG;
+	}
+    }
+#elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0
+    /* IPv6 support is disabled, just return error without raising assertion */
+    return PJ_EIPV6NOTSUP;
+#else
+    pj_assert(!"Not supported");
+    return PJ_EIPV6NOTSUP;
+#endif
+}
+
+/*
+ * Convert IPv4/IPv6 address to text.
+ */
+PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src,
+				 char *dst, int size)
+
+{
+    PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL);
+
+    *dst = '\0';
+
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EAFNOTSUP);
+
+#if defined(PJ_SOCK_HAS_INET_NTOP) && PJ_SOCK_HAS_INET_NTOP != 0
+    /*
+     * Implementation using inet_ntop()
+     */
+    if (inet_ntop(af, src, dst, size) == NULL) {
+	pj_status_t status = pj_get_netos_error();
+	if (status == PJ_SUCCESS)
+	    status = PJ_EUNKNOWN;
+
+	return status;
+    }
+
+    return PJ_SUCCESS;
+
+#elif defined(PJ_WIN32) || defined(PJ_WIN64) || defined(PJ_WIN32_WINCE)
+    /*
+     * Implementation on Windows, using WSAAddressToString().
+     * Should also work on Unicode systems.
+     */
+    {
+	PJ_DECL_UNICODE_TEMP_BUF(wtempaddr,PJ_INET6_ADDRSTRLEN)
+	pj_sockaddr sock_addr;
+	DWORD addr_len, addr_str_len;
+	int rc;
+
+	pj_bzero(&sock_addr, sizeof(sock_addr));
+	sock_addr.addr.sa_family = (pj_uint16_t)af;
+	if (af == PJ_AF_INET) {
+	    if (size < PJ_INET_ADDRSTRLEN)
+		return PJ_ETOOSMALL;
+	    pj_memcpy(&sock_addr.ipv4.sin_addr, src, 4);
+	    addr_len = sizeof(pj_sockaddr_in);
+	    addr_str_len = PJ_INET_ADDRSTRLEN;
+	} else if (af == PJ_AF_INET6) {
+	    if (size < PJ_INET6_ADDRSTRLEN)
+		return PJ_ETOOSMALL;
+	    pj_memcpy(&sock_addr.ipv6.sin6_addr, src, 16);
+	    addr_len = sizeof(pj_sockaddr_in6);
+	    addr_str_len = PJ_INET6_ADDRSTRLEN;
+	} else {
+	    pj_assert(!"Unsupported address family");
+	    return PJ_EAFNOTSUP;
+	}
+
+#if PJ_NATIVE_STRING_IS_UNICODE
+	rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len,
+				NULL, wtempaddr, &addr_str_len);
+	if (rc == 0) {
+	    pj_unicode_to_ansi(wtempaddr, wcslen(wtempaddr), dst, size);
+	}
+#else
+	rc = WSAAddressToString((LPSOCKADDR)&sock_addr, addr_len,
+				NULL, dst, &addr_str_len);
+#endif
+
+	if (rc != 0) {
+	    pj_status_t status = pj_get_netos_error();
+	    if (status == PJ_SUCCESS)
+		status = PJ_EUNKNOWN;
+
+	    return status;
+	}
+
+	return PJ_SUCCESS;
+    }
+
+#elif !defined(PJ_HAS_IPV6) || PJ_HAS_IPV6==0
+    /* IPv6 support is disabled, just return error without raising assertion */
+    return PJ_EIPV6NOTSUP;
+#else
+    pj_assert(!"Not supported");
+    return PJ_EIPV6NOTSUP;
+#endif
+}
+
+/*
+ * Get hostname.
+ */
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+    static char buf[PJ_MAX_HOSTNAME];
+    static pj_str_t hostname;
+
+    PJ_CHECK_STACK();
+
+    if (hostname.ptr == NULL) {
+	hostname.ptr = buf;
+	if (gethostname(buf, sizeof(buf)) != 0) {
+	    hostname.ptr[0] = '\0';
+	    hostname.slen = 0;
+	} else {
+            hostname.slen = strlen(buf);
+	}
+    }
+    return &hostname;
+}
+
+#if defined(PJ_WIN32) || defined(PJ_WIN64)
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af, 
+				   int type, 
+				   int proto,
+				   pj_sock_t *sock)
+{
+    PJ_CHECK_STACK();
+
+    /* Sanity checks. */
+    PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN((SOCKET)PJ_INVALID_SOCKET==INVALID_SOCKET, 
+                     (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
+
+    *sock = WSASocket(af, type, proto, NULL, 0, WSA_FLAG_OVERLAPPED);
+
+    if (*sock == PJ_INVALID_SOCKET) 
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    
+#if PJ_SOCK_DISABLE_WSAECONNRESET && \
+    (!defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0)
+
+#ifndef SIO_UDP_CONNRESET
+    #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12)
+#endif
+
+    /* Disable WSAECONNRESET for UDP.
+     * See https://trac.pjsip.org/repos/ticket/1197
+     */
+    if (type==PJ_SOCK_DGRAM) {
+	DWORD dwBytesReturned = 0;
+	BOOL bNewBehavior = FALSE;
+	DWORD rc;
+
+	rc = WSAIoctl(*sock, SIO_UDP_CONNRESET,
+		      &bNewBehavior, sizeof(bNewBehavior),
+		      NULL, 0, &dwBytesReturned,
+		      NULL, NULL);
+
+	if (rc==SOCKET_ERROR) {
+	    // Ignored..
+	}
+    }
+#endif
+
+    return PJ_SUCCESS;
+}
+
+#else
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af, 
+				   int type, 
+				   int proto, 
+				   pj_sock_t *sock)
+{
+
+    PJ_CHECK_STACK();
+
+    /* Sanity checks. */
+    PJ_ASSERT_RETURN(sock!=NULL, PJ_EINVAL);
+    PJ_ASSERT_RETURN(PJ_INVALID_SOCKET==-1, 
+                     (*sock=PJ_INVALID_SOCKET, PJ_EINVAL));
+    
+    *sock = socket(af, type, proto);
+    if (*sock == PJ_INVALID_SOCKET)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else {
+	pj_int32_t val = 1;
+	if (type == pj_SOCK_STREAM()) {
+	    pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), pj_SO_NOSIGPIPE(),
+			       &val, sizeof(val));
+	}
+#if defined(PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT) && \
+    PJ_IPHONE_OS_HAS_MULTITASKING_SUPPORT!=0
+	if (type == pj_SOCK_DGRAM()) {
+	    pj_sock_setsockopt(*sock, pj_SOL_SOCKET(), SO_NOSIGPIPE, 
+			       &val, sizeof(val));
+	}
+#endif
+	return PJ_SUCCESS;
+    }
+}
+#endif
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, 
+				  const pj_sockaddr_t *addr,
+				  int len)
+{
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(addr && len >= (int)sizeof(struct sockaddr_in), PJ_EINVAL);
+
+    CHECK_ADDR_LEN(addr, len);
+
+    if (bind(sock, (struct sockaddr*)addr, len) != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, 
+				     pj_uint32_t addr32,
+				     pj_uint16_t port)
+{
+    pj_sockaddr_in addr;
+
+    PJ_CHECK_STACK();
+
+    PJ_SOCKADDR_SET_LEN(&addr, sizeof(pj_sockaddr_in));
+    addr.sin_family = PJ_AF_INET;
+    pj_bzero(addr.sin_zero, sizeof(addr.sin_zero));
+    addr.sin_addr.s_addr = pj_htonl(addr32);
+    addr.sin_port = pj_htons(port);
+
+    return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in));
+}
+
+
+/*
+ * Close socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock)
+{
+    int rc;
+
+    PJ_CHECK_STACK();
+#if defined(PJ_WIN32) && PJ_WIN32!=0 || \
+    defined(PJ_WIN64) && PJ_WIN64 != 0 || \
+    defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
+    rc = closesocket(sock);
+#else
+    rc = close(sock);
+#endif
+
+    if (rc != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Get remote's name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock,
+					 pj_sockaddr_t *addr,
+					 int *namelen)
+{
+    PJ_CHECK_STACK();
+    if (getpeername(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else {
+	PJ_SOCKADDR_RESET_LEN(addr);
+	return PJ_SUCCESS;
+    }
+}
+
+/*
+ * Get socket name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock,
+					 pj_sockaddr_t *addr,
+					 int *namelen)
+{
+    PJ_CHECK_STACK();
+    if (getsockname(sock, (struct sockaddr*)addr, (socklen_t*)namelen) != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else {
+	PJ_SOCKADDR_RESET_LEN(addr);
+	return PJ_SUCCESS;
+    }
+}
+
+/*
+ * Send data
+ */
+PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock,
+				 const void *buf,
+				 pj_ssize_t *len,
+				 unsigned flags)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(len, PJ_EINVAL);
+
+#ifdef MSG_NOSIGNAL
+    /* Suppress SIGPIPE. See https://trac.pjsip.org/repos/ticket/1538 */
+    flags |= MSG_NOSIGNAL;
+#endif
+
+    *len = send(sock, (const char*)buf, (int)(*len), flags);
+
+    if (*len < 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+
+/*
+ * Send data.
+ */
+PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock,
+				   const void *buf,
+				   pj_ssize_t *len,
+				   unsigned flags,
+				   const pj_sockaddr_t *to,
+				   int tolen)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(len, PJ_EINVAL);
+    
+    CHECK_ADDR_LEN(to, tolen);
+
+    *len = sendto(sock, (const char*)buf, (int)(*len), flags, 
+		  (const struct sockaddr*)to, tolen);
+
+    if (*len < 0) 
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else 
+	return PJ_SUCCESS;
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock,
+				 void *buf,
+				 pj_ssize_t *len,
+				 unsigned flags)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
+
+    *len = recv(sock, (char*)buf, (int)(*len), flags);
+
+    if (*len < 0) 
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
+				     void *buf,
+				     pj_ssize_t *len,
+				     unsigned flags,
+				     pj_sockaddr_t *from,
+				     int *fromlen)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(buf && len, PJ_EINVAL);
+
+    *len = recvfrom(sock, (char*)buf, (int)(*len), flags, 
+		    (struct sockaddr*)from, (socklen_t*)fromlen);
+
+    if (*len < 0) 
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else {
+        if (from) {
+            PJ_SOCKADDR_RESET_LEN(from);
+        }
+	return PJ_SUCCESS;
+    }
+}
+
+/*
+ * Get socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock,
+					pj_uint16_t level,
+					pj_uint16_t optname,
+					void *optval,
+					int *optlen)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(optval && optlen, PJ_EINVAL);
+
+    if (getsockopt(sock, level, optname, (char*)optval, (socklen_t*)optlen)!=0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Set socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock,
+					pj_uint16_t level,
+					pj_uint16_t optname,
+					const void *optval,
+					int optlen)
+{
+    PJ_CHECK_STACK();
+    if (setsockopt(sock, level, optname, (const char*)optval, optlen) != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Connect socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock,
+				     const pj_sockaddr_t *addr,
+				     int namelen)
+{
+    PJ_CHECK_STACK();
+    if (connect(sock, (struct sockaddr*)addr, namelen) != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+
+/*
+ * Shutdown socket.
+ */
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock,
+				      int how)
+{
+    PJ_CHECK_STACK();
+    if (shutdown(sock, how) != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Start listening to incoming connections.
+ */
+PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock,
+				    int backlog)
+{
+    PJ_CHECK_STACK();
+    if (listen(sock, backlog) != 0)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Accept incoming connections
+ */
+PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
+				    pj_sock_t *newsock,
+				    pj_sockaddr_t *addr,
+				    int *addrlen)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(newsock != NULL, PJ_EINVAL);
+
+#if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0
+    if (addr) {
+	PJ_SOCKADDR_SET_LEN(addr, *addrlen);
+    }
+#endif
+    
+    *newsock = accept(serverfd, (struct sockaddr*)addr, (socklen_t*)addrlen);
+    if (*newsock==PJ_INVALID_SOCKET)
+	return PJ_RETURN_OS_ERROR(pj_get_native_netos_error());
+    else {
+	
+#if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0
+	if (addr) {
+	    PJ_SOCKADDR_RESET_LEN(addr);
+	}
+#endif
+	    
+	return PJ_SUCCESS;
+    }
+}
+#endif	/* PJ_HAS_TCP */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_common.c b/jni/pjproject-android/pjlib/src/pj/sock_common.c
new file mode 100644
index 0000000..d37d312
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_common.c
@@ -0,0 +1,1324 @@
+/* $Id: sock_common.c 4538 2013-06-19 09:06:55Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock.h>
+#include <pj/assert.h>
+#include <pj/ctype.h>
+#include <pj/errno.h>
+#include <pj/ip_helper.h>
+#include <pj/os.h>
+#include <pj/addr_resolv.h>
+#include <pj/rand.h>
+#include <pj/string.h>
+#include <pj/compat/socket.h>
+
+#if 0
+    /* Enable some tracing */
+    #include <pj/log.h>
+    #define THIS_FILE   "sock_common.c"
+    #define TRACE_(arg)	PJ_LOG(4,arg)
+#else
+    #define TRACE_(arg)
+#endif
+
+
+/*
+ * Convert address string with numbers and dots to binary IP address.
+ */ 
+PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
+{
+    pj_in_addr addr;
+
+    pj_inet_aton(cp, &addr);
+    return addr;
+}
+
+/*
+ * Convert address string with numbers and dots to binary IP address.
+ */ 
+PJ_DEF(pj_in_addr) pj_inet_addr2(const char *cp)
+{
+    pj_str_t str = pj_str((char*)cp);
+    return pj_inet_addr(&str);
+}
+
+/*
+ * Get text representation.
+ */
+PJ_DEF(char*) pj_inet_ntop2( int af, const void *src,
+			     char *dst, int size)
+{
+    pj_status_t status;
+
+    status = pj_inet_ntop(af, src, dst, size);
+    return (status==PJ_SUCCESS)? dst : NULL;
+}
+
+/*
+ * Print socket address.
+ */
+PJ_DEF(char*) pj_sockaddr_print( const pj_sockaddr_t *addr,
+				 char *buf, int size,
+				 unsigned flags)
+{
+    enum {
+	WITH_PORT = 1,
+	WITH_BRACKETS = 2
+    };
+
+    char txt[PJ_INET6_ADDRSTRLEN];
+    char port[32];
+    const pj_addr_hdr *h = (const pj_addr_hdr*)addr;
+    char *bquote, *equote;
+    pj_status_t status;
+
+    status = pj_inet_ntop(h->sa_family, pj_sockaddr_get_addr(addr),
+			  txt, sizeof(txt));
+    if (status != PJ_SUCCESS)
+	return "";
+
+    if (h->sa_family != PJ_AF_INET6 || (flags & WITH_BRACKETS)==0) {
+	bquote = ""; equote = "";
+    } else {
+	bquote = "["; equote = "]";
+    }
+
+    if (flags & WITH_PORT) {
+	pj_ansi_snprintf(port, sizeof(port), ":%d",
+			 pj_sockaddr_get_port(addr));
+    } else {
+	port[0] = '\0';
+    }
+
+    pj_ansi_snprintf(buf, size, "%s%s%s%s",
+		     bquote, txt, equote, port);
+
+    return buf;
+}
+
+/*
+ * Set the IP address of an IP socket address from string address, 
+ * with resolving the host if necessary. The string address may be in a
+ * standard numbers and dots notation or may be a hostname. If hostname
+ * is specified, then the function will resolve the host into the IP
+ * address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
+					         const pj_str_t *str_addr)
+{
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(!str_addr || str_addr->slen < PJ_MAX_HOSTNAME, 
+                     (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
+
+    PJ_SOCKADDR_RESET_LEN(addr);
+    addr->sin_family = AF_INET;
+    pj_bzero(addr->sin_zero, sizeof(addr->sin_zero));
+
+    if (str_addr && str_addr->slen) {
+	addr->sin_addr = pj_inet_addr(str_addr);
+	if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
+    	    pj_hostent he;
+	    pj_status_t rc;
+
+	    rc = pj_gethostbyname(str_addr, &he);
+	    if (rc == 0) {
+		addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+	    } else {
+		addr->sin_addr.s_addr = PJ_INADDR_NONE;
+		return rc;
+	    }
+	}
+
+    } else {
+	addr->sin_addr.s_addr = 0;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Set address from a name */
+PJ_DEF(pj_status_t) pj_sockaddr_set_str_addr(int af,
+					     pj_sockaddr *addr,
+					     const pj_str_t *str_addr)
+{
+    pj_status_t status;
+
+    if (af == PJ_AF_INET) {
+	return pj_sockaddr_in_set_str_addr(&addr->ipv4, str_addr);
+    }
+
+    PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP);
+
+    /* IPv6 specific */
+
+    addr->ipv6.sin6_family = PJ_AF_INET6;
+    PJ_SOCKADDR_RESET_LEN(addr);
+
+    if (str_addr && str_addr->slen) {
+	status = pj_inet_pton(PJ_AF_INET6, str_addr, &addr->ipv6.sin6_addr);
+	if (status != PJ_SUCCESS) {
+    	    pj_addrinfo ai;
+	    unsigned count = 1;
+
+	    status = pj_getaddrinfo(PJ_AF_INET6, str_addr, &count, &ai);
+	    if (status==PJ_SUCCESS) {
+		pj_memcpy(&addr->ipv6.sin6_addr, &ai.ai_addr.ipv6.sin6_addr,
+			  sizeof(pj_sockaddr_in6));
+	    }
+	}
+    } else {
+	status = PJ_SUCCESS;
+    }
+
+    return status;
+}
+
+/*
+ * Set the IP address and port of an IP socket address.
+ * The string address may be in a standard numbers and dots notation or 
+ * may be a hostname. If hostname is specified, then the function will 
+ * resolve the host into the IP address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
+				         const pj_str_t *str_addr,
+					 pj_uint16_t port)
+{
+    PJ_ASSERT_RETURN(addr, (addr->sin_addr.s_addr=PJ_INADDR_NONE, PJ_EINVAL));
+
+    PJ_SOCKADDR_RESET_LEN(addr);
+    addr->sin_family = PJ_AF_INET;
+    pj_bzero(addr->sin_zero, sizeof(addr->sin_zero));
+    pj_sockaddr_in_set_port(addr, port);
+    return pj_sockaddr_in_set_str_addr(addr, str_addr);
+}
+
+/*
+ * Initialize IP socket address based on the address and port info.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_init(int af, 
+				     pj_sockaddr *addr,
+				     const pj_str_t *cp,
+				     pj_uint16_t port)
+{
+    pj_status_t status;
+
+    if (af == PJ_AF_INET) {
+	return pj_sockaddr_in_init(&addr->ipv4, cp, port);
+    }
+
+    /* IPv6 specific */
+    PJ_ASSERT_RETURN(af==PJ_AF_INET6, PJ_EAFNOTSUP);
+
+    pj_bzero(addr, sizeof(pj_sockaddr_in6));
+    addr->addr.sa_family = PJ_AF_INET6;
+    
+    status = pj_sockaddr_set_str_addr(af, addr, cp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    addr->ipv6.sin6_port = pj_htons(port);
+    return PJ_SUCCESS;
+}
+
+/*
+ * Compare two socket addresses.
+ */
+PJ_DEF(int) pj_sockaddr_cmp( const pj_sockaddr_t *addr1,
+			     const pj_sockaddr_t *addr2)
+{
+    const pj_sockaddr *a1 = (const pj_sockaddr*) addr1;
+    const pj_sockaddr *a2 = (const pj_sockaddr*) addr2;
+    int port1, port2;
+    int result;
+
+    /* Compare address family */
+    if (a1->addr.sa_family < a2->addr.sa_family)
+	return -1;
+    else if (a1->addr.sa_family > a2->addr.sa_family)
+	return 1;
+
+    /* Compare addresses */
+    result = pj_memcmp(pj_sockaddr_get_addr(a1),
+		       pj_sockaddr_get_addr(a2),
+		       pj_sockaddr_get_addr_len(a1));
+    if (result != 0)
+	return result;
+
+    /* Compare port number */
+    port1 = pj_sockaddr_get_port(a1);
+    port2 = pj_sockaddr_get_port(a2);
+
+    if (port1 < port2)
+	return -1;
+    else if (port1 > port2)
+	return 1;
+
+    /* TODO:
+     *	Do we need to compare flow label and scope id in IPv6? 
+     */
+    
+    /* Looks equal */
+    return 0;
+}
+
+/*
+ * Get first IP address associated with the hostname.
+ */
+PJ_DEF(pj_in_addr) pj_gethostaddr(void)
+{
+    pj_sockaddr_in addr;
+    const pj_str_t *hostname = pj_gethostname();
+
+    pj_sockaddr_in_set_str_addr(&addr, hostname);
+    return addr.sin_addr;
+}
+
+/*
+ * Get port number of a pj_sockaddr_in
+ */
+PJ_DEF(pj_uint16_t) pj_sockaddr_in_get_port(const pj_sockaddr_in *addr)
+{
+    return pj_ntohs(addr->sin_port);
+}
+
+/*
+ * Get the address part
+ */
+PJ_DEF(void*) pj_sockaddr_get_addr(const pj_sockaddr_t *addr)
+{
+    const pj_sockaddr *a = (const pj_sockaddr*)addr;
+
+    PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET ||
+		     a->addr.sa_family == PJ_AF_INET6, NULL);
+
+    if (a->addr.sa_family == PJ_AF_INET6)
+	return (void*) &a->ipv6.sin6_addr;
+    else
+	return (void*) &a->ipv4.sin_addr;
+}
+
+/*
+ * Check if sockaddr contains a non-zero address
+ */
+PJ_DEF(pj_bool_t) pj_sockaddr_has_addr(const pj_sockaddr_t *addr)
+{
+    const pj_sockaddr *a = (const pj_sockaddr*)addr;
+
+    /* It's probably not wise to raise assertion here if
+     * the address doesn't contain a valid address family, and
+     * just return PJ_FALSE instead.
+     * 
+     * The reason is because application may need to distinguish 
+     * these three conditions with sockaddr:
+     *	a) sockaddr is not initialized. This is by convention
+     *	   indicated by sa_family==0.
+     *	b) sockaddr is initialized with zero address. This is
+     *	   indicated with the address field having zero address.
+     *	c) sockaddr is initialized with valid address/port.
+     *
+     * If we enable this assertion, then application will loose
+     * the capability to specify condition a), since it will be
+     * forced to always initialize sockaddr (even with zero address).
+     * This may break some parts of upper layer libraries.
+     */
+    //PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET ||
+    //		     a->addr.sa_family == PJ_AF_INET6, PJ_FALSE);
+
+    if (a->addr.sa_family!=PJ_AF_INET && a->addr.sa_family!=PJ_AF_INET6) {
+	return PJ_FALSE;
+    } else if (a->addr.sa_family == PJ_AF_INET6) {
+	pj_uint8_t zero[24];
+	pj_bzero(zero, sizeof(zero));
+	return pj_memcmp(a->ipv6.sin6_addr.s6_addr, zero, 
+			 sizeof(pj_in6_addr)) != 0;
+    } else
+	return a->ipv4.sin_addr.s_addr != PJ_INADDR_ANY;
+}
+
+/*
+ * Get port number
+ */
+PJ_DEF(pj_uint16_t) pj_sockaddr_get_port(const pj_sockaddr_t *addr)
+{
+    const pj_sockaddr *a = (const pj_sockaddr*) addr;
+
+    PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET ||
+		     a->addr.sa_family == PJ_AF_INET6, (pj_uint16_t)0xFFFF);
+
+    return pj_ntohs((pj_uint16_t)(a->addr.sa_family == PJ_AF_INET6 ?
+				    a->ipv6.sin6_port : a->ipv4.sin_port));
+}
+
+/*
+ * Get the length of the address part.
+ */
+PJ_DEF(unsigned) pj_sockaddr_get_addr_len(const pj_sockaddr_t *addr)
+{
+    const pj_sockaddr *a = (const pj_sockaddr*) addr;
+    PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET ||
+		     a->addr.sa_family == PJ_AF_INET6, 0);
+    return a->addr.sa_family == PJ_AF_INET6 ?
+	    sizeof(pj_in6_addr) : sizeof(pj_in_addr);
+}
+
+/*
+ * Get socket address length.
+ */
+PJ_DEF(unsigned) pj_sockaddr_get_len(const pj_sockaddr_t *addr)
+{
+    const pj_sockaddr *a = (const pj_sockaddr*) addr;
+    PJ_ASSERT_RETURN(a->addr.sa_family == PJ_AF_INET ||
+		     a->addr.sa_family == PJ_AF_INET6, 0);
+    return a->addr.sa_family == PJ_AF_INET6 ?
+	    sizeof(pj_sockaddr_in6) : sizeof(pj_sockaddr_in);
+}
+
+/*
+ * Copy only the address part (sin_addr/sin6_addr) of a socket address.
+ */
+PJ_DEF(void) pj_sockaddr_copy_addr( pj_sockaddr *dst,
+				    const pj_sockaddr *src)
+{
+    /* Destination sockaddr might not be initialized */
+    const char *srcbuf = (char*)pj_sockaddr_get_addr(src);
+    char *dstbuf = ((char*)dst) + (srcbuf - (char*)src);
+    pj_memcpy(dstbuf, srcbuf, pj_sockaddr_get_addr_len(src));
+}
+
+/*
+ * Copy socket address.
+ */
+PJ_DEF(void) pj_sockaddr_cp(pj_sockaddr_t *dst, const pj_sockaddr_t *src)
+{
+    pj_memcpy(dst, src, pj_sockaddr_get_len(src));
+}
+
+/*
+ * Set port number of pj_sockaddr_in
+ */
+PJ_DEF(void) pj_sockaddr_in_set_port(pj_sockaddr_in *addr, 
+				     pj_uint16_t hostport)
+{
+    addr->sin_port = pj_htons(hostport);
+}
+
+/*
+ * Set port number of pj_sockaddr
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_set_port(pj_sockaddr *addr, 
+					 pj_uint16_t hostport)
+{
+    int af = addr->addr.sa_family;
+
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL);
+
+    if (af == PJ_AF_INET6)
+	addr->ipv6.sin6_port = pj_htons(hostport);
+    else
+	addr->ipv4.sin_port = pj_htons(hostport);
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get IPv4 address
+ */
+PJ_DEF(pj_in_addr) pj_sockaddr_in_get_addr(const pj_sockaddr_in *addr)
+{
+    pj_in_addr in_addr;
+    in_addr.s_addr = pj_ntohl(addr->sin_addr.s_addr);
+    return in_addr;
+}
+
+/*
+ * Set IPv4 address
+ */
+PJ_DEF(void) pj_sockaddr_in_set_addr(pj_sockaddr_in *addr,
+				     pj_uint32_t hostaddr)
+{
+    addr->sin_addr.s_addr = pj_htonl(hostaddr);
+}
+
+/*
+ * Parse address
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_parse2(int af, unsigned options,
+				       const pj_str_t *str,
+				       pj_str_t *p_hostpart,
+				       pj_uint16_t *p_port,
+				       int *raf)
+{
+    const char *end = str->ptr + str->slen;
+    const char *last_colon_pos = NULL;
+    unsigned colon_cnt = 0;
+    const char *p;
+
+    PJ_ASSERT_RETURN((af==PJ_AF_INET || af==PJ_AF_INET6 || af==PJ_AF_UNSPEC) &&
+		     options==0 &&
+		     str!=NULL, PJ_EINVAL);
+
+    /* Special handling for empty input */
+    if (str->slen==0 || str->ptr==NULL) {
+	if (p_hostpart)
+	    p_hostpart->slen = 0;
+	if (p_port)
+	    *p_port = 0;
+	if (raf)
+	    *raf = PJ_AF_INET;
+	return PJ_SUCCESS;
+    }
+
+    /* Count the colon and get the last colon */
+    for (p=str->ptr; p!=end; ++p) {
+	if (*p == ':') {
+	    ++colon_cnt;
+	    last_colon_pos = p;
+	}
+    }
+
+    /* Deduce address family if it's not given */
+    if (af == PJ_AF_UNSPEC) {
+	if (colon_cnt > 1)
+	    af = PJ_AF_INET6;
+	else
+	    af = PJ_AF_INET;
+    } else if (af == PJ_AF_INET && colon_cnt > 1)
+	return PJ_EINVAL;
+
+    if (raf)
+	*raf = af;
+
+    if (af == PJ_AF_INET) {
+	/* Parse as IPv4. Supported formats:
+	 *  - "10.0.0.1:80"
+	 *  - "10.0.0.1"
+	 *  - "10.0.0.1:"
+	 *  - ":80"
+	 *  - ":"
+	 */
+	pj_str_t hostpart;
+	unsigned long port;
+
+	hostpart.ptr = (char*)str->ptr;
+
+	if (last_colon_pos) {
+	    pj_str_t port_part;
+	    int i;
+
+	    hostpart.slen = last_colon_pos - str->ptr;
+
+	    port_part.ptr = (char*)last_colon_pos + 1;
+	    port_part.slen = end - port_part.ptr;
+
+	    /* Make sure port number is valid */
+	    for (i=0; i<port_part.slen; ++i) {
+		if (!pj_isdigit(port_part.ptr[i]))
+		    return PJ_EINVAL;
+	    }
+	    port = pj_strtoul(&port_part);
+	    if (port > 65535)
+		return PJ_EINVAL;
+	} else {
+	    hostpart.slen = str->slen;
+	    port = 0;
+	}
+
+	if (p_hostpart)
+	    *p_hostpart = hostpart;
+	if (p_port)
+	    *p_port = (pj_uint16_t)port;
+
+	return PJ_SUCCESS;
+
+    } else if (af == PJ_AF_INET6) {
+
+	/* Parse as IPv6. Supported formats:
+	 *  - "fe::01:80"  ==> note: port number is zero in this case, not 80!
+	 *  - "[fe::01]:80"
+	 *  - "fe::01"
+	 *  - "fe::01:"
+	 *  - "[fe::01]"
+	 *  - "[fe::01]:"
+	 *  - "[::]:80"
+	 *  - ":::80"
+	 *  - "[::]"
+	 *  - "[::]:"
+	 *  - ":::"
+	 *  - "::"
+	 */
+	pj_str_t hostpart, port_part;
+
+	if (*str->ptr == '[') {
+	    char *end_bracket;
+	    int i;
+	    unsigned long port;
+
+	    if (last_colon_pos == NULL)
+		return PJ_EINVAL;
+
+	    end_bracket = pj_strchr(str, ']');
+	    if (end_bracket == NULL)
+		return PJ_EINVAL;
+
+	    hostpart.ptr = (char*)str->ptr + 1;
+	    hostpart.slen = end_bracket - hostpart.ptr;
+
+	    if (last_colon_pos < end_bracket) {
+		port_part.ptr = NULL;
+		port_part.slen = 0;
+	    } else {
+		port_part.ptr = (char*)last_colon_pos + 1;
+		port_part.slen = end - port_part.ptr;
+	    }
+
+	    /* Make sure port number is valid */
+	    for (i=0; i<port_part.slen; ++i) {
+		if (!pj_isdigit(port_part.ptr[i]))
+		    return PJ_EINVAL;
+	    }
+	    port = pj_strtoul(&port_part);
+	    if (port > 65535)
+		return PJ_EINVAL;
+
+	    if (p_hostpart)
+		*p_hostpart = hostpart;
+	    if (p_port)
+		*p_port = (pj_uint16_t)port;
+
+	    return PJ_SUCCESS;
+
+	} else {
+	    /* Treat everything as part of the IPv6 IP address */
+	    if (p_hostpart)
+		*p_hostpart = *str;
+	    if (p_port)
+		*p_port = 0;
+
+	    return PJ_SUCCESS;
+	}
+
+    } else {
+	return PJ_EAFNOTSUP;
+    }
+
+}
+
+/*
+ * Parse address
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_parse( int af, unsigned options,
+				       const pj_str_t *str,
+				       pj_sockaddr *addr)
+{
+    pj_str_t hostpart;
+    pj_uint16_t port;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(addr, PJ_EINVAL);
+    PJ_ASSERT_RETURN(af==PJ_AF_UNSPEC ||
+		     af==PJ_AF_INET ||
+		     af==PJ_AF_INET6, PJ_EINVAL);
+    PJ_ASSERT_RETURN(options == 0, PJ_EINVAL);
+
+    status = pj_sockaddr_parse2(af, options, str, &hostpart, &port, &af);
+    if (status != PJ_SUCCESS)
+	return status;
+    
+#if !defined(PJ_HAS_IPV6) || !PJ_HAS_IPV6
+    if (af==PJ_AF_INET6)
+	return PJ_EIPV6NOTSUP;
+#endif
+
+    status = pj_sockaddr_init(af, addr, &hostpart, port);
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
+    if (status != PJ_SUCCESS && af == PJ_AF_INET6) {
+	/* Parsing does not yield valid address. Try to treat the last 
+	 * portion after the colon as port number.
+	 */
+	const char *last_colon_pos=NULL, *p;
+	const char *end = str->ptr + str->slen;
+	unsigned long long_port;
+	pj_str_t port_part;
+	int i;
+
+	/* Parse as IPv6:port */
+	for (p=str->ptr; p!=end; ++p) {
+	    if (*p == ':')
+		last_colon_pos = p;
+	}
+
+	if (last_colon_pos == NULL)
+	    return status;
+
+	hostpart.ptr = (char*)str->ptr;
+	hostpart.slen = last_colon_pos - str->ptr;
+
+	port_part.ptr = (char*)last_colon_pos + 1;
+	port_part.slen = end - port_part.ptr;
+
+	/* Make sure port number is valid */
+	for (i=0; i<port_part.slen; ++i) {
+	    if (!pj_isdigit(port_part.ptr[i]))
+		return status;
+	}
+	long_port = pj_strtoul(&port_part);
+	if (long_port > 65535)
+	    return status;
+
+	port = (pj_uint16_t)long_port;
+
+	status = pj_sockaddr_init(PJ_AF_INET6, addr, &hostpart, port);
+    }
+#endif
+    
+    return status;
+}
+
+/* Resolve the IP address of local machine */
+PJ_DEF(pj_status_t) pj_gethostip(int af, pj_sockaddr *addr)
+{
+    unsigned i, count, cand_cnt;
+    enum {
+	CAND_CNT = 8,
+
+	/* Weighting to be applied to found addresses */
+	WEIGHT_HOSTNAME	= 1,	/* hostname IP is not always valid! */
+	WEIGHT_DEF_ROUTE = 2,
+	WEIGHT_INTERFACE = 1,
+	WEIGHT_LOOPBACK = -5,
+	WEIGHT_LINK_LOCAL = -4,
+	WEIGHT_DISABLED = -50,
+
+	MIN_WEIGHT = WEIGHT_DISABLED+1	/* minimum weight to use */
+    };
+    /* candidates: */
+    pj_sockaddr cand_addr[CAND_CNT];
+    int		cand_weight[CAND_CNT];
+    int	        selected_cand;
+    char	strip[PJ_INET6_ADDRSTRLEN+10];
+    /* Special IPv4 addresses. */
+    struct spec_ipv4_t
+    {
+	pj_uint32_t addr;
+	pj_uint32_t mask;
+	int	    weight;
+    } spec_ipv4[] =
+    {
+	/* 127.0.0.0/8, loopback addr will be used if there is no other
+	 * addresses.
+	 */
+	{ 0x7f000000, 0xFF000000, WEIGHT_LOOPBACK },
+
+	/* 0.0.0.0/8, special IP that doesn't seem to be practically useful */
+	{ 0x00000000, 0xFF000000, WEIGHT_DISABLED },
+
+	/* 169.254.0.0/16, a zeroconf/link-local address, which has higher
+	 * priority than loopback and will be used if there is no other
+	 * valid addresses.
+	 */
+	{ 0xa9fe0000, 0xFFFF0000, WEIGHT_LINK_LOCAL }
+    };
+    /* Special IPv6 addresses */
+    struct spec_ipv6_t
+    {
+	pj_uint8_t addr[16];
+	pj_uint8_t mask[16];
+	int	   weight;
+    } spec_ipv6[] =
+    {
+	/* Loopback address, ::1/128 */
+	{ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
+	  {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
+	  WEIGHT_LOOPBACK
+	},
+
+	/* Link local, fe80::/10 */
+	{ {0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+	  {0xff,0xc0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+	  WEIGHT_LINK_LOCAL
+	},
+
+	/* Disabled, ::/128 */
+	{ {0x0,0x0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+	{ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+	  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff},
+	  WEIGHT_DISABLED
+	}
+    };
+    pj_addrinfo ai;
+    pj_status_t status;
+
+    /* May not be used if TRACE_ is disabled */
+    PJ_UNUSED_ARG(strip);
+
+#ifdef _MSC_VER
+    /* Get rid of "uninitialized he variable" with MS compilers */
+    pj_bzero(&ai, sizeof(ai));
+#endif
+
+    cand_cnt = 0;
+    pj_bzero(cand_addr, sizeof(cand_addr));
+    pj_bzero(cand_weight, sizeof(cand_weight));
+    for (i=0; i<PJ_ARRAY_SIZE(cand_addr); ++i) {
+	cand_addr[i].addr.sa_family = (pj_uint16_t)af;
+	PJ_SOCKADDR_RESET_LEN(&cand_addr[i]);
+    }
+
+    addr->addr.sa_family = (pj_uint16_t)af;
+    PJ_SOCKADDR_RESET_LEN(addr);
+
+#if !defined(PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION) || \
+    PJ_GETHOSTIP_DISABLE_LOCAL_RESOLUTION == 0
+    /* Get hostname's IP address */
+    count = 1;
+    status = pj_getaddrinfo(af, pj_gethostname(), &count, &ai);
+    if (status == PJ_SUCCESS) {
+    	pj_assert(ai.ai_addr.addr.sa_family == (pj_uint16_t)af);
+    	pj_sockaddr_copy_addr(&cand_addr[cand_cnt], &ai.ai_addr);
+	pj_sockaddr_set_port(&cand_addr[cand_cnt], 0);
+	cand_weight[cand_cnt] += WEIGHT_HOSTNAME;
+	++cand_cnt;
+
+	TRACE_((THIS_FILE, "hostname IP is %s",
+		pj_sockaddr_print(&ai.ai_addr, strip, sizeof(strip), 0)));
+    }
+#else
+    PJ_UNUSED_ARG(ai);
+    PJ_UNUSED_ARG(count);
+#endif
+
+    /* Get default interface (interface for default route) */
+    if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) {
+	status = pj_getdefaultipinterface(af, addr);
+	if (status == PJ_SUCCESS) {
+	    TRACE_((THIS_FILE, "default IP is %s",
+		    pj_sockaddr_print(addr, strip, sizeof(strip), 0)));
+
+	    pj_sockaddr_set_port(addr, 0);
+	    for (i=0; i<cand_cnt; ++i) {
+		if (pj_sockaddr_cmp(&cand_addr[i], addr)==0)
+		    break;
+	    }
+
+	    cand_weight[i] += WEIGHT_DEF_ROUTE;
+	    if (i >= cand_cnt) {
+		pj_sockaddr_copy_addr(&cand_addr[i], addr);
+		++cand_cnt;
+	    }
+	}
+    }
+
+
+    /* Enumerate IP interfaces */
+    if (cand_cnt < PJ_ARRAY_SIZE(cand_addr)) {
+	unsigned start_if = cand_cnt;
+	unsigned count = PJ_ARRAY_SIZE(cand_addr) - start_if;
+
+	status = pj_enum_ip_interface(af, &count, &cand_addr[start_if]);
+	if (status == PJ_SUCCESS && count) {
+	    /* Clear the port number */
+	    for (i=0; i<count; ++i)
+		pj_sockaddr_set_port(&cand_addr[start_if+i], 0);
+
+	    /* For each candidate that we found so far (that is the hostname
+	     * address and default interface address, check if they're found
+	     * in the interface list. If found, add the weight, and if not,
+	     * decrease the weight.
+	     */
+	    for (i=0; i<cand_cnt; ++i) {
+		unsigned j;
+		for (j=0; j<count; ++j) {
+		    if (pj_sockaddr_cmp(&cand_addr[i], 
+					&cand_addr[start_if+j])==0)
+			break;
+		}
+
+		if (j == count) {
+		    /* Not found */
+		    cand_weight[i] -= WEIGHT_INTERFACE;
+		} else {
+		    cand_weight[i] += WEIGHT_INTERFACE;
+		}
+	    }
+
+	    /* Add remaining interface to candidate list. */
+	    for (i=0; i<count; ++i) {
+		unsigned j;
+		for (j=0; j<cand_cnt; ++j) {
+		    if (pj_sockaddr_cmp(&cand_addr[start_if+i], 
+					&cand_addr[j])==0)
+			break;
+		}
+
+		if (j == cand_cnt) {
+		    pj_sockaddr_copy_addr(&cand_addr[cand_cnt], 
+					  &cand_addr[start_if+i]);
+		    cand_weight[cand_cnt] += WEIGHT_INTERFACE;
+		    ++cand_cnt;
+		}
+	    }
+	}
+    }
+
+    /* Apply weight adjustment for special IPv4/IPv6 addresses
+     * See http://trac.pjsip.org/repos/ticket/1046
+     */
+    if (af == PJ_AF_INET) {
+	for (i=0; i<cand_cnt; ++i) {
+	    unsigned j;
+	    for (j=0; j<PJ_ARRAY_SIZE(spec_ipv4); ++j) {
+		    pj_uint32_t a = pj_ntohl(cand_addr[i].ipv4.sin_addr.s_addr);
+		    pj_uint32_t pa = spec_ipv4[j].addr;
+		    pj_uint32_t pm = spec_ipv4[j].mask;
+
+		    if ((a & pm) == pa) {
+			cand_weight[i] += spec_ipv4[j].weight;
+			break;
+		    }
+	    }
+	}
+    } else if (af == PJ_AF_INET6) {
+	for (i=0; i<PJ_ARRAY_SIZE(spec_ipv6); ++i) {
+		unsigned j;
+		for (j=0; j<cand_cnt; ++j) {
+		    pj_uint8_t *a = cand_addr[j].ipv6.sin6_addr.s6_addr;
+		    pj_uint8_t am[16];
+		    pj_uint8_t *pa = spec_ipv6[i].addr;
+		    pj_uint8_t *pm = spec_ipv6[i].mask;
+		    unsigned k;
+
+		    for (k=0; k<16; ++k) {
+			am[k] = (pj_uint8_t)((a[k] & pm[k]) & 0xFF);
+		    }
+
+		    if (pj_memcmp(am, pa, 16)==0) {
+			cand_weight[j] += spec_ipv6[i].weight;
+		    }
+		}
+	}
+    } else {
+	return PJ_EAFNOTSUP;
+    }
+
+    /* Enumerate candidates to get the best IP address to choose */
+    selected_cand = -1;
+    for (i=0; i<cand_cnt; ++i) {
+	TRACE_((THIS_FILE, "Checking candidate IP %s, weight=%d",
+		pj_sockaddr_print(&cand_addr[i], strip, sizeof(strip), 0),
+		cand_weight[i]));
+
+	if (cand_weight[i] < MIN_WEIGHT) {
+	    continue;
+	}
+
+	if (selected_cand == -1)
+	    selected_cand = i;
+	else if (cand_weight[i] > cand_weight[selected_cand])
+	    selected_cand = i;
+    }
+
+    /* If else fails, returns loopback interface as the last resort */
+    if (selected_cand == -1) {
+	if (af==PJ_AF_INET) {
+	    addr->ipv4.sin_addr.s_addr = pj_htonl (0x7f000001);
+	} else {
+	    pj_in6_addr *s6_addr;
+
+	    s6_addr = (pj_in6_addr*) pj_sockaddr_get_addr(addr);
+	    pj_bzero(s6_addr, sizeof(pj_in6_addr));
+	    s6_addr->s6_addr[15] = 1;
+	}
+	TRACE_((THIS_FILE, "Loopback IP %s returned",
+		pj_sockaddr_print(addr, strip, sizeof(strip), 0)));
+    } else {
+	pj_sockaddr_copy_addr(addr, &cand_addr[selected_cand]);
+	TRACE_((THIS_FILE, "Candidate %s selected",
+		pj_sockaddr_print(addr, strip, sizeof(strip), 0)));
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Get IP interface for sending to the specified destination */
+PJ_DEF(pj_status_t) pj_getipinterface(int af,
+                                      const pj_str_t *dst,
+                                      pj_sockaddr *itf_addr,
+                                      pj_bool_t allow_resolve,
+                                      pj_sockaddr *p_dst_addr)
+{
+    pj_sockaddr dst_addr;
+    pj_sock_t fd;
+    int len;
+    pj_uint8_t zero[64];
+    pj_status_t status;
+
+    pj_sockaddr_init(af, &dst_addr, NULL, 53);
+    status = pj_inet_pton(af, dst, pj_sockaddr_get_addr(&dst_addr));
+    if (status != PJ_SUCCESS) {
+	/* "dst" is not an IP address. */
+	if (allow_resolve) {
+	    status = pj_sockaddr_init(af, &dst_addr, dst, 53);
+	} else {
+	    pj_str_t cp;
+
+	    if (af == PJ_AF_INET) {
+		cp = pj_str("1.1.1.1");
+	    } else {
+		cp = pj_str("1::1");
+	    }
+	    status = pj_sockaddr_init(af, &dst_addr, &cp, 53);
+	}
+
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    /* Create UDP socket and connect() to the destination IP */
+    status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &fd);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    status = pj_sock_connect(fd, &dst_addr, pj_sockaddr_get_len(&dst_addr));
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(fd);
+	return status;
+    }
+
+    len = sizeof(*itf_addr);
+    status = pj_sock_getsockname(fd, itf_addr, &len);
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(fd);
+	return status;
+    }
+
+    pj_sock_close(fd);
+
+    /* Check that the address returned is not zero */
+    pj_bzero(zero, sizeof(zero));
+    if (pj_memcmp(pj_sockaddr_get_addr(itf_addr), zero,
+		  pj_sockaddr_get_addr_len(itf_addr))==0)
+    {
+	return PJ_ENOTFOUND;
+    }
+
+    if (p_dst_addr)
+	*p_dst_addr = dst_addr;
+
+    return PJ_SUCCESS;
+}
+
+/* Get the default IP interface */
+PJ_DEF(pj_status_t) pj_getdefaultipinterface(int af, pj_sockaddr *addr)
+{
+    pj_str_t cp;
+
+    if (af == PJ_AF_INET) {
+	cp = pj_str("1.1.1.1");
+    } else {
+	cp = pj_str("1::1");
+    }
+
+    return pj_getipinterface(af, &cp, addr, PJ_FALSE, NULL);
+}
+
+
+/*
+ * Bind socket at random port.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_random(  pj_sock_t sockfd,
+				          const pj_sockaddr_t *addr,
+				          pj_uint16_t port_range,
+				          pj_uint16_t max_try)
+{
+    pj_sockaddr bind_addr;
+    int addr_len;
+    pj_uint16_t base_port;
+    pj_status_t status = PJ_SUCCESS;
+
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(addr, PJ_EINVAL);
+
+    pj_sockaddr_cp(&bind_addr, addr);
+    addr_len = pj_sockaddr_get_len(addr);
+    base_port = pj_sockaddr_get_port(addr);
+
+    if (base_port == 0 || port_range == 0) {
+	return pj_sock_bind(sockfd, &bind_addr, addr_len);
+    }
+
+    for (; max_try; --max_try) {
+	pj_uint16_t port;
+	port = (pj_uint16_t)(base_port + pj_rand() % (port_range + 1));
+	pj_sockaddr_set_port(&bind_addr, port);
+	status = pj_sock_bind(sockfd, &bind_addr, addr_len);
+	if (status == PJ_SUCCESS)
+	    break;
+    }
+
+    return status;
+}
+
+
+/*
+ * Adjust socket send/receive buffer size.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt_sobuf( pj_sock_t sockfd,
+					      pj_uint16_t optname,
+					      pj_bool_t auto_retry,
+					      unsigned *buf_size)
+{
+    pj_status_t status;
+    int try_size, cur_size, i, step, size_len;
+    enum { MAX_TRY = 20 };
+
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sockfd != PJ_INVALID_SOCKET &&
+		     buf_size &&
+		     *buf_size > 0 &&
+		     (optname == pj_SO_RCVBUF() ||
+		      optname == pj_SO_SNDBUF()),
+		     PJ_EINVAL);
+
+    size_len = sizeof(cur_size);
+    status = pj_sock_getsockopt(sockfd, pj_SOL_SOCKET(), optname,
+				&cur_size, &size_len);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    try_size = *buf_size;
+    step = (try_size - cur_size) / MAX_TRY;
+    if (step < 4096)
+	step = 4096;
+
+    for (i = 0; i < (MAX_TRY-1); ++i) {
+	if (try_size <= cur_size) {
+	    /* Done, return current size */
+	    *buf_size = cur_size;
+	    break;
+	}
+
+	status = pj_sock_setsockopt(sockfd, pj_SOL_SOCKET(), optname,
+				    &try_size, sizeof(try_size));
+	if (status == PJ_SUCCESS) {
+	    status = pj_sock_getsockopt(sockfd, pj_SOL_SOCKET(), optname,
+					&cur_size, &size_len);
+	    if (status != PJ_SUCCESS) {
+		/* Ops! No info about current size, just return last try size
+		 * and quit.
+		 */
+		*buf_size = try_size;
+		break;
+	    }
+	}
+
+	if (!auto_retry)
+	    break;
+
+	try_size -= step;
+    }
+
+    return status;
+}
+
+
+/* Only need to implement these in DLL build */
+#if defined(PJ_DLL)
+
+PJ_DEF(pj_uint16_t) pj_AF_UNSPEC(void)
+{
+    return PJ_AF_UNSPEC;
+}
+
+PJ_DEF(pj_uint16_t) pj_AF_UNIX(void)
+{
+    return PJ_AF_UNIX;
+}
+
+PJ_DEF(pj_uint16_t) pj_AF_INET(void)
+{
+    return PJ_AF_INET;
+}
+
+PJ_DEF(pj_uint16_t) pj_AF_INET6(void)
+{
+    return PJ_AF_INET6;
+}
+
+PJ_DEF(pj_uint16_t) pj_AF_PACKET(void)
+{
+    return PJ_AF_PACKET;
+}
+
+PJ_DEF(pj_uint16_t) pj_AF_IRDA(void)
+{
+    return PJ_AF_IRDA;
+}
+
+PJ_DEF(int) pj_SOCK_STREAM(void)
+{
+    return PJ_SOCK_STREAM;
+}
+
+PJ_DEF(int) pj_SOCK_DGRAM(void)
+{
+    return PJ_SOCK_DGRAM;
+}
+
+PJ_DEF(int) pj_SOCK_RAW(void)
+{
+    return PJ_SOCK_RAW;
+}
+
+PJ_DEF(int) pj_SOCK_RDM(void)
+{
+    return PJ_SOCK_RDM;
+}
+
+PJ_DEF(pj_uint16_t) pj_SOL_SOCKET(void)
+{
+    return PJ_SOL_SOCKET;
+}
+
+PJ_DEF(pj_uint16_t) pj_SOL_IP(void)
+{
+    return PJ_SOL_IP;
+}
+
+PJ_DEF(pj_uint16_t) pj_SOL_TCP(void)
+{
+    return PJ_SOL_TCP;
+}
+
+PJ_DEF(pj_uint16_t) pj_SOL_UDP(void)
+{
+    return PJ_SOL_UDP;
+}
+
+PJ_DEF(pj_uint16_t) pj_SOL_IPV6(void)
+{
+    return PJ_SOL_IPV6;
+}
+
+PJ_DEF(int) pj_IP_TOS(void)
+{
+    return PJ_IP_TOS;
+}
+
+PJ_DEF(int) pj_IPTOS_LOWDELAY(void)
+{
+    return PJ_IPTOS_LOWDELAY;
+}
+
+PJ_DEF(int) pj_IPTOS_THROUGHPUT(void)
+{
+    return PJ_IPTOS_THROUGHPUT;
+}
+
+PJ_DEF(int) pj_IPTOS_RELIABILITY(void)
+{
+    return PJ_IPTOS_RELIABILITY;
+}
+
+PJ_DEF(int) pj_IPTOS_MINCOST(void)
+{
+    return PJ_IPTOS_MINCOST;
+}
+
+PJ_DEF(pj_uint16_t) pj_SO_TYPE(void)
+{
+    return PJ_SO_TYPE;
+}
+
+PJ_DEF(pj_uint16_t) pj_SO_RCVBUF(void)
+{
+    return PJ_SO_RCVBUF;
+}
+
+PJ_DEF(pj_uint16_t) pj_SO_SNDBUF(void)
+{
+    return PJ_SO_SNDBUF;
+}
+
+PJ_DEF(pj_uint16_t) pj_TCP_NODELAY(void)
+{
+    return PJ_TCP_NODELAY;
+}
+
+PJ_DEF(pj_uint16_t) pj_SO_REUSEADDR(void)
+{
+    return PJ_SO_REUSEADDR;
+}
+
+PJ_DEF(pj_uint16_t) pj_SO_NOSIGPIPE(void)
+{
+    return PJ_SO_NOSIGPIPE;
+}
+
+PJ_DEF(pj_uint16_t) pj_SO_PRIORITY(void)
+{
+    return PJ_SO_PRIORITY;
+}
+
+PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_IF(void)
+{
+    return PJ_IP_MULTICAST_IF;
+}
+
+PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_TTL(void)
+{
+    return PJ_IP_MULTICAST_TTL;
+}
+
+PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_LOOP(void)
+{
+    return PJ_IP_MULTICAST_LOOP;
+}
+
+PJ_DEF(pj_uint16_t) pj_IP_ADD_MEMBERSHIP(void)
+{
+    return PJ_IP_ADD_MEMBERSHIP;
+}
+
+PJ_DEF(pj_uint16_t) pj_IP_DROP_MEMBERSHIP(void)
+{
+    return PJ_IP_DROP_MEMBERSHIP;
+}
+
+PJ_DEF(int) pj_MSG_OOB(void)
+{
+    return PJ_MSG_OOB;
+}
+
+PJ_DEF(int) pj_MSG_PEEK(void)
+{
+    return PJ_MSG_PEEK;
+}
+
+PJ_DEF(int) pj_MSG_DONTROUTE(void)
+{
+    return PJ_MSG_DONTROUTE;
+}
+
+#endif	/* PJ_DLL */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_linux_kernel.c b/jni/pjproject-android/pjlib/src/pj/sock_linux_kernel.c
new file mode 100644
index 0000000..a1a7c7c
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_linux_kernel.c
@@ -0,0 +1,755 @@
+/* $Id: sock_linux_kernel.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock.h>
+#include <pj/assert.h>
+#include <pj/string.h>	    /* pj_memcpy()	    */
+#include <pj/os.h>	    /* PJ_CHECK_STACK()	    */
+#include <pj/addr_resolv.h> /* pj_gethostbyname()   */
+#include <pj/ctype.h>
+#include <pj/compat/sprintf.h>
+#include <pj/log.h>
+#include <pj/errno.h>
+
+/* Linux kernel specific. */
+#include <linux/socket.h>
+#include <linux/net.h>
+//#include <net/sock.h>
+#include <linux/security.h>
+#include <linux/syscalls.h>	/* sys_xxx()	*/
+#include <asm/ioctls.h>		/* FIONBIO	*/
+#include <linux/utsname.h>	/* for pj_gethostname() */
+
+/*
+ * Address families conversion.
+ * The values here are indexed based on pj_addr_family-0xFF00.
+ */
+const pj_uint16_t PJ_AF_UNIX	= AF_UNIX;
+const pj_uint16_t PJ_AF_INET	= AF_INET;
+const pj_uint16_t PJ_AF_INET6	= AF_INET6;
+#ifdef AF_PACKET
+const pj_uint16_t PJ_AF_PACKET	= AF_PACKET;
+#else
+#  error "AF_PACKET undeclared!"
+#endif
+#ifdef AF_IRDA
+const pj_uint16_t PJ_AF_IRDA	= AF_IRDA;
+#else
+#  error "AF_IRDA undeclared!"
+#endif
+
+/*
+ * Socket types conversion.
+ * The values here are indexed based on pj_sock_type-0xFF00
+ */
+const pj_uint16_t PJ_SOCK_STREAM= SOCK_STREAM;
+const pj_uint16_t PJ_SOCK_DGRAM	= SOCK_DGRAM;
+const pj_uint16_t PJ_SOCK_RAW	= SOCK_RAW;
+const pj_uint16_t PJ_SOCK_RDM	= SOCK_RDM;
+
+/*
+ * Socket level values.
+ */
+const pj_uint16_t PJ_SOL_SOCKET	= SOL_SOCKET;
+#ifdef SOL_IP
+const pj_uint16_t PJ_SOL_IP	= SOL_IP;
+#else
+#  error "SOL_IP undeclared!"
+#endif /* SOL_IP */
+#if defined(SOL_TCP)
+const pj_uint16_t PJ_SOL_TCP	= SOL_TCP;
+#else
+#  error "SOL_TCP undeclared!"
+#endif /* SOL_TCP */
+#ifdef SOL_UDP
+const pj_uint16_t PJ_SOL_UDP	= SOL_UDP;
+#else
+#  error "SOL_UDP undeclared!"
+#endif
+#ifdef SOL_IPV6
+const pj_uint16_t PJ_SOL_IPV6	= SOL_IPV6;
+#else
+#  error "SOL_IPV6 undeclared!"
+#endif
+
+/* optname values. */
+const pj_uint16_t PJ_SO_TYPE    = SO_TYPE;
+const pj_uint16_t PJ_SO_RCVBUF  = SO_RCVBUF;
+const pj_uint16_t PJ_SO_SNDBUF  = SO_SNDBUF;
+
+/*
+ * Convert 16-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
+{
+    return ntohs(netshort);
+}
+
+/*
+ * Convert 16-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
+{
+    return htons(hostshort);
+}
+
+/*
+ * Convert 32-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
+{
+    return ntohl(netlong);
+}
+
+/*
+ * Convert 32-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
+{
+    return htonl(hostlong);
+}
+
+/*
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ */
+PJ_DEF(char*) pj_inet_ntoa(pj_in_addr in)
+{
+#define	UC(b)	(((int)b)&0xff)
+    static char b[18];
+    char *p;
+
+    p = (char *)&in;
+    pj_snprintf(b, sizeof(b), "%d.%d.%d.%d", 
+	       UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
+
+    return b;
+}
+
+/*
+ * This function converts the Internet host address ccp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to. 
+ */
+PJ_DEF(int) pj_inet_aton(const pj_str_t *ccp, struct pj_in_addr *addr)
+{
+    pj_uint32_t val;
+    int base, n;
+    char c;
+    unsigned parts[4];
+    unsigned *pp = parts;
+    char cp_copy[18];
+    char *cp = cp_copy;
+    
+    addr->s_addr = PJ_INADDR_NONE;
+
+    if (ccp->slen > 15) return 0;
+
+    pj_memcpy(cp, ccp->ptr, ccp->slen);
+    cp[ccp->slen] = '\0';
+
+    c = *cp;
+    for (;;) {
+	/*
+	 * Collect number up to ``.''.
+	 * Values are specified as for C:
+	 * 0x=hex, 0=octal, isdigit=decimal.
+	 */
+	if (!pj_isdigit((int)c))
+	    return (0);
+	val = 0; base = 10;
+	if (c == '0') {
+	    c = *++cp;
+	    if (c == 'x' || c == 'X')
+		base = 16, c = *++cp;
+	    else
+		base = 8;
+	}
+
+	for (;;) {
+	    if (pj_isascii((int)c) && pj_isdigit((int)c)) {
+		val = (val * base) + (c - '0');
+		c = *++cp;
+	    } else if (base==16 && pj_isascii((int)c) && pj_isxdigit((int)c)) {
+		val = (val << 4) |
+		      (c + 10 - (pj_islower((int)c) ? 'a' : 'A'));
+		c = *++cp;
+	    } else
+		break;
+	}
+
+	if (c == '.') {
+	    /*
+	     * Internet format:
+	     *  a.b.c.d
+	     *  a.b.c   (with c treated as 16 bits)
+	     *  a.b	(with b treated as 24 bits)
+	     */
+	    if (pp >= parts + 3)
+		return (0);
+	    *pp++ = val;
+	    c = *++cp;
+	} else
+	    break;
+    }
+
+    /*
+     * Check for trailing characters.
+     */
+    if (c != '\0' && (!pj_isascii((int)c) || !pj_isspace((int)c)))
+        return (0);
+    /*
+     * Concoct the address according to
+     * the number of parts specified.
+     */
+    n = pp - parts + 1;
+    switch (n) {
+    case 0:
+	return (0);	    /* initial nondigit */
+    case 1:		/* a -- 32 bits */
+	break;
+    case 2:		/* a.b -- 8.24 bits */
+	if (val > 0xffffff)
+	    return (0);
+	val |= parts[0] << 24;
+	break;
+    case 3:		/* a.b.c -- 8.8.16 bits */
+	if (val > 0xffff)
+	    return (0);
+	val |= (parts[0] << 24) | (parts[1] << 16);
+	break;
+    case 4:		/* a.b.c.d -- 8.8.8.8 bits */
+	if (val > 0xff)
+	    return (0);
+	val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+	break;
+    }
+
+    if (addr)
+	addr->s_addr = pj_htonl(val);
+    return (1);
+}
+
+/*
+ * Convert address string with numbers and dots to binary IP address.
+ */ 
+PJ_DEF(pj_in_addr) pj_inet_addr(const pj_str_t *cp)
+{
+    pj_in_addr addr;
+    pj_inet_aton(cp, &addr);
+    return addr;
+}
+
+/*
+ * Set the IP address of an IP socket address from string address, 
+ * with resolving the host if necessary. The string address may be in a
+ * standard numbers and dots notation or may be a hostname. If hostname
+ * is specified, then the function will resolve the host into the IP
+ * address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_set_str_addr( pj_sockaddr_in *addr,
+					         const pj_str_t *str_addr)
+{
+    PJ_CHECK_STACK();
+
+    pj_assert(str_addr && str_addr->slen < PJ_MAX_HOSTNAME);
+
+    addr->sin_family = AF_INET;
+
+    if (str_addr && str_addr->slen) {
+	addr->sin_addr = pj_inet_addr(str_addr);
+	if (addr->sin_addr.s_addr == PJ_INADDR_NONE) {
+    	    pj_hostent he;
+	    if (pj_gethostbyname(str_addr, &he) == 0) {
+		addr->sin_addr.s_addr = *(pj_uint32_t*)he.h_addr;
+	    } else {
+		addr->sin_addr.s_addr = PJ_INADDR_NONE;
+		return -1;
+	    }
+	}
+
+    } else {
+	addr->sin_addr.s_addr = 0;
+    }
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Set the IP address and port of an IP socket address.
+ * The string address may be in a standard numbers and dots notation or 
+ * may be a hostname. If hostname is specified, then the function will 
+ * resolve the host into the IP address.
+ */
+PJ_DEF(pj_status_t) pj_sockaddr_in_init( pj_sockaddr_in *addr,
+				         const pj_str_t *str_addr,
+					 pj_uint16_t port)
+{
+    pj_assert(addr && str_addr);
+
+    addr->sin_family = PJ_AF_INET;
+    pj_sockaddr_in_set_port(addr, port);
+    return pj_sockaddr_in_set_str_addr(addr, str_addr);
+}
+
+
+/*
+ * Get hostname.
+ */
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+    static char buf[PJ_MAX_HOSTNAME];
+    static pj_str_t hostname;
+
+    PJ_CHECK_STACK();
+
+    if (hostname.ptr == NULL) {
+	hostname.ptr = buf;
+	down_read(&uts_sem);
+	hostname.slen = strlen(system_utsname.nodename);
+	if (hostname.slen > PJ_MAX_HOSTNAME) {
+	    hostname.ptr[0] = '\0';
+	    hostname.slen = 0;
+	} else {
+	    pj_memcpy(hostname.ptr, system_utsname.nodename, hostname.slen);
+	}
+	up_read(&uts_sem);
+    }
+    return &hostname;
+}
+
+/*
+ * Get first IP address associated with the hostname.
+ */
+PJ_DEF(pj_in_addr) pj_gethostaddr(void)
+{
+    pj_sockaddr_in addr;
+    const pj_str_t *hostname = pj_gethostname();
+
+    pj_sockaddr_in_set_str_addr(&addr, hostname);
+    return addr.sin_addr;
+}
+
+
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af, int type, int proto, 
+				   pj_sock_t *sock_fd)
+{
+    long result;
+
+    PJ_CHECK_STACK();
+
+    /* Sanity checks. */
+    PJ_ASSERT_RETURN(PJ_INVALID_SOCKET == -1 && sock_fd != NULL, PJ_EINVAL);
+
+    /* Initialize returned socket */
+    *sock_fd = PJ_INVALID_SOCKET;
+
+    /* Create socket. */
+    result = sys_socket(af, type, proto);
+    if (result < 0) {
+	return PJ_RETURN_OS_ERROR((-result));
+    }
+
+    *sock_fd = result;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sockfd, 
+				  const pj_sockaddr_t *addr,
+				  int len)
+{
+    long err;
+    mm_segment_t oldfs;
+
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(addr!=NULL && len >= sizeof(struct pj_sockaddr),
+		     PJ_EINVAL);
+
+    oldfs = get_fs();
+    set_fs(KERNEL_DS);
+    
+    err = sys_bind(sockfd, (struct sockaddr*)addr, len);
+
+    set_fs(oldfs);
+
+    if (err)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sockfd, 
+				     pj_uint32_t addr32,
+				     pj_uint16_t port)
+{
+    pj_sockaddr_in addr;
+
+    PJ_CHECK_STACK();
+
+    addr.sin_family = PJ_AF_INET;
+    addr.sin_addr.s_addr = pj_htonl(addr32);
+    addr.sin_port = pj_htons(port);
+
+    return pj_sock_bind(sockfd, &addr, sizeof(pj_sockaddr_in));
+}
+
+/*
+ * Close socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sockfd)
+{
+    long err;
+
+    err = sys_close(sockfd);
+
+    if (err != 0)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Get remote's name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sockfd,
+					 pj_sockaddr_t *addr,
+					 int *namelen)
+{
+    mm_segment_t oldfs;
+    long err;
+
+    PJ_CHECK_STACK();
+
+    oldfs = get_fs();
+    set_fs(KERNEL_DS);
+
+    err = sys_getpeername( sockfd, addr, namelen);
+
+    set_fs(oldfs);
+
+    if (err)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Get socket name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sockfd,
+					 pj_sockaddr_t *addr,
+					 int *namelen)
+{
+    mm_segment_t oldfs;
+    int err;
+
+    PJ_CHECK_STACK();
+
+    oldfs = get_fs();
+    set_fs(KERNEL_DS);
+    
+    err = sys_getsockname( sockfd, addr, namelen );
+
+    set_fs(oldfs);
+
+    if (err)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Send data
+ */
+PJ_DEF(pj_status_t) pj_sock_send( pj_sock_t sockfd,
+				  const void *buf,
+				  pj_ssize_t *len,
+				  unsigned flags)
+{
+    return pj_sock_sendto(sockfd, buf, len, flags, NULL, 0);
+}
+
+
+/*
+ * Send data.
+ */
+PJ_DEF(pj_status_t) pj_sock_sendto( pj_sock_t sockfd,
+				    const void *buff,
+				    pj_ssize_t *len,
+				    unsigned flags,
+				    const pj_sockaddr_t *addr,
+				    int addr_len)
+{
+    long err;
+    mm_segment_t oldfs;
+
+    PJ_CHECK_STACK();
+
+    oldfs = get_fs();
+    set_fs(KERNEL_DS);
+    
+    err = *len = sys_sendto( sockfd, (void*)buff, *len, flags, 
+			     (void*)addr, addr_len );
+
+    set_fs(oldfs);
+    
+    if (err >= 0) {
+	return PJ_SUCCESS;
+    }
+    else {
+	return PJ_RETURN_OS_ERROR(-err);
+    }
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recv( pj_sock_t sockfd,
+				  void *buf,
+				  pj_ssize_t *len,
+				  unsigned flags)
+{
+    return pj_sock_recvfrom(sockfd, buf, len, flags, NULL, NULL);
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recvfrom( pj_sock_t sockfd,
+				      void *buff,
+				      pj_ssize_t *size,
+				      unsigned flags,
+				      pj_sockaddr_t *from,
+				      int *fromlen)
+{
+    mm_segment_t oldfs;
+    long err;
+
+    PJ_CHECK_STACK();
+
+    oldfs = get_fs();
+    set_fs(KERNEL_DS);
+
+    err = *size = sys_recvfrom( sockfd, buff, *size, flags, from, fromlen);
+
+    set_fs(oldfs);
+
+    if (err >= 0) {
+	return PJ_SUCCESS;
+    }
+    else {
+	return PJ_RETURN_OS_ERROR(-err);
+    }
+}
+
+/*
+ * Get socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sockfd,
+					pj_uint16_t level,
+					pj_uint16_t optname,
+					void *optval,
+					int *optlen)
+{
+    mm_segment_t oldfs;
+    long err;
+
+    PJ_CHECK_STACK();
+
+    oldfs = get_fs();
+    set_fs(KERNEL_DS);
+
+    err = sys_getsockopt( sockfd, level, optname, optval, optlen);
+
+    set_fs(oldfs);
+
+    if (err)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Set socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sockfd,
+					pj_uint16_t level,
+					pj_uint16_t optname,
+					const void *optval,
+					int optlen)
+{
+    long err;
+    mm_segment_t oldfs;
+
+    PJ_CHECK_STACK();
+
+
+    oldfs = get_fs();
+    set_fs(KERNEL_DS);
+
+    err = sys_setsockopt( sockfd, level, optname, (void*)optval, optlen);
+
+    set_fs(oldfs);
+
+    if (err)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Shutdown socket.
+ */
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sockfd,
+				      int how)
+{
+    long err;
+
+    PJ_CHECK_STACK();
+
+    err = sys_shutdown(sockfd, how);
+
+    if (err)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Start listening to incoming connections.
+ */
+PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sockfd,
+				    int backlog)
+{
+    long err;
+
+    PJ_CHECK_STACK();
+
+    err = sys_listen( sockfd, backlog );
+
+    if (err)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Connect socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sockfd,
+				     const pj_sockaddr_t *addr,
+				     int namelen)
+{
+    long err;
+    mm_segment_t oldfs;
+    
+    PJ_CHECK_STACK();
+
+    oldfs = get_fs();
+    set_fs(KERNEL_DS);
+
+    err = sys_connect( sockfd, (void*)addr, namelen );
+
+    set_fs(oldfs);
+
+    if (err)
+	return PJ_RETURN_OS_ERROR(-err);
+    else
+	return PJ_SUCCESS;
+}
+
+/*
+ * Accept incoming connections
+ */
+PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t sockfd,
+				    pj_sock_t *newsockfd,
+				    pj_sockaddr_t *addr,
+				    int *addrlen)
+{
+    long err;
+
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(newsockfd != NULL, PJ_EINVAL);
+
+    err = sys_accept( sockfd, addr, addrlen);
+
+    if (err < 0) {
+	*newsockfd = PJ_INVALID_SOCKET;
+	return PJ_RETURN_OS_ERROR(-err);
+    }
+    else {
+	*newsockfd = err;
+	return PJ_SUCCESS;
+    }
+}
+#endif	/* PJ_HAS_TCP */
+
+
+
+/*
+ * Permission to steal inet_ntoa() and inet_aton() as long as this notice below
+ * is included:
+ */
+/*
+ * Copyright (c) 1983, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *  This product includes software developed by the University of
+ *  California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_qos_bsd.c b/jni/pjproject-android/pjlib/src/pj/sock_qos_bsd.c
new file mode 100644
index 0000000..ca28653
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_qos_bsd.c
@@ -0,0 +1,132 @@
+/* $Id: sock_qos_bsd.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock_qos.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+
+/* This is the implementation of QoS with BSD socket's setsockopt(),
+ * using IP_TOS and SO_PRIORITY
+ */ 
+#if !defined(PJ_QOS_IMPLEMENTATION) || PJ_QOS_IMPLEMENTATION==PJ_QOS_BSD
+
+PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock,
+					   pj_qos_params *param)
+{
+    pj_status_t last_err = PJ_ENOTSUP;
+    pj_status_t status;
+
+    /* No op? */
+    if (!param->flags)
+	return PJ_SUCCESS;
+
+    /* Clear WMM field since we don't support it */
+    param->flags &= ~(PJ_QOS_PARAM_HAS_WMM);
+
+    /* Set TOS/DSCP */
+    if (param->flags & PJ_QOS_PARAM_HAS_DSCP) {
+	/* Value is dscp_val << 2 */
+	int val = (param->dscp_val << 2);
+	status = pj_sock_setsockopt(sock, pj_SOL_IP(), pj_IP_TOS(), 
+				    &val, sizeof(val));
+	if (status != PJ_SUCCESS) {
+	    param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP);
+	    last_err = status;
+	}
+    }
+
+    /* Set SO_PRIORITY */
+    if (param->flags & PJ_QOS_PARAM_HAS_SO_PRIO) {
+	int val = param->so_prio;
+	status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_PRIORITY(),
+				    &val, sizeof(val));
+	if (status != PJ_SUCCESS) {
+	    param->flags &= ~(PJ_QOS_PARAM_HAS_SO_PRIO);
+	    last_err = status;
+	}
+    }
+
+    return param->flags ? PJ_SUCCESS : last_err;
+}
+
+PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock,
+					 pj_qos_type type)
+{
+    pj_qos_params param;
+    pj_status_t status;
+
+    status = pj_qos_get_params(type, &param);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    return pj_sock_set_qos_params(sock, &param);
+}
+
+
+PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock,
+					   pj_qos_params *p_param)
+{
+    pj_status_t last_err = PJ_ENOTSUP;
+    int val, optlen;
+    pj_status_t status;
+
+    pj_bzero(p_param, sizeof(*p_param));
+
+    /* Get DSCP/TOS value */
+    optlen = sizeof(val);
+    status = pj_sock_getsockopt(sock, pj_SOL_IP(), pj_IP_TOS(), 
+				&val, &optlen);
+    if (status == PJ_SUCCESS) {
+	p_param->flags |= PJ_QOS_PARAM_HAS_DSCP;
+	p_param->dscp_val = (pj_uint8_t)(val >> 2);
+    } else {
+	last_err = status;
+    }
+
+    /* Get SO_PRIORITY */
+    optlen = sizeof(val);
+    status = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), pj_SO_PRIORITY(),
+				&val, &optlen);
+    if (status == PJ_SUCCESS) {
+	p_param->flags |= PJ_QOS_PARAM_HAS_SO_PRIO;
+	p_param->so_prio = (pj_uint8_t)val;
+    } else {
+	last_err = status;
+    }
+
+    /* WMM is not supported */
+
+    return p_param->flags ? PJ_SUCCESS : last_err;
+}
+
+PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock,
+					 pj_qos_type *p_type)
+{
+    pj_qos_params param;
+    pj_status_t status;
+
+    status = pj_sock_get_qos_params(sock, &param);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    return pj_qos_get_type(&param, p_type);
+}
+
+#endif	/* PJ_QOS_IMPLEMENTATION */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_qos_common.c b/jni/pjproject-android/pjlib/src/pj/sock_qos_common.c
new file mode 100644
index 0000000..643f0ea
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_qos_common.c
@@ -0,0 +1,151 @@
+/* $Id: sock_qos_common.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock_qos.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/string.h>
+
+#define THIS_FILE   "sock_qos_common.c"
+#define ALL_FLAGS   (PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_SO_PRIO | \
+                     PJ_QOS_PARAM_HAS_WMM)
+
+/* "Standard" mapping between traffic type and QoS params */
+static const pj_qos_params qos_map[] = 
+{
+    /* flags	dscp  prio wmm_prio */
+    {ALL_FLAGS, 0x00, 0,    PJ_QOS_WMM_PRIO_BULK_EFFORT},   /* BE */
+    {ALL_FLAGS, 0x08, 2,    PJ_QOS_WMM_PRIO_BULK},	    /* BK */
+    {ALL_FLAGS, 0x28, 5,    PJ_QOS_WMM_PRIO_VIDEO},	    /* VI */
+    {ALL_FLAGS, 0x30, 6,    PJ_QOS_WMM_PRIO_VOICE},	    /* VO */
+    {ALL_FLAGS, 0x38, 7,    PJ_QOS_WMM_PRIO_VOICE}	    /* CO */
+};
+
+
+/* Retrieve the mapping for the specified type */
+PJ_DEF(pj_status_t) pj_qos_get_params(pj_qos_type type, 
+				      pj_qos_params *p_param)
+{
+    PJ_ASSERT_RETURN(type<=PJ_QOS_TYPE_CONTROL && p_param, PJ_EINVAL);
+    pj_memcpy(p_param, &qos_map[type], sizeof(*p_param));
+    return PJ_SUCCESS;
+}
+
+/* Get the matching traffic type */
+PJ_DEF(pj_status_t) pj_qos_get_type( const pj_qos_params *param,
+				     pj_qos_type *p_type)
+{
+    unsigned dscp_type = PJ_QOS_TYPE_BEST_EFFORT,
+	     prio_type = PJ_QOS_TYPE_BEST_EFFORT,
+	     wmm_type = PJ_QOS_TYPE_BEST_EFFORT;
+    unsigned i, count=0;
+
+    PJ_ASSERT_RETURN(param && p_type, PJ_EINVAL);
+
+    if (param->flags & PJ_QOS_PARAM_HAS_DSCP)  {
+	for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) {
+	    if (param->dscp_val >= qos_map[i].dscp_val)
+		dscp_type = (pj_qos_type)i;
+	}
+	++count;
+    }
+
+    if (param->flags & PJ_QOS_PARAM_HAS_SO_PRIO) {
+	for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) {
+	    if (param->so_prio >= qos_map[i].so_prio)
+		prio_type = (pj_qos_type)i;
+	}
+	++count;
+    }
+
+    if (param->flags & PJ_QOS_PARAM_HAS_WMM) {
+	for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) {
+	    if (param->wmm_prio >= qos_map[i].wmm_prio)
+		wmm_type = (pj_qos_type)i;
+	}
+	++count;
+    }
+
+    if (count)
+	*p_type = (pj_qos_type)((dscp_type + prio_type + wmm_type) / count);
+    else
+	*p_type = PJ_QOS_TYPE_BEST_EFFORT;
+
+    return PJ_SUCCESS;
+}
+
+/* Apply QoS */
+PJ_DEF(pj_status_t) pj_sock_apply_qos( pj_sock_t sock,
+				       pj_qos_type qos_type,
+				       pj_qos_params *qos_params,
+				       unsigned log_level,
+				       const char *log_sender,
+				       const char *sock_name)
+{
+    pj_status_t qos_type_rc = PJ_SUCCESS,
+		qos_params_rc = PJ_SUCCESS;
+
+    if (!log_sender)
+	log_sender = THIS_FILE;
+    if (!sock_name)
+	sock_name = "socket";
+
+    if (qos_type != PJ_QOS_TYPE_BEST_EFFORT) {
+	qos_type_rc = pj_sock_set_qos_type(sock, qos_type);
+
+	if (qos_type_rc != PJ_SUCCESS) {
+	    pj_perror(log_level, log_sender,  qos_type_rc, 
+		      "Error setting QoS type %d to %s", 
+		      qos_type, sock_name);
+	}
+    }
+
+    if (qos_params && qos_params->flags) {
+	qos_params_rc = pj_sock_set_qos_params(sock, qos_params);
+	if (qos_params_rc != PJ_SUCCESS) {
+	    pj_perror(log_level, log_sender,  qos_params_rc, 
+		      "Error setting QoS params (flags=%d) to %s", 
+		      qos_params->flags, sock_name);
+	    if (qos_type_rc != PJ_SUCCESS)
+		return qos_params_rc;
+	}
+    } else if (qos_type_rc != PJ_SUCCESS)
+	return qos_type_rc;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_sock_apply_qos2( pj_sock_t sock,
+ 				        pj_qos_type qos_type,
+				        const pj_qos_params *qos_params,
+				        unsigned log_level,
+				        const char *log_sender,
+				        const char *sock_name)
+{
+    pj_qos_params qos_params_buf, *qos_params_copy = NULL;
+
+    if (qos_params) {
+	pj_memcpy(&qos_params_buf, qos_params, sizeof(*qos_params));
+	qos_params_copy = &qos_params_buf;
+    }
+
+    return pj_sock_apply_qos(sock, qos_type, qos_params_copy,
+			     log_level, log_sender, sock_name);
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_qos_dummy.c b/jni/pjproject-android/pjlib/src/pj/sock_qos_dummy.c
new file mode 100644
index 0000000..fbbb56f
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_qos_dummy.c
@@ -0,0 +1,76 @@
+/* $Id: sock_qos_dummy.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock_qos.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+
+/* Dummy implementation of QoS API. 
+ * (this is controlled by pjlib's config.h)
+ */
+#if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_DUMMY
+
+#define THIS_FILE   "sock_qos_dummy.c"
+
+
+PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock,
+					   pj_qos_params *param)
+{
+    PJ_UNUSED_ARG(sock);
+    PJ_UNUSED_ARG(param);
+
+    PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_params() is not implemented "
+			 "for this platform"));
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock,
+					 pj_qos_type type)
+{
+    PJ_UNUSED_ARG(sock);
+    PJ_UNUSED_ARG(type);
+
+    PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_type() is not implemented "
+			 "for this platform"));
+    return PJ_ENOTSUP;
+}
+
+
+PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock,
+					   pj_qos_params *p_param)
+{
+    PJ_UNUSED_ARG(sock);
+    PJ_UNUSED_ARG(p_param);
+
+    PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_params() is not implemented "
+			 "for this platform"));
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock,
+					 pj_qos_type *p_type)
+{
+    PJ_UNUSED_ARG(sock);
+    PJ_UNUSED_ARG(p_type);
+
+    PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_type() is not implemented "
+			 "for this platform"));
+    return PJ_ENOTSUP;
+}
+
+#endif	/* PJ_QOS_DUMMY */
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_qos_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/sock_qos_symbian.cpp
new file mode 100644
index 0000000..35779b8
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_qos_symbian.cpp
@@ -0,0 +1,95 @@
+/* $Id: sock_qos_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock_qos.h>
+#include "os_symbian.h"
+
+PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock,
+					   pj_qos_params *param)
+{
+    PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL);
+    
+    CPjSocket *pjsock = (CPjSocket*)sock;
+    RSocket & rsock = pjsock->Socket();
+    pj_status_t last_err = PJ_ENOTSUP;
+    
+    /* SO_PRIORITY and WMM are not supported */
+    param->flags &= ~(PJ_QOS_PARAM_HAS_SO_PRIO | PJ_QOS_PARAM_HAS_WMM);
+    
+    if (param->flags & PJ_QOS_PARAM_HAS_DSCP) {
+	TInt err;
+	
+	err = rsock.SetOpt(KSoIpTOS, KProtocolInetIp,
+		           (param->dscp_val << 2));
+	if (err != KErrNone) {
+	    last_err = PJ_RETURN_OS_ERROR(err);
+	    param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP);
+	}
+    }
+    
+    return param->flags ? PJ_SUCCESS : last_err;
+}
+
+PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock,
+					 pj_qos_type type)
+{
+    pj_qos_params param;
+    pj_status_t status;
+    
+    status = pj_qos_get_params(type, &param);
+    if (status != PJ_SUCCESS)
+	return status;
+    
+    return pj_sock_set_qos_params(sock, &param);
+}
+
+
+PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock,
+					   pj_qos_params *p_param)
+{
+    PJ_ASSERT_RETURN(sock!=0 && sock!=PJ_INVALID_SOCKET, PJ_EINVAL);
+    
+    CPjSocket *pjsock = (CPjSocket*)sock;
+    RSocket & rsock = pjsock->Socket();
+    TInt err, dscp;
+    
+    pj_bzero(p_param, sizeof(*p_param));
+
+    err = rsock.GetOpt(KSoIpTOS, KProtocolInetIp, dscp);
+    if (err == KErrNone) {
+	p_param->flags |= PJ_QOS_PARAM_HAS_DSCP;
+	p_param->dscp_val = (dscp >> 2);
+	return PJ_SUCCESS;
+    } else {
+	return PJ_RETURN_OS_ERROR(err);
+    }
+}
+
+PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock,
+					 pj_qos_type *p_type)
+{
+    pj_qos_params param;
+    pj_status_t status;
+    
+    status = pj_sock_get_qos_params(sock, &param);
+    if (status != PJ_SUCCESS)
+	return status;
+    
+    return pj_qos_get_type(&param, p_type);
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_qos_wm.c b/jni/pjproject-android/pjlib/src/pj/sock_qos_wm.c
new file mode 100644
index 0000000..e607d96
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_qos_wm.c
@@ -0,0 +1,103 @@
+/* $Id: sock_qos_wm.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock_qos.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+
+#include <winsock.h>
+
+/* QoS implementation for Windows Mobile 6, must be enabled explicitly
+ * (this is controlled by pjlib's config.h)
+ */
+#if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_WM
+
+#define THIS_FILE   "sock_qos_wm.c"
+
+/* Mapping between our traffic type and WM's DSCP traffic types */
+static const int dscp_map[] = 
+{
+    DSCPBestEffort,
+    DSCPBackground,
+    DSCPVideo,
+    DSCPAudio,
+    DSCPControl
+};
+
+PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock,
+					   pj_qos_params *param)
+{
+    PJ_UNUSED_ARG(sock);
+    PJ_UNUSED_ARG(param);
+
+    PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_params() is not implemented "
+			 "for this platform"));
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock,
+					 pj_qos_type type)
+{
+    int value;
+
+    PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(dscp_map), PJ_EINVAL);
+
+    value = dscp_map[type];
+    return pj_sock_setsockopt(sock, IPPROTO_IP, IP_DSCP_TRAFFIC_TYPE,
+			      &value, sizeof(value));
+}
+
+
+PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock,
+					   pj_qos_params *p_param)
+{
+    PJ_UNUSED_ARG(sock);
+    PJ_UNUSED_ARG(p_param);
+
+    PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_params() is not implemented "
+			 "for this platform"));
+    return PJ_ENOTSUP;
+}
+
+PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock,
+					 pj_qos_type *p_type)
+{
+    pj_status_t status;
+    int value, optlen;
+    unsigned i;
+
+    optlen = sizeof(value);
+    value = 0;
+    status = pj_sock_getsockopt(sock, IPPROTO_IP, IP_DSCP_TRAFFIC_TYPE,
+			        &value, &optlen);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    *p_type = PJ_QOS_TYPE_BEST_EFFORT;
+    for (i=0; i<PJ_ARRAY_SIZE(dscp_map); ++i) {
+	if (value == dscp_map[i]) {
+	    *p_type = (pj_qos_type)i;
+	    break;
+	}
+    }
+
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJ_QOS_IMPLEMENTATION */
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_select.c b/jni/pjproject-android/pjlib/src/pj/sock_select.c
new file mode 100644
index 0000000..2b930a3
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_select.c
@@ -0,0 +1,113 @@
+/* $Id: sock_select.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock_select.h>
+#include <pj/compat/socket.h>
+#include <pj/os.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+
+#if defined(PJ_HAS_STRING_H) && PJ_HAS_STRING_H!=0
+#   include <string.h>
+#endif
+
+#if defined(PJ_HAS_SYS_TIME_H) && PJ_HAS_SYS_TIME_H!=0
+#   include <sys/time.h>
+#endif
+
+#ifdef _MSC_VER
+#   pragma warning(disable: 4018)    // Signed/unsigned mismatch in FD_*
+#   pragma warning(disable: 4389)    // Signed/unsigned mismatch in FD_*
+#endif
+
+#define PART_FDSET(ps)		((fd_set*)&ps->data[1])
+#define PART_FDSET_OR_NULL(ps)	(ps ? PART_FDSET(ps) : NULL)
+#define PART_COUNT(ps)		(ps->data[0])
+
+PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp)
+{
+    PJ_CHECK_STACK();
+    pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+    FD_ZERO(PART_FDSET(fdsetp));
+    PART_COUNT(fdsetp) = 0;
+}
+
+
+PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp)
+{
+    PJ_CHECK_STACK();
+    pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+    if (!PJ_FD_ISSET(fd, fdsetp))
+        ++PART_COUNT(fdsetp);
+    FD_SET(fd, PART_FDSET(fdsetp));
+}
+
+
+PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp)
+{
+    PJ_CHECK_STACK();
+    pj_assert(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set));
+
+    if (PJ_FD_ISSET(fd, fdsetp))
+        --PART_COUNT(fdsetp);
+    FD_CLR(fd, PART_FDSET(fdsetp));
+}
+
+
+PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
+                     0);
+
+    return FD_ISSET(fd, PART_FDSET(fdsetp));
+}
+
+PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp)
+{
+    return PART_COUNT(fdsetp);
+}
+
+PJ_DEF(int) pj_sock_select( int n, 
+			    pj_fd_set_t *readfds, 
+			    pj_fd_set_t *writefds,
+			    pj_fd_set_t *exceptfds, 
+			    const pj_time_val *timeout)
+{
+    struct timeval os_timeout, *p_os_timeout;
+
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sizeof(pj_fd_set_t)-sizeof(pj_sock_t) >= sizeof(fd_set),
+                     PJ_EBUG);
+
+    if (timeout) {
+	os_timeout.tv_sec = timeout->sec;
+	os_timeout.tv_usec = timeout->msec * 1000;
+	p_os_timeout = &os_timeout;
+    } else {
+	p_os_timeout = NULL;
+    }
+
+    return select(n, PART_FDSET_OR_NULL(readfds), PART_FDSET_OR_NULL(writefds),
+		  PART_FDSET_OR_NULL(exceptfds), p_os_timeout);
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_select_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/sock_select_symbian.cpp
new file mode 100644
index 0000000..aeaa2bc
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_select_symbian.cpp
@@ -0,0 +1,163 @@
+/* $Id: sock_select_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock_select.h>
+#include <pj/array.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+#include "os_symbian.h"
+
+ 
+struct symbian_fd_set
+{
+    unsigned	 count;
+    CPjSocket	*sock[PJ_IOQUEUE_MAX_HANDLES];
+};
+
+
+PJ_DEF(void) PJ_FD_ZERO(pj_fd_set_t *fdsetp)
+{
+    symbian_fd_set *fds = (symbian_fd_set *)fdsetp;
+    fds->count = 0;
+}
+
+
+PJ_DEF(void) PJ_FD_SET(pj_sock_t fd, pj_fd_set_t *fdsetp)
+{
+    symbian_fd_set *fds = (symbian_fd_set *)fdsetp;
+
+    PJ_ASSERT_ON_FAIL(fds->count < PJ_IOQUEUE_MAX_HANDLES, return);
+    fds->sock[fds->count++] = (CPjSocket*)fd;
+}
+
+
+PJ_DEF(void) PJ_FD_CLR(pj_sock_t fd, pj_fd_set_t *fdsetp)
+{
+    symbian_fd_set *fds = (symbian_fd_set *)fdsetp;
+    unsigned i;
+    
+    for (i=0; i<fds->count; ++i) {
+	if (fds->sock[i] == (CPjSocket*)fd) {
+	    pj_array_erase(fds->sock, sizeof(fds->sock[0]), fds->count, i);
+	    --fds->count;
+	    return;
+	}
+    }
+}
+
+
+PJ_DEF(pj_bool_t) PJ_FD_ISSET(pj_sock_t fd, const pj_fd_set_t *fdsetp)
+{
+    symbian_fd_set *fds = (symbian_fd_set *)fdsetp;
+    unsigned i;
+    
+    for (i=0; i<fds->count; ++i) {
+	if (fds->sock[i] == (CPjSocket*)fd) {
+	    return PJ_TRUE;
+	}
+    }
+
+    return PJ_FALSE;
+}
+
+PJ_DEF(pj_size_t) PJ_FD_COUNT(const pj_fd_set_t *fdsetp)
+{
+    symbian_fd_set *fds = (symbian_fd_set *)fdsetp;
+    return fds->count;
+}
+
+
+PJ_DEF(int) pj_sock_select( int n, 
+			    pj_fd_set_t *readfds, 
+			    pj_fd_set_t *writefds,
+			    pj_fd_set_t *exceptfds, 
+			    const pj_time_val *timeout)
+{
+    CPjTimeoutTimer *pjTimer;
+    unsigned i;
+
+    PJ_UNUSED_ARG(n);
+    PJ_UNUSED_ARG(writefds);
+    PJ_UNUSED_ARG(exceptfds);
+
+    if (timeout) {
+	pjTimer = PjSymbianOS::Instance()->SelectTimeoutTimer();
+	pjTimer->StartTimer(timeout->sec*1000 + timeout->msec);
+
+    } else {
+	pjTimer = NULL;
+    }
+
+    /* Scan for readable sockets */
+
+    if (readfds) {
+	symbian_fd_set *fds = (symbian_fd_set *)readfds;
+
+	do {
+	    /* Scan sockets for readily available data */
+	    for (i=0; i<fds->count; ++i) {
+		CPjSocket *pjsock = fds->sock[i];
+
+		if (pjsock->Reader()) {
+		    if (pjsock->Reader()->HasData() && !pjsock->Reader()->IsActive()) {
+
+			/* Found socket with data ready */
+			PJ_FD_ZERO(readfds);
+			PJ_FD_SET((pj_sock_t)pjsock, readfds);
+
+			/* Cancel timer, if any */
+			if (pjTimer) {
+			    pjTimer->Cancel();
+			}
+
+			/* Clear writable and exception fd_set */
+			if (writefds)
+			    PJ_FD_ZERO(writefds);
+			if (exceptfds)
+			    PJ_FD_ZERO(exceptfds);
+
+			return 1;
+
+		    } else if (!pjsock->Reader()->IsActive())
+			pjsock->Reader()->StartRecvFrom();
+
+		} else {
+		    pjsock->CreateReader();
+		    pjsock->Reader()->StartRecvFrom();
+		}
+	    }
+
+	    PjSymbianOS::Instance()->WaitForActiveObjects();
+
+	} while (pjTimer==NULL || !pjTimer->HasTimedOut());
+    }
+
+
+    /* Timeout */
+
+    if (readfds)
+	PJ_FD_ZERO(readfds);
+    if (writefds)
+	PJ_FD_ZERO(writefds);
+    if (exceptfds)
+	PJ_FD_ZERO(exceptfds);
+
+    return 0;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/sock_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/sock_symbian.cpp
new file mode 100644
index 0000000..35db1ea
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/sock_symbian.cpp
@@ -0,0 +1,1021 @@
+/* $Id: sock_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/sock.h>
+#include <pj/addr_resolv.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/unicode.h>
+
+#include "os_symbian.h"
+
+
+/*
+ * Address families.
+ */
+const pj_uint16_t PJ_AF_UNSPEC	= KAFUnspec;
+const pj_uint16_t PJ_AF_UNIX	= 0xFFFF;
+const pj_uint16_t PJ_AF_INET	= KAfInet;
+const pj_uint16_t PJ_AF_INET6	= KAfInet6;
+const pj_uint16_t PJ_AF_PACKET	= 0xFFFF;
+const pj_uint16_t PJ_AF_IRDA	= 0xFFFF;
+
+/*
+ * Socket types conversion.
+ * The values here are indexed based on pj_sock_type
+ */
+const pj_uint16_t PJ_SOCK_STREAM= KSockStream;
+const pj_uint16_t PJ_SOCK_DGRAM	= KSockDatagram;
+const pj_uint16_t PJ_SOCK_RAW	= 0xFFFF;
+const pj_uint16_t PJ_SOCK_RDM	= 0xFFFF;
+
+/* we don't support setsockopt(), these are just dummy values */
+const pj_uint16_t PJ_SOL_SOCKET	= 0xFFFF;
+const pj_uint16_t PJ_SOL_IP	= 0xFFFF;
+const pj_uint16_t PJ_SOL_TCP	= 0xFFFF;
+const pj_uint16_t PJ_SOL_UDP	= 0xFFFF;
+const pj_uint16_t PJ_SOL_IPV6	= 0xFFFF;
+const pj_uint16_t PJ_SO_NOSIGPIPE = 0xFFFF;
+
+/* TOS */
+const pj_uint16_t PJ_IP_TOS		= 0;
+const pj_uint16_t PJ_IPTOS_LOWDELAY	= 0;
+const pj_uint16_t PJ_IPTOS_THROUGHPUT	= 0;
+const pj_uint16_t PJ_IPTOS_RELIABILITY	= 0;
+const pj_uint16_t PJ_IPTOS_MINCOST	= 0;
+
+/* Misc */
+const pj_uint16_t PJ_TCP_NODELAY = 0xFFFF;
+const pj_uint16_t PJ_SO_REUSEADDR = 0xFFFF;
+const pj_uint16_t PJ_SO_PRIORITY = 0xFFFF;
+
+/* ioctl() is also not supported. */
+const pj_uint16_t PJ_SO_TYPE    = 0xFFFF;
+const pj_uint16_t PJ_SO_RCVBUF  = 0xFFFF;
+const pj_uint16_t PJ_SO_SNDBUF  = 0xFFFF;
+
+/* IP multicast is also not supported. */
+const pj_uint16_t PJ_IP_MULTICAST_IF    = 0xFFFF;
+const pj_uint16_t PJ_IP_MULTICAST_TTL   = 0xFFFF;
+const pj_uint16_t PJ_IP_MULTICAST_LOOP  = 0xFFFF;
+const pj_uint16_t PJ_IP_ADD_MEMBERSHIP  = 0xFFFF;
+const pj_uint16_t PJ_IP_DROP_MEMBERSHIP = 0xFFFF;
+
+/* Flags */
+const int PJ_MSG_OOB	     = 0;
+const int PJ_MSG_PEEK	     = KSockReadPeek;
+const int PJ_MSG_DONTROUTE   = 0;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// CPjSocket implementation.
+// (declaration is in os_symbian.h)
+//
+
+CPjSocket::~CPjSocket()
+{
+    DestroyReader();
+    sock_.Close();
+}
+
+
+// Create socket reader.
+CPjSocketReader *CPjSocket::CreateReader(unsigned max_len)
+{
+    pj_assert(sockReader_ == NULL);
+    return sockReader_ = CPjSocketReader::NewL(*this, max_len);
+}
+
+// Delete socket reader when it's not wanted.
+void CPjSocket::DestroyReader() 
+{
+    if (sockReader_) {
+	sockReader_->Cancel();
+	delete sockReader_;
+	sockReader_ = NULL;
+    }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// CPjSocketReader implementation
+// (declaration in os_symbian.h)
+//
+
+
+CPjSocketReader::CPjSocketReader(CPjSocket &sock)
+: CActive(EPriorityStandard), 
+  sock_(sock), buffer_(NULL, 0), readCb_(NULL), key_(NULL)
+{
+}
+
+
+void CPjSocketReader::ConstructL(unsigned max_len)
+{
+    isDatagram_ = sock_.IsDatagram();
+
+    TUint8 *ptr = new TUint8[max_len];
+    buffer_.Set(ptr, 0, (TInt)max_len);
+    CActiveScheduler::Add(this);
+}
+
+CPjSocketReader *CPjSocketReader::NewL(CPjSocket &sock, unsigned max_len)
+{
+    CPjSocketReader *self = new (ELeave) CPjSocketReader(sock);
+    CleanupStack::PushL(self);
+    self->ConstructL(max_len);
+    CleanupStack::Pop(self);
+
+    return self;
+}
+
+
+CPjSocketReader::~CPjSocketReader()
+{
+    const TUint8 *data = buffer_.Ptr();
+    delete [] data;
+}
+
+void CPjSocketReader::StartRecv(void (*cb)(void *key), 
+			        void *key, 
+			        TDes8 *aDesc,
+			        TUint flags)
+{
+    StartRecvFrom(cb, key, aDesc, flags, NULL);
+}
+
+void CPjSocketReader::StartRecvFrom(void (*cb)(void *key), 
+				    void *key, 
+				    TDes8 *aDesc,
+				    TUint flags,
+				    TSockAddr *fromAddr)
+{
+    readCb_ = cb;
+    key_ = key;
+
+    if (aDesc == NULL) aDesc = &buffer_;
+    if (fromAddr == NULL) fromAddr = &recvAddr_;
+
+    sock_.Socket().RecvFrom(*aDesc, *fromAddr, flags, iStatus);
+    SetActive();
+}
+
+void CPjSocketReader::DoCancel()
+{
+    sock_.Socket().CancelRecv();
+}
+
+void CPjSocketReader::RunL()
+{
+    void (*old_cb)(void *key) = readCb_;
+    void *old_key = key_;
+
+    readCb_ = NULL;
+    key_ = NULL;
+
+    if (old_cb) {
+	(*old_cb)(old_key);
+    }
+}
+
+// Append data to aDesc, up to aDesc's maximum size.
+// If socket is datagram based, buffer_ will be clared.
+void CPjSocketReader::ReadData(TDes8 &aDesc, TInetAddr *addr)
+{
+    if (isDatagram_)
+	aDesc.Zero();
+
+    if (buffer_.Length() == 0)
+	return;
+
+    TInt size_to_copy = aDesc.MaxLength() - aDesc.Length();
+    if (size_to_copy > buffer_.Length())
+	size_to_copy = buffer_.Length();
+
+    aDesc.Append(buffer_.Ptr(), size_to_copy);
+
+    if (isDatagram_)
+	buffer_.Zero();
+    else
+	buffer_.Delete(0, size_to_copy);
+
+    if (addr)
+	*addr = recvAddr_;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// PJLIB's sock.h implementation
+//
+
+/*
+ * Convert 16-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_ntohs(pj_uint16_t netshort)
+{
+#if PJ_IS_LITTLE_ENDIAN
+    return pj_swap16(netshort);
+#else
+    return netshort;
+#endif
+}
+
+/*
+ * Convert 16-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint16_t) pj_htons(pj_uint16_t hostshort)
+{
+#if PJ_IS_LITTLE_ENDIAN
+    return pj_swap16(hostshort);
+#else
+    return hostshort;
+#endif
+}
+
+/*
+ * Convert 32-bit value from network byte order to host byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_ntohl(pj_uint32_t netlong)
+{
+#if PJ_IS_LITTLE_ENDIAN
+    return pj_swap32(netlong);
+#else
+    return netlong;
+#endif
+}
+
+/*
+ * Convert 32-bit value from host byte order to network byte order.
+ */
+PJ_DEF(pj_uint32_t) pj_htonl(pj_uint32_t hostlong)
+{
+#if PJ_IS_LITTLE_ENDIAN
+    return pj_swap32(hostlong);
+#else
+    return netlong;
+#endif
+}
+
+/*
+ * Convert an Internet host address given in network byte order
+ * to string in standard numbers and dots notation.
+ */
+PJ_DEF(char*) pj_inet_ntoa(pj_in_addr inaddr)
+{
+	static char str8[PJ_INET_ADDRSTRLEN];
+    TBuf<PJ_INET_ADDRSTRLEN> str16(0);
+
+    /* (Symbian IP address is in host byte order) */
+    TInetAddr temp_addr((TUint32)pj_ntohl(inaddr.s_addr), (TUint)0);
+    temp_addr.Output(str16);
+ 
+    return pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(),
+			      str8, sizeof(str8));
+}
+
+/*
+ * This function converts the Internet host address cp from the standard
+ * numbers-and-dots notation into binary data and stores it in the structure
+ * that inp points to. 
+ */
+PJ_DEF(int) pj_inet_aton(const pj_str_t *cp, struct pj_in_addr *inp)
+{
+    enum { MAXIPLEN = PJ_INET_ADDRSTRLEN };
+
+    /* Initialize output with PJ_INADDR_NONE.
+     * Some apps relies on this instead of the return value
+     * (and anyway the return value is quite confusing!)
+     */
+    inp->s_addr = PJ_INADDR_NONE;
+
+    /* Caution:
+     *	this function might be called with cp->slen >= 16
+     *  (i.e. when called with hostname to check if it's an IP addr).
+     */
+    PJ_ASSERT_RETURN(cp && cp->slen && inp, 0);
+    if (cp->slen >= 16) {
+	return 0;
+    }
+
+    char tempaddr8[MAXIPLEN];
+    pj_memcpy(tempaddr8, cp->ptr, cp->slen);
+    tempaddr8[cp->slen] = '\0';
+
+    wchar_t tempaddr16[MAXIPLEN];
+    pj_ansi_to_unicode(tempaddr8, pj_ansi_strlen(tempaddr8),
+		       tempaddr16, sizeof(tempaddr16));
+
+    TBuf<MAXIPLEN> ip_addr((const TText*)tempaddr16);
+
+    TInetAddr addr;
+    addr.Init(KAfInet);
+    if (addr.Input(ip_addr) == KErrNone) {
+	/* Success (Symbian IP address is in host byte order) */
+	inp->s_addr = pj_htonl(addr.Address());
+	return 1;
+    } else {
+	/* Error */
+	return 0;
+    }
+}
+
+/*
+ * Convert text to IPv4/IPv6 address.
+ */
+PJ_DEF(pj_status_t) pj_inet_pton(int af, const pj_str_t *src, void *dst)
+{
+    char tempaddr[PJ_INET6_ADDRSTRLEN];
+
+    PJ_ASSERT_RETURN(af==PJ_AF_INET || af==PJ_AF_INET6, PJ_EINVAL);
+    PJ_ASSERT_RETURN(src && src->slen && dst, PJ_EINVAL);
+
+    /* Initialize output with PJ_IN_ADDR_NONE for IPv4 (to be 
+     * compatible with pj_inet_aton()
+     */
+    if (af==PJ_AF_INET) {
+	((pj_in_addr*)dst)->s_addr = PJ_INADDR_NONE;
+    }
+
+    /* Caution:
+     *	this function might be called with cp->slen >= 46
+     *  (i.e. when called with hostname to check if it's an IP addr).
+     */
+    if (src->slen >= PJ_INET6_ADDRSTRLEN) {
+	return PJ_ENAMETOOLONG;
+    }
+
+    pj_memcpy(tempaddr, src->ptr, src->slen);
+    tempaddr[src->slen] = '\0';
+
+
+    wchar_t tempaddr16[PJ_INET6_ADDRSTRLEN];
+    pj_ansi_to_unicode(tempaddr, pj_ansi_strlen(tempaddr),
+		       tempaddr16, sizeof(tempaddr16));
+
+    TBuf<PJ_INET6_ADDRSTRLEN> ip_addr((const TText*)tempaddr16);
+
+    TInetAddr addr;
+    addr.Init(KAfInet6);
+    if (addr.Input(ip_addr) == KErrNone) {
+	if (af==PJ_AF_INET) {
+	    /* Success (Symbian IP address is in host byte order) */
+	    pj_uint32_t ip = pj_htonl(addr.Address());
+	    pj_memcpy(dst, &ip, 4);
+	} else if (af==PJ_AF_INET6) {
+	    const TIp6Addr & ip6 = addr.Ip6Address();
+	    pj_memcpy(dst, ip6.u.iAddr8, 16);
+	} else {
+	    pj_assert(!"Unexpected!");
+	    return PJ_EBUG;
+	}
+	return PJ_SUCCESS;
+    } else {
+	/* Error */
+	return PJ_EINVAL;
+    }
+}
+
+/*
+ * Convert IPv4/IPv6 address to text.
+ */
+PJ_DEF(pj_status_t) pj_inet_ntop(int af, const void *src,
+				 char *dst, int size)
+
+{
+    PJ_ASSERT_RETURN(src && dst && size, PJ_EINVAL);
+
+    *dst = '\0';
+
+    if (af==PJ_AF_INET) {
+
+	TBuf<PJ_INET_ADDRSTRLEN> str16;
+	pj_in_addr inaddr;
+
+	if (size < PJ_INET_ADDRSTRLEN)
+	    return PJ_ETOOSMALL;
+
+	pj_memcpy(&inaddr, src, 4);
+
+	/* Symbian IP address is in host byte order */
+	TInetAddr temp_addr((TUint32)pj_ntohl(inaddr.s_addr), (TUint)0);
+	temp_addr.Output(str16);
+ 
+	pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(),
+			   dst, size);
+	return PJ_SUCCESS;
+
+    } else if (af==PJ_AF_INET6) {
+	TBuf<PJ_INET6_ADDRSTRLEN> str16;
+
+	if (size < PJ_INET6_ADDRSTRLEN)
+	    return PJ_ETOOSMALL;
+
+	TIp6Addr ip6;
+	pj_memcpy(ip6.u.iAddr8, src, 16);
+
+	TInetAddr temp_addr(ip6, (TUint)0);
+	temp_addr.Output(str16);
+ 
+	pj_unicode_to_ansi((const wchar_t*)str16.PtrZ(), str16.Length(),
+			   dst, size);
+	return PJ_SUCCESS;
+
+    } else {
+	pj_assert(!"Unsupport address family");
+	return PJ_EINVAL;
+    }
+
+}
+
+/*
+ * Get hostname.
+ */
+PJ_DEF(const pj_str_t*) pj_gethostname(void)
+{
+    static char buf[PJ_MAX_HOSTNAME];
+    static pj_str_t hostname;
+
+    PJ_CHECK_STACK();
+
+    if (hostname.ptr == NULL) {
+	RHostResolver &resv = PjSymbianOS::Instance()->GetResolver(PJ_AF_INET);
+	TRequestStatus reqStatus;
+	THostName tmpName;
+
+	// Return empty hostname if access point is marked as down by app.
+	PJ_SYMBIAN_CHECK_CONNECTION2(&hostname);
+
+	resv.GetHostName(tmpName, reqStatus);
+	User::WaitForRequest(reqStatus);
+
+	hostname.ptr = pj_unicode_to_ansi((const wchar_t*)tmpName.Ptr(), tmpName.Length(),
+					  buf, sizeof(buf));
+	hostname.slen = tmpName.Length();
+    }
+    return &hostname;
+}
+
+/*
+ * Create new socket/endpoint for communication and returns a descriptor.
+ */
+PJ_DEF(pj_status_t) pj_sock_socket(int af, 
+				   int type, 
+				   int proto,
+				   pj_sock_t *p_sock)
+{
+    TInt rc;
+
+    PJ_CHECK_STACK();
+
+    /* Sanity checks. */
+    PJ_ASSERT_RETURN(p_sock!=NULL, PJ_EINVAL);
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    /* Set proto if none is specified. */
+    if (proto == 0) {
+	if (type == pj_SOCK_STREAM())
+	    proto = KProtocolInetTcp;
+	else if (type == pj_SOCK_DGRAM())
+	    proto = KProtocolInetUdp;
+    }
+
+    /* Create Symbian RSocket */
+    RSocket rSock;
+    if (PjSymbianOS::Instance()->Connection())
+    	rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), 
+    			af, type, proto,
+    			*PjSymbianOS::Instance()->Connection());
+    else
+    	rc = rSock.Open(PjSymbianOS::Instance()->SocketServ(), 
+    			af, type, proto);
+        
+    if (rc != KErrNone)
+	return PJ_RETURN_OS_ERROR(rc);
+
+
+    /* Wrap Symbian RSocket into PJLIB's CPjSocket, and return to caller */
+    CPjSocket *pjSock = new CPjSocket(af, type, rSock);
+    *p_sock = (pj_sock_t)pjSock;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind( pj_sock_t sock, 
+				  const pj_sockaddr_t *addr,
+				  int len)
+{
+    pj_status_t status;
+    TInt rc;
+
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sock != 0, PJ_EINVAL);
+    PJ_ASSERT_RETURN(addr && len>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+    // Convert PJLIB's pj_sockaddr into Symbian's TInetAddr
+    TInetAddr inetAddr;
+    status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)addr, len, inetAddr);
+    if (status != PJ_SUCCESS)
+    	return status;
+
+    // Get the RSocket instance
+    RSocket &rSock = ((CPjSocket*)sock)->Socket();
+
+    // Bind
+    rc = rSock.Bind(inetAddr);
+
+    return (rc==KErrNone) ? PJ_SUCCESS : PJ_RETURN_OS_ERROR(rc);
+}
+
+
+/*
+ * Bind socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_bind_in( pj_sock_t sock, 
+				     pj_uint32_t addr32,
+				     pj_uint16_t port)
+{
+    pj_sockaddr_in addr;
+
+    PJ_CHECK_STACK();
+
+    pj_bzero(&addr, sizeof(addr));
+    addr.sin_family = PJ_AF_INET;
+    addr.sin_addr.s_addr = pj_htonl(addr32);
+    addr.sin_port = pj_htons(port);
+
+    return pj_sock_bind(sock, &addr, sizeof(pj_sockaddr_in));
+}
+
+
+/*
+ * Close socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_close(pj_sock_t sock)
+{
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sock != 0, PJ_EINVAL);
+
+    CPjSocket *pjSock = (CPjSocket*)sock;
+
+    // This will close the socket.
+    delete pjSock;
+
+    return PJ_SUCCESS;
+}
+
+/*
+ * Get remote's name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getpeername( pj_sock_t sock,
+					 pj_sockaddr_t *addr,
+					 int *namelen)
+{
+    PJ_CHECK_STACK();
+    
+    PJ_ASSERT_RETURN(sock && addr && namelen && 
+		     *namelen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+    CPjSocket *pjSock = (CPjSocket*)sock;
+    RSocket &rSock = pjSock->Socket();
+
+    // Socket must be connected.
+    PJ_ASSERT_RETURN(pjSock->IsConnected(), PJ_EINVALIDOP);
+
+    TInetAddr inetAddr;
+    rSock.RemoteName(inetAddr);
+
+    return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)addr, namelen);
+}
+
+/*
+ * Get socket name.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockname( pj_sock_t sock,
+					 pj_sockaddr_t *addr,
+					 int *namelen)
+{
+    PJ_CHECK_STACK();
+    
+    PJ_ASSERT_RETURN(sock && addr && namelen && 
+		     *namelen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+    CPjSocket *pjSock = (CPjSocket*)sock;
+    RSocket &rSock = pjSock->Socket();
+
+    TInetAddr inetAddr;
+    rSock.LocalName(inetAddr);
+
+    return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)addr, namelen);
+}
+
+/*
+ * Send data
+ */
+PJ_DEF(pj_status_t) pj_sock_send(pj_sock_t sock,
+				 const void *buf,
+				 pj_ssize_t *len,
+				 unsigned flags)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL);
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    CPjSocket *pjSock = (CPjSocket*)sock;
+    RSocket &rSock = pjSock->Socket();
+
+    // send() should only be called to connected socket
+    PJ_ASSERT_RETURN(pjSock->IsConnected(), PJ_EINVALIDOP);
+
+    TPtrC8 data((const TUint8*)buf, (TInt)*len);
+    TRequestStatus reqStatus;
+    TSockXfrLength sentLen;
+
+    rSock.Send(data, flags, reqStatus, sentLen);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus.Int()==KErrNone) {
+	//*len = (TInt) sentLen.Length();
+	return PJ_SUCCESS;
+    } else
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+}
+
+
+/*
+ * Send data.
+ */
+PJ_DEF(pj_status_t) pj_sock_sendto(pj_sock_t sock,
+				   const void *buf,
+				   pj_ssize_t *len,
+				   unsigned flags,
+				   const pj_sockaddr_t *to,
+				   int tolen)
+{
+    pj_status_t status;
+    
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL);
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    CPjSocket *pjSock = (CPjSocket*)sock;
+    RSocket &rSock = pjSock->Socket();
+
+    // Only supports AF_INET for now
+    PJ_ASSERT_RETURN(tolen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+    TInetAddr inetAddr;
+    status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)to, tolen, inetAddr);
+    if (status != PJ_SUCCESS)
+    	return status;
+
+    TPtrC8 data((const TUint8*)buf, (TInt)*len);
+    TRequestStatus reqStatus;
+    TSockXfrLength sentLen;
+
+    rSock.SendTo(data, inetAddr, flags, reqStatus, sentLen);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus.Int()==KErrNone) {
+	//For some reason TSockXfrLength is not returning correctly!
+	//*len = (TInt) sentLen.Length();
+	return PJ_SUCCESS;
+    } else 
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recv(pj_sock_t sock,
+				 void *buf,
+				 pj_ssize_t *len,
+				 unsigned flags)
+{
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL);
+    PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL);
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+
+    CPjSocket *pjSock = (CPjSocket*)sock;
+
+    if (pjSock->Reader()) {
+	CPjSocketReader *reader = pjSock->Reader();
+
+	while (reader->IsActive() && !reader->HasData()) {
+	    User::WaitForAnyRequest();
+	}
+
+	if (reader->HasData()) {
+	    TPtr8 data((TUint8*)buf, (TInt)*len);
+	    TInetAddr inetAddr;
+
+	    reader->ReadData(data, &inetAddr);
+
+	    *len = data.Length();
+	    return PJ_SUCCESS;
+	}
+    }
+
+    TRequestStatus reqStatus;
+    TSockXfrLength recvLen;
+    TPtr8 data((TUint8*)buf, (TInt)*len, (TInt)*len);
+
+    if (pjSock->IsDatagram()) {
+	pjSock->Socket().Recv(data, flags, reqStatus);
+    } else {
+	// Using static like this is not pretty, but we don't need to use
+	// the value anyway, hence doing it like this is probably most
+	// optimal.
+	static TSockXfrLength len;
+	pjSock->Socket().RecvOneOrMore(data, flags, reqStatus, len);
+    }
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus == KErrNone) {
+	//*len = (TInt)recvLen.Length();
+	*len = data.Length();
+	return PJ_SUCCESS;
+    } else {
+	*len = -1;
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+    }
+}
+
+/*
+ * Receive data.
+ */
+PJ_DEF(pj_status_t) pj_sock_recvfrom(pj_sock_t sock,
+				     void *buf,
+				     pj_ssize_t *len,
+				     unsigned flags,
+				     pj_sockaddr_t *from,
+				     int *fromlen)
+{
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sock && buf && len && from && fromlen, PJ_EINVAL);
+    PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL);
+    PJ_ASSERT_RETURN(*fromlen >= (int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+
+    CPjSocket *pjSock = (CPjSocket*)sock;
+    RSocket &rSock = pjSock->Socket();
+
+    if (pjSock->Reader()) {
+	CPjSocketReader *reader = pjSock->Reader();
+
+	while (reader->IsActive() && !reader->HasData()) {
+	    User::WaitForAnyRequest();
+	}
+
+	if (reader->HasData()) {
+	    TPtr8 data((TUint8*)buf, (TInt)*len);
+	    TInetAddr inetAddr;
+
+	    reader->ReadData(data, &inetAddr);
+
+	    *len = data.Length();
+
+	    if (from && fromlen) {
+		return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)from, 
+					    fromlen);
+	    } else {
+	    	return PJ_SUCCESS;
+	    }
+	}
+    }
+
+    TInetAddr inetAddr;
+    TRequestStatus reqStatus;
+    TSockXfrLength recvLen;
+    TPtr8 data((TUint8*)buf, (TInt)*len, (TInt)*len);
+
+    rSock.RecvFrom(data, inetAddr, flags, reqStatus, recvLen);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus == KErrNone) {
+	//*len = (TInt)recvLen.Length();
+	*len = data.Length();
+	return PjSymbianOS::Addr2pj(inetAddr, *(pj_sockaddr*)from, fromlen);
+    } else {
+	*len = -1;
+	*fromlen = -1;
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+    }
+}
+
+/*
+ * Get socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_getsockopt( pj_sock_t sock,
+					pj_uint16_t level,
+					pj_uint16_t optname,
+					void *optval,
+					int *optlen)
+{
+    // Not supported for now.
+    PJ_UNUSED_ARG(sock);
+    PJ_UNUSED_ARG(level);
+    PJ_UNUSED_ARG(optname);
+    PJ_UNUSED_ARG(optval);
+    PJ_UNUSED_ARG(optlen);
+    return PJ_EINVALIDOP;
+}
+
+/*
+ * Set socket option.
+ */
+PJ_DEF(pj_status_t) pj_sock_setsockopt( pj_sock_t sock,
+					pj_uint16_t level,
+					pj_uint16_t optname,
+					const void *optval,
+					int optlen)
+{
+    // Not supported for now.
+    PJ_UNUSED_ARG(sock);
+    PJ_UNUSED_ARG(level);
+    PJ_UNUSED_ARG(optname);
+    PJ_UNUSED_ARG(optval);
+    PJ_UNUSED_ARG(optlen);
+    return PJ_EINVALIDOP;
+}
+
+/*
+ * Connect socket.
+ */
+PJ_DEF(pj_status_t) pj_sock_connect( pj_sock_t sock,
+				     const pj_sockaddr_t *addr,
+				     int namelen)
+{
+    pj_status_t status;
+    
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sock && addr && namelen, PJ_EINVAL);
+    PJ_ASSERT_RETURN(((pj_sockaddr*)addr)->addr.sa_family == PJ_AF_INET, 
+		     PJ_EINVAL);
+
+    // Return failure if access point is marked as down by app.
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    CPjSocket *pjSock = (CPjSocket*)sock;
+    RSocket &rSock = pjSock->Socket();
+
+    TInetAddr inetAddr;
+    TRequestStatus reqStatus;
+
+    status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)addr, namelen, inetAddr);
+    if (status != PJ_SUCCESS)
+    	return status;
+
+    rSock.Connect(inetAddr, reqStatus);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus == KErrNone) {
+	pjSock->SetConnected(true);
+	return PJ_SUCCESS;
+    } else {
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+    }
+}
+
+
+/*
+ * Shutdown socket.
+ */
+#if PJ_HAS_TCP
+PJ_DEF(pj_status_t) pj_sock_shutdown( pj_sock_t sock,
+				      int how)
+{
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sock, PJ_EINVAL);
+
+    CPjSocket *pjSock = (CPjSocket*)sock;
+    RSocket &rSock = pjSock->Socket();
+
+    RSocket::TShutdown aHow;
+    if (how == PJ_SD_RECEIVE)
+	aHow = RSocket::EStopInput;
+    else if (how == PJ_SHUT_WR)
+	aHow = RSocket::EStopOutput;
+    else
+	aHow = RSocket::ENormal;
+
+    TRequestStatus reqStatus;
+
+    rSock.Shutdown(aHow, reqStatus);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus == KErrNone) {
+	return PJ_SUCCESS;
+    } else {
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+    }
+}
+
+/*
+ * Start listening to incoming connections.
+ */
+PJ_DEF(pj_status_t) pj_sock_listen( pj_sock_t sock,
+				    int backlog)
+{
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(sock && backlog, PJ_EINVAL);
+
+    CPjSocket *pjSock = (CPjSocket*)sock;
+    RSocket &rSock = pjSock->Socket();
+
+    TInt rc = rSock.Listen((TUint)backlog);
+
+    if (rc == KErrNone) {
+	return PJ_SUCCESS;
+    } else {
+	return PJ_RETURN_OS_ERROR(rc);
+    }
+}
+
+/*
+ * Accept incoming connections
+ */
+PJ_DEF(pj_status_t) pj_sock_accept( pj_sock_t serverfd,
+				    pj_sock_t *newsock,
+				    pj_sockaddr_t *addr,
+				    int *addrlen)
+{
+    PJ_CHECK_STACK();
+
+    PJ_ASSERT_RETURN(serverfd && newsock, PJ_EINVAL);
+
+    CPjSocket *pjSock = (CPjSocket*)serverfd;
+    RSocket &rSock = pjSock->Socket();
+
+    // Create a 'blank' socket
+    RSocket newSock;
+    newSock.Open(PjSymbianOS::Instance()->SocketServ());
+
+    // Call Accept()
+    TRequestStatus reqStatus;
+
+    rSock.Accept(newSock, reqStatus);
+    User::WaitForRequest(reqStatus);
+
+    if (reqStatus != KErrNone) {
+	return PJ_RETURN_OS_ERROR(reqStatus.Int());
+    }
+
+    // Create PJ socket
+    CPjSocket *newPjSock = new CPjSocket(pjSock->GetAf(), pjSock->GetSockType(),
+					 newSock);
+    newPjSock->SetConnected(true);
+
+    *newsock = (pj_sock_t) newPjSock;
+
+    if (addr && addrlen) {
+	return pj_sock_getpeername(*newsock, addr, addrlen);
+    }
+
+    return PJ_SUCCESS;
+}
+#endif	/* PJ_HAS_TCP */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ssl_sock_common.c b/jni/pjproject-android/pjlib/src/pj/ssl_sock_common.c
new file mode 100644
index 0000000..768a640
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ssl_sock_common.c
@@ -0,0 +1,138 @@
+/* $Id: ssl_sock_common.c 3999 2012-03-30 07:10:13Z bennylp $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ssl_sock.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/string.h>
+
+/*
+ * Initialize the SSL socket configuration with the default values.
+ */
+PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
+{
+    pj_bzero(param, sizeof(*param));
+
+    /* Socket config */
+    param->sock_af = PJ_AF_INET;
+    param->sock_type = pj_SOCK_STREAM();
+    param->async_cnt = 1;
+    param->concurrency = -1;
+    param->whole_data = PJ_TRUE;
+    param->send_buffer_size = 8192;
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+    param->read_buffer_size = 1500;
+#endif
+    param->qos_type = PJ_QOS_TYPE_BEST_EFFORT;
+    param->qos_ignore_error = PJ_TRUE;
+
+    /* Security config */
+    param->proto = PJ_SSL_SOCK_PROTO_DEFAULT;
+}
+
+
+PJ_DEF(pj_status_t) pj_ssl_cert_get_verify_status_strings(
+						pj_uint32_t verify_status, 
+						const char *error_strings[],
+						unsigned *count)
+{
+    unsigned i = 0, shift_idx = 0;
+    unsigned unknown = 0;
+    pj_uint32_t errs;
+
+    PJ_ASSERT_RETURN(error_strings && count, PJ_EINVAL);
+
+    if (verify_status == PJ_SSL_CERT_ESUCCESS && *count) {
+	error_strings[0] = "OK";
+	*count = 1;
+	return PJ_SUCCESS;
+    }
+
+    errs = verify_status;
+
+    while (errs && i < *count) {
+	pj_uint32_t err;
+	const char *p = NULL;
+
+	if ((errs & 1) == 0) {
+	    shift_idx++;
+	    errs >>= 1;
+	    continue;
+	}
+
+	err = (1 << shift_idx);
+
+	switch (err) {
+	case PJ_SSL_CERT_EISSUER_NOT_FOUND:
+	    p = "The issuer certificate cannot be found";
+	    break;
+	case PJ_SSL_CERT_EUNTRUSTED:
+	    p = "The certificate is untrusted";
+	    break;
+	case PJ_SSL_CERT_EVALIDITY_PERIOD:
+	    p = "The certificate has expired or not yet valid";
+	    break;
+	case PJ_SSL_CERT_EINVALID_FORMAT:
+	    p = "One or more fields of the certificate cannot be decoded "
+		"due to invalid format";
+	    break;
+	case PJ_SSL_CERT_EISSUER_MISMATCH:
+	    p = "The issuer info in the certificate does not match to the "
+		"(candidate) issuer certificate";
+	    break;
+	case PJ_SSL_CERT_ECRL_FAILURE:
+	    p = "The CRL certificate cannot be found or cannot be read "
+		"properly";
+	    break;
+	case PJ_SSL_CERT_EREVOKED:
+	    p = "The certificate has been revoked";
+	    break;
+	case PJ_SSL_CERT_EINVALID_PURPOSE:
+	    p = "The certificate or CA certificate cannot be used for the "
+		"specified purpose";
+	    break;
+	case PJ_SSL_CERT_ECHAIN_TOO_LONG:
+	    p = "The certificate chain length is too long";
+	    break;
+	case PJ_SSL_CERT_EIDENTITY_NOT_MATCH:
+	    p = "The server identity does not match to any identities "
+		"specified in the certificate";
+	    break;
+	case PJ_SSL_CERT_EUNKNOWN:
+	default:
+	    unknown++;
+	    break;
+	}
+	
+	/* Set error string */
+	if (p)
+	    error_strings[i++] = p;
+
+	/* Next */
+	shift_idx++;
+	errs >>= 1;
+    }
+
+    /* Unknown error */
+    if (unknown && i < *count)
+	error_strings[i++] = "Unknown verification error";
+
+    *count = i;
+
+    return PJ_SUCCESS;
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/ssl_sock_dump.c b/jni/pjproject-android/pjlib/src/pj/ssl_sock_dump.c
new file mode 100644
index 0000000..118992e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ssl_sock_dump.c
@@ -0,0 +1,148 @@
+/* $Id: ssl_sock_dump.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ssl_sock.h>
+#include <pj/errno.h>
+#include <pj/os.h>
+#include <pj/string.h>
+
+
+/* Only build when PJ_HAS_SSL_SOCK is enabled */
+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0
+
+#define THIS_FILE	"ssl_sock_dump.c"
+
+#define CHECK_BUF_LEN()						\
+    if ((len < 0) || (len >= end-p)) {				\
+	*p = '\0';						\
+	return -1;						\
+    }								\
+    p += len;
+
+PJ_DEF(pj_ssize_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci,
+					 const char *indent,
+					 char *buf,
+					 pj_size_t buf_size)
+{
+    const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+    pj_parsed_time pt1;
+    pj_parsed_time pt2;
+    unsigned i;
+    int len = 0;
+    char *p, *end;
+
+    p = buf;
+    end = buf + buf_size;
+
+    pj_time_decode(&ci->validity.start, &pt1);
+    pj_time_decode(&ci->validity.end, &pt2);
+
+    /* Version */
+    len = pj_ansi_snprintf(p, end-p, "%sVersion    : v%d\n", 
+			   indent, ci->version);
+    CHECK_BUF_LEN();
+    
+    /* Serial number */
+    len = pj_ansi_snprintf(p, end-p, "%sSerial     : ", indent);
+    CHECK_BUF_LEN();
+
+    for (i = 0; i < sizeof(ci->serial_no) && !ci->serial_no[i]; ++i);
+    for (; i < sizeof(ci->serial_no); ++i) {
+	len = pj_ansi_snprintf(p, end-p, "%02X ", ci->serial_no[i]);
+	CHECK_BUF_LEN();
+    }
+    *(p-1) = '\n';
+    
+    /* Subject */
+    len = pj_ansi_snprintf( p, end-p, "%sSubject    : %.*s\n", indent,
+			    (int)ci->subject.cn.slen,
+			    ci->subject.cn.ptr);
+    CHECK_BUF_LEN();
+    len = pj_ansi_snprintf( p, end-p, "%s             %.*s\n", indent,
+			    (int)ci->subject.info.slen,
+			    ci->subject.info.ptr);
+    CHECK_BUF_LEN();
+
+    /* Issuer */
+    len = pj_ansi_snprintf( p, end-p, "%sIssuer     : %.*s\n", indent,
+			    (int)ci->issuer.cn.slen,
+			    ci->issuer.cn.ptr);
+    CHECK_BUF_LEN();
+    len = pj_ansi_snprintf( p, end-p, "%s             %.*s\n", indent,
+			    (int)ci->issuer.info.slen,
+			    ci->issuer.info.ptr);
+    CHECK_BUF_LEN();
+
+    /* Validity period */
+    len = pj_ansi_snprintf( p, end-p, "%sValid from : %s %4d-%02d-%02d "
+			    "%02d:%02d:%02d.%03d %s\n", indent,
+			    wdays[pt1.wday], pt1.year, pt1.mon+1, pt1.day,
+			    pt1.hour, pt1.min, pt1.sec, pt1.msec,
+			    (ci->validity.gmt? "GMT":""));
+    CHECK_BUF_LEN();
+
+    len = pj_ansi_snprintf( p, end-p, "%sValid to   : %s %4d-%02d-%02d "
+			    "%02d:%02d:%02d.%03d %s\n", indent,
+			    wdays[pt2.wday], pt2.year, pt2.mon+1, pt2.day,
+			    pt2.hour, pt2.min, pt2.sec, pt2.msec,
+			    (ci->validity.gmt? "GMT":""));
+    CHECK_BUF_LEN();
+
+    /* Subject alternative name extension */
+    if (ci->subj_alt_name.cnt) {
+	unsigned i;
+
+	len = pj_ansi_snprintf(p, end-p, "%ssubjectAltName extension\n", 
+			       indent);
+	CHECK_BUF_LEN();
+
+	for (i = 0; i < ci->subj_alt_name.cnt; ++i) {
+	    const char *type = NULL;
+
+	    switch(ci->subj_alt_name.entry[i].type) {
+	    case PJ_SSL_CERT_NAME_RFC822:
+		type = "MAIL";
+		break;
+	    case PJ_SSL_CERT_NAME_DNS:
+		type = " DNS";
+		break;
+	    case PJ_SSL_CERT_NAME_URI:
+		type = " URI";
+		break;
+	    case PJ_SSL_CERT_NAME_IP:
+		type = "  IP";
+		break;
+	    default:
+		break;
+	    }
+	    if (type) {
+		len = pj_ansi_snprintf( p, end-p, "%s      %s : %.*s\n", indent, 
+					type, 
+					(int)ci->subj_alt_name.entry[i].name.slen,
+					ci->subj_alt_name.entry[i].name.ptr);
+		CHECK_BUF_LEN();
+	    }
+	}
+    }
+
+    return (p-buf);
+}
+
+
+#endif  /* PJ_HAS_SSL_SOCK */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ssl_sock_ossl.c b/jni/pjproject-android/pjlib/src/pj/ssl_sock_ossl.c
new file mode 100644
index 0000000..10ca392
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ssl_sock_ossl.c
@@ -0,0 +1,2577 @@
+/* $Id: ssl_sock_ossl.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ssl_sock.h>
+#include <pj/activesock.h>
+#include <pj/compat/socket.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/list.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/timer.h>
+
+
+/* Only build when PJ_HAS_SSL_SOCK is enabled */
+#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0
+
+#define THIS_FILE		"ssl_sock_ossl.c"
+
+/* Workaround for ticket #985 */
+#define DELAYED_CLOSE_TIMEOUT	200
+
+/* Maximum ciphers */
+#define MAX_CIPHERS		100
+
+/* 
+ * Include OpenSSL headers 
+ */
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+
+
+#ifdef _MSC_VER
+#  pragma comment( lib, "libeay32")
+#  pragma comment( lib, "ssleay32")
+#endif
+
+
+/*
+ * SSL/TLS state enumeration.
+ */
+enum ssl_state {
+    SSL_STATE_NULL,
+    SSL_STATE_HANDSHAKING,
+    SSL_STATE_ESTABLISHED
+};
+
+/*
+ * Internal timer types.
+ */
+enum timer_id
+{
+    TIMER_NONE,
+    TIMER_HANDSHAKE_TIMEOUT,
+    TIMER_CLOSE
+};
+
+/*
+ * Structure of SSL socket read buffer.
+ */
+typedef struct read_data_t
+{
+    void		 *data;
+    pj_size_t		  len;
+} read_data_t;
+
+/*
+ * Get the offset of pointer to read-buffer of SSL socket from read-buffer
+ * of active socket. Note that both SSL socket and active socket employ 
+ * different but correlated read-buffers (as much as async_cnt for each),
+ * and to make it easier/faster to find corresponding SSL socket's read-buffer
+ * from known active socket's read-buffer, the pointer of corresponding 
+ * SSL socket's read-buffer is stored right after the end of active socket's
+ * read-buffer.
+ */
+#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \
+					(read_data_t**) \
+					((pj_int8_t*)(asock_rbuf) + \
+					ssock->param.read_buffer_size)
+
+/*
+ * Structure of SSL socket write data.
+ */
+typedef struct write_data_t {
+    PJ_DECL_LIST_MEMBER(struct write_data_t);
+    pj_ioqueue_op_key_t	 key;
+    pj_size_t 	 	 record_len;
+    pj_ioqueue_op_key_t	*app_key;
+    pj_size_t 	 	 plain_data_len;
+    pj_size_t 	 	 data_len;
+    unsigned		 flags;
+    union {
+	char		 content[1];
+	const char	*ptr;
+    } data;
+} write_data_t;
+
+/*
+ * Structure of SSL socket write buffer (circular buffer).
+ */
+typedef struct send_buf_t {
+    char		*buf;
+    pj_size_t		 max_len;    
+    char		*start;
+    pj_size_t		 len;
+} send_buf_t;
+
+/*
+ * Secure socket structure definition.
+ */
+struct pj_ssl_sock_t
+{
+    pj_pool_t		 *pool;
+    pj_ssl_sock_t	 *parent;
+    pj_ssl_sock_param	  param;
+    pj_ssl_cert_t	 *cert;
+    
+    pj_ssl_cert_info	  local_cert_info;
+    pj_ssl_cert_info	  remote_cert_info;
+
+    pj_bool_t		  is_server;
+    enum ssl_state	  ssl_state;
+    pj_ioqueue_op_key_t	  handshake_op_key;
+    pj_timer_entry	  timer;
+    pj_status_t		  verify_status;
+
+    unsigned long	  last_err;
+
+    pj_sock_t		  sock;
+    pj_activesock_t	 *asock;
+
+    pj_sockaddr		  local_addr;
+    pj_sockaddr		  rem_addr;
+    int			  addr_len;
+    
+    pj_bool_t		  read_started;
+    pj_size_t		  read_size;
+    pj_uint32_t		  read_flags;
+    void		**asock_rbuf;
+    read_data_t		 *ssock_rbuf;
+
+    write_data_t	  write_pending;/* list of pending write to OpenSSL */
+    write_data_t	  write_pending_empty; /* cache for write_pending   */
+    pj_bool_t		  flushing_write_pend; /* flag of flushing is ongoing*/
+    send_buf_t		  send_buf;
+    write_data_t	  send_pending;	/* list of pending write to network */
+    pj_lock_t		 *write_mutex;	/* protect write BIO and send_buf   */
+
+    SSL_CTX		 *ossl_ctx;
+    SSL			 *ossl_ssl;
+    BIO			 *ossl_rbio;
+    BIO			 *ossl_wbio;
+};
+
+
+/*
+ * Certificate/credential structure definition.
+ */
+struct pj_ssl_cert_t
+{
+    pj_str_t CA_file;
+    pj_str_t cert_file;
+    pj_str_t privkey_file;
+    pj_str_t privkey_pass;
+};
+
+
+static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len);
+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata);
+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
+
+/*
+ *******************************************************************
+ * Static/internal functions.
+ *******************************************************************
+ */
+
+/**
+ * Mapping from OpenSSL error codes to pjlib error space.
+ */
+
+#define PJ_SSL_ERRNO_START		(PJ_ERRNO_START_USER + \
+					 PJ_ERRNO_SPACE_SIZE*6)
+
+#define PJ_SSL_ERRNO_SPACE_SIZE		PJ_ERRNO_SPACE_SIZE
+
+/* Expected maximum value of reason component in OpenSSL error code */
+#define MAX_OSSL_ERR_REASON		1200
+
+static pj_status_t STATUS_FROM_SSL_ERR(pj_ssl_sock_t *ssock,
+				       unsigned long err)
+{
+    pj_status_t status;
+
+    /* General SSL error, dig more from OpenSSL error queue */
+    if (err == SSL_ERROR_SSL)
+	err = ERR_get_error();
+
+    /* OpenSSL error range is much wider than PJLIB errno space, so
+     * if it exceeds the space, only the error reason will be kept.
+     * Note that the last native error will be kept as is and can be
+     * retrieved via SSL socket info.
+     */
+    status = ERR_GET_LIB(err)*MAX_OSSL_ERR_REASON + ERR_GET_REASON(err);
+    if (status > PJ_SSL_ERRNO_SPACE_SIZE)
+	status = ERR_GET_REASON(err);
+
+    status += PJ_SSL_ERRNO_START;
+    ssock->last_err = err;
+    return status;
+}
+
+static pj_status_t GET_SSL_STATUS(pj_ssl_sock_t *ssock)
+{
+    return STATUS_FROM_SSL_ERR(ssock, ERR_get_error());
+}
+
+
+/*
+ * Get error string of OpenSSL.
+ */
+static pj_str_t ssl_strerror(pj_status_t status, 
+			     char *buf, pj_size_t bufsize)
+{
+    pj_str_t errstr;
+    unsigned long ssl_err = status;
+
+    if (ssl_err) {
+	unsigned long l, r;
+	ssl_err -= PJ_SSL_ERRNO_START;
+	l = ssl_err / MAX_OSSL_ERR_REASON;
+	r = ssl_err % MAX_OSSL_ERR_REASON;
+	ssl_err = ERR_PACK(l, 0, r);
+    }
+
+#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
+
+    {
+	const char *tmp = NULL;
+	tmp = ERR_reason_error_string(ssl_err);
+	if (tmp) {
+	    pj_ansi_strncpy(buf, tmp, bufsize);
+	    errstr = pj_str(buf);
+	    return errstr;
+	}
+    }
+
+#endif	/* PJ_HAS_ERROR_STRING */
+
+    errstr.ptr = buf;
+    errstr.slen = pj_ansi_snprintf(buf, bufsize, 
+				   "Unknown OpenSSL error %lu",
+				   ssl_err);
+
+    return errstr;
+}
+
+
+/* OpenSSL library initialization counter */
+static int openssl_init_count;
+
+/* OpenSSL available ciphers */
+static unsigned openssl_cipher_num;
+static struct openssl_ciphers_t {
+    pj_ssl_cipher    id;
+    const char	    *name;
+} openssl_ciphers[MAX_CIPHERS];
+
+/* OpenSSL application data index */
+static int sslsock_idx;
+
+
+/* Initialize OpenSSL */
+static pj_status_t init_openssl(void)
+{
+    pj_status_t status;
+
+    if (openssl_init_count)
+	return PJ_SUCCESS;
+
+    openssl_init_count = 1;
+
+    /* Register error subsystem */
+    status = pj_register_strerror(PJ_SSL_ERRNO_START, 
+				  PJ_SSL_ERRNO_SPACE_SIZE, 
+				  &ssl_strerror);
+    pj_assert(status == PJ_SUCCESS);
+
+    /* Init OpenSSL lib */
+    SSL_library_init();
+    SSL_load_error_strings();
+    OpenSSL_add_all_algorithms();
+
+    /* Init available ciphers */
+    if (openssl_cipher_num == 0) {
+	SSL_METHOD *meth = NULL;
+	SSL_CTX *ctx;
+	SSL *ssl;
+	STACK_OF(SSL_CIPHER) *sk_cipher;
+	unsigned i, n;
+
+	meth = (SSL_METHOD*)SSLv23_server_method();
+	if (!meth)
+	    meth = (SSL_METHOD*)TLSv1_server_method();
+	if (!meth)
+	    meth = (SSL_METHOD*)SSLv3_server_method();
+#ifndef OPENSSL_NO_SSL2
+	if (!meth)
+	    meth = (SSL_METHOD*)SSLv2_server_method();
+#endif
+	pj_assert(meth);
+
+	ctx=SSL_CTX_new(meth);
+	SSL_CTX_set_cipher_list(ctx, "ALL");
+
+	ssl = SSL_new(ctx);
+	sk_cipher = SSL_get_ciphers(ssl);
+
+	n = sk_SSL_CIPHER_num(sk_cipher);
+	if (n > PJ_ARRAY_SIZE(openssl_ciphers))
+	    n = PJ_ARRAY_SIZE(openssl_ciphers);
+
+	for (i = 0; i < n; ++i) {
+	    SSL_CIPHER *c;
+	    c = sk_SSL_CIPHER_value(sk_cipher,i);
+	    openssl_ciphers[i].id = (pj_ssl_cipher)
+				    (pj_uint32_t)c->id & 0x00FFFFFF;
+	    openssl_ciphers[i].name = SSL_CIPHER_get_name(c);
+	}
+
+	SSL_free(ssl);
+	SSL_CTX_free(ctx);
+
+	openssl_cipher_num = n;
+    }
+
+    /* Create OpenSSL application data index for SSL socket */
+    sslsock_idx = SSL_get_ex_new_index(0, "SSL socket", NULL, NULL, NULL);
+
+    return PJ_SUCCESS;
+}
+
+
+/* Shutdown OpenSSL */
+static void shutdown_openssl(void)
+{
+    PJ_UNUSED_ARG(openssl_init_count);
+}
+
+
+/* SSL password callback. */
+static int password_cb(char *buf, int num, int rwflag, void *user_data)
+{
+    pj_ssl_cert_t *cert = (pj_ssl_cert_t*) user_data;
+
+    PJ_UNUSED_ARG(rwflag);
+
+    if(num < cert->privkey_pass.slen)
+	return 0;
+    
+    pj_memcpy(buf, cert->privkey_pass.ptr, cert->privkey_pass.slen);
+    return (int)cert->privkey_pass.slen;
+}
+
+
+/* SSL password callback. */
+static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+    pj_ssl_sock_t *ssock;
+    SSL *ossl_ssl;
+    int err;
+
+    /* Get SSL instance */
+    ossl_ssl = X509_STORE_CTX_get_ex_data(x509_ctx, 
+				    SSL_get_ex_data_X509_STORE_CTX_idx());
+    pj_assert(ossl_ssl);
+
+    /* Get SSL socket instance */
+    ssock = SSL_get_ex_data(ossl_ssl, sslsock_idx);
+    pj_assert(ssock);
+
+    /* Store verification status */
+    err = X509_STORE_CTX_get_error(x509_ctx);
+    switch (err) {
+    case X509_V_OK:
+	break;
+
+    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+	ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND;
+	break;
+
+    case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+    case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+    case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+    case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+	ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
+	break;
+
+    case X509_V_ERR_CERT_NOT_YET_VALID:
+    case X509_V_ERR_CERT_HAS_EXPIRED:
+	ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD;
+	break;
+
+    case X509_V_ERR_UNABLE_TO_GET_CRL:
+    case X509_V_ERR_CRL_NOT_YET_VALID:
+    case X509_V_ERR_CRL_HAS_EXPIRED:
+    case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+    case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+    case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+    case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+	ssock->verify_status |= PJ_SSL_CERT_ECRL_FAILURE;
+	break;	
+
+    case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+    case X509_V_ERR_CERT_UNTRUSTED:
+    case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+	ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED;
+	break;	
+
+    case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+    case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+    case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+    case X509_V_ERR_AKID_SKID_MISMATCH:
+    case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+    case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+	ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH;
+	break;
+
+    case X509_V_ERR_CERT_REVOKED:
+	ssock->verify_status |= PJ_SSL_CERT_EREVOKED;
+	break;	
+
+    case X509_V_ERR_INVALID_PURPOSE:
+    case X509_V_ERR_CERT_REJECTED:
+    case X509_V_ERR_INVALID_CA:
+	ssock->verify_status |= PJ_SSL_CERT_EINVALID_PURPOSE;
+	break;
+
+    case X509_V_ERR_CERT_CHAIN_TOO_LONG: /* not really used */
+    case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+	ssock->verify_status |= PJ_SSL_CERT_ECHAIN_TOO_LONG;
+	break;
+
+    /* Unknown errors */
+    case X509_V_ERR_OUT_OF_MEM:
+    default:
+	ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
+	break;
+    }
+
+    /* When verification is not requested just return ok here, however
+     * application can still get the verification status.
+     */
+    if (PJ_FALSE == ssock->param.verify_peer)
+	preverify_ok = 1;
+
+    return preverify_ok;
+}
+
+/* Setting SSL sock cipher list */
+static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock);
+
+
+/* Create and initialize new SSL context and instance */
+static pj_status_t create_ssl(pj_ssl_sock_t *ssock)
+{
+    SSL_METHOD *ssl_method;
+    SSL_CTX *ctx;
+    pj_ssl_cert_t *cert;
+    int mode, rc;
+    pj_status_t status;
+        
+    pj_assert(ssock);
+
+    cert = ssock->cert;
+
+    /* Make sure OpenSSL library has been initialized */
+    init_openssl();
+
+    /* Determine SSL method to use */
+    switch (ssock->param.proto) {
+    case PJ_SSL_SOCK_PROTO_DEFAULT:
+    case PJ_SSL_SOCK_PROTO_TLS1:
+	ssl_method = (SSL_METHOD*)TLSv1_method();
+	break;
+#ifndef OPENSSL_NO_SSL2
+    case PJ_SSL_SOCK_PROTO_SSL2:
+	ssl_method = (SSL_METHOD*)SSLv2_method();
+	break;
+#endif
+    case PJ_SSL_SOCK_PROTO_SSL3:
+	ssl_method = (SSL_METHOD*)SSLv3_method();
+	break;
+    case PJ_SSL_SOCK_PROTO_SSL23:
+	ssl_method = (SSL_METHOD*)SSLv23_method();
+	break;
+    //case PJ_SSL_SOCK_PROTO_DTLS1:
+	//ssl_method = (SSL_METHOD*)DTLSv1_method();
+	//break;
+    default:
+	return PJ_EINVAL;
+    }
+
+    /* Create SSL context */
+    ctx = SSL_CTX_new(ssl_method);
+    if (ctx == NULL) {
+	return GET_SSL_STATUS(ssock);
+    }
+
+    /* Apply credentials */
+    if (cert) {
+	/* Load CA list if one is specified. */
+	if (cert->CA_file.slen) {
+
+	    rc = SSL_CTX_load_verify_locations(ctx, cert->CA_file.ptr, NULL);
+
+	    if (rc != 1) {
+		status = GET_SSL_STATUS(ssock);
+		PJ_LOG(1,(ssock->pool->obj_name, "Error loading CA list file "
+			  "'%s'", cert->CA_file.ptr));
+		SSL_CTX_free(ctx);
+		return status;
+	    }
+	}
+    
+	/* Set password callback */
+	if (cert->privkey_pass.slen) {
+	    SSL_CTX_set_default_passwd_cb(ctx, password_cb);
+	    SSL_CTX_set_default_passwd_cb_userdata(ctx, cert);
+	}
+
+
+	/* Load certificate if one is specified */
+	if (cert->cert_file.slen) {
+
+	    /* Load certificate chain from file into ctx */
+	    rc = SSL_CTX_use_certificate_chain_file(ctx, cert->cert_file.ptr);
+
+	    if(rc != 1) {
+		status = GET_SSL_STATUS(ssock);
+		PJ_LOG(1,(ssock->pool->obj_name, "Error loading certificate "
+			  "chain file '%s'", cert->cert_file.ptr));
+		SSL_CTX_free(ctx);
+		return status;
+	    }
+	}
+
+
+	/* Load private key if one is specified */
+	if (cert->privkey_file.slen) {
+	    /* Adds the first private key found in file to ctx */
+	    rc = SSL_CTX_use_PrivateKey_file(ctx, cert->privkey_file.ptr, 
+					     SSL_FILETYPE_PEM);
+
+	    if(rc != 1) {
+		status = GET_SSL_STATUS(ssock);
+		PJ_LOG(1,(ssock->pool->obj_name, "Error adding private key "
+			  "from '%s'", cert->privkey_file.ptr));
+		SSL_CTX_free(ctx);
+		return status;
+	    }
+	}
+    }
+
+    /* Create SSL instance */
+    ssock->ossl_ctx = ctx;
+    ssock->ossl_ssl = SSL_new(ssock->ossl_ctx);
+    if (ssock->ossl_ssl == NULL) {
+	return GET_SSL_STATUS(ssock);
+    }
+
+    /* Set SSL sock as application data of SSL instance */
+    SSL_set_ex_data(ssock->ossl_ssl, sslsock_idx, ssock);
+
+    /* SSL verification options */
+    mode = SSL_VERIFY_PEER;
+    if (ssock->is_server && ssock->param.require_client_cert)
+	mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+
+    SSL_set_verify(ssock->ossl_ssl, mode, &verify_cb);
+
+    /* Set cipher list */
+    status = set_cipher_list(ssock);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Setup SSL BIOs */
+    ssock->ossl_rbio = BIO_new(BIO_s_mem());
+    ssock->ossl_wbio = BIO_new(BIO_s_mem());
+    BIO_set_close(ssock->ossl_rbio, BIO_CLOSE);
+    BIO_set_close(ssock->ossl_wbio, BIO_CLOSE);
+    SSL_set_bio(ssock->ossl_ssl, ssock->ossl_rbio, ssock->ossl_wbio);
+
+    return PJ_SUCCESS;
+}
+
+
+/* Destroy SSL context and instance */
+static void destroy_ssl(pj_ssl_sock_t *ssock)
+{
+    /* Destroy SSL instance */
+    if (ssock->ossl_ssl) {
+	SSL_shutdown(ssock->ossl_ssl);
+	SSL_free(ssock->ossl_ssl); /* this will also close BIOs */
+	ssock->ossl_ssl = NULL;
+    }
+
+    /* Destroy SSL context */
+    if (ssock->ossl_ctx) {
+	SSL_CTX_free(ssock->ossl_ctx);
+	ssock->ossl_ctx = NULL;
+    }
+
+    /* Potentially shutdown OpenSSL library if this is the last
+     * context exists.
+     */
+    shutdown_openssl();
+}
+
+
+/* Reset SSL socket state */
+static void reset_ssl_sock_state(pj_ssl_sock_t *ssock)
+{
+    ssock->ssl_state = SSL_STATE_NULL;
+
+    destroy_ssl(ssock);
+
+    if (ssock->asock) {
+	pj_activesock_close(ssock->asock);
+	ssock->asock = NULL;
+	ssock->sock = PJ_INVALID_SOCKET;
+    }
+    if (ssock->sock != PJ_INVALID_SOCKET) {
+	pj_sock_close(ssock->sock);
+	ssock->sock = PJ_INVALID_SOCKET;
+    }
+
+    /* Upon error, OpenSSL may leave any error description in the thread 
+     * error queue, which sometime may cause next call to SSL API returning
+     * false error alarm, e.g: in Linux, SSL_CTX_use_certificate_chain_file()
+     * returning false error after a handshake error (in different SSL_CTX!).
+     * For now, just clear thread error queue here.
+     */
+    ERR_clear_error();
+}
+
+
+/* Generate cipher list with user preference order in OpenSSL format */
+static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock)
+{
+    char buf[1024];
+    pj_str_t cipher_list;
+    STACK_OF(SSL_CIPHER) *sk_cipher;
+    unsigned i;
+    int j, ret;
+
+    if (ssock->param.ciphers_num == 0)
+	return PJ_SUCCESS;
+
+    pj_strset(&cipher_list, buf, 0);
+
+    /* Set SSL with ALL available ciphers */
+    SSL_set_cipher_list(ssock->ossl_ssl, "ALL");
+
+    /* Generate user specified cipher list in OpenSSL format */
+    sk_cipher = SSL_get_ciphers(ssock->ossl_ssl);
+    for (i = 0; i < ssock->param.ciphers_num; ++i) {
+	for (j = 0; j < sk_SSL_CIPHER_num(sk_cipher); ++j) {
+	    SSL_CIPHER *c;
+	    c = sk_SSL_CIPHER_value(sk_cipher, j);
+	    if (ssock->param.ciphers[i] == (pj_ssl_cipher)
+					   ((pj_uint32_t)c->id & 0x00FFFFFF))
+	    {
+		const char *c_name;
+
+		c_name = SSL_CIPHER_get_name(c);
+
+		/* Check buffer size */
+		if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > sizeof(buf)) {
+		    pj_assert(!"Insufficient temporary buffer for cipher");
+		    return PJ_ETOOMANY;
+		}
+
+		/* Add colon separator */
+		if (cipher_list.slen)
+		    pj_strcat2(&cipher_list, ":");
+
+		/* Add the cipher */
+		pj_strcat2(&cipher_list, c_name);
+		break;
+	    }
+	}
+    }
+
+    /* Put NULL termination in the generated cipher list */
+    cipher_list.ptr[cipher_list.slen] = '\0';
+
+    /* Finally, set chosen cipher list */
+    ret = SSL_set_cipher_list(ssock->ossl_ssl, buf);
+    if (ret < 1) {
+	return GET_SSL_STATUS(ssock);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* Parse OpenSSL ASN1_TIME to pj_time_val and GMT info */
+static pj_bool_t parse_ossl_asn1_time(pj_time_val *tv, pj_bool_t *gmt,
+				      const ASN1_TIME *tm)
+{
+    unsigned long parts[7] = {0};
+    char *p, *end;
+    unsigned len;
+    pj_bool_t utc;
+    pj_parsed_time pt;
+    int i;
+
+    utc = tm->type == V_ASN1_UTCTIME;
+    p = (char*)tm->data;
+    len = tm->length;
+    end = p + len - 1;
+
+    /* GMT */
+    *gmt = (*end == 'Z');
+
+    /* parse parts */
+    for (i = 0; i < 7 && p < end; ++i) {
+	pj_str_t st;
+
+	if (i==0 && !utc) {
+	    /* 4 digits year part for non-UTC time format */
+	    st.slen = 4;
+	} else if (i==6) {
+	    /* fraction of seconds */
+	    if (*p == '.') ++p;
+	    st.slen = end - p + 1;
+	} else {
+	    /* other parts always 2 digits length */
+	    st.slen = 2;
+	}
+	st.ptr = p;
+
+	parts[i] = pj_strtoul(&st);
+	p += st.slen;
+    }
+
+    /* encode parts to pj_time_val */
+    pt.year = parts[0];
+    if (utc)
+	pt.year += (pt.year < 50)? 2000:1900;
+    pt.mon = parts[1] - 1;
+    pt.day = parts[2];
+    pt.hour = parts[3];
+    pt.min = parts[4];
+    pt.sec = parts[5];
+    pt.msec = parts[6];
+
+    pj_time_encode(&pt, tv);
+
+    return PJ_TRUE;
+}
+
+
+/* Get Common Name field string from a general name string */
+static void get_cn_from_gen_name(const pj_str_t *gen_name, pj_str_t *cn)
+{
+    pj_str_t CN_sign = {"/CN=", 4};
+    char *p, *q;
+
+    pj_bzero(cn, sizeof(cn));
+
+    p = pj_strstr(gen_name, &CN_sign);
+    if (!p)
+	return;
+
+    p += 4; /* shift pointer to value part */
+    pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr));
+    q = pj_strchr(cn, '/');
+    if (q)
+	cn->slen = q - p;
+}
+
+
+/* Get certificate info from OpenSSL X509, in case the certificate info
+ * hal already populated, this function will check if the contents need 
+ * to be updated by inspecting the issuer and the serial number.
+ */
+static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, X509 *x)
+{
+    pj_bool_t update_needed;
+    char buf[512];
+    pj_uint8_t serial_no[64] = {0}; /* should be >= sizeof(ci->serial_no) */
+    pj_uint8_t *p;
+    unsigned len;
+    GENERAL_NAMES *names = NULL;
+
+    pj_assert(pool && ci && x);
+
+    /* Get issuer */
+    X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof(buf));
+
+    /* Get serial no */
+    p = (pj_uint8_t*) M_ASN1_STRING_data(X509_get_serialNumber(x));
+    len = M_ASN1_STRING_length(X509_get_serialNumber(x));
+    if (len > sizeof(ci->serial_no)) 
+	len = sizeof(ci->serial_no);
+    pj_memcpy(serial_no + sizeof(ci->serial_no) - len, p, len);
+
+    /* Check if the contents need to be updated. */
+    update_needed = pj_strcmp2(&ci->issuer.info, buf) || 
+	            pj_memcmp(ci->serial_no, serial_no, sizeof(ci->serial_no));
+    if (!update_needed)
+	return;
+
+    /* Update cert info */
+
+    pj_bzero(ci, sizeof(pj_ssl_cert_info));
+
+    /* Version */
+    ci->version = X509_get_version(x) + 1;
+
+    /* Issuer */
+    pj_strdup2(pool, &ci->issuer.info, buf);
+    get_cn_from_gen_name(&ci->issuer.info, &ci->issuer.cn);
+
+    /* Serial number */
+    pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no));
+
+    /* Subject */
+    pj_strdup2(pool, &ci->subject.info, 
+	       X509_NAME_oneline(X509_get_subject_name(x),
+				 buf, sizeof(buf)));
+    get_cn_from_gen_name(&ci->subject.info, &ci->subject.cn);
+
+    /* Validity */
+    parse_ossl_asn1_time(&ci->validity.start, &ci->validity.gmt,
+			 X509_get_notBefore(x));
+    parse_ossl_asn1_time(&ci->validity.end, &ci->validity.gmt,
+			 X509_get_notAfter(x));
+
+    /* Subject Alternative Name extension */
+    if (ci->version >= 3) {
+	names = (GENERAL_NAMES*) X509_get_ext_d2i(x, NID_subject_alt_name,
+						  NULL, NULL);
+    }
+    if (names) {
+        unsigned i, cnt;
+
+        cnt = sk_GENERAL_NAME_num(names);
+	ci->subj_alt_name.entry = pj_pool_calloc(pool, cnt, 
+					    sizeof(*ci->subj_alt_name.entry));
+
+        for (i = 0; i < cnt; ++i) {
+	    unsigned char *p = 0;
+	    pj_ssl_cert_name_type type = PJ_SSL_CERT_NAME_UNKNOWN;
+            const GENERAL_NAME *name;
+	    
+	    name = sk_GENERAL_NAME_value(names, i);
+
+            switch (name->type) {
+                case GEN_EMAIL:
+                    len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+		    type = PJ_SSL_CERT_NAME_RFC822;
+                    break;
+                case GEN_DNS:
+                    len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+		    type = PJ_SSL_CERT_NAME_DNS;
+                    break;
+                case GEN_URI:
+                    len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+		    type = PJ_SSL_CERT_NAME_URI;
+                    break;
+                case GEN_IPADD:
+		    p = ASN1_STRING_data(name->d.ip);
+		    len = ASN1_STRING_length(name->d.ip);
+		    type = PJ_SSL_CERT_NAME_IP;
+                    break;
+		default:
+		    break;
+            }
+
+	    if (p && len && type != PJ_SSL_CERT_NAME_UNKNOWN) {
+		ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
+		if (type == PJ_SSL_CERT_NAME_IP) {
+		    int af = pj_AF_INET();
+		    if (len == sizeof(pj_in6_addr)) af = pj_AF_INET6();
+		    pj_inet_ntop2(af, p, buf, sizeof(buf));
+		    pj_strdup2(pool, 
+		          &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
+		          buf);
+		} else {
+		    pj_strdup2(pool, 
+			  &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name, 
+			  (char*)p);
+		    OPENSSL_free(p);
+		}
+		ci->subj_alt_name.cnt++;
+	    }
+        }
+    }
+}
+
+
+/* Update local & remote certificates info. This function should be
+ * called after handshake or renegotiation successfully completed.
+ */
+static void update_certs_info(pj_ssl_sock_t *ssock)
+{
+    X509 *x;
+
+    pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED);
+
+    /* Active local certificate */
+    x = SSL_get_certificate(ssock->ossl_ssl);
+    if (x) {
+	get_cert_info(ssock->pool, &ssock->local_cert_info, x);
+	/* Don't free local's X509! */
+    } else {
+	pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info));
+    }
+
+    /* Active remote certificate */
+    x = SSL_get_peer_certificate(ssock->ossl_ssl);
+    if (x) {
+	get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
+	/* Free peer's X509 */
+	X509_free(x);
+    } else {
+	pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
+    }
+}
+
+
+/* When handshake completed:
+ * - notify application
+ * - if handshake failed, reset SSL state
+ * - return PJ_FALSE when SSL socket instance is destroyed by application.
+ */
+static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, 
+				       pj_status_t status)
+{
+    /* Cancel handshake timer */
+    if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) {
+	pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
+	ssock->timer.id = TIMER_NONE;
+    }
+
+    /* Update certificates info on successful handshake */
+    if (status == PJ_SUCCESS)
+	update_certs_info(ssock);
+
+    /* Accepting */
+    if (ssock->is_server) {
+	if (status != PJ_SUCCESS) {
+	    /* Handshake failed in accepting, destroy our self silently. */
+
+	    char errmsg[PJ_ERR_MSG_SIZE];
+	    char buf[PJ_INET6_ADDRSTRLEN+10];
+
+	    pj_strerror(status, errmsg, sizeof(errmsg));
+	    PJ_LOG(3,(ssock->pool->obj_name, "Handshake failed in accepting "
+		      "%s: %s",
+		      pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3),
+		      errmsg));
+
+	    /* Workaround for ticket #985 */
+#if (defined(PJ_WIN32) && PJ_WIN32!=0) || (defined(PJ_WIN64) && PJ_WIN64!=0)
+	    if (ssock->param.timer_heap) {
+		pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT};
+
+		reset_ssl_sock_state(ssock);
+
+		ssock->timer.id = TIMER_CLOSE;
+		pj_time_val_normalize(&interval);
+		if (pj_timer_heap_schedule(ssock->param.timer_heap, 
+					   &ssock->timer, &interval) != 0)
+		{
+		    ssock->timer.id = TIMER_NONE;
+		    pj_ssl_sock_close(ssock);
+		}
+	    } else 
+#endif	/* PJ_WIN32 */
+	    {
+		pj_ssl_sock_close(ssock);
+	    }
+	    return PJ_FALSE;
+	}
+	/* Notify application the newly accepted SSL socket */
+	if (ssock->param.cb.on_accept_complete) {
+	    pj_bool_t ret;
+	    ret = (*ssock->param.cb.on_accept_complete)
+		      (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
+		       pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr));
+	    if (ret == PJ_FALSE)
+		return PJ_FALSE;
+	}
+    }
+
+    /* Connecting */
+    else {
+	/* On failure, reset SSL socket state first, as app may try to 
+	 * reconnect in the callback.
+	 */
+	if (status != PJ_SUCCESS) {
+	    /* Server disconnected us, possibly due to SSL nego failure */
+	    if (status == PJ_EEOF) {
+		unsigned long err;
+		err = ERR_get_error();
+		if (err != SSL_ERROR_NONE)
+		    status = STATUS_FROM_SSL_ERR(ssock, err);
+	    }
+	    reset_ssl_sock_state(ssock);
+	}
+	if (ssock->param.cb.on_connect_complete) {
+	    pj_bool_t ret;
+	    ret = (*ssock->param.cb.on_connect_complete)(ssock, status);
+	    if (ret == PJ_FALSE)
+		return PJ_FALSE;
+	}
+    }
+
+    return PJ_TRUE;
+}
+
+static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len)
+{
+    send_buf_t *send_buf = &ssock->send_buf;
+    pj_size_t avail_len, skipped_len = 0;
+    char *reg1, *reg2;
+    pj_size_t reg1_len, reg2_len;
+    write_data_t *p;
+
+    /* Check buffer availability */
+    avail_len = send_buf->max_len - send_buf->len;
+    if (avail_len < len)
+	return NULL;
+
+    /* If buffer empty, reset start pointer and return it */
+    if (send_buf->len == 0) {
+	send_buf->start = send_buf->buf;
+	send_buf->len   = len;
+	p = (write_data_t*)send_buf->start;
+	goto init_send_data;
+    }
+
+    /* Free space may be wrapped/splitted into two regions, so let's
+     * analyze them if any region can hold the write data.
+     */
+    reg1 = send_buf->start + send_buf->len;
+    if (reg1 >= send_buf->buf + send_buf->max_len)
+	reg1 -= send_buf->max_len;
+    reg1_len = send_buf->max_len - send_buf->len;
+    if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) {
+	reg1_len = send_buf->buf + send_buf->max_len - reg1;
+	reg2 = send_buf->buf;
+	reg2_len = send_buf->start - send_buf->buf;
+    } else {
+	reg2 = NULL;
+	reg2_len = 0;
+    }
+
+    /* More buffer availability check, note that the write data must be in
+     * a contigue buffer.
+     */
+    avail_len = PJ_MAX(reg1_len, reg2_len);
+    if (avail_len < len)
+	return NULL;
+
+    /* Get the data slot */
+    if (reg1_len >= len) {
+	p = (write_data_t*)reg1;
+    } else {
+	p = (write_data_t*)reg2;
+	skipped_len = reg1_len;
+    }
+
+    /* Update buffer length */
+    send_buf->len += len + skipped_len;
+
+init_send_data:
+    /* Init the new send data */
+    pj_bzero(p, sizeof(*p));
+    pj_list_init(p);
+    pj_list_push_back(&ssock->send_pending, p);
+
+    return p;
+}
+
+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata)
+{
+    send_buf_t *buf = &ssock->send_buf;
+    write_data_t *spl = &ssock->send_pending;
+
+    pj_assert(!pj_list_empty(&ssock->send_pending));
+    
+    /* Free slot from the buffer */
+    if (spl->next == wdata && spl->prev == wdata) {
+	/* This is the only data, reset the buffer */
+	buf->start = buf->buf;
+	buf->len = 0;
+    } else if (spl->next == wdata) {
+	/* This is the first data, shift start pointer of the buffer and
+	 * adjust the buffer length.
+	 */
+	buf->start = (char*)wdata->next;
+	if (wdata->next > wdata) {
+	    buf->len -= ((char*)wdata->next - buf->start);
+	} else {
+	    /* Overlapped */
+	    pj_size_t right_len, left_len;
+	    right_len = buf->buf + buf->max_len - (char*)wdata;
+	    left_len  = (char*)wdata->next - buf->buf;
+	    buf->len -= (right_len + left_len);
+	}
+    } else if (spl->prev == wdata) {
+	/* This is the last data, just adjust the buffer length */
+	if (wdata->prev < wdata) {
+	    pj_size_t jump_len;
+	    jump_len = (char*)wdata -
+		       ((char*)wdata->prev + wdata->prev->record_len);
+	    buf->len -= (wdata->record_len + jump_len);
+	} else {
+	    /* Overlapped */
+	    pj_size_t right_len, left_len;
+	    right_len = buf->buf + buf->max_len -
+			((char*)wdata->prev + wdata->prev->record_len);
+	    left_len  = (char*)wdata + wdata->record_len - buf->buf;
+	    buf->len -= (right_len + left_len);
+	}
+    }
+    /* For data in the middle buffer, just do nothing on the buffer. The slot
+     * will be freed later when freeing the first/last data.
+     */
+    
+    /* Remove the data from send pending list */
+    pj_list_erase(wdata);
+}
+
+#if 0
+/* Just for testing send buffer alloc/free */
+#include <pj/rand.h>
+pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool)
+{
+    enum { MAX_CHUNK_NUM = 20 };
+    unsigned chunk_size, chunk_cnt, i;
+    write_data_t *wdata[MAX_CHUNK_NUM] = {0};
+    pj_time_val now;
+    pj_ssl_sock_t *ssock = NULL;
+    pj_ssl_sock_param param;
+    pj_status_t status;
+
+    pj_gettimeofday(&now);
+    pj_srand((unsigned)now.sec);
+
+    pj_ssl_sock_param_default(&param);
+    status = pj_ssl_sock_create(pool, &param, &ssock);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    if (ssock->send_buf.max_len == 0) {
+	ssock->send_buf.buf = (char*)
+			      pj_pool_alloc(ssock->pool, 
+					    ssock->param.send_buffer_size);
+	ssock->send_buf.max_len = ssock->param.send_buffer_size;
+	ssock->send_buf.start = ssock->send_buf.buf;
+	ssock->send_buf.len = 0;
+    }
+
+    chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2;
+    chunk_cnt = 0;
+    for (i = 0; i < MAX_CHUNK_NUM; i++) {
+	wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321);
+	if (wdata[i])
+	    chunk_cnt++;
+	else
+	    break;
+    }
+
+    while (chunk_cnt) {
+	i = pj_rand() % MAX_CHUNK_NUM;
+	if (wdata[i]) {
+	    free_send_data(ssock, wdata[i]);
+	    wdata[i] = NULL;
+	    chunk_cnt--;
+	}
+    }
+
+    if (ssock->send_buf.len != 0)
+	status = PJ_EBUG;
+
+    pj_ssl_sock_close(ssock);
+    return status;
+}
+#endif
+
+
+/* Flush write BIO to network socket. Note that any access to write BIO
+ * MUST be serialized, so mutex protection must cover any call to OpenSSL
+ * API (that possibly generate data for write BIO) along with the call to
+ * this function (flushing all data in write BIO generated by above 
+ * OpenSSL API call).
+ */
+static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock, 
+				   pj_ioqueue_op_key_t *send_key,
+				   pj_size_t orig_len,
+				   unsigned flags)
+{
+    char *data;
+    pj_ssize_t len;
+    write_data_t *wdata;
+    pj_size_t needed_len;
+    pj_status_t status;
+
+    pj_lock_acquire(ssock->write_mutex);
+
+    /* Check if there is data in write BIO, flush it if any */
+    if (!BIO_pending(ssock->ossl_wbio)) {
+	pj_lock_release(ssock->write_mutex);
+	return PJ_SUCCESS;
+    }
+
+    /* Get data and its length */
+    len = BIO_get_mem_data(ssock->ossl_wbio, &data);
+    if (len == 0) {
+	pj_lock_release(ssock->write_mutex);
+	return PJ_SUCCESS;
+    }
+
+    /* Calculate buffer size needed, and align it to 8 */
+    needed_len = len + sizeof(write_data_t);
+    needed_len = ((needed_len + 7) >> 3) << 3;
+
+    /* Allocate buffer for send data */
+    wdata = alloc_send_data(ssock, needed_len);
+    if (wdata == NULL) {
+	pj_lock_release(ssock->write_mutex);
+	return PJ_ENOMEM;
+    }
+
+    /* Copy the data and set its properties into the send data */
+    pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t));
+    wdata->key.user_data = wdata;
+    wdata->app_key = send_key;
+    wdata->record_len = needed_len;
+    wdata->data_len = len;
+    wdata->plain_data_len = orig_len;
+    wdata->flags = flags;
+    pj_memcpy(&wdata->data, data, len);
+
+    /* Reset write BIO */
+    BIO_reset(ssock->ossl_wbio);
+
+    /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */
+    pj_lock_release(ssock->write_mutex);
+
+    /* Send it */
+    if (ssock->param.sock_type == pj_SOCK_STREAM()) {
+	status = pj_activesock_send(ssock->asock, &wdata->key, 
+				    wdata->data.content, &len,
+				    flags);
+    } else {
+	status = pj_activesock_sendto(ssock->asock, &wdata->key, 
+				      wdata->data.content, &len,
+				      flags,
+				      (pj_sockaddr_t*)&ssock->rem_addr,
+				      ssock->addr_len);
+    }
+
+    if (status != PJ_EPENDING) {
+	/* When the sending is not pending, remove the wdata from send
+	 * pending list.
+	 */
+	pj_lock_acquire(ssock->write_mutex);
+	free_send_data(ssock, wdata);
+	pj_lock_release(ssock->write_mutex);
+    }
+
+    return status;
+}
+
+
+static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data;
+    int timer_id = te->id;
+
+    te->id = TIMER_NONE;
+
+    PJ_UNUSED_ARG(th);
+
+    switch (timer_id) {
+    case TIMER_HANDSHAKE_TIMEOUT:
+	PJ_LOG(1,(ssock->pool->obj_name, "SSL timeout after %d.%ds",
+		  ssock->param.timeout.sec, ssock->param.timeout.msec));
+
+	on_handshake_complete(ssock, PJ_ETIMEDOUT);
+	break;
+    case TIMER_CLOSE:
+	pj_ssl_sock_close(ssock);
+	break;
+    default:
+	pj_assert(!"Unknown timer");
+	break;
+    }
+}
+
+
+/* Asynchronouse handshake */
+static pj_status_t do_handshake(pj_ssl_sock_t *ssock)
+{
+    pj_status_t status;
+    int err;
+
+    /* Perform SSL handshake */
+    pj_lock_acquire(ssock->write_mutex);
+    err = SSL_do_handshake(ssock->ossl_ssl);
+    pj_lock_release(ssock->write_mutex);
+
+    /* SSL_do_handshake() may put some pending data into SSL write BIO, 
+     * flush it if any.
+     */
+    status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0);
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	return status;
+    }
+
+    if (err < 0) {
+	err = SSL_get_error(ssock->ossl_ssl, err);
+	if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) 
+	{
+	    /* Handshake fails */
+	    status = STATUS_FROM_SSL_ERR(ssock, err);
+	    return status;
+	}
+    }
+
+    /* Check if handshake has been completed */
+    if (SSL_is_init_finished(ssock->ossl_ssl)) {
+	ssock->ssl_state = SSL_STATE_ESTABLISHED;
+	return PJ_SUCCESS;
+    }
+
+    return PJ_EPENDING;
+}
+
+
+/*
+ *******************************************************************
+ * Active socket callbacks.
+ *******************************************************************
+ */
+
+static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
+				     void *data,
+				     pj_size_t size,
+				     pj_status_t status,
+				     pj_size_t *remainder)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+			   pj_activesock_get_user_data(asock);
+    pj_size_t nwritten;
+
+    /* Socket error or closed */
+    if (data && size > 0) {
+	/* Consume the whole data */
+	nwritten = BIO_write(ssock->ossl_rbio, data, (int)size);
+	if (nwritten < size) {
+	    status = GET_SSL_STATUS(ssock);
+	    goto on_error;
+	}
+    }
+
+    /* Check if SSL handshake hasn't finished yet */
+    if (ssock->ssl_state == SSL_STATE_HANDSHAKING) {
+	pj_bool_t ret = PJ_TRUE;
+
+	if (status == PJ_SUCCESS)
+	    status = do_handshake(ssock);
+
+	/* Not pending is either success or failed */
+	if (status != PJ_EPENDING)
+	    ret = on_handshake_complete(ssock, status);
+
+	return ret;
+    }
+
+    /* See if there is any decrypted data for the application */
+    if (ssock->read_started) {
+	do {
+	    read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data));
+	    void *data_ = (pj_int8_t*)buf->data + buf->len;
+	    int size_ = (int)(ssock->read_size - buf->len);
+
+	    /* SSL_read() may write some data to BIO write when re-negotiation
+	     * is on progress, so let's protect it with write mutex.
+	     */
+	    pj_lock_acquire(ssock->write_mutex);
+	    size_ = SSL_read(ssock->ossl_ssl, data_, size_);
+	    pj_lock_release(ssock->write_mutex);
+
+	    if (size_ > 0 || status != PJ_SUCCESS) {
+		if (ssock->param.cb.on_data_read) {
+		    pj_bool_t ret;
+		    pj_size_t remainder_ = 0;
+
+		    if (size_ > 0)
+			buf->len += size_;
+    		
+		    ret = (*ssock->param.cb.on_data_read)(ssock, buf->data,
+							  buf->len, status,
+							  &remainder_);
+		    if (!ret) {
+			/* We've been destroyed */
+			return PJ_FALSE;
+		    }
+
+		    /* Application may have left some data to be consumed 
+		     * later.
+		     */
+		    buf->len = remainder_;
+		}
+
+		/* Active socket signalled connection closed/error, this has
+		 * been signalled to the application along with any remaining
+		 * buffer. So, let's just reset SSL socket now.
+		 */
+		if (status != PJ_SUCCESS) {
+		    reset_ssl_sock_state(ssock);
+		    return PJ_FALSE;
+		}
+
+	    } else {
+
+		int err = SSL_get_error(ssock->ossl_ssl, (int)size);
+		
+		/* SSL might just return SSL_ERROR_WANT_READ in 
+		 * re-negotiation.
+		 */
+		if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ)
+		{
+		    /* Reset SSL socket state, then return PJ_FALSE */
+		    status = STATUS_FROM_SSL_ERR(ssock, err);
+		    reset_ssl_sock_state(ssock);
+		    goto on_error;
+		}
+
+		status = do_handshake(ssock);
+		if (status == PJ_SUCCESS) {
+		    /* Renegotiation completed */
+
+		    /* Update certificates */
+		    update_certs_info(ssock);
+
+		    // Ticket #1573: Don't hold mutex while calling
+		    //               PJLIB socket send(). 
+		    //pj_lock_acquire(ssock->write_mutex);
+		    status = flush_delayed_send(ssock);
+		    //pj_lock_release(ssock->write_mutex);
+
+		    /* If flushing is ongoing, treat it as success */
+		    if (status == PJ_EBUSY)
+			status = PJ_SUCCESS;
+
+		    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+			PJ_PERROR(1,(ssock->pool->obj_name, status, 
+				     "Failed to flush delayed send"));
+			goto on_error;
+		    }
+		} else if (status != PJ_EPENDING) {
+		    PJ_PERROR(1,(ssock->pool->obj_name, status, 
+			         "Renegotiation failed"));
+		    goto on_error;
+		}
+
+		break;
+	    }
+	} while (1);
+    }
+
+    return PJ_TRUE;
+
+on_error:
+    if (ssock->ssl_state == SSL_STATE_HANDSHAKING)
+	return on_handshake_complete(ssock, status);
+
+    if (ssock->read_started && ssock->param.cb.on_data_read) {
+	pj_bool_t ret;
+	ret = (*ssock->param.cb.on_data_read)(ssock, NULL, 0, status,
+					      remainder);
+	if (!ret) {
+	    /* We've been destroyed */
+	    return PJ_FALSE;
+	}
+    }
+
+    reset_ssl_sock_state(ssock);
+    return PJ_FALSE;
+}
+
+
+static pj_bool_t asock_on_data_sent (pj_activesock_t *asock,
+				     pj_ioqueue_op_key_t *send_key,
+				     pj_ssize_t sent)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+			   pj_activesock_get_user_data(asock);
+
+    PJ_UNUSED_ARG(send_key);
+    PJ_UNUSED_ARG(sent);
+
+    if (ssock->ssl_state == SSL_STATE_HANDSHAKING) {
+	/* Initial handshaking */
+	pj_status_t status;
+	
+	status = do_handshake(ssock);
+	/* Not pending is either success or failed */
+	if (status != PJ_EPENDING)
+	    return on_handshake_complete(ssock, status);
+
+    } else if (send_key != &ssock->handshake_op_key) {
+	/* Some data has been sent, notify application */
+	write_data_t *wdata = (write_data_t*)send_key->user_data;
+	if (ssock->param.cb.on_data_sent) {
+	    pj_bool_t ret;
+	    pj_ssize_t sent_len;
+
+	    sent_len = (sent > 0)? wdata->plain_data_len : sent;
+	    ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key, 
+						  sent_len);
+	    if (!ret) {
+		/* We've been destroyed */
+		return PJ_FALSE;
+	    }
+	}
+
+	/* Update write buffer state */
+	pj_lock_acquire(ssock->write_mutex);
+	free_send_data(ssock, wdata);
+	pj_lock_release(ssock->write_mutex);
+
+    } else {
+	/* SSL re-negotiation is on-progress, just do nothing */
+    }
+
+    return PJ_TRUE;
+}
+
+
+static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
+					   pj_sock_t newsock,
+					   const pj_sockaddr_t *src_addr,
+					   int src_addr_len)
+{
+    pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t*)
+				  pj_activesock_get_user_data(asock);
+    pj_ssl_sock_t *ssock;
+    pj_activesock_cb asock_cb;
+    pj_activesock_cfg asock_cfg;
+    unsigned i;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(src_addr_len);
+
+    /* Create new SSL socket instance */
+    status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param,
+				&ssock);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Update new SSL socket attributes */
+    ssock->sock = newsock;
+    ssock->parent = ssock_parent;
+    ssock->is_server = PJ_TRUE;
+    if (ssock_parent->cert) {
+	status = pj_ssl_sock_set_certificate(ssock, ssock->pool, 
+					     ssock_parent->cert);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+    }
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+				&ssock->param.qos_params, 1, 
+				ssock->pool->obj_name, NULL);
+    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+	goto on_return;
+
+    /* Update local address */
+    ssock->addr_len = src_addr_len;
+    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
+				 &ssock->addr_len);
+    if (status != PJ_SUCCESS) {
+	/* This fails on few envs, e.g: win IOCP, just tolerate this and
+	 * use parent local address instead.
+	 */
+	pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr);
+    }
+
+    /* Set remote address */
+    pj_sockaddr_cp(&ssock->rem_addr, src_addr);
+
+    /* Create SSL context */
+    status = create_ssl(ssock);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Prepare read buffer */
+    ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, 
+					       ssock->param.async_cnt,
+					       sizeof(void*));
+    for (i = 0; i<ssock->param.async_cnt; ++i) {
+	ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
+					    ssock->pool, 
+					    ssock->param.read_buffer_size + 
+					    sizeof(read_data_t*));
+    }
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    asock_cfg.async_cnt = ssock->param.async_cnt;
+    asock_cfg.concurrency = ssock->param.concurrency;
+    asock_cfg.whole_data = PJ_TRUE;
+
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    asock_cb.on_data_read = asock_on_data_read;
+    asock_cb.on_data_sent = asock_on_data_sent;
+
+    status = pj_activesock_create(ssock->pool,
+				  ssock->sock, 
+				  ssock->param.sock_type,
+				  &asock_cfg,
+				  ssock->param.ioqueue, 
+				  &asock_cb,
+				  ssock,
+				  &ssock->asock);
+
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Start read */
+    status = pj_activesock_start_read2(ssock->asock, ssock->pool, 
+				       (unsigned)ssock->param.read_buffer_size,
+				       ssock->asock_rbuf,
+				       PJ_IOQUEUE_ALWAYS_ASYNC);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Prepare write/send state */
+    pj_assert(ssock->send_buf.max_len == 0);
+    ssock->send_buf.buf = (char*)
+			  pj_pool_alloc(ssock->pool, 
+					ssock->param.send_buffer_size);
+    ssock->send_buf.max_len = ssock->param.send_buffer_size;
+    ssock->send_buf.start = ssock->send_buf.buf;
+    ssock->send_buf.len = 0;
+
+    /* Start handshake timer */
+    if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 ||
+	ssock->param.timeout.msec != 0))
+    {
+	pj_assert(ssock->timer.id == TIMER_NONE);
+	ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
+	status = pj_timer_heap_schedule(ssock->param.timer_heap, 
+				        &ssock->timer,
+					&ssock->param.timeout);
+	if (status != PJ_SUCCESS)
+	    ssock->timer.id = TIMER_NONE;
+    }
+
+    /* Start SSL handshake */
+    ssock->ssl_state = SSL_STATE_HANDSHAKING;
+    SSL_set_accept_state(ssock->ossl_ssl);
+    status = do_handshake(ssock);
+
+on_return:
+    if (ssock && status != PJ_EPENDING)
+	on_handshake_complete(ssock, status);
+
+    /* Must return PJ_TRUE whatever happened, as active socket must 
+     * continue listening.
+     */
+    return PJ_TRUE;
+}
+
+
+static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
+					    pj_status_t status)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+			   pj_activesock_get_user_data(asock);
+    unsigned i;
+
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Update local address */
+    ssock->addr_len = sizeof(pj_sockaddr);
+    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
+				 &ssock->addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Create SSL context */
+    status = create_ssl(ssock);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Prepare read buffer */
+    ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, 
+					       ssock->param.async_cnt,
+					       sizeof(void*));
+    for (i = 0; i<ssock->param.async_cnt; ++i) {
+	ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
+					    ssock->pool, 
+					    ssock->param.read_buffer_size + 
+					    sizeof(read_data_t*));
+    }
+
+    /* Start read */
+    status = pj_activesock_start_read2(ssock->asock, ssock->pool, 
+				       (unsigned)ssock->param.read_buffer_size,
+				       ssock->asock_rbuf,
+				       PJ_IOQUEUE_ALWAYS_ASYNC);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Prepare write/send state */
+    pj_assert(ssock->send_buf.max_len == 0);
+    ssock->send_buf.buf = (char*)
+			     pj_pool_alloc(ssock->pool, 
+					   ssock->param.send_buffer_size);
+    ssock->send_buf.max_len = ssock->param.send_buffer_size;
+    ssock->send_buf.start = ssock->send_buf.buf;
+    ssock->send_buf.len = 0;
+
+#ifdef SSL_set_tlsext_host_name
+    /* Set server name to connect */
+    if (ssock->param.server_name.slen) {
+	/* Server name is null terminated already */
+	if (!SSL_set_tlsext_host_name(ssock->ossl_ssl, 
+				      ssock->param.server_name.ptr))
+	{
+	    char err_str[PJ_ERR_MSG_SIZE];
+
+	    ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str));
+	    PJ_LOG(3,(ssock->pool->obj_name, "SSL_set_tlsext_host_name() "
+		"failed: %s", err_str));
+	}
+    }
+#endif
+
+    /* Start SSL handshake */
+    ssock->ssl_state = SSL_STATE_HANDSHAKING;
+    SSL_set_connect_state(ssock->ossl_ssl);
+
+    status = do_handshake(ssock);
+    if (status != PJ_EPENDING)
+	goto on_return;
+
+    return PJ_TRUE;
+
+on_return:
+    return on_handshake_complete(ssock, status);
+}
+
+
+
+/*
+ *******************************************************************
+ * API
+ *******************************************************************
+ */
+
+/* Load credentials from files. */
+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files (pj_pool_t *pool,
+						 const pj_str_t *CA_file,
+						 const pj_str_t *cert_file,
+						 const pj_str_t *privkey_file,
+						 const pj_str_t *privkey_pass,
+						 pj_ssl_cert_t **p_cert)
+{
+    pj_ssl_cert_t *cert;
+
+    PJ_ASSERT_RETURN(pool && CA_file && cert_file && privkey_file, PJ_EINVAL);
+
+    cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+    pj_strdup_with_null(pool, &cert->CA_file, CA_file);
+    pj_strdup_with_null(pool, &cert->cert_file, cert_file);
+    pj_strdup_with_null(pool, &cert->privkey_file, privkey_file);
+    pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
+
+    *p_cert = cert;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Set SSL socket credentials. */
+PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate(
+					    pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    const pj_ssl_cert_t *cert)
+{
+    pj_ssl_cert_t *cert_;
+
+    PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL);
+
+    cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+    pj_memcpy(cert_, cert, sizeof(cert));
+    pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file);
+    pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file);
+    pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file);
+    pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass);
+
+    ssock->cert = cert_;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Get available ciphers. */
+PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
+					         unsigned *cipher_num)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
+
+    if (openssl_cipher_num == 0) {
+	init_openssl();
+	shutdown_openssl();
+    }
+
+    if (openssl_cipher_num == 0) {
+	*cipher_num = 0;
+	return PJ_ENOTFOUND;
+    }
+
+    *cipher_num = PJ_MIN(*cipher_num, openssl_cipher_num);
+
+    for (i = 0; i < *cipher_num; ++i)
+	ciphers[i] = openssl_ciphers[i].id;
+
+    return PJ_SUCCESS;
+}
+
+
+/* Get cipher name string */
+PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher)
+{
+    unsigned i;
+
+    if (openssl_cipher_num == 0) {
+	init_openssl();
+	shutdown_openssl();
+    }
+
+    for (i = 0; i < openssl_cipher_num; ++i) {
+	if (cipher == openssl_ciphers[i].id)
+	    return openssl_ciphers[i].name;
+    }
+
+    return NULL;
+}
+
+/* Check if the specified cipher is supported by SSL/TLS backend. */
+PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher)
+{
+    unsigned i;
+
+    if (openssl_cipher_num == 0) {
+	init_openssl();
+	shutdown_openssl();
+    }
+
+    for (i = 0; i < openssl_cipher_num; ++i) {
+	if (cipher == openssl_ciphers[i].id)
+	    return PJ_TRUE;
+    }
+
+    return PJ_FALSE;
+}
+
+
+/*
+ * Create SSL socket instance. 
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
+					const pj_ssl_sock_param *param,
+					pj_ssl_sock_t **p_ssock)
+{
+    pj_ssl_sock_t *ssock;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
+    PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP);
+
+    pool = pj_pool_create(pool->factory, "ssl%p", 512, 512, NULL);
+
+    /* Create secure socket */
+    ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
+    ssock->pool = pool;
+    ssock->sock = PJ_INVALID_SOCKET;
+    ssock->ssl_state = SSL_STATE_NULL;
+    pj_list_init(&ssock->write_pending);
+    pj_list_init(&ssock->write_pending_empty);
+    pj_list_init(&ssock->send_pending);
+    pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer);
+    pj_ioqueue_op_key_init(&ssock->handshake_op_key,
+			   sizeof(pj_ioqueue_op_key_t));
+
+    /* Create secure socket mutex */
+    status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
+					    &ssock->write_mutex);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Init secure socket param */
+    ssock->param = *param;
+    ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3;
+    if (param->ciphers_num > 0) {
+	unsigned i;
+	ssock->param.ciphers = (pj_ssl_cipher*)
+			       pj_pool_calloc(pool, param->ciphers_num, 
+					      sizeof(pj_ssl_cipher));
+	for (i = 0; i < param->ciphers_num; ++i)
+	    ssock->param.ciphers[i] = param->ciphers[i];
+    }
+
+    /* Server name must be null-terminated */
+    pj_strdup_with_null(pool, &ssock->param.server_name, 
+			&param->server_name);
+
+    /* Finally */
+    *p_ssock = ssock;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Close the secure socket. This will unregister the socket from the
+ * ioqueue and ultimately close the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
+{
+    pj_pool_t *pool;
+
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+    if (!ssock->pool)
+	return PJ_SUCCESS;
+
+    if (ssock->timer.id != TIMER_NONE) {
+	pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
+	ssock->timer.id = TIMER_NONE;
+    }
+
+    reset_ssl_sock_state(ssock);
+    pj_lock_destroy(ssock->write_mutex);
+    
+    pool = ssock->pool;
+    ssock->pool = NULL;
+    if (pool)
+	pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Associate arbitrary data with the secure socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
+					      void *user_data)
+{
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+    ssock->param.user_data = user_data;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Retrieve the user data previously associated with this secure
+ * socket.
+ */
+PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
+{
+    PJ_ASSERT_RETURN(ssock, NULL);
+
+    return ssock->param.user_data;
+}
+
+
+/*
+ * Retrieve the local address and port used by specified SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
+					  pj_ssl_sock_info *info)
+{
+    pj_bzero(info, sizeof(*info));
+
+    /* Established flag */
+    info->established = (ssock->ssl_state == SSL_STATE_ESTABLISHED);
+
+    /* Protocol */
+    info->proto = ssock->param.proto;
+
+    /* Local address */
+    pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
+    
+    if (info->established) {
+	const SSL_CIPHER *cipher;
+
+	/* Current cipher */
+	cipher = SSL_get_current_cipher(ssock->ossl_ssl);
+	info->cipher = (cipher->id & 0x00FFFFFF);
+
+	/* Remote address */
+	pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
+
+	/* Certificates info */
+	info->local_cert_info = &ssock->local_cert_info;
+	info->remote_cert_info = &ssock->remote_cert_info;
+
+	/* Verification status */
+	info->verify_status = ssock->verify_status;
+    }
+
+    /* Last known OpenSSL error code */
+    info->last_native_err = ssock->last_err;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Starts read operation on this secure socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    unsigned buff_size,
+					    pj_uint32_t flags)
+{
+    void **readbuf;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+
+    readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt, 
+				      sizeof(void*));
+
+    for (i=0; i<ssock->param.async_cnt; ++i) {
+	readbuf[i] = pj_pool_alloc(pool, buff_size);
+    }
+
+    return pj_ssl_sock_start_read2(ssock, pool, buff_size, 
+				   readbuf, flags);
+}
+
+
+/*
+ * Same as #pj_ssl_sock_start_read(), except that the application
+ * supplies the buffers for the read operation so that the acive socket
+ * does not have to allocate the buffers.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
+					     pj_pool_t *pool,
+					     unsigned buff_size,
+					     void *readbuf[],
+					     pj_uint32_t flags)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+
+    /* Create SSL socket read buffer */
+    ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool, 
+					       ssock->param.async_cnt,
+					       sizeof(read_data_t));
+
+    /* Store SSL socket read buffer pointer in the activesock read buffer */
+    for (i=0; i<ssock->param.async_cnt; ++i) {
+	read_data_t **p_ssock_rbuf = 
+			OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]);
+
+	ssock->ssock_rbuf[i].data = readbuf[i];
+	ssock->ssock_rbuf[i].len = 0;
+
+	*p_ssock_rbuf = &ssock->ssock_rbuf[i];
+    }
+
+    ssock->read_size = buff_size;
+    ssock->read_started = PJ_TRUE;
+    ssock->read_flags = flags;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Same as pj_ssl_sock_start_read(), except that this function is used
+ * only for datagram sockets, and it will trigger \a on_data_recvfrom()
+ * callback instead.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
+						pj_pool_t *pool,
+						unsigned buff_size,
+						pj_uint32_t flags)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(buff_size);
+    PJ_UNUSED_ARG(flags);
+
+    return PJ_ENOTSUP;
+}
+
+
+/*
+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() 
+ * operation takes the buffer from the argument rather than creating
+ * new ones.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
+						 pj_pool_t *pool,
+						 unsigned buff_size,
+						 void *readbuf[],
+						 pj_uint32_t flags)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(buff_size);
+    PJ_UNUSED_ARG(readbuf);
+    PJ_UNUSED_ARG(flags);
+
+    return PJ_ENOTSUP;
+}
+
+/* Write plain data to SSL and flush write BIO. */
+static pj_status_t ssl_write(pj_ssl_sock_t *ssock, 
+			     pj_ioqueue_op_key_t *send_key,
+			     const void *data,
+			     pj_ssize_t size,
+			     unsigned flags)
+{
+    pj_status_t status;
+    int nwritten;
+
+    /* Write the plain data to SSL, after SSL encrypts it, write BIO will
+     * contain the secured data to be sent via socket. Note that re-
+     * negotitation may be on progress, so sending data should be delayed
+     * until re-negotiation is completed.
+     */
+    pj_lock_acquire(ssock->write_mutex);
+    nwritten = SSL_write(ssock->ossl_ssl, data, (int)size);
+    pj_lock_release(ssock->write_mutex);
+    
+    if (nwritten == size) {
+	/* All data written, flush write BIO to network socket */
+	status = flush_write_bio(ssock, send_key, size, flags);
+    } else if (nwritten <= 0) {
+	/* SSL failed to process the data, it may just that re-negotiation
+	 * is on progress.
+	 */
+	int err;
+	err = SSL_get_error(ssock->ossl_ssl, nwritten);
+	if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) {
+	    /* Re-negotiation is on progress, flush re-negotiation data */
+	    status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0);
+	    if (status == PJ_SUCCESS || status == PJ_EPENDING)
+		/* Just return PJ_EBUSY when re-negotiation is on progress */
+		status = PJ_EBUSY;
+	} else {
+	    /* Some problem occured */
+	    status = STATUS_FROM_SSL_ERR(ssock, err);
+	}
+    } else {
+	/* nwritten < *size, shouldn't happen, unless write BIO cannot hold 
+	 * the whole secured data, perhaps because of insufficient memory.
+	 */
+	status = PJ_ENOMEM;
+    }
+
+    return status;
+}
+
+/* Flush delayed data sending in the write pending list. */
+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock)
+{
+    /* Check for another ongoing flush */
+    if (ssock->flushing_write_pend)
+	return PJ_EBUSY;
+
+    pj_lock_acquire(ssock->write_mutex);
+
+    /* Again, check for another ongoing flush */
+    if (ssock->flushing_write_pend) {
+	pj_lock_release(ssock->write_mutex);
+	return PJ_EBUSY;
+    }
+
+    /* Set ongoing flush flag */
+    ssock->flushing_write_pend = PJ_TRUE;
+
+    while (!pj_list_empty(&ssock->write_pending)) {
+        write_data_t *wp;
+	pj_status_t status;
+
+	wp = ssock->write_pending.next;
+
+	/* Ticket #1573: Don't hold mutex while calling socket send. */
+	pj_lock_release(ssock->write_mutex);
+
+	status = ssl_write(ssock, &wp->key, wp->data.ptr, 
+			   wp->plain_data_len, wp->flags);
+	if (status != PJ_SUCCESS) {
+	    /* Reset ongoing flush flag first. */
+	    ssock->flushing_write_pend = PJ_FALSE;
+	    return status;
+	}
+
+	pj_lock_acquire(ssock->write_mutex);
+	pj_list_erase(wp);
+	pj_list_push_back(&ssock->write_pending_empty, wp);
+    }
+
+    /* Reset ongoing flush flag */
+    ssock->flushing_write_pend = PJ_FALSE;
+
+    pj_lock_release(ssock->write_mutex);
+
+    return PJ_SUCCESS;
+}
+
+/* Sending is delayed, push back the sending data into pending list. */
+static pj_status_t delay_send (pj_ssl_sock_t *ssock,
+			       pj_ioqueue_op_key_t *send_key,
+			       const void *data,
+			       pj_ssize_t size,
+			       unsigned flags)
+{
+    write_data_t *wp;
+
+    pj_lock_acquire(ssock->write_mutex);
+
+    /* Init write pending instance */
+    if (!pj_list_empty(&ssock->write_pending_empty)) {
+	wp = ssock->write_pending_empty.next;
+	pj_list_erase(wp);
+    } else {
+	wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t);
+    }
+
+    wp->app_key = send_key;
+    wp->plain_data_len = size;
+    wp->data.ptr = data;
+    wp->flags = flags;
+
+    pj_list_push_back(&ssock->write_pending, wp);
+    
+    pj_lock_release(ssock->write_mutex);
+
+    /* Must return PJ_EPENDING */
+    return PJ_EPENDING;
+}
+
+/**
+ * Send data using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
+				      pj_ioqueue_op_key_t *send_key,
+				      const void *data,
+				      pj_ssize_t *size,
+				      unsigned flags)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+
+    // Ticket #1573: Don't hold mutex while calling PJLIB socket send().
+    //pj_lock_acquire(ssock->write_mutex);
+
+    /* Flush delayed send first. Sending data might be delayed when 
+     * re-negotiation is on-progress.
+     */
+    status = flush_delayed_send(ssock);
+    if (status == PJ_EBUSY) {
+	/* Re-negotiation or flushing is on progress, delay sending */
+	status = delay_send(ssock, send_key, data, *size, flags);
+	goto on_return;
+    } else if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Write data to SSL */
+    status = ssl_write(ssock, send_key, data, *size, flags);
+    if (status == PJ_EBUSY) {
+	/* Re-negotiation is on progress, delay sending */
+	status = delay_send(ssock, send_key, data, *size, flags);
+    }
+
+on_return:
+    //pj_lock_release(ssock->write_mutex);
+    return status;
+}
+
+
+/**
+ * Send datagram using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
+					pj_ioqueue_op_key_t *send_key,
+					const void *data,
+					pj_ssize_t *size,
+					unsigned flags,
+					const pj_sockaddr_t *addr,
+					int addr_len)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(send_key);
+    PJ_UNUSED_ARG(data);
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(flags);
+    PJ_UNUSED_ARG(addr);
+    PJ_UNUSED_ARG(addr_len);
+
+    return PJ_ENOTSUP;
+}
+
+
+/**
+ * Starts asynchronous socket accept() operations on this secure socket. 
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
+					      pj_pool_t *pool,
+					      const pj_sockaddr_t *localaddr,
+					      int addr_len)
+{
+    pj_activesock_cb asock_cb;
+    pj_activesock_cfg asock_cfg;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
+
+    /* Create socket */
+    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, 
+			    &ssock->sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Apply SO_REUSEADDR */
+    if (ssock->param.reuse_addr) {
+	int enabled = 1;
+	status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(),
+				    pj_SO_REUSEADDR(),
+				    &enabled, sizeof(enabled));
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(4,(ssock->pool->obj_name, status,
+		         "Warning: error applying SO_REUSEADDR"));
+	}
+    }
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+				&ssock->param.qos_params, 2, 
+				ssock->pool->obj_name, NULL);
+    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+	goto on_error;
+
+    /* Bind socket */
+    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Start listening to the address */
+    status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    asock_cfg.async_cnt = ssock->param.async_cnt;
+    asock_cfg.concurrency = ssock->param.concurrency;
+    asock_cfg.whole_data = PJ_TRUE;
+
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    asock_cb.on_accept_complete = asock_on_accept_complete;
+
+    status = pj_activesock_create(pool,
+				  ssock->sock, 
+				  ssock->param.sock_type,
+				  &asock_cfg,
+				  ssock->param.ioqueue, 
+				  &asock_cb,
+				  ssock,
+				  &ssock->asock);
+
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Start accepting */
+    status = pj_activesock_start_accept(ssock->asock, pool);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Update local address */
+    ssock->addr_len = addr_len;
+    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
+				 &ssock->addr_len);
+    if (status != PJ_SUCCESS)
+	pj_sockaddr_cp(&ssock->local_addr, localaddr);
+
+    ssock->is_server = PJ_TRUE;
+
+    return PJ_SUCCESS;
+
+on_error:
+    reset_ssl_sock_state(ssock);
+    return status;
+}
+
+
+/**
+ * Starts asynchronous socket connect() operation.
+ */
+PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock,
+					       pj_pool_t *pool,
+					       const pj_sockaddr_t *localaddr,
+					       const pj_sockaddr_t *remaddr,
+					       int addr_len)
+{
+    pj_activesock_cb asock_cb;
+    pj_activesock_cfg asock_cfg;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
+		     PJ_EINVAL);
+
+    /* Create socket */
+    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, 
+			    &ssock->sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+				&ssock->param.qos_params, 2, 
+				ssock->pool->obj_name, NULL);
+    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+	goto on_error;
+
+    /* Bind socket */
+    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    asock_cfg.async_cnt = ssock->param.async_cnt;
+    asock_cfg.concurrency = ssock->param.concurrency;
+    asock_cfg.whole_data = PJ_TRUE;
+
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    asock_cb.on_connect_complete = asock_on_connect_complete;
+    asock_cb.on_data_read = asock_on_data_read;
+    asock_cb.on_data_sent = asock_on_data_sent;
+
+    status = pj_activesock_create(pool,
+				  ssock->sock, 
+				  ssock->param.sock_type,
+				  &asock_cfg,
+				  ssock->param.ioqueue, 
+				  &asock_cb,
+				  ssock,
+				  &ssock->asock);
+
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Save remote address */
+    pj_sockaddr_cp(&ssock->rem_addr, remaddr);
+
+    /* Start timer */
+    if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 ||
+	ssock->param.timeout.msec != 0))
+    {
+	pj_assert(ssock->timer.id == TIMER_NONE);
+	ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
+	status = pj_timer_heap_schedule(ssock->param.timer_heap,
+					&ssock->timer,
+				        &ssock->param.timeout);
+	if (status != PJ_SUCCESS)
+	    ssock->timer.id = TIMER_NONE;
+    }
+
+    status = pj_activesock_start_connect(ssock->asock, pool, remaddr,
+					 addr_len);
+
+    if (status == PJ_SUCCESS)
+	asock_on_connect_complete(ssock->asock, PJ_SUCCESS);
+    else if (status != PJ_EPENDING)
+	goto on_error;
+
+    /* Update local address */
+    ssock->addr_len = addr_len;
+    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+				 &ssock->addr_len);
+    /* Note that we may not get an IP address here. This can
+     * happen for example on Windows, where getsockname()
+     * would return 0.0.0.0 if socket has just started the
+     * async connect. In this case, just leave the local
+     * address with 0.0.0.0 for now; it will be updated
+     * once the socket is established.
+     */
+
+    /* Update SSL state */
+    ssock->is_server = PJ_FALSE;
+
+    return PJ_EPENDING;
+
+on_error:
+    reset_ssl_sock_state(ssock);
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock)
+{
+    int ret;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(ssock->ssl_state == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+
+    if (SSL_renegotiate_pending(ssock->ossl_ssl))
+	return PJ_EPENDING;
+
+    ret = SSL_renegotiate(ssock->ossl_ssl);
+    if (ret <= 0) {
+	status = GET_SSL_STATUS(ssock);
+    } else {
+	status = do_handshake(ssock);
+    }
+
+    return status;
+}
+
+#endif  /* PJ_HAS_SSL_SOCK */
+
diff --git a/jni/pjproject-android/pjlib/src/pj/ssl_sock_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/ssl_sock_symbian.cpp
new file mode 100644
index 0000000..6b9b311
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/ssl_sock_symbian.cpp
@@ -0,0 +1,1426 @@
+/* $Id: ssl_sock_symbian.cpp 3999 2012-03-30 07:10:13Z bennylp $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ssl_sock.h>
+#include <pj/compat/socket.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/sock.h>
+#include <pj/string.h>
+
+#include "os_symbian.h"
+#include <securesocket.h>
+#include <x509cert.h>
+#include <e32des8.h>
+
+#define THIS_FILE "ssl_sock_symbian.cpp"
+
+
+/* Cipher name structure */
+typedef struct cipher_name_t {
+    pj_ssl_cipher    cipher;
+    const char	    *name;
+} cipher_name_t;
+
+/* Cipher name constants */
+static cipher_name_t cipher_names[] =
+{
+    {PJ_TLS_NULL_WITH_NULL_NULL,               "NULL"},
+
+    /* TLS/SSLv3 */
+    {PJ_TLS_RSA_WITH_NULL_MD5,                 "TLS_RSA_WITH_NULL_MD5"},
+    {PJ_TLS_RSA_WITH_NULL_SHA,                 "TLS_RSA_WITH_NULL_SHA"},
+    {PJ_TLS_RSA_WITH_NULL_SHA256,              "TLS_RSA_WITH_NULL_SHA256"},
+    {PJ_TLS_RSA_WITH_RC4_128_MD5,              "TLS_RSA_WITH_RC4_128_MD5"},
+    {PJ_TLS_RSA_WITH_RC4_128_SHA,              "TLS_RSA_WITH_RC4_128_SHA"},
+    {PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA,         "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {PJ_TLS_RSA_WITH_AES_128_CBC_SHA,          "TLS_RSA_WITH_AES_128_CBC_SHA"},
+    {PJ_TLS_RSA_WITH_AES_256_CBC_SHA,          "TLS_RSA_WITH_AES_256_CBC_SHA"},
+    {PJ_TLS_RSA_WITH_AES_128_CBC_SHA256,       "TLS_RSA_WITH_AES_128_CBC_SHA256"},
+    {PJ_TLS_RSA_WITH_AES_256_CBC_SHA256,       "TLS_RSA_WITH_AES_256_CBC_SHA256"},
+    {PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,      "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"},
+    {PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,      "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,     "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"},
+    {PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,     "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA,       "TLS_DH_DSS_WITH_AES_128_CBC_SHA"},
+    {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA,       "TLS_DH_RSA_WITH_AES_128_CBC_SHA"},
+    {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA,      "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"},
+    {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,      "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"},
+    {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA,       "TLS_DH_DSS_WITH_AES_256_CBC_SHA"},
+    {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA,       "TLS_DH_RSA_WITH_AES_256_CBC_SHA"},
+    {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA,      "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"},
+    {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,      "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"},
+    {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256,    "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"},
+    {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256,    "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"},
+    {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,   "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"},
+    {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,   "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"},
+    {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256,    "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"},
+    {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256,    "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"},
+    {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,   "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"},
+    {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,   "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"},
+    {PJ_TLS_DH_anon_WITH_RC4_128_MD5,          "TLS_DH_anon_WITH_RC4_128_MD5"},
+    {PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,     "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"},
+    {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA,      "TLS_DH_anon_WITH_AES_128_CBC_SHA"},
+    {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA,      "TLS_DH_anon_WITH_AES_256_CBC_SHA"},
+    {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256,   "TLS_DH_anon_WITH_AES_128_CBC_SHA256"},
+    {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256,   "TLS_DH_anon_WITH_AES_256_CBC_SHA256"},
+
+    /* TLS (deprecated) */
+    {PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5,        "TLS_RSA_EXPORT_WITH_RC4_40_MD5"},
+    {PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,    "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"},
+    {PJ_TLS_RSA_WITH_IDEA_CBC_SHA,             "TLS_RSA_WITH_IDEA_CBC_SHA"},
+    {PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,     "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+    {PJ_TLS_RSA_WITH_DES_CBC_SHA,              "TLS_RSA_WITH_DES_CBC_SHA"},
+    {PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,  "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"},
+    {PJ_TLS_DH_DSS_WITH_DES_CBC_SHA,           "TLS_DH_DSS_WITH_DES_CBC_SHA"},
+    {PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,  "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+    {PJ_TLS_DH_RSA_WITH_DES_CBC_SHA,           "TLS_DH_RSA_WITH_DES_CBC_SHA"},
+    {PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"},
+    {PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA,          "TLS_DHE_DSS_WITH_DES_CBC_SHA"},
+    {PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+    {PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA,          "TLS_DHE_RSA_WITH_DES_CBC_SHA"},
+    {PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5,    "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"},
+    {PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"},
+    {PJ_TLS_DH_anon_WITH_DES_CBC_SHA,          "TLS_DH_anon_WITH_DES_CBC_SHA"},
+
+    /* SSLv3 */
+    {PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA,        "SSL_FORTEZZA_KEA_WITH_NULL_SHA"},
+    {PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,"SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"},
+    {PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA,     "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"},
+
+    /* SSLv2 */
+    {PJ_SSL_CK_RC4_128_WITH_MD5,               "SSL_CK_RC4_128_WITH_MD5"},
+    {PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5,      "SSL_CK_RC4_128_EXPORT40_WITH_MD5"},
+    {PJ_SSL_CK_RC2_128_CBC_WITH_MD5,           "SSL_CK_RC2_128_CBC_WITH_MD5"},
+    {PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5,  "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"},
+    {PJ_SSL_CK_IDEA_128_CBC_WITH_MD5,          "SSL_CK_IDEA_128_CBC_WITH_MD5"},
+    {PJ_SSL_CK_DES_64_CBC_WITH_MD5,            "SSL_CK_DES_64_CBC_WITH_MD5"},
+    {PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5,      "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"}
+};
+
+
+/* Get cipher name string */
+static const char* get_cipher_name(pj_ssl_cipher cipher)
+{
+    unsigned i, n;
+
+    n = PJ_ARRAY_SIZE(cipher_names);
+    for (i = 0; i < n; ++i) {
+       if (cipher == cipher_names[i].cipher)
+           return cipher_names[i].name;
+    }
+
+    return "CIPHER_UNKNOWN";
+}
+
+typedef void (*CPjSSLSocket_cb)(int err, void *key);
+
+class CPjSSLSocketReader : public CActive
+{
+public:
+    static CPjSSLSocketReader *NewL(CSecureSocket &sock) 
+    {
+	CPjSSLSocketReader *self = new (ELeave) 
+				   CPjSSLSocketReader(sock);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+    }
+
+    ~CPjSSLSocketReader() {
+	Cancel();
+    }
+
+    /* Asynchronous read from the socket. */
+    int Read(CPjSSLSocket_cb cb, void *key, TPtr8 &data, TUint flags)
+    {
+	PJ_ASSERT_RETURN(!IsActive(), PJ_EBUSY);
+	
+	cb_ = cb;
+	key_ = key;
+	sock_.RecvOneOrMore(data, iStatus, len_received_);
+	SetActive();
+	
+	return PJ_EPENDING;
+    }
+
+private:
+    CSecureSocket  	&sock_;
+    CPjSSLSocket_cb	 cb_;
+    void		*key_;
+    TSockXfrLength  	 len_received_; /* not really useful? */
+
+    void DoCancel() {
+	sock_.CancelAll();
+    }
+    
+    void RunL() {
+	(*cb_)(iStatus.Int(), key_);
+    }
+
+    CPjSSLSocketReader(CSecureSocket &sock) : 
+	CActive(0), sock_(sock), cb_(NULL), key_(NULL) 
+    {}
+    
+    void ConstructL() {
+	CActiveScheduler::Add(this);
+    }
+};
+
+class CPjSSLSocket : public CActive
+{
+public:
+    enum ssl_state {
+	SSL_STATE_NULL,
+	SSL_STATE_CONNECTING,
+	SSL_STATE_HANDSHAKING,
+	SSL_STATE_ESTABLISHED
+    };
+    
+    static CPjSSLSocket *NewL(const TDesC8 &ssl_proto,
+			      pj_qos_type qos_type,
+			      const pj_qos_params &qos_params) 
+    {
+	CPjSSLSocket *self = new (ELeave) CPjSSLSocket(qos_type, qos_params);
+	CleanupStack::PushL(self);
+	self->ConstructL(ssl_proto);
+	CleanupStack::Pop(self);
+	return self;
+    }
+
+    ~CPjSSLSocket() {
+	Cancel();
+	CleanupSubObjects();
+    }
+
+    int Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr, 
+		const TInetAddr &rem_addr, 
+		const TDesC8 &servername = TPtrC8(NULL,0),
+		const TDesC8 &ciphers = TPtrC8(NULL,0));
+    int Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, TUint flags);
+    int SendSync(const TDesC8 &aDesc, TUint flags);
+
+    CPjSSLSocketReader* GetReader();
+    enum ssl_state GetState() const { return state_; }
+    const TInetAddr* GetLocalAddr() const { return &local_addr_; }
+    int GetCipher(TDes8 &cipher) const {
+	if (securesock_)
+	    return securesock_->CurrentCipherSuite(cipher);
+	return KErrNotFound;
+    }
+    const CX509Certificate *GetPeerCert() {
+	if (securesock_)
+	    return securesock_->ServerCert();
+	return NULL;
+    }
+
+private:
+    enum ssl_state	 state_;
+    pj_sock_t	    	 sock_;
+    CSecureSocket  	*securesock_;
+    bool	    	 is_connected_;
+    
+    pj_qos_type 	 qos_type_;
+    pj_qos_params 	 qos_params_;
+    			      
+    CPjSSLSocketReader  *reader_;
+    TBuf<32> 	    	 ssl_proto_;
+    TInetAddr       	 rem_addr_;
+    TPtrC8		 servername_;
+    TPtrC8		 ciphers_;
+    TInetAddr       	 local_addr_;
+    TSockXfrLength 	 sent_len_;
+
+    CPjSSLSocket_cb 	 cb_;
+    void 	   	*key_;
+    
+    void DoCancel();
+    void RunL();
+
+    CPjSSLSocket(pj_qos_type qos_type, const pj_qos_params &qos_params) :
+	CActive(0), state_(SSL_STATE_NULL), sock_(PJ_INVALID_SOCKET), 
+	securesock_(NULL), is_connected_(false),
+	qos_type_(qos_type), qos_params_(qos_params),
+	reader_(NULL), 	cb_(NULL), key_(NULL)
+    {}
+    
+    void ConstructL(const TDesC8 &ssl_proto) {
+	ssl_proto_.Copy(ssl_proto);
+	CActiveScheduler::Add(this);
+    }
+
+    void CleanupSubObjects() {
+	delete reader_;
+	reader_ = NULL;
+	if (securesock_) {
+	    if (state_ == SSL_STATE_ESTABLISHED)
+		securesock_->Close();
+	    delete securesock_;
+	    securesock_ = NULL;
+	}
+	if (sock_ != PJ_INVALID_SOCKET) {
+	    pj_sock_close(sock_);
+	    sock_ = PJ_INVALID_SOCKET;
+	}	    
+    }
+};
+
+int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key, 
+			  const TInetAddr &local_addr, 
+			  const TInetAddr &rem_addr,
+			  const TDesC8 &servername,
+			  const TDesC8 &ciphers)
+{
+    pj_status_t status;
+    
+    PJ_ASSERT_RETURN(state_ == SSL_STATE_NULL, PJ_EINVALIDOP);
+    
+    status = pj_sock_socket(rem_addr.Family(), pj_SOCK_STREAM(), 0, &sock_);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    // Apply QoS
+    status = pj_sock_apply_qos2(sock_, qos_type_, &qos_params_, 
+    				2,  THIS_FILE, NULL);
+    
+    RSocket &rSock = ((CPjSocket*)sock_)->Socket();
+
+    local_addr_ = local_addr;
+    
+    if (!local_addr_.IsUnspecified()) {
+	TInt err = rSock.Bind(local_addr_);
+	if (err != KErrNone)
+	    return PJ_RETURN_OS_ERROR(err);
+    }
+    
+    cb_ = cb;
+    key_ = key;
+    rem_addr_ = rem_addr;
+    
+    /* Note: the following members only keep the pointer, not the data */
+    servername_.Set(servername);
+    ciphers_.Set(ciphers);
+
+    rSock.Connect(rem_addr_, iStatus);
+    SetActive();
+    state_ = SSL_STATE_CONNECTING;
+    
+    rSock.LocalName(local_addr_);
+
+    return PJ_EPENDING;
+}
+
+int CPjSSLSocket::Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, 
+		       TUint flags)
+{
+    PJ_UNUSED_ARG(flags);
+
+    PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+    
+    if (IsActive())
+	return PJ_EBUSY;
+    
+    cb_ = cb;
+    key_ = key;
+    
+    securesock_->Send(aDesc, iStatus, sent_len_);
+    SetActive();
+    
+    return PJ_EPENDING;
+}
+
+int CPjSSLSocket::SendSync(const TDesC8 &aDesc, TUint flags)
+{
+    PJ_UNUSED_ARG(flags);
+
+    PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, PJ_EINVALIDOP);
+    
+    TRequestStatus reqStatus;
+    securesock_->Send(aDesc, reqStatus, sent_len_);
+    User::WaitForRequest(reqStatus);
+    
+    return PJ_RETURN_OS_ERROR(reqStatus.Int());
+}
+
+CPjSSLSocketReader* CPjSSLSocket::GetReader()
+{
+    PJ_ASSERT_RETURN(state_ == SSL_STATE_ESTABLISHED, NULL);
+    
+    if (reader_)
+	return reader_;
+    
+    TRAPD(err,	reader_ = CPjSSLSocketReader::NewL(*securesock_));
+    if (err != KErrNone)
+	return NULL;
+    
+    return reader_;
+}
+
+void CPjSSLSocket::DoCancel()
+{
+    /* Operation to be cancelled depends on current state */
+    switch (state_) {
+    case SSL_STATE_CONNECTING:
+	{
+	    RSocket &rSock = ((CPjSocket*)sock_)->Socket();
+
+	    rSock.CancelConnect();
+	    CleanupSubObjects();
+	    state_ = SSL_STATE_NULL;
+	}
+	break;
+    case SSL_STATE_HANDSHAKING:
+	{
+	    securesock_->CancelHandshake();
+	    CleanupSubObjects();
+	    state_ = SSL_STATE_NULL;
+	}
+	break;
+    case SSL_STATE_ESTABLISHED:
+	securesock_->CancelSend();
+	break;
+    default:
+	break;
+    }
+}
+
+void CPjSSLSocket::RunL()
+{
+    switch (state_) {
+    case SSL_STATE_CONNECTING:
+	if (iStatus != KErrNone) {
+	    CleanupSubObjects();
+	    state_ = SSL_STATE_NULL;
+	    /* Dispatch connect failure notification */
+	    if (cb_) (*cb_)(iStatus.Int(), key_);
+	} else {
+	    RSocket &rSock = ((CPjSocket*)sock_)->Socket();
+
+	    /* Get local addr */
+	    rSock.LocalName(local_addr_);
+	    
+	    /* Prepare and start handshake */
+	    securesock_ = CSecureSocket::NewL(rSock, ssl_proto_);
+	    securesock_->SetDialogMode(EDialogModeAttended);
+	    if (servername_.Length() > 0)
+		securesock_->SetOpt(KSoSSLDomainName, KSolInetSSL,
+				    servername_);
+	    if (ciphers_.Length() > 0)
+		securesock_->SetAvailableCipherSuites(ciphers_);
+
+	    // FlushSessionCache() seems to also fire signals to all 
+	    // completed AOs (something like CActiveScheduler::RunIfReady())
+	    // which may cause problem, e.g: we've experienced that when 
+	    // SSL timeout is set to 1s, the SSL timeout timer fires up
+	    // at this point and securesock_ instance gets deleted here!
+	    // So be careful using this. And we don't think we need it here.
+	    //securesock_->FlushSessionCache();
+
+	    securesock_->StartClientHandshake(iStatus);
+	    SetActive();
+	    state_ = SSL_STATE_HANDSHAKING;
+	}
+	break;
+    case SSL_STATE_HANDSHAKING:
+	if (iStatus == KErrNone) {
+	    state_ = SSL_STATE_ESTABLISHED;
+	} else {
+	    state_ = SSL_STATE_NULL;
+	    CleanupSubObjects();
+	}
+	/* Dispatch connect status notification */
+	if (cb_) (*cb_)(iStatus.Int(), key_);
+	break;
+    case SSL_STATE_ESTABLISHED:
+	/* Dispatch data sent notification */
+	if (cb_) (*cb_)(iStatus.Int(), key_);
+	break;
+    default:
+	pj_assert(0);
+	break;
+    }
+}
+
+typedef void (*CPjTimer_cb)(void *user_data);
+
+class CPjTimer : public CActive 
+{
+public:
+    CPjTimer(const pj_time_val *delay, CPjTimer_cb cb, void *user_data) : 
+	CActive(0), cb_(cb), user_data_(user_data)
+    {
+	CActiveScheduler::Add(this);
+
+	rtimer_.CreateLocal();
+	pj_int32_t interval = PJ_TIME_VAL_MSEC(*delay) * 1000;
+	if (interval < 0) {
+	    interval = 0;
+	}
+	rtimer_.After(iStatus, interval);
+	SetActive();
+    }
+    
+    ~CPjTimer() { Cancel(); }
+    
+private:	
+    RTimer		 rtimer_;
+    CPjTimer_cb		 cb_;
+    void		*user_data_;
+    
+    void RunL() { if (cb_) (*cb_)(user_data_); }
+    void DoCancel() { rtimer_.Cancel(); }
+};
+
+/*
+ * Structure of recv/read state.
+ */
+typedef struct read_state_t {
+    TPtr8		*read_buf;
+    TPtr8		*orig_buf;
+    pj_uint32_t		 flags;    
+} read_state_t;
+
+/*
+ * Structure of send/write data.
+ */
+typedef struct write_data_t {
+    pj_size_t 	 	 len;
+    pj_ioqueue_op_key_t	*key;
+    pj_size_t 	 	 data_len;
+    char		 data[1];
+} write_data_t;
+
+/*
+ * Structure of send/write state.
+ */
+typedef struct write_state_t {
+    char		*buf;
+    pj_size_t		 max_len;    
+    char		*start;
+    pj_size_t		 len;
+    write_data_t	*current_data;
+    TPtrC8		 send_ptr;
+} write_state_t;
+
+/*
+ * Secure socket structure definition.
+ */
+struct pj_ssl_sock_t
+{
+    pj_pool_t		*pool;
+    pj_ssl_sock_cb	 cb;
+    void		*user_data;
+    
+    pj_bool_t		 established;
+    write_state_t	 write_state;
+    read_state_t	 read_state;
+    CPjTimer		*connect_timer;
+
+    CPjSSLSocket   	*sock;
+    int			 sock_af;
+    int			 sock_type;
+    pj_sockaddr		 local_addr;
+    pj_sockaddr		 rem_addr;
+
+    /* QoS settings */
+    pj_qos_type		 qos_type;
+    pj_qos_params	 qos_params;
+    pj_bool_t		 qos_ignore_error;
+
+
+    pj_ssl_sock_proto	 proto;
+    pj_time_val		 timeout;
+    pj_str_t		 servername;
+    pj_str_t		 ciphers;
+    pj_ssl_cert_info	 remote_cert_info;
+};
+
+
+static pj_str_t get_cert_name(char *buf, unsigned buf_len,
+                              const CX500DistinguishedName &name)
+{
+    TInt i;
+    TUint8 *p;
+    TInt l = buf_len;
+    
+    p = (TUint8*)buf;
+    for(i = 0; i < name.Count(); ++i) {
+	const CX520AttributeTypeAndValue &attr = name.Element(i);
+
+	/* Print element separator */
+	*p++ = '/';
+	if (0 == --l) break;
+
+	/* Print the type. */
+	TPtr8 type(p, l);
+	type.Copy(attr.Type());
+	p += type.Length();
+	l -= type.Length();
+	if (0 >= --l) break;
+
+	/* Print equal sign */
+	*p++ = '=';
+	if (0 == --l) break;
+	
+	/* Print the value. Let's just get the raw data here */
+	TPtr8 value(p, l);
+	value.Copy(attr.EncodedValue().Mid(2));
+	p += value.Length();
+	l -= value.Length();
+	if (0 >= --l) break;
+    }
+    
+    pj_str_t src;
+    pj_strset(&src, buf, buf_len - l);
+    
+    return src;
+}
+                            
+/* Get certificate info from CX509Certificate.
+ */
+static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci,
+                          const CX509Certificate *x)
+{
+    enum { tmp_buf_len = 512 };
+    char *tmp_buf;
+    unsigned len;
+    
+    pj_assert(pool && ci && x);
+    
+    /* Init */
+    tmp_buf = new char[tmp_buf_len];
+    pj_bzero(ci, sizeof(*ci));
+    
+    /* Version */
+    ci->version = x->Version();
+    
+    /* Serial number */
+    len = x->SerialNumber().Length();
+    if (len > sizeof(ci->serial_no)) 
+	len = sizeof(ci->serial_no);
+    pj_memcpy(ci->serial_no + sizeof(ci->serial_no) - len, 
+              x->SerialNumber().Ptr(), len);
+    
+    /* Subject */
+    {
+	HBufC *subject = NULL;
+	TRAPD(err, subject = x->SubjectL());
+	if (err == KErrNone) {
+	    TPtr16 ptr16(subject->Des());
+	    len = ptr16.Length();
+	    TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len);
+	    ptr8.Copy(ptr16);
+	    pj_strset(&ci->subject.cn, (char*)ptr8.Ptr(), ptr8.Length());
+	}
+	pj_str_t tmp = get_cert_name(tmp_buf, tmp_buf_len,
+				     x->SubjectName());
+	pj_strdup(pool, &ci->subject.info, &tmp);
+    }
+
+    /* Issuer */
+    {
+	HBufC *issuer = NULL;
+	TRAPD(err, issuer = x->IssuerL());
+	if (err == KErrNone) {
+	    TPtr16 ptr16(issuer->Des());
+	    len = ptr16.Length();
+	    TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len);
+	    ptr8.Copy(ptr16);
+	    pj_strset(&ci->issuer.cn, (char*)ptr8.Ptr(), ptr8.Length());
+	}
+	pj_str_t tmp = get_cert_name(tmp_buf, tmp_buf_len,
+				     x->IssuerName());
+	pj_strdup(pool, &ci->issuer.info, &tmp);
+    }
+    
+    /* Validity */
+    const CValidityPeriod &valid_period = x->ValidityPeriod();
+    TTime base_time(TDateTime(1970, EJanuary, 0, 0, 0, 0, 0));
+    TTimeIntervalSeconds tmp_sec;
+    valid_period.Start().SecondsFrom(base_time, tmp_sec);
+    ci->validity.start.sec = tmp_sec.Int(); 
+    valid_period.Finish().SecondsFrom(base_time, tmp_sec);
+    ci->validity.end.sec = tmp_sec.Int();
+    
+    /* Deinit */
+    delete [] tmp_buf;
+}
+
+
+/* Update certificates info. This function should be called after handshake
+ * or renegotiation successfully completed.
+ */
+static void update_certs_info(pj_ssl_sock_t *ssock)
+{
+    const CX509Certificate *x;
+
+    pj_assert(ssock && ssock->sock &&
+              ssock->sock->GetState() == CPjSSLSocket::SSL_STATE_ESTABLISHED);
+        
+    /* Active remote certificate */
+    x = ssock->sock->GetPeerCert();
+    if (x) {
+	get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
+    } else {
+	pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
+    }
+}
+
+
+/* Available ciphers */
+static unsigned ciphers_num_ = 0;
+static struct ciphers_t
+{
+    pj_ssl_cipher    id;
+    const char	    *name;
+} ciphers_[64];
+
+/*
+ * Get cipher list supported by SSL/TLS backend.
+ */
+PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[],
+					          unsigned *cipher_num)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
+    
+    if (ciphers_num_ == 0) {
+        RSocket sock;
+        CSecureSocket *secure_sock;
+        TPtrC16 proto(_L16("TLS1.0"));
+
+        secure_sock = CSecureSocket::NewL(sock, proto);
+        if (secure_sock) {
+            TBuf8<128> ciphers_buf(0);
+            secure_sock->AvailableCipherSuites(ciphers_buf);
+            
+            ciphers_num_ = ciphers_buf.Length() / 2;
+            if (ciphers_num_ > PJ_ARRAY_SIZE(ciphers_))
+        	ciphers_num_ = PJ_ARRAY_SIZE(ciphers_);
+            for (i = 0; i < ciphers_num_; ++i) {
+                ciphers_[i].id = (pj_ssl_cipher)(ciphers_buf[i*2]*10 + 
+					         ciphers_buf[i*2+1]);
+		ciphers_[i].name = get_cipher_name(ciphers_[i].id);
+	    }
+        }
+        
+        delete secure_sock;
+    }
+    
+    if (ciphers_num_ == 0) {
+	*cipher_num = 0;
+	return PJ_ENOTFOUND;
+    }
+    
+    *cipher_num = PJ_MIN(*cipher_num, ciphers_num_);
+    for (i = 0; i < *cipher_num; ++i)
+        ciphers[i] = ciphers_[i].id;
+    
+    return PJ_SUCCESS;
+}
+
+
+/* Get cipher name string */
+PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher)
+{
+    unsigned i;
+
+    if (ciphers_num_ == 0) {
+	pj_ssl_cipher c[1];
+	i = 0;
+	pj_ssl_cipher_get_availables(c, &i);
+    }
+	
+    for (i = 0; i < ciphers_num_; ++i) {
+	if (cipher == ciphers_[i].id)
+	    return ciphers_[i].name;
+    }
+
+    return NULL;
+}
+
+
+/* Check if the specified cipher is supported by SSL/TLS backend. */
+PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher)
+{
+    unsigned i;
+
+    if (ciphers_num_ == 0) {
+	pj_ssl_cipher c[1];
+	i = 0;
+	pj_ssl_cipher_get_availables(c, &i);
+    }
+	
+    for (i = 0; i < ciphers_num_; ++i) {
+	if (cipher == ciphers_[i].id)
+	    return PJ_TRUE;
+    }
+
+    return PJ_FALSE;
+}
+
+
+/*
+ * Create SSL socket instance. 
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
+					const pj_ssl_sock_param *param,
+					pj_ssl_sock_t **p_ssock)
+{
+    pj_ssl_sock_t *ssock;
+
+    PJ_ASSERT_RETURN(param->async_cnt == 1, PJ_EINVAL);
+    PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
+
+    /* Allocate secure socket */
+    ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
+    
+    /* Allocate write buffer */
+    ssock->write_state.buf = (char*)pj_pool_alloc(pool, 
+						  param->send_buffer_size);
+    ssock->write_state.max_len = param->send_buffer_size;
+    ssock->write_state.start = ssock->write_state.buf;
+    
+    /* Init secure socket */
+    ssock->pool = pool;
+    ssock->sock_af = param->sock_af;
+    ssock->sock_type = param->sock_type;
+    ssock->cb = param->cb;
+    ssock->user_data = param->user_data;
+    ssock->timeout = param->timeout;
+    if (param->ciphers_num > 0) {
+	/* Cipher list in Symbian is represented as array of two-octets. */
+	ssock->ciphers.slen = param->ciphers_num*2;
+	ssock->ciphers.ptr  = (char*)pj_pool_alloc(pool, ssock->ciphers.slen);
+	pj_uint8_t *c = (pj_uint8_t*)ssock->ciphers.ptr;
+	for (unsigned i = 0; i < param->ciphers_num; ++i) {
+	    *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF00) >> 8;
+	    *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF);
+	}
+    }
+    pj_strdup_with_null(pool, &ssock->servername, &param->server_name);
+
+    ssock->qos_type = param->qos_type;
+    ssock->qos_ignore_error = param->qos_ignore_error;
+    pj_memcpy(&ssock->qos_params, &param->qos_params,
+	      sizeof(param->qos_params));
+
+    /* Finally */
+    *p_ssock = ssock;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
+                                        	const pj_str_t *CA_file,
+                                        	const pj_str_t *cert_file,
+                                        	const pj_str_t *privkey_file,
+                                        	const pj_str_t *privkey_pass,
+                                        	pj_ssl_cert_t **p_cert)
+{
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(CA_file);
+    PJ_UNUSED_ARG(cert_file);
+    PJ_UNUSED_ARG(privkey_file);
+    PJ_UNUSED_ARG(privkey_pass);
+    PJ_UNUSED_ARG(p_cert);
+    return PJ_ENOTSUP;
+}
+
+/*
+ * Set SSL socket credential.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
+					    pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    const pj_ssl_cert_t *cert)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(cert);
+    return PJ_ENOTSUP;
+}
+
+/*
+ * Close the SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
+{
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+    
+    delete ssock->connect_timer;
+    ssock->connect_timer = NULL;
+    
+    delete ssock->sock;
+    ssock->sock = NULL;
+
+    delete ssock->read_state.read_buf;
+    delete ssock->read_state.orig_buf;
+    ssock->read_state.read_buf = NULL;
+    ssock->read_state.orig_buf = NULL;
+    
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Associate arbitrary data with the SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data (pj_ssl_sock_t *ssock,
+					       void *user_data)
+{
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+    
+    ssock->user_data = user_data;
+    
+    return PJ_SUCCESS;
+}
+					       
+
+/*
+ * Retrieve the user data previously associated with this SSL
+ * socket.
+ */
+PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
+{
+    PJ_ASSERT_RETURN(ssock, NULL);
+    
+    return ssock->user_data;
+}
+
+
+/*
+ * Retrieve the local address and port used by specified SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
+					  pj_ssl_sock_info *info)
+{
+    PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL);
+    
+    pj_bzero(info, sizeof(*info));
+    
+    info->established = ssock->established;
+    
+    /* Local address */
+    if (ssock->sock) {
+	const TInetAddr* local_addr_ = ssock->sock->GetLocalAddr();
+	int addrlen = sizeof(pj_sockaddr);
+	pj_status_t status;
+	
+	status = PjSymbianOS::Addr2pj(*local_addr_, info->local_addr, &addrlen);
+	if (status != PJ_SUCCESS)
+	    return status;
+    } else {
+	pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
+    }
+
+    if (info->established) {
+	/* Cipher suite */
+	TBuf8<4> cipher;
+	if (ssock->sock->GetCipher(cipher) == KErrNone) {
+	    info->cipher = (pj_ssl_cipher)cipher[1]; 
+	}
+
+	/* Remote address */
+        pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr, 
+    		       (pj_sockaddr_t*)&ssock->rem_addr);
+        
+        /* Certificates info */
+        info->remote_cert_info = &ssock->remote_cert_info;
+    }
+
+    /* Protocol */
+    info->proto = ssock->proto;
+    
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Starts read operation on this SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    unsigned buff_size,
+					    pj_uint32_t flags)
+{
+    PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP);
+
+    /* Reading is already started */
+    if (ssock->read_state.orig_buf) {
+	return PJ_SUCCESS;
+    }
+
+    void *readbuf[1];
+    readbuf[0] = pj_pool_alloc(pool, buff_size);
+    return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags);
+}
+
+static void read_cb(int err, void *key)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
+    pj_status_t status;
+
+    status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
+
+    /* Check connection status */
+    if (err == KErrEof || !PjSymbianOS::Instance()->IsConnectionUp() ||
+	!ssock->established) 
+    {
+	status = PJ_EEOF;
+    }
+    
+    /* Notify data arrival */
+    if (ssock->cb.on_data_read) {
+	pj_size_t remainder = 0;
+	char *data = (char*)ssock->read_state.orig_buf->Ptr();
+	pj_size_t data_len = ssock->read_state.read_buf->Length() + 
+			     ssock->read_state.read_buf->Ptr() -
+			     ssock->read_state.orig_buf->Ptr();
+	
+	if (data_len > 0) {
+	    /* Notify received data */
+	    pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, data, data_len, 
+						      status, &remainder);
+	    if (!ret) {
+		/* We've been destroyed */
+		return;
+	    }
+	    
+	    /* Calculate available data for next READ operation */
+	    if (remainder > 0) {
+		pj_size_t data_maxlen = ssock->read_state.orig_buf->MaxLength();
+		
+		/* There is some data left unconsumed by application, we give
+		 * smaller buffer for next READ operation.
+		 */
+		ssock->read_state.read_buf->Set((TUint8*)data+remainder, 0, 
+					        data_maxlen - remainder);
+	    } else {
+		/* Give all buffer for next READ operation. 
+		 */
+		ssock->read_state.read_buf->Set(*ssock->read_state.orig_buf);
+	    }
+	}
+    }
+
+    if (status == PJ_SUCCESS) {
+	/* Perform the "next" READ operation */
+	CPjSSLSocketReader *reader = ssock->sock->GetReader(); 
+	ssock->read_state.read_buf->SetLength(0);
+	status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, 
+			      ssock->read_state.flags);
+    }
+    
+    /* Connection closed or something goes wrong */
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	/* Notify error */
+	if (ssock->cb.on_data_read) {
+	    pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, NULL, 0, 
+						      status, NULL);
+	    if (!ret) {
+		/* We've been destroyed */
+		return;
+	    }
+	}
+	
+	delete ssock->read_state.read_buf;
+	delete ssock->read_state.orig_buf;
+	ssock->read_state.read_buf = NULL;
+	ssock->read_state.orig_buf = NULL;
+	ssock->established = PJ_FALSE;
+    }
+}
+
+/*
+ * Same as #pj_ssl_sock_start_read(), except that the application
+ * supplies the buffers for the read operation so that the acive socket
+ * does not have to allocate the buffers.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
+					     pj_pool_t *pool,
+					     unsigned buff_size,
+					     void *readbuf[],
+					     pj_uint32_t flags)
+{
+    PJ_ASSERT_RETURN(ssock && buff_size && readbuf, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->established, PJ_EINVALIDOP);
+    
+    /* Return failure if access point is marked as down by app. */
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    /* Reading is already started */
+    if (ssock->read_state.orig_buf) {
+	return PJ_SUCCESS;
+    }
+    
+    PJ_UNUSED_ARG(pool);
+
+    /* Get reader instance */
+    CPjSSLSocketReader *reader = ssock->sock->GetReader();
+    if (!reader)
+	return PJ_ENOMEM;
+    
+    /* We manage two buffer pointers here:
+     * 1. orig_buf keeps the orginal buffer address (and its max length).
+     * 2. read_buf provides buffer for READ operation, mind that there may be
+     *    some remainder data left by application.
+     */
+    ssock->read_state.read_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size);
+    ssock->read_state.orig_buf = new TPtr8((TUint8*)readbuf[0], 0, buff_size);
+    ssock->read_state.flags = flags;
+    
+    pj_status_t status;
+    status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf, 
+			  ssock->read_state.flags);
+    
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	delete ssock->read_state.read_buf;
+	delete ssock->read_state.orig_buf;
+	ssock->read_state.read_buf = NULL;
+	ssock->read_state.orig_buf = NULL;
+	
+	return status;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/*
+ * Same as pj_ssl_sock_start_read(), except that this function is used
+ * only for datagram sockets, and it will trigger \a on_data_recvfrom()
+ * callback instead.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
+						pj_pool_t *pool,
+						unsigned buff_size,
+						pj_uint32_t flags)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(buff_size);
+    PJ_UNUSED_ARG(flags);
+    return PJ_ENOTSUP;
+}
+
+/*
+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() 
+ * operation takes the buffer from the argument rather than creating
+ * new ones.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
+						 pj_pool_t *pool,
+						 unsigned buff_size,
+						 void *readbuf[],
+						 pj_uint32_t flags)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(buff_size);
+    PJ_UNUSED_ARG(readbuf);
+    PJ_UNUSED_ARG(flags);
+    return PJ_ENOTSUP;
+}
+
+static void send_cb(int err, void *key)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
+    write_state_t *st = &ssock->write_state;
+
+    /* Check connection status */
+    if (err != KErrNone || !PjSymbianOS::Instance()->IsConnectionUp() ||
+	!ssock->established) 
+    {
+	ssock->established = PJ_FALSE;
+	return;
+    }
+
+    /* Remove sent data from buffer */
+    st->start += st->current_data->len;
+    st->len -= st->current_data->len;
+
+    /* Reset current outstanding send */
+    st->current_data = NULL;
+
+    /* Let's check if there is pending data to send */
+    if (st->len) {
+	write_data_t *wdata = (write_data_t*)st->start;
+	pj_status_t status;
+	
+	st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len);
+	st->current_data = wdata;
+	status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, 0);
+	if (status != PJ_EPENDING) {
+	    ssock->established = PJ_FALSE;
+	    st->len = 0;
+	    return;
+	}
+    } else {
+        /* Buffer empty, reset the start position */
+        st->start = st->buf;
+    }
+}
+
+/*
+ * Send data using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
+				      pj_ioqueue_op_key_t *send_key,
+				      const void *data,
+				      pj_ssize_t *size,
+				      unsigned flags)
+{
+    PJ_CHECK_STACK();
+    PJ_ASSERT_RETURN(ssock && data && size, PJ_EINVAL);
+    PJ_ASSERT_RETURN(ssock->write_state.max_len == 0 || 
+		     ssock->write_state.max_len >= (pj_size_t)*size, 
+		     PJ_ETOOSMALL);
+    
+    /* Check connection status */
+    if (!PjSymbianOS::Instance()->IsConnectionUp() || !ssock->established) 
+    {
+	ssock->established = PJ_FALSE;
+	return PJ_ECANCELLED;
+    }
+
+    write_state_t *st = &ssock->write_state;
+    
+    /* Synchronous mode */
+    if (st->max_len == 0) {
+	st->send_ptr.Set((TUint8*)data, (TInt)*size);
+	return ssock->sock->SendSync(st->send_ptr, flags);
+    }
+
+    /* CSecureSocket only allows one outstanding send operation, so
+     * we use buffering mechanism to allow application to perform send 
+     * operations at any time.
+     */
+    
+    pj_size_t needed_len = *size + sizeof(write_data_t) - 1;
+    
+    /* Align needed_len to be multiplication of 4 */
+    needed_len = ((needed_len + 3) >> 2) << 2; 
+
+    /* Block until there is buffer slot available and contiguous! */
+    while (st->start + st->len + needed_len > st->buf + st->max_len) {
+	pj_symbianos_poll(-1, -1);
+    }
+
+    /* Push back the send data into the buffer */
+    write_data_t *wdata = (write_data_t*)(st->start + st->len);
+    
+    wdata->len = needed_len;
+    wdata->key = send_key;
+    wdata->data_len = (pj_size_t)*size;
+    pj_memcpy(wdata->data, data, *size);
+    st->len += needed_len;
+
+    /* If no outstanding send, send it */
+    if (st->current_data == NULL) {
+	pj_status_t status;
+	    
+	wdata = (write_data_t*)st->start;
+	st->current_data = wdata;
+	st->send_ptr.Set((TUint8*)wdata->data, (TInt)wdata->data_len);
+	status = ssock->sock->Send(&send_cb, ssock, st->send_ptr, flags);
+	
+	if (status != PJ_EPENDING) {
+	    *size = -status;
+	    return status;
+	}
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/*
+ * Send datagram using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
+					pj_ioqueue_op_key_t *send_key,
+					const void *data,
+					pj_ssize_t *size,
+					unsigned flags,
+					const pj_sockaddr_t *addr,
+					int addr_len)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(send_key);
+    PJ_UNUSED_ARG(data);
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(flags);
+    PJ_UNUSED_ARG(addr);
+    PJ_UNUSED_ARG(addr_len);
+    return PJ_ENOTSUP;
+}
+
+/*
+ * Starts asynchronous socket accept() operations on this SSL socket. 
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
+					      pj_pool_t *pool,
+					      const pj_sockaddr_t *local_addr,
+					      int addr_len)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(local_addr);
+    PJ_UNUSED_ARG(addr_len);
+    
+    return PJ_ENOTSUP;
+}
+
+static void connect_cb(int err, void *key)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)key;
+    pj_status_t status;
+    
+    if (ssock->connect_timer) {
+	delete ssock->connect_timer;
+	ssock->connect_timer = NULL;
+    }
+
+    status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
+    if (status == PJ_SUCCESS) {
+	ssock->established = PJ_TRUE;
+	update_certs_info(ssock);
+    } else {
+	delete ssock->sock;
+	ssock->sock = NULL;
+	if (err == KErrTimedOut) status = PJ_ETIMEDOUT;
+    }
+    
+    if (ssock->cb.on_connect_complete) {
+	pj_bool_t ret = (*ssock->cb.on_connect_complete)(ssock, status);
+	if (!ret) {
+	    /* We've been destroyed */
+	    return;
+	}
+    }
+}
+
+static void connect_timer_cb(void *key)
+{
+    connect_cb(KErrTimedOut, key);
+}
+
+/*
+ * Starts asynchronous socket connect() operation and SSL/TLS handshaking 
+ * for this socket. Once the connection is done (either successfully or not),
+ * the \a on_connect_complete() callback will be called.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock,
+					       pj_pool_t *pool,
+					       const pj_sockaddr_t *localaddr,
+					       const pj_sockaddr_t *remaddr,
+					       int addr_len)
+{
+    CPjSSLSocket *sock = NULL;
+    pj_status_t status;
+    
+    PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
+		     PJ_EINVAL);
+
+    /* Check connection status */
+    PJ_SYMBIAN_CHECK_CONNECTION();
+    
+    if (ssock->sock != NULL) {
+	CPjSSLSocket::ssl_state state = ssock->sock->GetState();
+	switch (state) {
+	case CPjSSLSocket::SSL_STATE_ESTABLISHED:
+	    return PJ_SUCCESS;
+	default:
+	    return PJ_EPENDING;
+	}
+    }
+
+    /* Set SSL protocol */
+    TPtrC8 proto;
+    
+    if (ssock->proto == PJ_SSL_SOCK_PROTO_DEFAULT)
+	ssock->proto = PJ_SSL_SOCK_PROTO_TLS1;
+
+    /* CSecureSocket only support TLS1.0 and SSL3.0 */
+    switch(ssock->proto) {
+    case PJ_SSL_SOCK_PROTO_TLS1:
+	proto.Set((const TUint8*)"TLS1.0", 6);
+	break;
+    case PJ_SSL_SOCK_PROTO_SSL3:
+	proto.Set((const TUint8*)"SSL3.0", 6);
+	break;
+    default:
+	return PJ_ENOTSUP;
+    }
+
+    /* Prepare addresses */
+    TInetAddr localaddr_, remaddr_;
+    status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)localaddr, addr_len, 
+				  localaddr_);
+    if (status != PJ_SUCCESS)
+	return status;
+    
+    status = PjSymbianOS::pj2Addr(*(pj_sockaddr*)remaddr, addr_len,
+				  remaddr_);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_sockaddr_cp((pj_sockaddr_t*)&ssock->rem_addr, remaddr);
+
+    /* Init SSL engine */
+    TRAPD(err, sock = CPjSSLSocket::NewL(proto, ssock->qos_type, 
+				         ssock->qos_params));
+    if (err != KErrNone)
+	return PJ_ENOMEM;
+    
+    if (ssock->timeout.sec != 0 || ssock->timeout.msec != 0) {
+	ssock->connect_timer = new CPjTimer(&ssock->timeout, 
+					    &connect_timer_cb, ssock);
+    }
+    
+    /* Convert server name to Symbian descriptor */
+    TPtrC8 servername_((TUint8*)ssock->servername.ptr, 
+		       ssock->servername.slen);
+    
+    /* Convert cipher list to Symbian descriptor */
+    TPtrC8 ciphers_((TUint8*)ssock->ciphers.ptr, 
+		    ssock->ciphers.slen);
+    
+    /* Try to connect */
+    status = sock->Connect(&connect_cb, ssock, localaddr_, remaddr_,
+			   servername_, ciphers_);
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	delete sock;
+	return status;
+    }
+
+    ssock->sock = sock;
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock)
+{
+    PJ_UNUSED_ARG(ssock);
+    return PJ_ENOTSUP;
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/string.c b/jni/pjproject-android/pjlib/src/pj/string.c
new file mode 100644
index 0000000..22c3fde
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/string.c
@@ -0,0 +1,215 @@
+/* $Id: string.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/pool.h>
+#include <pj/ctype.h>
+#include <pj/rand.h>
+#include <pj/os.h>
+
+#if PJ_FUNCTIONS_ARE_INLINED==0
+#  include <pj/string_i.h>
+#endif
+
+
+PJ_DEF(char*) pj_strstr(const pj_str_t *str, const pj_str_t *substr)
+{
+    const char *s, *ends;
+
+    /* Special case when substr is zero */
+    if (substr->slen == 0) {
+	return (char*)str->ptr;
+    }
+
+    s = str->ptr;
+    ends = str->ptr + str->slen - substr->slen;
+    for (; s<=ends; ++s) {
+	if (pj_ansi_strncmp(s, substr->ptr, substr->slen)==0)
+	    return (char*)s;
+    }
+    return NULL;
+}
+
+
+PJ_DEF(char*) pj_stristr(const pj_str_t *str, const pj_str_t *substr)
+{
+    const char *s, *ends;
+
+    /* Special case when substr is zero */
+    if (substr->slen == 0) {
+	return (char*)str->ptr;
+    }
+
+    s = str->ptr;
+    ends = str->ptr + str->slen - substr->slen;
+    for (; s<=ends; ++s) {
+	if (pj_ansi_strnicmp(s, substr->ptr, substr->slen)==0)
+	    return (char*)s;
+    }
+    return NULL;
+}
+
+
+PJ_DEF(pj_str_t*) pj_strltrim( pj_str_t *str )
+{
+    char *end = str->ptr + str->slen;
+    register char *p = str->ptr;
+    while (p < end && pj_isspace(*p))
+	++p;
+    str->slen -= (p - str->ptr);
+    str->ptr = p;
+    return str;
+}
+
+PJ_DEF(pj_str_t*) pj_strrtrim( pj_str_t *str )
+{
+    char *end = str->ptr + str->slen;
+    register char *p = end - 1;
+    while (p >= str->ptr && pj_isspace(*p))
+        --p;
+    str->slen -= ((end - p) - 1);
+    return str;
+}
+
+PJ_DEF(char*) pj_create_random_string(char *str, pj_size_t len)
+{
+    unsigned i;
+    char *p = str;
+
+    PJ_CHECK_STACK();
+
+    for (i=0; i<len/8; ++i) {
+	pj_uint32_t val = pj_rand();
+	pj_val_to_hex_digit( (val & 0xFF000000) >> 24, p+0 );
+	pj_val_to_hex_digit( (val & 0x00FF0000) >> 16, p+2 );
+	pj_val_to_hex_digit( (val & 0x0000FF00) >>  8, p+4 );
+	pj_val_to_hex_digit( (val & 0x000000FF) >>  0, p+6 );
+	p += 8;
+    }
+    for (i=i * 8; i<len; ++i) {
+	*p++ = pj_hex_digits[ pj_rand() & 0x0F ];
+    }
+    return str;
+}
+
+PJ_DEF(long) pj_strtol(const pj_str_t *str)
+{
+    PJ_CHECK_STACK();
+
+    if (str->slen > 0 && (str->ptr[0] == '+' || str->ptr[0] == '-')) {
+        pj_str_t s;
+        
+        s.ptr = str->ptr + 1;
+        s.slen = str->slen - 1;
+        return (str->ptr[0] == '-'? -(long)pj_strtoul(&s) : pj_strtoul(&s));
+    } else
+        return pj_strtoul(str);
+}
+
+PJ_DEF(unsigned long) pj_strtoul(const pj_str_t *str)
+{
+    unsigned long value;
+    unsigned i;
+
+    PJ_CHECK_STACK();
+
+    value = 0;
+    for (i=0; i<(unsigned)str->slen; ++i) {
+	if (!pj_isdigit(str->ptr[i]))
+	    break;
+	value = value * 10 + (str->ptr[i] - '0');
+    }
+    return value;
+}
+
+PJ_DEF(unsigned long) pj_strtoul2(const pj_str_t *str, pj_str_t *endptr,
+				  unsigned base)
+{
+    unsigned long value;
+    unsigned i;
+
+    PJ_CHECK_STACK();
+
+    value = 0;
+    if (base <= 10) {
+	for (i=0; i<(unsigned)str->slen; ++i) {
+	    unsigned c = (str->ptr[i] - '0');
+	    if (c >= base)
+		break;
+	    value = value * base + c;
+	}
+    } else if (base == 16) {
+	for (i=0; i<(unsigned)str->slen; ++i) {
+	    if (!pj_isxdigit(str->ptr[i]))
+		break;
+	    value = value * 16 + pj_hex_digit_to_val(str->ptr[i]);
+	}
+    } else {
+	pj_assert(!"Unsupported base");
+	i = 0;
+	value = 0xFFFFFFFFUL;
+    }
+
+    if (endptr) {
+	endptr->ptr = str->ptr + i;
+	endptr->slen = str->slen - i;
+    }
+
+    return value;
+}
+
+PJ_DEF(int) pj_utoa(unsigned long val, char *buf)
+{
+    return pj_utoa_pad(val, buf, 0, 0);
+}
+
+PJ_DEF(int) pj_utoa_pad( unsigned long val, char *buf, int min_dig, int pad)
+{
+    char *p;
+    int len;
+
+    PJ_CHECK_STACK();
+
+    p = buf;
+    do {
+        unsigned long digval = (unsigned long) (val % 10);
+        val /= 10;
+        *p++ = (char) (digval + '0');
+    } while (val > 0);
+
+    len = (int)(p-buf);
+    while (len < min_dig) {
+	*p++ = (char)pad;
+	++len;
+    }
+    *p-- = '\0';
+
+    do {
+        char temp = *p;
+        *p = *buf;
+        *buf = temp;
+        --p;
+        ++buf;
+    } while (buf < p);
+
+    return len;
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/symbols.c b/jni/pjproject-android/pjlib/src/pj/symbols.c
new file mode 100644
index 0000000..61e9526
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/symbols.c
@@ -0,0 +1,348 @@
+/* $Id: symbols.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjlib.h>
+
+/*
+ * addr_resolv.h
+ */
+PJ_EXPORT_SYMBOL(pj_gethostbyname)
+
+/*
+ * array.h
+ */
+PJ_EXPORT_SYMBOL(pj_array_insert)
+PJ_EXPORT_SYMBOL(pj_array_erase)
+PJ_EXPORT_SYMBOL(pj_array_find)
+
+/*
+ * config.h
+ */
+PJ_EXPORT_SYMBOL(pj_dump_config)
+	
+/*
+ * errno.h
+ */
+PJ_EXPORT_SYMBOL(pj_get_os_error)
+PJ_EXPORT_SYMBOL(pj_set_os_error)
+PJ_EXPORT_SYMBOL(pj_get_netos_error)
+PJ_EXPORT_SYMBOL(pj_set_netos_error)
+PJ_EXPORT_SYMBOL(pj_strerror)
+
+/*
+ * except.h
+ */
+PJ_EXPORT_SYMBOL(pj_throw_exception_)
+PJ_EXPORT_SYMBOL(pj_push_exception_handler_)
+PJ_EXPORT_SYMBOL(pj_pop_exception_handler_)
+PJ_EXPORT_SYMBOL(pj_setjmp)
+PJ_EXPORT_SYMBOL(pj_longjmp)
+PJ_EXPORT_SYMBOL(pj_exception_id_alloc)
+PJ_EXPORT_SYMBOL(pj_exception_id_free)
+PJ_EXPORT_SYMBOL(pj_exception_id_name)
+
+
+/*
+ * fifobuf.h
+ */
+PJ_EXPORT_SYMBOL(pj_fifobuf_init)
+PJ_EXPORT_SYMBOL(pj_fifobuf_max_size)
+PJ_EXPORT_SYMBOL(pj_fifobuf_alloc)
+PJ_EXPORT_SYMBOL(pj_fifobuf_unalloc)
+PJ_EXPORT_SYMBOL(pj_fifobuf_free)
+
+/*
+ * guid.h
+ */
+PJ_EXPORT_SYMBOL(pj_generate_unique_string)
+PJ_EXPORT_SYMBOL(pj_create_unique_string)
+
+/*
+ * hash.h
+ */
+PJ_EXPORT_SYMBOL(pj_hash_calc)
+PJ_EXPORT_SYMBOL(pj_hash_create)
+PJ_EXPORT_SYMBOL(pj_hash_get)
+PJ_EXPORT_SYMBOL(pj_hash_set)
+PJ_EXPORT_SYMBOL(pj_hash_count)
+PJ_EXPORT_SYMBOL(pj_hash_first)
+PJ_EXPORT_SYMBOL(pj_hash_next)
+PJ_EXPORT_SYMBOL(pj_hash_this)
+
+/*
+ * ioqueue.h
+ */
+PJ_EXPORT_SYMBOL(pj_ioqueue_create)
+PJ_EXPORT_SYMBOL(pj_ioqueue_destroy)
+PJ_EXPORT_SYMBOL(pj_ioqueue_set_lock)
+PJ_EXPORT_SYMBOL(pj_ioqueue_register_sock)
+PJ_EXPORT_SYMBOL(pj_ioqueue_unregister)
+PJ_EXPORT_SYMBOL(pj_ioqueue_get_user_data)
+PJ_EXPORT_SYMBOL(pj_ioqueue_poll)
+PJ_EXPORT_SYMBOL(pj_ioqueue_read)
+PJ_EXPORT_SYMBOL(pj_ioqueue_recv)
+PJ_EXPORT_SYMBOL(pj_ioqueue_recvfrom)
+PJ_EXPORT_SYMBOL(pj_ioqueue_write)
+PJ_EXPORT_SYMBOL(pj_ioqueue_send)
+PJ_EXPORT_SYMBOL(pj_ioqueue_sendto)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+PJ_EXPORT_SYMBOL(pj_ioqueue_accept)
+PJ_EXPORT_SYMBOL(pj_ioqueue_connect)
+#endif
+
+/*
+ * list.h
+ */
+PJ_EXPORT_SYMBOL(pj_list_insert_before)
+PJ_EXPORT_SYMBOL(pj_list_insert_nodes_before)
+PJ_EXPORT_SYMBOL(pj_list_insert_after)
+PJ_EXPORT_SYMBOL(pj_list_insert_nodes_after)
+PJ_EXPORT_SYMBOL(pj_list_merge_first)
+PJ_EXPORT_SYMBOL(pj_list_merge_last)
+PJ_EXPORT_SYMBOL(pj_list_erase)
+PJ_EXPORT_SYMBOL(pj_list_find_node)
+PJ_EXPORT_SYMBOL(pj_list_search)
+
+
+/*
+ * log.h
+ */
+PJ_EXPORT_SYMBOL(pj_log_write)
+#if PJ_LOG_MAX_LEVEL >= 1
+PJ_EXPORT_SYMBOL(pj_log_set_log_func)
+PJ_EXPORT_SYMBOL(pj_log_get_log_func)
+PJ_EXPORT_SYMBOL(pj_log_set_level)
+PJ_EXPORT_SYMBOL(pj_log_get_level)
+PJ_EXPORT_SYMBOL(pj_log_set_decor)
+PJ_EXPORT_SYMBOL(pj_log_get_decor)
+PJ_EXPORT_SYMBOL(pj_log_1)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 2
+PJ_EXPORT_SYMBOL(pj_log_2)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 3
+PJ_EXPORT_SYMBOL(pj_log_3)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 4
+PJ_EXPORT_SYMBOL(pj_log_4)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 5
+PJ_EXPORT_SYMBOL(pj_log_5)
+#endif
+#if PJ_LOG_MAX_LEVEL >= 6
+PJ_EXPORT_SYMBOL(pj_log_6)
+#endif
+
+/*
+ * os.h
+ */
+PJ_EXPORT_SYMBOL(pj_init)
+PJ_EXPORT_SYMBOL(pj_getpid)
+PJ_EXPORT_SYMBOL(pj_thread_register)
+PJ_EXPORT_SYMBOL(pj_thread_create)
+PJ_EXPORT_SYMBOL(pj_thread_get_name)
+PJ_EXPORT_SYMBOL(pj_thread_resume)
+PJ_EXPORT_SYMBOL(pj_thread_this)
+PJ_EXPORT_SYMBOL(pj_thread_join)
+PJ_EXPORT_SYMBOL(pj_thread_destroy)
+PJ_EXPORT_SYMBOL(pj_thread_sleep)
+#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0
+PJ_EXPORT_SYMBOL(pj_thread_check_stack)
+PJ_EXPORT_SYMBOL(pj_thread_get_stack_max_usage)
+PJ_EXPORT_SYMBOL(pj_thread_get_stack_info)
+#endif
+PJ_EXPORT_SYMBOL(pj_atomic_create)
+PJ_EXPORT_SYMBOL(pj_atomic_destroy)
+PJ_EXPORT_SYMBOL(pj_atomic_set)
+PJ_EXPORT_SYMBOL(pj_atomic_get)
+PJ_EXPORT_SYMBOL(pj_atomic_inc)
+PJ_EXPORT_SYMBOL(pj_atomic_dec)
+PJ_EXPORT_SYMBOL(pj_thread_local_alloc)
+PJ_EXPORT_SYMBOL(pj_thread_local_free)
+PJ_EXPORT_SYMBOL(pj_thread_local_set)
+PJ_EXPORT_SYMBOL(pj_thread_local_get)
+PJ_EXPORT_SYMBOL(pj_enter_critical_section)
+PJ_EXPORT_SYMBOL(pj_leave_critical_section)
+PJ_EXPORT_SYMBOL(pj_mutex_create)
+PJ_EXPORT_SYMBOL(pj_mutex_lock)
+PJ_EXPORT_SYMBOL(pj_mutex_unlock)
+PJ_EXPORT_SYMBOL(pj_mutex_trylock)
+PJ_EXPORT_SYMBOL(pj_mutex_destroy)
+#if defined(PJ_DEBUG) && PJ_DEBUG != 0
+PJ_EXPORT_SYMBOL(pj_mutex_is_locked)
+#endif
+#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
+PJ_EXPORT_SYMBOL(pj_sem_create)
+PJ_EXPORT_SYMBOL(pj_sem_wait)
+PJ_EXPORT_SYMBOL(pj_sem_trywait)
+PJ_EXPORT_SYMBOL(pj_sem_post)
+PJ_EXPORT_SYMBOL(pj_sem_destroy)
+#endif
+PJ_EXPORT_SYMBOL(pj_gettimeofday)
+PJ_EXPORT_SYMBOL(pj_time_decode)
+#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
+PJ_EXPORT_SYMBOL(pj_gettickcount)
+PJ_EXPORT_SYMBOL(pj_get_timestamp)
+PJ_EXPORT_SYMBOL(pj_get_timestamp_freq)
+PJ_EXPORT_SYMBOL(pj_elapsed_time)
+PJ_EXPORT_SYMBOL(pj_elapsed_usec)
+PJ_EXPORT_SYMBOL(pj_elapsed_nanosec)
+PJ_EXPORT_SYMBOL(pj_elapsed_cycle)
+#endif
+
+	
+/*
+ * pool.h
+ */
+PJ_EXPORT_SYMBOL(pj_pool_create)
+PJ_EXPORT_SYMBOL(pj_pool_release)
+PJ_EXPORT_SYMBOL(pj_pool_getobjname)
+PJ_EXPORT_SYMBOL(pj_pool_reset)
+PJ_EXPORT_SYMBOL(pj_pool_get_capacity)
+PJ_EXPORT_SYMBOL(pj_pool_get_used_size)
+PJ_EXPORT_SYMBOL(pj_pool_alloc)
+PJ_EXPORT_SYMBOL(pj_pool_calloc)
+PJ_EXPORT_SYMBOL(pj_pool_factory_default_policy)
+PJ_EXPORT_SYMBOL(pj_pool_create_int)
+PJ_EXPORT_SYMBOL(pj_pool_init_int)
+PJ_EXPORT_SYMBOL(pj_pool_destroy_int)
+PJ_EXPORT_SYMBOL(pj_caching_pool_init)
+PJ_EXPORT_SYMBOL(pj_caching_pool_destroy)
+
+/*
+ * rand.h
+ */
+PJ_EXPORT_SYMBOL(pj_rand)
+PJ_EXPORT_SYMBOL(pj_srand)
+
+/*
+ * rbtree.h
+ */
+PJ_EXPORT_SYMBOL(pj_rbtree_init)
+PJ_EXPORT_SYMBOL(pj_rbtree_first)
+PJ_EXPORT_SYMBOL(pj_rbtree_last)
+PJ_EXPORT_SYMBOL(pj_rbtree_next)
+PJ_EXPORT_SYMBOL(pj_rbtree_prev)
+PJ_EXPORT_SYMBOL(pj_rbtree_insert)
+PJ_EXPORT_SYMBOL(pj_rbtree_find)
+PJ_EXPORT_SYMBOL(pj_rbtree_erase)
+PJ_EXPORT_SYMBOL(pj_rbtree_max_height)
+PJ_EXPORT_SYMBOL(pj_rbtree_min_height)
+
+/*
+ * sock.h
+ */
+PJ_EXPORT_SYMBOL(PJ_AF_UNIX)
+PJ_EXPORT_SYMBOL(PJ_AF_INET)
+PJ_EXPORT_SYMBOL(PJ_AF_INET6)
+PJ_EXPORT_SYMBOL(PJ_AF_PACKET)
+PJ_EXPORT_SYMBOL(PJ_AF_IRDA)
+PJ_EXPORT_SYMBOL(PJ_SOCK_STREAM)
+PJ_EXPORT_SYMBOL(PJ_SOCK_DGRAM)
+PJ_EXPORT_SYMBOL(PJ_SOCK_RAW)
+PJ_EXPORT_SYMBOL(PJ_SOCK_RDM)
+PJ_EXPORT_SYMBOL(PJ_SOL_SOCKET)
+PJ_EXPORT_SYMBOL(PJ_SOL_IP)
+PJ_EXPORT_SYMBOL(PJ_SOL_TCP)
+PJ_EXPORT_SYMBOL(PJ_SOL_UDP)
+PJ_EXPORT_SYMBOL(PJ_SOL_IPV6)
+PJ_EXPORT_SYMBOL(pj_ntohs)
+PJ_EXPORT_SYMBOL(pj_htons)
+PJ_EXPORT_SYMBOL(pj_ntohl)
+PJ_EXPORT_SYMBOL(pj_htonl)
+PJ_EXPORT_SYMBOL(pj_inet_ntoa)
+PJ_EXPORT_SYMBOL(pj_inet_aton)
+PJ_EXPORT_SYMBOL(pj_inet_addr)
+PJ_EXPORT_SYMBOL(pj_sockaddr_in_set_str_addr)
+PJ_EXPORT_SYMBOL(pj_sockaddr_in_init)
+PJ_EXPORT_SYMBOL(pj_gethostname)
+PJ_EXPORT_SYMBOL(pj_gethostaddr)
+PJ_EXPORT_SYMBOL(pj_sock_socket)
+PJ_EXPORT_SYMBOL(pj_sock_close)
+PJ_EXPORT_SYMBOL(pj_sock_bind)
+PJ_EXPORT_SYMBOL(pj_sock_bind_in)
+#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
+PJ_EXPORT_SYMBOL(pj_sock_listen)
+PJ_EXPORT_SYMBOL(pj_sock_accept)
+PJ_EXPORT_SYMBOL(pj_sock_shutdown)
+#endif
+PJ_EXPORT_SYMBOL(pj_sock_connect)
+PJ_EXPORT_SYMBOL(pj_sock_getpeername)
+PJ_EXPORT_SYMBOL(pj_sock_getsockname)
+PJ_EXPORT_SYMBOL(pj_sock_getsockopt)
+PJ_EXPORT_SYMBOL(pj_sock_setsockopt)
+PJ_EXPORT_SYMBOL(pj_sock_recv)
+PJ_EXPORT_SYMBOL(pj_sock_recvfrom)
+PJ_EXPORT_SYMBOL(pj_sock_send)
+PJ_EXPORT_SYMBOL(pj_sock_sendto)
+
+/*
+ * sock_select.h
+ */
+PJ_EXPORT_SYMBOL(PJ_FD_ZERO)
+PJ_EXPORT_SYMBOL(PJ_FD_SET)
+PJ_EXPORT_SYMBOL(PJ_FD_CLR)
+PJ_EXPORT_SYMBOL(PJ_FD_ISSET)
+PJ_EXPORT_SYMBOL(pj_sock_select)
+
+/*
+ * string.h
+ */
+PJ_EXPORT_SYMBOL(pj_str)
+PJ_EXPORT_SYMBOL(pj_strassign)
+PJ_EXPORT_SYMBOL(pj_strcpy)
+PJ_EXPORT_SYMBOL(pj_strcpy2)
+PJ_EXPORT_SYMBOL(pj_strdup)
+PJ_EXPORT_SYMBOL(pj_strdup_with_null)
+PJ_EXPORT_SYMBOL(pj_strdup2)
+PJ_EXPORT_SYMBOL(pj_strdup3)
+PJ_EXPORT_SYMBOL(pj_strcmp)
+PJ_EXPORT_SYMBOL(pj_strcmp2)
+PJ_EXPORT_SYMBOL(pj_strncmp)
+PJ_EXPORT_SYMBOL(pj_strncmp2)
+PJ_EXPORT_SYMBOL(pj_stricmp)
+PJ_EXPORT_SYMBOL(pj_stricmp2)
+PJ_EXPORT_SYMBOL(pj_strnicmp)
+PJ_EXPORT_SYMBOL(pj_strnicmp2)
+PJ_EXPORT_SYMBOL(pj_strcat)
+PJ_EXPORT_SYMBOL(pj_strltrim)
+PJ_EXPORT_SYMBOL(pj_strrtrim)
+PJ_EXPORT_SYMBOL(pj_strtrim)
+PJ_EXPORT_SYMBOL(pj_create_random_string)
+PJ_EXPORT_SYMBOL(pj_strtoul)
+PJ_EXPORT_SYMBOL(pj_utoa)
+PJ_EXPORT_SYMBOL(pj_utoa_pad)
+
+/*
+ * timer.h
+ */
+PJ_EXPORT_SYMBOL(pj_timer_heap_mem_size)
+PJ_EXPORT_SYMBOL(pj_timer_heap_create)
+PJ_EXPORT_SYMBOL(pj_timer_entry_init)
+PJ_EXPORT_SYMBOL(pj_timer_heap_schedule)
+PJ_EXPORT_SYMBOL(pj_timer_heap_cancel)
+PJ_EXPORT_SYMBOL(pj_timer_heap_count)
+PJ_EXPORT_SYMBOL(pj_timer_heap_earliest_time)
+PJ_EXPORT_SYMBOL(pj_timer_heap_poll)
+
+/*
+ * types.h
+ */
+PJ_EXPORT_SYMBOL(pj_time_val_normalize)
+
diff --git a/jni/pjproject-android/pjlib/src/pj/timer.c b/jni/pjproject-android/pjlib/src/pj/timer.c
new file mode 100644
index 0000000..04e318e
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/timer.c
@@ -0,0 +1,714 @@
+/* $Id: timer.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * The PJLIB's timer heap is based (or more correctly, copied and modied)
+ * from ACE library by Douglas C. Schmidt. ACE is an excellent OO framework
+ * that implements many core patterns for concurrent communication software.
+ * If you're looking for C++ alternative of PJLIB, then ACE is your best
+ * solution.
+ *
+ * You may use this file according to ACE open source terms or PJLIB open
+ * source terms. You can find the fine ACE library at:
+ *  http://www.cs.wustl.edu/~schmidt/ACE.html
+ *
+ * ACE is Copyright (C)1993-2006 Douglas C. Schmidt <d.schmidt@vanderbilt.edu>
+ *
+ * GNU Public License:
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/timer.h>
+#include <pj/pool.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/lock.h>
+#include <pj/log.h>
+#include <pj/rand.h>
+
+#define THIS_FILE	"timer.c"
+
+#define HEAP_PARENT(X)	(X == 0 ? 0 : (((X) - 1) / 2))
+#define HEAP_LEFT(X)	(((X)+(X))+1)
+
+
+#define DEFAULT_MAX_TIMED_OUT_PER_POLL  (64)
+
+enum
+{
+    F_DONT_CALL = 1,
+    F_DONT_ASSERT = 2,
+    F_SET_ID = 4
+};
+
+
+/**
+ * The implementation of timer heap.
+ */
+struct pj_timer_heap_t
+{
+    /** Pool from which the timer heap resize will get the storage from */
+    pj_pool_t *pool;
+
+    /** Maximum size of the heap. */
+    pj_size_t max_size;
+
+    /** Current size of the heap. */
+    pj_size_t cur_size;
+
+    /** Max timed out entries to process per poll. */
+    unsigned max_entries_per_poll;
+
+    /** Lock object. */
+    pj_lock_t *lock;
+
+    /** Autodelete lock. */
+    pj_bool_t auto_delete_lock;
+
+    /**
+     * Current contents of the Heap, which is organized as a "heap" of
+     * pj_timer_entry *'s.  In this context, a heap is a "partially
+     * ordered, almost complete" binary tree, which is stored in an
+     * array.
+     */
+    pj_timer_entry **heap;
+
+    /**
+     * An array of "pointers" that allows each pj_timer_entry in the
+     * <heap_> to be located in O(1) time.  Basically, <timer_id_[i]>
+     * contains the slot in the <heap_> array where an pj_timer_entry
+     * with timer id <i> resides.  Thus, the timer id passed back from
+     * <schedule_entry> is really an slot into the <timer_ids> array.  The
+     * <timer_ids_> array serves two purposes: negative values are
+     * treated as "pointers" for the <freelist_>, whereas positive
+     * values are treated as "pointers" into the <heap_> array.
+     */
+    pj_timer_id_t *timer_ids;
+
+    /**
+     * "Pointer" to the first element in the freelist contained within
+     * the <timer_ids_> array, which is organized as a stack.
+     */
+    pj_timer_id_t timer_ids_freelist;
+
+    /** Callback to be called when a timer expires. */
+    pj_timer_heap_callback *callback;
+
+};
+
+
+
+PJ_INLINE(void) lock_timer_heap( pj_timer_heap_t *ht )
+{
+    if (ht->lock) {
+	pj_lock_acquire(ht->lock);
+    }
+}
+
+PJ_INLINE(void) unlock_timer_heap( pj_timer_heap_t *ht )
+{
+    if (ht->lock) {
+	pj_lock_release(ht->lock);
+    }
+}
+
+
+static void copy_node( pj_timer_heap_t *ht, pj_size_t slot, 
+		       pj_timer_entry *moved_node )
+{
+    PJ_CHECK_STACK();
+
+    // Insert <moved_node> into its new location in the heap.
+    ht->heap[slot] = moved_node;
+    
+    // Update the corresponding slot in the parallel <timer_ids_> array.
+    ht->timer_ids[moved_node->_timer_id] = (int)slot;
+}
+
+static pj_timer_id_t pop_freelist( pj_timer_heap_t *ht )
+{
+    // We need to truncate this to <int> for backwards compatibility.
+    pj_timer_id_t new_id = ht->timer_ids_freelist;
+    
+    PJ_CHECK_STACK();
+
+    // The freelist values in the <timer_ids_> are negative, so we need
+    // to negate them to get the next freelist "pointer."
+    ht->timer_ids_freelist =
+	-ht->timer_ids[ht->timer_ids_freelist];
+    
+    return new_id;
+    
+}
+
+static void push_freelist (pj_timer_heap_t *ht, pj_timer_id_t old_id)
+{
+    PJ_CHECK_STACK();
+
+    // The freelist values in the <timer_ids_> are negative, so we need
+    // to negate them to get the next freelist "pointer."
+    ht->timer_ids[old_id] = -ht->timer_ids_freelist;
+    ht->timer_ids_freelist = old_id;
+}
+
+
+static void reheap_down(pj_timer_heap_t *ht, pj_timer_entry *moved_node,
+                        size_t slot, size_t child)
+{
+    PJ_CHECK_STACK();
+
+    // Restore the heap property after a deletion.
+    
+    while (child < ht->cur_size)
+    {
+	// Choose the smaller of the two children.
+	if (child + 1 < ht->cur_size
+	    && PJ_TIME_VAL_LT(ht->heap[child + 1]->_timer_value, ht->heap[child]->_timer_value))
+	    child++;
+	
+	// Perform a <copy> if the child has a larger timeout value than
+	// the <moved_node>.
+	if (PJ_TIME_VAL_LT(ht->heap[child]->_timer_value, moved_node->_timer_value))
+        {
+	    copy_node( ht, slot, ht->heap[child]);
+	    slot = child;
+	    child = HEAP_LEFT(child);
+        }
+	else
+	    // We've found our location in the heap.
+	    break;
+    }
+    
+    copy_node( ht, slot, moved_node);
+}
+
+static void reheap_up( pj_timer_heap_t *ht, pj_timer_entry *moved_node,
+		       size_t slot, size_t parent)
+{
+    // Restore the heap property after an insertion.
+    
+    while (slot > 0)
+    {
+	// If the parent node is greater than the <moved_node> we need
+	// to copy it down.
+	if (PJ_TIME_VAL_LT(moved_node->_timer_value, ht->heap[parent]->_timer_value))
+        {
+	    copy_node(ht, slot, ht->heap[parent]);
+	    slot = parent;
+	    parent = HEAP_PARENT(slot);
+        }
+	else
+	    break;
+    }
+    
+    // Insert the new node into its proper resting place in the heap and
+    // update the corresponding slot in the parallel <timer_ids> array.
+    copy_node(ht, slot, moved_node);
+}
+
+
+static pj_timer_entry * remove_node( pj_timer_heap_t *ht, size_t slot)
+{
+    pj_timer_entry *removed_node = ht->heap[slot];
+    
+    // Return this timer id to the freelist.
+    push_freelist( ht, removed_node->_timer_id );
+    
+    // Decrement the size of the heap by one since we're removing the
+    // "slot"th node.
+    ht->cur_size--;
+    
+    // Set the ID
+    removed_node->_timer_id = -1;
+
+    // Only try to reheapify if we're not deleting the last entry.
+    
+    if (slot < ht->cur_size)
+    {
+	pj_size_t parent;
+	pj_timer_entry *moved_node = ht->heap[ht->cur_size];
+	
+	// Move the end node to the location being removed and update
+	// the corresponding slot in the parallel <timer_ids> array.
+	copy_node( ht, slot, moved_node);
+	
+	// If the <moved_node->time_value_> is great than or equal its
+	// parent it needs be moved down the heap.
+	parent = HEAP_PARENT (slot);
+	
+	if (PJ_TIME_VAL_GTE(moved_node->_timer_value, ht->heap[parent]->_timer_value))
+	    reheap_down( ht, moved_node, slot, HEAP_LEFT(slot));
+	else
+	    reheap_up( ht, moved_node, slot, parent);
+    }
+    
+    return removed_node;
+}
+
+static void grow_heap(pj_timer_heap_t *ht)
+{
+    // All the containers will double in size from max_size_
+    size_t new_size = ht->max_size * 2;
+    pj_timer_id_t *new_timer_ids;
+    pj_size_t i;
+    
+    // First grow the heap itself.
+    
+    pj_timer_entry **new_heap = 0;
+    
+    new_heap = (pj_timer_entry**) 
+    	       pj_pool_alloc(ht->pool, sizeof(pj_timer_entry*) * new_size);
+    memcpy(new_heap, ht->heap, ht->max_size * sizeof(pj_timer_entry*));
+    //delete [] this->heap_;
+    ht->heap = new_heap;
+    
+    // Grow the array of timer ids.
+    
+    new_timer_ids = 0;
+    new_timer_ids = (pj_timer_id_t*)
+    		    pj_pool_alloc(ht->pool, new_size * sizeof(pj_timer_id_t));
+    
+    memcpy( new_timer_ids, ht->timer_ids, ht->max_size * sizeof(pj_timer_id_t));
+    
+    //delete [] timer_ids_;
+    ht->timer_ids = new_timer_ids;
+    
+    // And add the new elements to the end of the "freelist".
+    for (i = ht->max_size; i < new_size; i++)
+	ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
+    
+    ht->max_size = new_size;
+}
+
+static void insert_node(pj_timer_heap_t *ht, pj_timer_entry *new_node)
+{
+    if (ht->cur_size + 2 >= ht->max_size)
+	grow_heap(ht);
+    
+    reheap_up( ht, new_node, ht->cur_size, HEAP_PARENT(ht->cur_size));
+    ht->cur_size++;
+}
+
+
+static pj_status_t schedule_entry( pj_timer_heap_t *ht,
+				   pj_timer_entry *entry, 
+				   const pj_time_val *future_time )
+{
+    if (ht->cur_size < ht->max_size)
+    {
+	// Obtain the next unique sequence number.
+	// Set the entry
+	entry->_timer_id = pop_freelist(ht);
+	entry->_timer_value = *future_time;
+	insert_node( ht, entry);
+	return 0;
+    }
+    else
+	return -1;
+}
+
+
+static int cancel( pj_timer_heap_t *ht, 
+		   pj_timer_entry *entry, 
+		   unsigned flags)
+{
+  long timer_node_slot;
+
+  PJ_CHECK_STACK();
+
+  // Check to see if the timer_id is out of range
+  if (entry->_timer_id < 0 || (pj_size_t)entry->_timer_id > ht->max_size)
+    return 0;
+
+  timer_node_slot = ht->timer_ids[entry->_timer_id];
+
+  if (timer_node_slot < 0) // Check to see if timer_id is still valid.
+    return 0;
+
+  if (entry != ht->heap[timer_node_slot])
+    {
+      if ((flags & F_DONT_ASSERT) == 0)
+	  pj_assert(entry == ht->heap[timer_node_slot]);
+      return 0;
+    }
+  else
+    {
+      remove_node( ht, timer_node_slot);
+
+      if ((flags & F_DONT_CALL) == 0)
+        // Call the close hook.
+	(*ht->callback)(ht, entry);
+      return 1;
+    }
+}
+
+
+/*
+ * Calculate memory size required to create a timer heap.
+ */
+PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
+{
+    return /* size of the timer heap itself: */
+           sizeof(pj_timer_heap_t) + 
+           /* size of each entry: */
+           (count+2) * (sizeof(pj_timer_entry*)+sizeof(pj_timer_id_t)) +
+           /* lock, pool etc: */
+           132;
+}
+
+/*
+ * Create a new timer heap.
+ */
+PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
+					  pj_size_t size,
+                                          pj_timer_heap_t **p_heap)
+{
+    pj_timer_heap_t *ht;
+    pj_size_t i;
+
+    PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
+
+    *p_heap = NULL;
+
+    /* Magic? */
+    size += 2;
+
+    /* Allocate timer heap data structure from the pool */
+    ht = PJ_POOL_ALLOC_T(pool, pj_timer_heap_t);
+    if (!ht)
+        return PJ_ENOMEM;
+
+    /* Initialize timer heap sizes */
+    ht->max_size = size;
+    ht->cur_size = 0;
+    ht->max_entries_per_poll = DEFAULT_MAX_TIMED_OUT_PER_POLL;
+    ht->timer_ids_freelist = 1;
+    ht->pool = pool;
+
+    /* Lock. */
+    ht->lock = NULL;
+    ht->auto_delete_lock = 0;
+
+    // Create the heap array.
+    ht->heap = (pj_timer_entry**)
+    	       pj_pool_alloc(pool, sizeof(pj_timer_entry*) * size);
+    if (!ht->heap)
+        return PJ_ENOMEM;
+
+    // Create the parallel
+    ht->timer_ids = (pj_timer_id_t *)
+    		    pj_pool_alloc( pool, sizeof(pj_timer_id_t) * size);
+    if (!ht->timer_ids)
+        return PJ_ENOMEM;
+
+    // Initialize the "freelist," which uses negative values to
+    // distinguish freelist elements from "pointers" into the <heap_>
+    // array.
+    for (i=0; i<size; ++i)
+	ht->timer_ids[i] = -((pj_timer_id_t) (i + 1));
+
+    *p_heap = ht;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
+{
+    if (ht->lock && ht->auto_delete_lock) {
+        pj_lock_destroy(ht->lock);
+        ht->lock = NULL;
+    }
+}
+
+PJ_DEF(void) pj_timer_heap_set_lock(  pj_timer_heap_t *ht,
+                                      pj_lock_t *lock,
+                                      pj_bool_t auto_del )
+{
+    if (ht->lock && ht->auto_delete_lock)
+        pj_lock_destroy(ht->lock);
+
+    ht->lock = lock;
+    ht->auto_delete_lock = auto_del;
+}
+
+
+PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
+                                                          unsigned count )
+{
+    unsigned old_count = ht->max_entries_per_poll;
+    ht->max_entries_per_poll = count;
+    return old_count;
+}
+
+PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+                                             int id,
+                                             void *user_data,
+                                             pj_timer_heap_callback *cb )
+{
+    pj_assert(entry && cb);
+
+    entry->_timer_id = -1;
+    entry->id = id;
+    entry->user_data = user_data;
+    entry->cb = cb;
+    entry->_grp_lock = NULL;
+
+    return entry;
+}
+
+#if PJ_TIMER_DEBUG
+static pj_status_t schedule_w_grp_lock_dbg(pj_timer_heap_t *ht,
+                                           pj_timer_entry *entry,
+                                           const pj_time_val *delay,
+                                           pj_bool_t set_id,
+                                           int id_val,
+					   pj_grp_lock_t *grp_lock,
+					   const char *src_file,
+					   int src_line)
+#else
+static pj_status_t schedule_w_grp_lock(pj_timer_heap_t *ht,
+                                       pj_timer_entry *entry,
+                                       const pj_time_val *delay,
+                                       pj_bool_t set_id,
+                                       int id_val,
+                                       pj_grp_lock_t *grp_lock)
+#endif
+{
+    pj_status_t status;
+    pj_time_val expires;
+
+    PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
+    PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL);
+
+    /* Prevent same entry from being scheduled more than once */
+    PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);
+
+#if PJ_TIMER_DEBUG
+    entry->src_file = src_file;
+    entry->src_line = src_line;
+#endif
+    pj_gettickcount(&expires);
+    PJ_TIME_VAL_ADD(expires, *delay);
+    
+    lock_timer_heap(ht);
+    status = schedule_entry(ht, entry, &expires);
+    if (status == PJ_SUCCESS) {
+	if (set_id)
+	    entry->id = id_val;
+	entry->_grp_lock = grp_lock;
+	if (entry->_grp_lock) {
+	    pj_grp_lock_add_ref(entry->_grp_lock);
+	}
+    }
+    unlock_timer_heap(ht);
+
+    return status;
+}
+
+
+#if PJ_TIMER_DEBUG
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_dbg( pj_timer_heap_t *ht,
+						pj_timer_entry *entry,
+						const pj_time_val *delay,
+						const char *src_file,
+						int src_line)
+{
+    return schedule_w_grp_lock_dbg(ht, entry, delay, PJ_FALSE, 1, NULL,
+                                   src_file, src_line);
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock_dbg(
+						pj_timer_heap_t *ht,
+						pj_timer_entry *entry,
+						const pj_time_val *delay,
+						int id_val,
+                                                pj_grp_lock_t *grp_lock,
+						const char *src_file,
+						int src_line)
+{
+    return schedule_w_grp_lock_dbg(ht, entry, delay, PJ_TRUE, id_val,
+                                   grp_lock, src_file, src_line);
+}
+
+#else
+PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+                                            pj_timer_entry *entry,
+                                            const pj_time_val *delay)
+{
+    return schedule_w_grp_lock(ht, entry, delay, PJ_FALSE, 1, NULL);
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht,
+                                                      pj_timer_entry *entry,
+                                                      const pj_time_val *delay,
+                                                      int id_val,
+                                                      pj_grp_lock_t *grp_lock)
+{
+    return schedule_w_grp_lock(ht, entry, delay, PJ_TRUE, id_val, grp_lock);
+}
+#endif
+
+static int cancel_timer(pj_timer_heap_t *ht,
+			pj_timer_entry *entry,
+			unsigned flags,
+			int id_val)
+{
+    int count;
+
+    PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
+
+    lock_timer_heap(ht);
+    count = cancel(ht, entry, flags | F_DONT_CALL);
+    if (flags & F_SET_ID) {
+	entry->id = id_val;
+    }
+    if (entry->_grp_lock) {
+	pj_grp_lock_t *grp_lock = entry->_grp_lock;
+	entry->_grp_lock = NULL;
+	pj_grp_lock_dec_ref(grp_lock);
+    }
+    unlock_timer_heap(ht);
+
+    return count;
+}
+
+PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+				  pj_timer_entry *entry)
+{
+    return cancel_timer(ht, entry, 0, 0);
+}
+
+PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+                                           pj_timer_entry *entry,
+                                           int id_val)
+{
+    return cancel_timer(ht, entry, F_SET_ID | F_DONT_ASSERT, id_val);
+}
+
+PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, 
+                                     pj_time_val *next_delay )
+{
+    pj_time_val now;
+    unsigned count;
+
+    PJ_ASSERT_RETURN(ht, 0);
+
+    lock_timer_heap(ht);
+    if (!ht->cur_size && next_delay) {
+	next_delay->sec = next_delay->msec = PJ_MAXINT32;
+        unlock_timer_heap(ht);
+	return 0;
+    }
+
+    count = 0;
+    pj_gettickcount(&now);
+
+    while ( ht->cur_size && 
+	    PJ_TIME_VAL_LTE(ht->heap[0]->_timer_value, now) &&
+            count < ht->max_entries_per_poll ) 
+    {
+	pj_timer_entry *node = remove_node(ht, 0);
+	pj_grp_lock_t *grp_lock;
+
+	++count;
+
+	grp_lock = node->_grp_lock;
+	node->_grp_lock = NULL;
+
+	unlock_timer_heap(ht);
+
+	PJ_RACE_ME(5);
+
+	if (node->cb)
+	    (*node->cb)(ht, node);
+
+	if (grp_lock)
+	    pj_grp_lock_dec_ref(grp_lock);
+
+	lock_timer_heap(ht);
+    }
+    if (ht->cur_size && next_delay) {
+	*next_delay = ht->heap[0]->_timer_value;
+	PJ_TIME_VAL_SUB(*next_delay, now);
+	if (next_delay->sec < 0 || next_delay->msec < 0)
+	    next_delay->sec = next_delay->msec = 0;
+    } else if (next_delay) {
+	next_delay->sec = next_delay->msec = PJ_MAXINT32;
+    }
+    unlock_timer_heap(ht);
+
+    return count;
+}
+
+PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
+{
+    PJ_ASSERT_RETURN(ht, 0);
+
+    return ht->cur_size;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
+					         pj_time_val *timeval)
+{
+    pj_assert(ht->cur_size != 0);
+    if (ht->cur_size == 0)
+        return PJ_ENOTFOUND;
+
+    lock_timer_heap(ht);
+    *timeval = ht->heap[0]->_timer_value;
+    unlock_timer_heap(ht);
+
+    return PJ_SUCCESS;
+}
+
+#if PJ_TIMER_DEBUG
+PJ_DEF(void) pj_timer_heap_dump(pj_timer_heap_t *ht)
+{
+    lock_timer_heap(ht);
+
+    PJ_LOG(3,(THIS_FILE, "Dumping timer heap:"));
+    PJ_LOG(3,(THIS_FILE, "  Cur size: %d entries, max: %d",
+			 (int)ht->cur_size, (int)ht->max_size));
+
+    if (ht->cur_size) {
+	unsigned i;
+	pj_time_val now;
+
+	PJ_LOG(3,(THIS_FILE, "  Entries: "));
+	PJ_LOG(3,(THIS_FILE, "    _id\tId\tElapsed\tSource"));
+	PJ_LOG(3,(THIS_FILE, "    ----------------------------------"));
+
+	pj_gettickcount(&now);
+
+	for (i=0; i<(unsigned)ht->cur_size; ++i) {
+	    pj_timer_entry *e = ht->heap[i];
+	    pj_time_val delta;
+
+	    if (PJ_TIME_VAL_LTE(e->_timer_value, now))
+		delta.sec = delta.msec = 0;
+	    else {
+		delta = e->_timer_value;
+		PJ_TIME_VAL_SUB(delta, now);
+	    }
+
+	    PJ_LOG(3,(THIS_FILE, "    %d\t%d\t%d.%03d\t%s:%d",
+		      e->_timer_id, e->id,
+		      (int)delta.sec, (int)delta.msec,
+		      e->src_file, e->src_line));
+	}
+    }
+
+    unlock_timer_heap(ht);
+}
+#endif
+
diff --git a/jni/pjproject-android/pjlib/src/pj/timer_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/timer_symbian.cpp
new file mode 100644
index 0000000..8b18525
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/timer_symbian.cpp
@@ -0,0 +1,475 @@
+/* $Id: timer_symbian.cpp 4374 2013-02-27 07:15:57Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/timer.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/lock.h>
+
+#include "os_symbian.h"
+
+
+#define DEFAULT_MAX_TIMED_OUT_PER_POLL  (64)
+
+// Maximum number of miliseconds that RTimer.At() supports
+#define MAX_RTIMER_INTERVAL		2147
+
+/* Absolute maximum number of timer entries */
+#ifndef PJ_SYMBIAN_TIMER_MAX_COUNT
+#  define PJ_SYMBIAN_TIMER_MAX_COUNT	65535
+#endif
+
+/* Get the number of free slots in the timer heap */
+#define FREECNT(th)	(th->max_size - th->cur_size)
+
+// Forward declaration
+class CPjTimerEntry;
+
+/**
+ * The implementation of timer heap.
+ */
+struct pj_timer_heap_t
+{
+    /** Maximum size of the heap. */
+    pj_size_t max_size;
+
+    /** Current size of the heap. */
+    pj_size_t cur_size;
+
+    /** Array of timer entries. A scheduled timer will occupy one slot, and
+     *  the slot number will be saved in entry->_timer_id
+     */
+    CPjTimerEntry **entries;
+    
+    /** Array of free slot indexes in the "entries" array */
+    int *free_slots;
+};
+
+/**
+ * Active object for each timer entry.
+ */
+class CPjTimerEntry : public CActive 
+{
+public:
+    pj_timer_entry  *entry_;
+    
+    static CPjTimerEntry* NewL(	pj_timer_heap_t *timer_heap,
+    				pj_timer_entry *entry,
+    				const pj_time_val *delay);
+    
+    ~CPjTimerEntry();
+    
+    virtual void RunL();
+    virtual void DoCancel();
+
+private:	
+    pj_timer_heap_t *timer_heap_;
+    RTimer	     rtimer_;
+    pj_uint32_t	     interval_left_;
+    
+    CPjTimerEntry(pj_timer_heap_t *timer_heap, pj_timer_entry *entry);
+    void ConstructL(const pj_time_val *delay);
+    void Schedule();
+};
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * Implementation.
+ */
+
+/* Grow timer heap to the specified size */
+static pj_status_t realloc_timer_heap(pj_timer_heap_t *th, pj_size_t new_size)
+{
+    typedef CPjTimerEntry *entry_ptr;
+    CPjTimerEntry **entries = NULL;
+    int *free_slots = NULL;
+    unsigned i, j;
+ 
+    if (new_size > PJ_SYMBIAN_TIMER_MAX_COUNT) {
+	/* Just some sanity limit */
+	new_size = PJ_SYMBIAN_TIMER_MAX_COUNT;
+	if (new_size <= th->max_size) {
+	    /* We've grown large enough */
+	    pj_assert(!"Too many timer heap entries");
+	    return PJ_ETOOMANY;
+	}
+    }
+    
+    /* Allocate entries, move entries from the old array if there is one */
+    entries = new entry_ptr[new_size];
+    if (th->entries) {
+	pj_memcpy(entries, th->entries, th->max_size * sizeof(th->entries[0]));
+    }
+    /* Initialize the remaining new area */
+    pj_bzero(&entries[th->max_size], 
+	    (new_size - th->max_size) * sizeof(th->entries[0]));
+    
+    /* Allocate free slots array */
+    free_slots = new int[new_size];
+    if (th->free_slots) {
+	pj_memcpy(free_slots, th->free_slots, 
+		  FREECNT(th) * sizeof(th->free_slots[0]));
+    }
+    /* Initialize the remaining new area */
+    for (i=FREECNT(th), j=th->max_size; j<new_size; ++i, ++j) {
+	free_slots[i] = j;
+    }
+    for ( ; i<new_size; ++i) {
+	free_slots[i] = -1;
+    }
+    
+    /* Apply */
+    delete [] th->entries;
+    th->entries = entries;
+    th->max_size = new_size;
+    delete [] th->free_slots;
+    th->free_slots = free_slots;
+
+    return PJ_SUCCESS;
+}
+
+/* Allocate and register an entry to timer heap for newly scheduled entry */
+static pj_status_t add_entry(pj_timer_heap_t *th, CPjTimerEntry *entry)
+{
+    pj_status_t status;
+    int slot;
+    
+    /* Check that there's still capacity left in the timer heap */
+    if (FREECNT(th) < 1) {
+	// Grow the timer heap twice the capacity
+	status = realloc_timer_heap(th, th->max_size * 2);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+    
+    /* Allocate one free slot. Use LIFO */
+    slot = th->free_slots[FREECNT(th)-1];
+    PJ_ASSERT_RETURN((slot >= 0) && (slot < (int)th->max_size) && 
+		     (th->entries[slot]==NULL), PJ_EBUG);
+    
+    th->free_slots[FREECNT(th)-1] = -1;
+    th->entries[slot] = entry;
+    entry->entry_->_timer_id = slot;
+    ++th->cur_size;
+    
+    return PJ_SUCCESS;
+}
+
+/* Free a slot when an entry's timer has elapsed or cancel */
+static pj_status_t remove_entry(pj_timer_heap_t *th, CPjTimerEntry *entry)
+{
+    int slot = entry->entry_->_timer_id;
+    
+    PJ_ASSERT_RETURN(slot >= 0 && slot < (int)th->max_size, PJ_EBUG);
+    PJ_ASSERT_RETURN(FREECNT(th) < th->max_size, PJ_EBUG);
+    PJ_ASSERT_RETURN(th->entries[slot]==entry, PJ_EBUG);
+    PJ_ASSERT_RETURN(th->free_slots[FREECNT(th)]==-1, PJ_EBUG);
+    
+    th->entries[slot] = NULL;
+    th->free_slots[FREECNT(th)] = slot;
+    entry->entry_->_timer_id = -1;
+    --th->cur_size;
+    
+    return PJ_SUCCESS;
+}
+
+
+CPjTimerEntry::CPjTimerEntry(pj_timer_heap_t *timer_heap,
+			     pj_timer_entry *entry)
+: CActive(PJ_SYMBIAN_TIMER_PRIORITY), entry_(entry), timer_heap_(timer_heap), 
+  interval_left_(0)
+{
+}
+
+CPjTimerEntry::~CPjTimerEntry() 
+{
+    Cancel();
+    rtimer_.Close();
+}
+
+void CPjTimerEntry::Schedule()
+{
+    pj_int32_t interval;
+    
+    if (interval_left_ > MAX_RTIMER_INTERVAL) {
+	interval = MAX_RTIMER_INTERVAL;
+    } else {
+	interval = interval_left_;
+    }
+    
+    interval_left_ -= interval;
+    rtimer_.After(iStatus, interval * 1000);
+    SetActive();
+}
+
+void CPjTimerEntry::ConstructL(const pj_time_val *delay) 
+{
+    rtimer_.CreateLocal();
+    CActiveScheduler::Add(this);
+    
+    interval_left_ = PJ_TIME_VAL_MSEC(*delay);
+    Schedule();
+}
+
+CPjTimerEntry* CPjTimerEntry::NewL(pj_timer_heap_t *timer_heap,
+				   pj_timer_entry *entry,
+				   const pj_time_val *delay) 
+{
+    CPjTimerEntry *self = new CPjTimerEntry(timer_heap, entry);
+    CleanupStack::PushL(self);
+    self->ConstructL(delay);
+    CleanupStack::Pop(self);
+
+    return self;
+}
+
+void CPjTimerEntry::RunL() 
+{
+    if (interval_left_ > 0) {
+	Schedule();
+	return;
+    }
+    
+    remove_entry(timer_heap_, this);
+    entry_->cb(timer_heap_, entry_);
+    
+    // Finger's crossed!
+    delete this;
+}
+
+void CPjTimerEntry::DoCancel() 
+{
+    /* It's possible that _timer_id is -1, see schedule(). In this case,
+     * the entry has not been added to the timer heap, so don't remove
+     * it.
+     */
+    if (entry_ && entry_->_timer_id != -1)
+	remove_entry(timer_heap_, this);
+    
+    rtimer_.Cancel();
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+
+/*
+ * Calculate memory size required to create a timer heap.
+ */
+PJ_DEF(pj_size_t) pj_timer_heap_mem_size(pj_size_t count)
+{
+    return /* size of the timer heap itself: */
+           sizeof(pj_timer_heap_t) + 
+           /* size of each entry: */
+           (count+2) * (sizeof(void*)+sizeof(int)) +
+           /* lock, pool etc: */
+           132;
+}
+
+/*
+ * Create a new timer heap.
+ */
+PJ_DEF(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
+					  pj_size_t size,
+                                          pj_timer_heap_t **p_heap)
+{
+    pj_timer_heap_t *ht;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && p_heap, PJ_EINVAL);
+
+    *p_heap = NULL;
+
+    /* Allocate timer heap data structure from the pool */
+    ht = PJ_POOL_ZALLOC_T(pool, pj_timer_heap_t);
+    if (!ht)
+        return PJ_ENOMEM;
+
+    /* Allocate slots */
+    status = realloc_timer_heap(ht, size);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    *p_heap = ht;
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pj_timer_heap_destroy( pj_timer_heap_t *ht )
+{
+    /* Cancel and delete pending active objects */
+    if (ht->entries) {
+	unsigned i;
+	for (i=0; i<ht->max_size; ++i) {
+	    if (ht->entries[i]) {
+		ht->entries[i]->entry_ = NULL;
+		ht->entries[i]->Cancel();
+		delete ht->entries[i];
+		ht->entries[i] = NULL;
+	    }
+	}
+    }
+    
+    delete [] ht->entries;
+    delete [] ht->free_slots;
+    
+    ht->entries = NULL;
+    ht->free_slots = NULL;
+}
+
+PJ_DEF(void) pj_timer_heap_set_lock(  pj_timer_heap_t *ht,
+                                      pj_lock_t *lock,
+                                      pj_bool_t auto_del )
+{
+    PJ_UNUSED_ARG(ht);
+    if (auto_del)
+    	pj_lock_destroy(lock);
+}
+
+
+PJ_DEF(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
+                                                          unsigned count )
+{
+    /* Not applicable */
+    PJ_UNUSED_ARG(count);
+    return ht->max_size;
+}
+
+PJ_DEF(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+                                             int id,
+                                             void *user_data,
+                                             pj_timer_heap_callback *cb )
+{
+    pj_assert(entry && cb);
+
+    entry->_timer_id = -1;
+    entry->id = id;
+    entry->user_data = user_data;
+    entry->cb = cb;
+
+    return entry;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+					    pj_timer_entry *entry, 
+					    const pj_time_val *delay)
+{
+    CPjTimerEntry *timerObj;
+    pj_status_t status;
+    
+    PJ_ASSERT_RETURN(ht && entry && delay, PJ_EINVAL);
+    PJ_ASSERT_RETURN(entry->cb != NULL, PJ_EINVAL);
+
+    /* Prevent same entry from being scheduled more than once */
+    PJ_ASSERT_RETURN(entry->_timer_id < 1, PJ_EINVALIDOP);
+
+    entry->_timer_id = -1;
+    
+    timerObj = CPjTimerEntry::NewL(ht, entry, delay);
+    status = add_entry(ht, timerObj);
+    if (status != PJ_SUCCESS) {
+	timerObj->Cancel();
+	delete timerObj;
+	return status;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_schedule_w_grp_lock(pj_timer_heap_t *ht,
+                                                      pj_timer_entry *entry,
+                                                      const pj_time_val *delay,
+                                                      int id_val,
+                                                      pj_grp_lock_t *grp_lock)
+{
+    pj_status_t status;
+	    
+    PJ_UNUSED_ARG(grp_lock);
+
+    status = pj_timer_heap_schedule(ht, entry, delay);
+    
+    if (status == PJ_SUCCESS)
+    	entry->id = id_val;
+    
+    return status;
+}
+
+PJ_DEF(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+				  pj_timer_entry *entry)
+{
+    PJ_ASSERT_RETURN(ht && entry, PJ_EINVAL);
+    
+    if (entry->_timer_id >= 0 && entry->_timer_id < (int)ht->max_size) {
+    	CPjTimerEntry *timerObj = ht->entries[entry->_timer_id];
+    	if (timerObj) {
+    	    timerObj->Cancel();
+    	    delete timerObj;
+    	    return 1;
+    	} else {
+    	    return 0;
+    	}
+    } else {
+    	return 0;
+    }
+}
+
+PJ_DEF(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+                                           pj_timer_entry *entry,
+                                           int id_val)
+{
+    int count = pj_timer_heap_cancel(ht, entry);
+    if (count == 1)
+    	entry->id = id_val;
+    
+    return count;
+}
+
+PJ_DEF(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, 
+                                     pj_time_val *next_delay )
+{
+    /* Polling is not necessary on Symbian, since all async activities
+     * are registered to active scheduler.
+     */
+    PJ_UNUSED_ARG(ht);
+    if (next_delay) {
+    	next_delay->sec = 1;
+    	next_delay->msec = 0;
+    }
+    return 0;
+}
+
+PJ_DEF(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht )
+{
+    PJ_ASSERT_RETURN(ht, 0);
+
+    return ht->cur_size;
+}
+
+PJ_DEF(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t * ht,
+					         pj_time_val *timeval)
+{
+    /* We don't support this! */
+    PJ_UNUSED_ARG(ht);
+    
+    timeval->sec = 1;
+    timeval->msec = 0;
+    
+    return PJ_SUCCESS;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pj/types.c b/jni/pjproject-android/pjlib/src/pj/types.c
new file mode 100644
index 0000000..212afcf
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/types.c
@@ -0,0 +1,46 @@
+/* $Id: types.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/types.h>
+#include <pj/os.h>
+
+PJ_DEF(void) pj_time_val_normalize(pj_time_val *t)
+{
+    PJ_CHECK_STACK();
+
+    if (t->msec >= 1000) {
+	t->sec += (t->msec / 1000);
+	t->msec = (t->msec % 1000);
+    }
+    else if (t->msec <= -1000) {
+	do {
+	    t->sec--;
+	    t->msec += 1000;
+        } while (t->msec <= -1000);
+    }
+
+    if (t->sec >= 1 && t->msec < 0) {
+	t->sec--;
+	t->msec += 1000;
+
+    } else if (t->sec < 0 && t->msec > 0) {
+	t->sec++;
+	t->msec -= 1000;
+    }
+}
diff --git a/jni/pjproject-android/pjlib/src/pj/unicode_symbian.cpp b/jni/pjproject-android/pjlib/src/pj/unicode_symbian.cpp
new file mode 100644
index 0000000..be109e7
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/unicode_symbian.cpp
@@ -0,0 +1,76 @@
+/* $Id: unicode_symbian.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/unicode.h>
+
+#include "os_symbian.h"
+
+
+/*
+ * Convert ANSI strings to Unicode strings.
+ */
+PJ_DEF(wchar_t*) pj_ansi_to_unicode( const char *str, pj_size_t len,
+				     wchar_t *wbuf, pj_size_t wbuf_count)
+{
+    TPtrC8 aForeign((const TUint8*)str, (TInt)len);
+    TPtr16 aUnicode((TUint16*)wbuf, (TInt)(wbuf_count-1));
+    TInt left;
+
+    left = PjSymbianOS::Instance()->ConvertToUnicode(aUnicode, aForeign);
+
+    if (left != 0) {
+	// Error, or there are unconvertable characters
+	*wbuf = 0;
+    } else {
+	if (len < wbuf_count)
+	    wbuf[len] = 0;
+	else
+	    wbuf[len-1] = 0;
+    }
+
+    return wbuf;
+}
+
+
+/*
+ * Convert Unicode string to ANSI string.
+ */
+PJ_DEF(char*) pj_unicode_to_ansi( const wchar_t *wstr, pj_size_t len,
+				  char *buf, pj_size_t buf_size)
+{
+    TPtrC16 aUnicode((const TUint16*)wstr, (TInt)len);
+    TPtr8 aForeign((TUint8*)buf, (TInt)(buf_size-1));
+    TInt left;
+
+    left = PjSymbianOS::Instance()->ConvertFromUnicode(aForeign, aUnicode);
+
+    if (left != 0) {
+	// Error, or there are unconvertable characters
+	buf[0] = '\0';
+    } else {
+	if (len < buf_size)
+	    buf[len] = '\0';
+	else
+	    buf[len-1] = '\0';
+    }
+
+    return buf;
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pj/unicode_win32.c b/jni/pjproject-android/pjlib/src/pj/unicode_win32.c
new file mode 100644
index 0000000..dda63be
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pj/unicode_win32.c
@@ -0,0 +1,60 @@
+/* $Id: unicode_win32.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/unicode.h>
+#include <pj/assert.h>
+#include <pj/string.h>
+#include <windows.h>
+
+
+PJ_DEF(wchar_t*) pj_ansi_to_unicode(const char *s, int len,
+				    wchar_t *buf, int buf_count)
+{
+    PJ_ASSERT_RETURN(s && buf, NULL);
+
+    len = MultiByteToWideChar(CP_ACP, 0, s, len, 
+			      buf, buf_count);
+    if (buf_count) {
+	if (len < buf_count)
+	    buf[len] = 0;
+	else
+	    buf[len-1] = 0;
+    }
+
+    return buf;
+}
+
+
+PJ_DEF(char*) pj_unicode_to_ansi( const wchar_t *wstr, pj_ssize_t len,
+				  char *buf, int buf_size)
+{
+    PJ_ASSERT_RETURN(wstr && buf, NULL);
+
+    len = WideCharToMultiByte(CP_ACP, 0, wstr, (int)len, buf, buf_size, 
+			      NULL, NULL);
+    if (buf_size) {
+	if (len < buf_size)
+	    buf[len] = '\0';
+	else
+	    buf[len-1] = '\0';
+    }
+
+    return buf;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib++-test/main.cpp b/jni/pjproject-android/pjlib/src/pjlib++-test/main.cpp
new file mode 100644
index 0000000..1675644
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib++-test/main.cpp
@@ -0,0 +1,62 @@
+/* $Id */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj++/file.hpp>
+#include <pj++/list.hpp>
+#include <pj++/lock.hpp>
+#include <pj++/hash.hpp>
+#include <pj++/os.hpp>
+#include <pj++/proactor.hpp>
+#include <pj++/sock.hpp>
+#include <pj++/string.hpp>
+#include <pj++/timer.hpp>
+#include <pj++/tree.hpp>
+
+class My_Async_Op : public Pj_Async_Op
+{
+};
+
+class My_Event_Handler : public Pj_Event_Handler
+{
+};
+
+int main()
+{
+    Pjlib lib;
+    Pj_Caching_Pool mem;
+    Pj_Pool the_pool;
+    Pj_Pool *pool = &the_pool;
+    
+    the_pool.attach(mem.create_pool(4000,4000));
+
+    Pj_Semaphore_Lock lsem(pool);
+    Pj_Semaphore_Lock *plsem;
+
+    plsem = new(pool) Pj_Semaphore_Lock(pool);
+    delete plsem;
+
+    Pj_Proactor proactor(pool, 100, 100);
+
+    My_Event_Handler *event_handler = new(the_pool) My_Event_Handler;
+    proactor.register_socket_handler(pool, event_handler);
+    proactor.unregister_handler(event_handler);
+
+    return 0;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-samples/except.c b/jni/pjproject-android/pjlib/src/pjlib-samples/except.c
new file mode 100644
index 0000000..2e7aa33
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-samples/except.c
@@ -0,0 +1,82 @@
+/* $Id: except.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/except.h>
+#include <pj/rand.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ * \page page_pjlib_samples_except_c Example: Exception Handling
+ *
+ * Below is sample program to demonstrate how to use exception handling.
+ *
+ * \includelineno pjlib-samples/except.c
+ */
+
+static pj_exception_id_t NO_MEMORY, OTHER_EXCEPTION;
+
+static void randomly_throw_exception()
+{
+    if (pj_rand() % 2)
+        PJ_THROW(OTHER_EXCEPTION);
+}
+
+static void *my_malloc(size_t size)
+{
+    void *ptr = malloc(size);
+    if (!ptr)
+        PJ_THROW(NO_MEMORY);
+    return ptr;
+}
+
+static int test_exception()
+{
+    PJ_USE_EXCEPTION;
+    
+    PJ_TRY {
+        void *data = my_malloc(200);
+        free(data);
+        randomly_throw_exception();
+    }
+    PJ_CATCH_ANY {
+        pj_exception_id_t x_id;
+        
+        x_id = PJ_GET_EXCEPTION();
+        printf("Caught exception %d (%s)\n", 
+            x_id, pj_exception_id_name(x_id));
+    }
+    PJ_END
+        return 1;
+}
+
+int main()
+{
+    pj_status_t rc;
+    
+    // Error handling is omited for clarity.
+    
+    rc = pj_init();
+
+    rc = pj_exception_id_alloc("No Memory", &NO_MEMORY);
+    rc = pj_exception_id_alloc("Other Exception", &OTHER_EXCEPTION);
+    
+    return test_exception();
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-samples/list.c b/jni/pjproject-android/pjlib/src/pjlib-samples/list.c
new file mode 100644
index 0000000..dece342
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-samples/list.c
@@ -0,0 +1,72 @@
+/* $Id: list.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/list.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+
+/**
+ * \page page_pjlib_samples_list_c Example: List Manipulation
+ *
+ * Below is sample program to demonstrate how to manipulate linked list.
+ *
+ * \includelineno pjlib-samples/list.c
+ */
+
+struct my_node
+{
+    // This must be the first member declared in the struct!
+    PJ_DECL_LIST_MEMBER(struct my_node);
+    int value;
+};
+
+
+int main()
+{
+    struct my_node nodes[10];
+    struct my_node list;
+    struct my_node *it;
+    int i;
+    
+    // Initialize the list as empty.
+    pj_list_init(&list);
+    
+    // Insert nodes.
+    for (i=0; i<10; ++i) {
+        nodes[i].value = i;
+        pj_list_insert_before(&list, &nodes[i]);
+    }
+    
+    // Iterate list nodes.
+    it = list.next;
+    while (it != &list) {
+        PJ_LOG(3,("list", "value = %d", it->value));
+        it = it->next;
+    }
+    
+    // Erase all nodes.
+    for (i=0; i<10; ++i) {
+        pj_list_erase(&nodes[i]);
+    }
+    
+    // List must be empty by now.
+    pj_assert( pj_list_empty(&list) );
+    
+    return 0;
+};
diff --git a/jni/pjproject-android/pjlib/src/pjlib-samples/log.c b/jni/pjproject-android/pjlib/src/pjlib-samples/log.c
new file mode 100644
index 0000000..c571ec5
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-samples/log.c
@@ -0,0 +1,43 @@
+/* $Id: log.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/log.h>
+
+/**
+ * \page page_pjlib_samples_log_c Example: Log, Hello World
+ *
+ * Very simple program to write log.
+ *
+ * \includelineno pjlib-samples/log.c
+ */
+
+int main()
+{
+    pj_status_t rc;
+
+    // Error handling omited for clarity
+    
+    // Must initialize PJLIB first!
+    rc = pj_init();
+
+    PJ_LOG(3, ("main.c", "Hello world!"));
+
+    return 0;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/activesock.c b/jni/pjproject-android/pjlib/src/pjlib-test/activesock.c
new file mode 100644
index 0000000..7a6dcca
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/activesock.c
@@ -0,0 +1,520 @@
+/* $Id: activesock.c 4487 2013-04-23 05:37:41Z bennylp $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+/**
+ * \page page_pjlib_activesock_test Test: Active Socket
+ *
+ * This file is <b>pjlib-test/activesock.c</b>
+ *
+ * \include pjlib-test/activesock.c
+ */
+
+#if INCLUDE_ACTIVESOCK_TEST
+
+#define THIS_FILE   "activesock.c"
+
+
+/*******************************************************************
+ * Simple UDP echo server.
+ */
+struct udp_echo_srv
+{
+    pj_activesock_t	*asock;
+    pj_bool_t		 echo_enabled;
+    pj_uint16_t		 port;
+    pj_ioqueue_op_key_t	 send_key;
+    pj_status_t		 status;
+    unsigned		 rx_cnt;
+    unsigned		 rx_err_cnt, tx_err_cnt;
+};
+
+static void udp_echo_err(const char *title, pj_status_t status)
+{
+    char errmsg[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, errmsg, sizeof(errmsg));
+    PJ_LOG(3,(THIS_FILE, "   error: %s: %s", title, errmsg));
+}
+
+static pj_bool_t udp_echo_srv_on_data_recvfrom(pj_activesock_t *asock,
+					       void *data,
+					       pj_size_t size,
+					       const pj_sockaddr_t *src_addr,
+					       int addr_len,
+					       pj_status_t status)
+{
+    struct udp_echo_srv *srv;
+    pj_ssize_t sent;
+
+
+    srv = (struct udp_echo_srv*) pj_activesock_get_user_data(asock);
+
+    if (status != PJ_SUCCESS) {
+	srv->status = status;
+	srv->rx_err_cnt++;
+	udp_echo_err("recvfrom() callback", status);
+	return PJ_TRUE;
+    }
+
+    srv->rx_cnt++;
+
+    /* Send back if echo is enabled */
+    if (srv->echo_enabled) {
+	sent = size;
+	srv->status = pj_activesock_sendto(asock, &srv->send_key, data, 
+					   &sent, 0,
+					   src_addr, addr_len);
+	if (srv->status != PJ_SUCCESS) {
+	    srv->tx_err_cnt++;
+	    udp_echo_err("sendto()", status);
+	}
+    }
+
+    return PJ_TRUE;
+}
+
+
+static pj_status_t udp_echo_srv_create(pj_pool_t *pool,
+				       pj_ioqueue_t *ioqueue,
+				       pj_bool_t enable_echo,
+				       struct udp_echo_srv **p_srv)
+{
+    struct udp_echo_srv *srv;
+    pj_sock_t sock_fd = PJ_INVALID_SOCKET;
+    pj_sockaddr addr;
+    pj_activesock_cb activesock_cb;
+    pj_status_t status;
+
+    srv = PJ_POOL_ZALLOC_T(pool, struct udp_echo_srv);
+    srv->echo_enabled = enable_echo;
+
+    pj_sockaddr_in_init(&addr.ipv4, NULL, 0);
+
+    pj_bzero(&activesock_cb, sizeof(activesock_cb));
+    activesock_cb.on_data_recvfrom = &udp_echo_srv_on_data_recvfrom;
+
+    status = pj_activesock_create_udp(pool, &addr, NULL, ioqueue, &activesock_cb, 
+				      srv, &srv->asock, &addr);
+    if (status != PJ_SUCCESS) {
+	pj_sock_close(sock_fd);
+	udp_echo_err("pj_activesock_create()", status);
+	return status;
+    }
+
+    srv->port = pj_ntohs(addr.ipv4.sin_port);
+
+    pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
+
+    status = pj_activesock_start_recvfrom(srv->asock, pool, 32, 0);
+    if (status != PJ_SUCCESS) {
+	pj_activesock_close(srv->asock);
+	udp_echo_err("pj_activesock_start_recvfrom()", status);
+	return status;
+    }
+
+
+    *p_srv = srv;
+    return PJ_SUCCESS;
+}
+
+static void udp_echo_srv_destroy(struct udp_echo_srv *srv)
+{
+    pj_activesock_close(srv->asock);
+}
+
+/*******************************************************************
+ * UDP ping pong test (send packet back and forth between two UDP echo
+ * servers.
+ */
+static int udp_ping_pong_test(void)
+{
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_pool_t *pool = NULL;
+    struct udp_echo_srv *srv1=NULL, *srv2=NULL;
+    pj_bool_t need_send = PJ_TRUE;
+    unsigned data = 0;
+    int count, ret;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "pingpong", 512, 512, NULL);
+    if (!pool)
+	return -10;
+
+    status = pj_ioqueue_create(pool, 4, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	ret = -20;
+	udp_echo_err("pj_ioqueue_create()", status);
+	goto on_return;
+    }
+
+    status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv1);
+    if (status != PJ_SUCCESS) {
+	ret = -30;
+	goto on_return;
+    }
+
+    status = udp_echo_srv_create(pool, ioqueue, PJ_TRUE, &srv2);
+    if (status != PJ_SUCCESS) {
+	ret = -40;
+	goto on_return;
+    }
+
+    /* initiate the first send */
+    for (count=0; count<1000; ++count) {
+	unsigned last_rx1, last_rx2;
+	unsigned i;
+
+	if (need_send) {
+	    pj_str_t loopback;
+	    pj_sockaddr_in addr;
+	    pj_ssize_t sent;
+
+	    ++data;
+
+	    sent = sizeof(data);
+	    loopback = pj_str("127.0.0.1");
+	    pj_sockaddr_in_init(&addr, &loopback, srv2->port);
+	    status = pj_activesock_sendto(srv1->asock, &srv1->send_key,
+					  &data, &sent, 0,
+					  &addr, sizeof(addr));
+	    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+		ret = -50;
+		udp_echo_err("sendto()", status);
+		goto on_return;
+	    }
+
+	    need_send = PJ_FALSE;
+	}
+
+	last_rx1 = srv1->rx_cnt;
+	last_rx2 = srv2->rx_cnt;
+
+	for (i=0; i<10 && last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt; ++i) {
+	    pj_time_val delay = {0, 10};
+#ifdef PJ_SYMBIAN
+	    PJ_UNUSED_ARG(delay);
+	    pj_symbianos_poll(-1, 100);
+#else
+	    pj_ioqueue_poll(ioqueue, &delay);
+#endif
+	}
+
+	if (srv1->rx_err_cnt+srv1->tx_err_cnt != 0 ||
+	    srv2->rx_err_cnt+srv2->tx_err_cnt != 0)
+	{
+	    /* Got error */
+	    ret = -60;
+	    goto on_return;
+	}
+
+	if (last_rx1 == srv1->rx_cnt && last_rx2 == srv2->rx_cnt) {
+	    /* Packet lost */
+	    ret = -70;
+	    udp_echo_err("packets have been lost", PJ_ETIMEDOUT);
+	    goto on_return;
+	}
+    }
+
+    ret = 0;
+
+on_return:
+    if (srv2)
+	udp_echo_srv_destroy(srv2);
+    if (srv1)
+	udp_echo_srv_destroy(srv1);
+    if (ioqueue)
+	pj_ioqueue_destroy(ioqueue);
+    if (pool)
+	pj_pool_release(pool);
+    
+    return ret;
+}
+
+
+
+#define SIGNATURE   0xdeadbeef
+struct tcp_pkt
+{
+    pj_uint32_t	signature;
+    pj_uint32_t	seq;
+    char	fill[513];
+};
+
+struct tcp_state
+{
+    pj_bool_t	err;
+    pj_bool_t	sent;
+    pj_uint32_t	next_recv_seq;
+    pj_uint8_t	pkt[600];
+};
+
+struct send_key
+{
+    pj_ioqueue_op_key_t	op_key;
+};
+
+
+static pj_bool_t tcp_on_data_read(pj_activesock_t *asock,
+				  void *data,
+				  pj_size_t size,
+				  pj_status_t status,
+				  pj_size_t *remainder)
+{
+    struct tcp_state *st = (struct tcp_state*) pj_activesock_get_user_data(asock);
+    char *next = (char*) data;
+
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	PJ_LOG(1,("", "   err: status=%d", status));
+	st->err = PJ_TRUE;
+	return PJ_FALSE;
+    }
+
+    while (size >= sizeof(struct tcp_pkt)) {
+	struct tcp_pkt *tcp_pkt = (struct tcp_pkt*) next;
+
+	if (tcp_pkt->signature != SIGNATURE) {
+	    PJ_LOG(1,("", "   err: invalid signature at seq=%d", 
+			  st->next_recv_seq));
+	    st->err = PJ_TRUE;
+	    return PJ_FALSE;
+	}
+	if (tcp_pkt->seq != st->next_recv_seq) {
+	    PJ_LOG(1,("", "   err: wrong sequence"));
+	    st->err = PJ_TRUE;
+	    return PJ_FALSE;
+	}
+
+	st->next_recv_seq++;
+	next += sizeof(struct tcp_pkt);
+	size -= sizeof(struct tcp_pkt);
+    }
+
+    if (size) {
+	pj_memmove(data, next, size);
+	*remainder = size;
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t tcp_on_data_sent(pj_activesock_t *asock,
+				  pj_ioqueue_op_key_t *op_key,
+				  pj_ssize_t sent)
+{
+    struct tcp_state *st=(struct tcp_state*)pj_activesock_get_user_data(asock);
+
+    PJ_UNUSED_ARG(op_key);
+
+    st->sent = 1;
+
+    if (sent < 1) {
+	st->err = PJ_TRUE;
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+static int tcp_perf_test(void)
+{
+    enum { COUNT=10000 };
+    pj_pool_t *pool = NULL;
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_sock_t sock1=PJ_INVALID_SOCKET, sock2=PJ_INVALID_SOCKET;
+    pj_activesock_t *asock1 = NULL, *asock2 = NULL;
+    pj_activesock_cb cb;
+    struct tcp_state *state1, *state2;
+    unsigned i;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "tcpperf", 256, 256, NULL);
+
+    status = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock1, 
+			    &sock2);
+    if (status != PJ_SUCCESS) {
+	status = -100;
+	goto on_return;
+    }
+
+    status = pj_ioqueue_create(pool, 4, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	status = -110;
+	goto on_return;
+    }
+
+    pj_bzero(&cb, sizeof(cb));
+    cb.on_data_read = &tcp_on_data_read;
+    cb.on_data_sent = &tcp_on_data_sent;
+
+    state1 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
+    status = pj_activesock_create(pool, sock1, pj_SOCK_STREAM(), NULL, ioqueue,
+				  &cb, state1, &asock1);
+    if (status != PJ_SUCCESS) {
+	status = -120;
+	goto on_return;
+    }
+
+    state2 = PJ_POOL_ZALLOC_T(pool, struct tcp_state);
+    status = pj_activesock_create(pool, sock2, pj_SOCK_STREAM(), NULL, ioqueue,
+				  &cb, state2, &asock2);
+    if (status != PJ_SUCCESS) {
+	status = -130;
+	goto on_return;
+    }
+
+    status = pj_activesock_start_read(asock1, pool, 1000, 0);
+    if (status != PJ_SUCCESS) {
+	status = -140;
+	goto on_return;
+    }
+
+    /* Send packet as quickly as possible */
+    for (i=0; i<COUNT && !state1->err && !state2->err; ++i) {
+	struct tcp_pkt *pkt;
+	struct send_key send_key[2], *op_key;
+	pj_ssize_t len;
+
+	pkt = (struct tcp_pkt*)state2->pkt;
+	pkt->signature = SIGNATURE;
+	pkt->seq = i;
+	pj_memset(pkt->fill, 'a', sizeof(pkt->fill));
+
+	op_key = &send_key[i%2];
+	pj_ioqueue_op_key_init(&op_key->op_key, sizeof(*op_key));
+
+	state2->sent = PJ_FALSE;
+	len = sizeof(*pkt);
+	status = pj_activesock_send(asock2, &op_key->op_key, pkt, &len, 0);
+	if (status == PJ_EPENDING) {
+	    do {
+#if PJ_SYMBIAN
+		pj_symbianos_poll(-1, -1);
+#else
+		pj_ioqueue_poll(ioqueue, NULL);
+#endif
+	    } while (!state2->sent);
+	} else {
+#if PJ_SYMBIAN
+		/* The Symbian socket always returns PJ_SUCCESS for TCP send,
+		 * eventhough the remote end hasn't received the data yet.
+		 * If we continue sending, eventually send() will block,
+		 * possibly because the send buffer is full. So we need to
+		 * poll the ioqueue periodically, to let receiver gets the 
+		 * data.
+		 */
+		pj_symbianos_poll(-1, 0);
+#endif
+		if (status != PJ_SUCCESS) {
+		    PJ_LOG(1,("", "   err: send status=%d", status));
+		    status = -180;
+		    break;
+		} else if (status == PJ_SUCCESS) {
+		    if (len != sizeof(*pkt)) {
+			PJ_LOG(1,("", "   err: shouldn't report partial sent"));
+			status = -190;
+			break;
+		    }
+		}
+	}
+
+#ifndef PJ_SYMBIAN
+	for (;;) {
+	    pj_time_val timeout = {0, 10};
+	    if (pj_ioqueue_poll(ioqueue, &timeout) < 1)
+		break;
+	}
+#endif
+
+    }
+
+    /* Wait until everything has been sent/received */
+    if (state1->next_recv_seq < COUNT) {
+#ifdef PJ_SYMBIAN
+	while (pj_symbianos_poll(-1, 1000) == PJ_TRUE)
+	    ;
+#else
+	pj_time_val delay = {0, 100};
+	while (pj_ioqueue_poll(ioqueue, &delay) > 0)
+	    ;
+#endif
+    }
+
+    if (status == PJ_EPENDING)
+	status = PJ_SUCCESS;
+
+    if (status != 0)
+	goto on_return;
+
+    if (state1->err) {
+	status = -183;
+	goto on_return;
+    }
+    if (state2->err) {
+	status = -186;
+	goto on_return;
+    }
+    if (state1->next_recv_seq != COUNT) {
+	PJ_LOG(3,("", "   err: only %u packets received, expecting %u", 
+		      state1->next_recv_seq, COUNT));
+	status = -195;
+	goto on_return;
+    }
+
+on_return:
+    if (asock2)
+	pj_activesock_close(asock2);
+    if (asock1)
+	pj_activesock_close(asock1);
+    if (ioqueue)
+	pj_ioqueue_destroy(ioqueue);
+    if (pool)
+	pj_pool_release(pool);
+
+    return status;
+}
+
+
+
+int activesock_test(void)
+{
+    int ret;
+
+    PJ_LOG(3,("", "..udp ping/pong test"));
+    ret = udp_ping_pong_test();
+    if (ret != 0)
+	return ret;
+
+    PJ_LOG(3,("", "..tcp perf test"));
+    ret = tcp_perf_test();
+    if (ret != 0)
+	return ret;
+
+    return 0;
+}
+
+#else	/* INCLUDE_ACTIVESOCK_TEST */
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_active_sock_test;
+#endif	/* INCLUDE_ACTIVESOCK_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/atomic.c b/jni/pjproject-android/pjlib/src/pjlib-test/atomic.c
new file mode 100644
index 0000000..f9cddf4
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/atomic.c
@@ -0,0 +1,109 @@
+/* $Id: atomic.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+/**
+ * \page page_pjlib_atomic_test Test: Atomic Variable
+ *
+ * This file provides implementation of \b atomic_test(). It tests the
+ * functionality of the atomic variable API.
+ *
+ * \section atomic_test_sec Scope of the Test
+ *
+ * API tested:
+ *  - pj_atomic_create()
+ *  - pj_atomic_get()
+ *  - pj_atomic_inc()
+ *  - pj_atomic_dec()
+ *  - pj_atomic_set()
+ *  - pj_atomic_destroy()
+ *
+ *
+ * This file is <b>pjlib-test/atomic.c</b>
+ *
+ * \include pjlib-test/atomic.c
+ */
+
+
+#if INCLUDE_ATOMIC_TEST
+
+int atomic_test(void)
+{
+    pj_pool_t *pool;
+    pj_atomic_t *atomic_var;
+    pj_status_t rc;
+
+    pool = pj_pool_create(mem, NULL, 4096, 0, NULL);
+    if (!pool)
+        return -10;
+
+    /* create() */
+    rc = pj_atomic_create(pool, 111, &atomic_var);
+    if (rc != 0) {
+        return -20;
+    }
+
+    /* get: check the value. */
+    if (pj_atomic_get(atomic_var) != 111)
+        return -30;
+
+    /* increment. */
+    pj_atomic_inc(atomic_var);
+    if (pj_atomic_get(atomic_var) != 112)
+        return -40;
+
+    /* decrement. */
+    pj_atomic_dec(atomic_var);
+    if (pj_atomic_get(atomic_var) != 111)
+        return -50;
+
+    /* set */
+    pj_atomic_set(atomic_var, 211);
+    if (pj_atomic_get(atomic_var) != 211)
+        return -60;
+
+    /* add */
+    pj_atomic_add(atomic_var, 10);
+    if (pj_atomic_get(atomic_var) != 221)
+        return -60;
+
+    /* check the value again. */
+    if (pj_atomic_get(atomic_var) != 221)
+        return -70;
+
+    /* destroy */
+    rc = pj_atomic_destroy(atomic_var);
+    if (rc != 0)
+        return -80;
+
+    pj_pool_release(pool);
+
+    return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_atomic_test;
+#endif  /* INCLUDE_ATOMIC_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/echo_clt.c b/jni/pjproject-android/pjlib/src/pjlib-test/echo_clt.c
new file mode 100644
index 0000000..0ce27dc
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/echo_clt.c
@@ -0,0 +1,269 @@
+/* $Id: echo_clt.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_ECHO_CLIENT
+
+enum { BUF_SIZE = 512 };
+
+struct client
+{
+    int sock_type;
+    const char *server;
+    int port;
+};
+
+static pj_atomic_t *totalBytes;
+static pj_atomic_t *timeout_counter;
+static pj_atomic_t *invalid_counter;
+
+#define MSEC_PRINT_DURATION 1000
+
+static int wait_socket(pj_sock_t sock, unsigned msec_timeout)
+{
+    pj_fd_set_t fdset;
+    pj_time_val timeout;
+
+    timeout.sec = 0;
+    timeout.msec = msec_timeout;
+    pj_time_val_normalize(&timeout);
+
+    PJ_FD_ZERO(&fdset);
+    PJ_FD_SET(sock, &fdset);
+    
+    return pj_sock_select(FD_SETSIZE, &fdset, NULL, NULL, &timeout);
+}
+
+static int echo_client_thread(void *arg)
+{
+    pj_sock_t sock;
+    char send_buf[BUF_SIZE];
+    char recv_buf[BUF_SIZE];
+    pj_sockaddr_in addr;
+    pj_str_t s;
+    pj_status_t rc;
+    pj_uint32_t buffer_id;
+    pj_uint32_t buffer_counter;
+    struct client *client = arg;
+    pj_status_t last_recv_err = PJ_SUCCESS, last_send_err = PJ_SUCCESS;
+    unsigned counter = 0;
+
+    rc = app_socket(pj_AF_INET(), client->sock_type, 0, -1, &sock);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...unable to create socket", rc);
+        return -10;
+    }
+
+    rc = pj_sockaddr_in_init( &addr, pj_cstr(&s, client->server), 
+                              (pj_uint16_t)client->port);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...unable to resolve server", rc);
+        return -15;
+    }
+
+    rc = pj_sock_connect(sock, &addr, sizeof(addr));
+    if (rc != PJ_SUCCESS) {
+        app_perror("...connect() error", rc);
+        pj_sock_close(sock);
+        return -20;
+    }
+
+    PJ_LOG(3,("", "...socket connected to %s:%d", 
+		  pj_inet_ntoa(addr.sin_addr),
+		  pj_ntohs(addr.sin_port)));
+
+    pj_memset(send_buf, 'A', BUF_SIZE);
+    send_buf[BUF_SIZE-1]='\0';
+
+    /* Give other thread chance to initialize themselves! */
+    pj_thread_sleep(200);
+
+    //PJ_LOG(3,("", "...thread %p running", pj_thread_this()));
+
+    buffer_id = (pj_uint32_t) pj_thread_this();
+    buffer_counter = 0;
+
+    *(pj_uint32_t*)send_buf = buffer_id;
+
+    for (;;) {
+        int rc;
+        pj_ssize_t bytes;
+
+	++counter;
+
+	//while (wait_socket(sock,0) > 0)
+	//    ;
+
+        /* Send a packet. */
+        bytes = BUF_SIZE;
+	*(pj_uint32_t*)(send_buf+4) = ++buffer_counter;
+        rc = pj_sock_send(sock, send_buf, &bytes, 0);
+        if (rc != PJ_SUCCESS || bytes != BUF_SIZE) {
+            if (rc != last_send_err) {
+                app_perror("...send() error", rc);
+                PJ_LOG(3,("", "...ignoring subsequent error.."));
+                last_send_err = rc;
+                pj_thread_sleep(100);
+            }
+            continue;
+        }
+
+        rc = wait_socket(sock, 500);
+        if (rc == 0) {
+            PJ_LOG(3,("", "...timeout"));
+	    bytes = 0;
+	    pj_atomic_inc(timeout_counter);
+	} else if (rc < 0) {
+	    rc = pj_get_netos_error();
+	    app_perror("...select() error", rc);
+	    break;
+        } else {
+            /* Receive back the original packet. */
+            bytes = 0;
+            do {
+                pj_ssize_t received = BUF_SIZE - bytes;
+                rc = pj_sock_recv(sock, recv_buf+bytes, &received, 0);
+                if (rc != PJ_SUCCESS || received == 0) {
+                    if (rc != last_recv_err) {
+                        app_perror("...recv() error", rc);
+                        PJ_LOG(3,("", "...ignoring subsequent error.."));
+                        last_recv_err = rc;
+                        pj_thread_sleep(100);
+                    }
+                    bytes = 0;
+		    received = 0;
+                    break;
+                }
+                bytes += received;
+            } while (bytes != BUF_SIZE && bytes != 0);
+        }
+
+        if (bytes == 0)
+            continue;
+
+        if (pj_memcmp(send_buf, recv_buf, BUF_SIZE) != 0) {
+	    recv_buf[BUF_SIZE-1] = '\0';
+            PJ_LOG(3,("", "...error: buffer %u has changed!\n"
+			  "send_buf=%s\n"
+			  "recv_buf=%s\n", 
+			  counter, send_buf, recv_buf));
+	    pj_atomic_inc(invalid_counter);
+        }
+
+        /* Accumulate total received. */
+	pj_atomic_add(totalBytes, bytes);
+    }
+
+    pj_sock_close(sock);
+    return 0;
+}
+
+int echo_client(int sock_type, const char *server, int port)
+{
+    pj_pool_t *pool;
+    pj_thread_t *thread[ECHO_CLIENT_MAX_THREADS];
+    pj_status_t rc;
+    struct client client;
+    int i;
+    pj_atomic_value_t last_received;
+    pj_timestamp last_report;
+
+    client.sock_type = sock_type;
+    client.server = server;
+    client.port = port;
+
+    pool = pj_pool_create( mem, NULL, 4000, 4000, NULL );
+
+    rc = pj_atomic_create(pool, 0, &totalBytes);
+    if (rc != PJ_SUCCESS) {
+        PJ_LOG(3,("", "...error: unable to create atomic variable", rc));
+        return -30;
+    }
+    rc = pj_atomic_create(pool, 0, &invalid_counter);
+    rc = pj_atomic_create(pool, 0, &timeout_counter);
+
+    PJ_LOG(3,("", "Echo client started"));
+    PJ_LOG(3,("", "  Destination: %s:%d", 
+                  ECHO_SERVER_ADDRESS, ECHO_SERVER_START_PORT));
+    PJ_LOG(3,("", "  Press Ctrl-C to exit"));
+
+    for (i=0; i<ECHO_CLIENT_MAX_THREADS; ++i) {
+        rc = pj_thread_create( pool, NULL, &echo_client_thread, &client, 
+                               PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+                               &thread[i]);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: unable to create thread", rc);
+            return -10;
+        }
+    }
+
+    last_received = 0;
+    pj_get_timestamp(&last_report);
+
+    for (;;) {
+	pj_timestamp now;
+	unsigned long received, cur_received;
+	unsigned msec;
+	pj_highprec_t bw;
+	pj_time_val elapsed;
+	unsigned bw32;
+	pj_uint32_t timeout, invalid;
+
+	pj_thread_sleep(1000);
+
+	pj_get_timestamp(&now);
+	elapsed = pj_elapsed_time(&last_report, &now);
+	msec = PJ_TIME_VAL_MSEC(elapsed);
+
+	received = pj_atomic_get(totalBytes);
+	cur_received = received - last_received;
+	
+	bw = cur_received;
+	pj_highprec_mul(bw, 1000);
+	pj_highprec_div(bw, msec);
+
+	bw32 = (unsigned)bw;
+	
+	last_report = now;
+	last_received = received;
+
+	timeout = pj_atomic_get(timeout_counter);
+	invalid = pj_atomic_get(invalid_counter);
+
+        PJ_LOG(3,("", 
+	          "...%d threads, total bandwidth: %d KB/s, "
+		  "timeout=%d, invalid=%d", 
+                  ECHO_CLIENT_MAX_THREADS, bw32/1000,
+		  timeout, invalid));
+    }
+
+    for (i=0; i<ECHO_CLIENT_MAX_THREADS; ++i) {
+        pj_thread_join( thread[i] );
+    }
+
+    pj_pool_release(pool);
+    return 0;
+}
+
+
+#else
+int dummy_echo_client;
+#endif  /* INCLUDE_ECHO_CLIENT */
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/errno.c b/jni/pjproject-android/pjlib/src/pjlib-test/errno.c
new file mode 100644
index 0000000..d8f1fc2
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/errno.c
@@ -0,0 +1,172 @@
+/* $Id: errno.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/ctype.h>
+#include <pj/compat/socket.h>
+#include <pj/string.h>
+
+#if INCLUDE_ERRNO_TEST
+
+#define THIS_FILE   "errno"
+
+#if (defined(PJ_WIN32) && PJ_WIN32 != 0) || \
+    (defined(PJ_WIN64) && PJ_WIN64 != 0)
+#   include <windows.h>
+#endif
+
+#if defined(PJ_HAS_ERRNO_H) && PJ_HAS_ERRNO_H != 0
+#   include <errno.h>
+#endif
+
+static void trim_newlines(char *s)
+{
+    while (*s) {
+        if (*s == '\r' || *s == '\n')
+            *s = ' ';
+        ++s;
+    }
+}
+
+int my_strncasecmp(const char *s1, const char *s2, int max_len)
+{
+    while (*s1 && *s2 && max_len > 0) {
+        if (pj_tolower(*s1) != pj_tolower(*s2))
+            return -1;
+        ++s1;
+        ++s2;
+        --max_len;
+    }
+    return 0;
+}
+
+const char *my_stristr(const char *whole, const char *part)
+{
+    int part_len = (int)strlen(part);
+    while (*whole) {
+        if (my_strncasecmp(whole, part, part_len) == 0)
+            return whole;
+        ++whole;
+    }
+    return NULL;
+}
+
+int errno_test(void)
+{
+    enum { CUT = 6 };
+    pj_status_t rc = 0;
+    char errbuf[256];
+
+    PJ_LOG(3,(THIS_FILE, "...errno test: check the msg carefully"));
+
+    PJ_UNUSED_ARG(rc);
+    
+    /* 
+     * Windows platform error. 
+     */
+#   ifdef ERROR_INVALID_DATA
+    rc = PJ_STATUS_FROM_OS(ERROR_INVALID_DATA);
+    pj_set_os_error(rc);
+
+    /* Whole */
+    pj_strerror(rc, errbuf, sizeof(errbuf));
+    trim_newlines(errbuf);
+    PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA: '%s'", errbuf));
+    if (my_stristr(errbuf, "invalid") == NULL) {
+        PJ_LOG(3, (THIS_FILE, 
+                   "...error: expecting \"invalid\" string in the msg"));
+#ifndef PJ_WIN32_WINCE
+        return -20;
+#endif
+    }
+
+    /* Cut version. */
+    pj_strerror(rc, errbuf, CUT);
+    PJ_LOG(3,(THIS_FILE, "...msg for rc=ERROR_INVALID_DATA (cut): '%s'", errbuf));
+#   endif
+
+    /*
+     * Unix errors
+     */
+#   if defined(EINVAL) && !defined(PJ_SYMBIAN)
+    rc = PJ_STATUS_FROM_OS(EINVAL);
+    pj_set_os_error(rc);
+
+    /* Whole */
+    pj_strerror(rc, errbuf, sizeof(errbuf));
+    trim_newlines(errbuf);
+    PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL: '%s'", errbuf));
+    if (my_stristr(errbuf, "invalid") == NULL) {
+        PJ_LOG(3, (THIS_FILE, 
+                   "...error: expecting \"invalid\" string in the msg"));
+        return -30;
+    }
+
+    /* Cut */
+    pj_strerror(rc, errbuf, CUT);
+    PJ_LOG(3,(THIS_FILE, "...msg for rc=EINVAL (cut): '%s'", errbuf));
+#   endif
+
+    /* 
+     * Windows WSA errors
+     */
+#   ifdef WSAEINVAL
+    rc = PJ_STATUS_FROM_OS(WSAEINVAL);
+    pj_set_os_error(rc);
+
+    /* Whole */
+    pj_strerror(rc, errbuf, sizeof(errbuf));
+    trim_newlines(errbuf);
+    PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL: '%s'", errbuf));
+    if (my_stristr(errbuf, "invalid") == NULL) {
+        PJ_LOG(3, (THIS_FILE, 
+                   "...error: expecting \"invalid\" string in the msg"));
+        return -40;
+    }
+
+    /* Cut */
+    pj_strerror(rc, errbuf, CUT);
+    PJ_LOG(3,(THIS_FILE, "...msg for rc=WSAEINVAL (cut): '%s'", errbuf));
+#   endif
+
+    pj_strerror(PJ_EBUG, errbuf, sizeof(errbuf));
+    PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG: '%s'", errbuf));
+    if (my_stristr(errbuf, "BUG") == NULL) {
+        PJ_LOG(3, (THIS_FILE, 
+                   "...error: expecting \"BUG\" string in the msg"));
+        return -20;
+    }
+
+    pj_strerror(PJ_EBUG, errbuf, CUT);
+    PJ_LOG(3,(THIS_FILE, "...msg for rc=PJ_EBUG, cut at %d chars: '%s'", 
+              CUT, errbuf));
+
+    /* Perror */
+    pj_perror(3, THIS_FILE, PJ_SUCCESS, "...testing %s", "pj_perror");
+    PJ_PERROR(3,(THIS_FILE, PJ_SUCCESS, "...testing %s", "PJ_PERROR"));
+
+    return 0;
+}
+
+
+#endif	/* INCLUDE_ERRNO_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/exception.c b/jni/pjproject-android/pjlib/src/pjlib-test/exception.c
new file mode 100644
index 0000000..4d4b1cd
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/exception.c
@@ -0,0 +1,282 @@
+/* $Id: exception.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_exception_test Test: Exception Handling
+ *
+ * This file provides implementation of \b exception_test(). It tests the
+ * functionality of the exception handling API.
+ *
+ * @note This test use static ID not acquired through proper registration.
+ * This is not recommended, since it may create ID collissions.
+ *
+ * \section exception_test_sec Scope of the Test
+ *
+ * Some scenarios tested:
+ *  - no exception situation
+ *  - basic TRY/CATCH
+ *  - multiple exception handlers
+ *  - default handlers
+ *
+ *
+ * This file is <b>pjlib-test/exception.c</b>
+ *
+ * \include pjlib-test/exception.c
+ */
+
+
+#if INCLUDE_EXCEPTION_TEST
+
+#include <pjlib.h>
+
+#define	ID_1	1
+#define ID_2	2
+
+static int throw_id_1(void)
+{
+    PJ_THROW( ID_1 );
+    PJ_UNREACHED(return -1;)
+}
+
+static int throw_id_2(void)
+{
+    PJ_THROW( ID_2 );
+    PJ_UNREACHED(return -1;)
+}
+
+static int try_catch_test(void)
+{
+    PJ_USE_EXCEPTION;
+    int rc = -200;
+
+    PJ_TRY {
+	PJ_THROW(ID_1);
+    }
+    PJ_CATCH_ANY {
+	rc = 0;
+    }
+    PJ_END;
+    return rc;
+}
+
+static int throw_in_handler(void)
+{
+    PJ_USE_EXCEPTION;
+    int rc = 0;
+
+    PJ_TRY {
+	PJ_THROW(ID_1);
+    }
+    PJ_CATCH_ANY {
+	if (PJ_GET_EXCEPTION() != ID_1)
+	    rc = -300;
+	else
+	    PJ_THROW(ID_2);
+    }
+    PJ_END;
+    return rc;
+}
+
+static int return_in_handler(void)
+{
+    PJ_USE_EXCEPTION;
+
+    PJ_TRY {
+	PJ_THROW(ID_1);
+    }
+    PJ_CATCH_ANY {
+	return 0;
+    }
+    PJ_END;
+    return -400;
+}
+
+
+static int test(void)
+{
+    int rc = 0;
+    PJ_USE_EXCEPTION;
+
+    /*
+     * No exception situation.
+     */
+    PJ_TRY {
+        rc = rc;
+    }
+    PJ_CATCH_ANY {
+        rc = -3;
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return rc;
+
+
+    /*
+     * Basic TRY/CATCH
+     */ 
+    PJ_TRY {
+	rc = throw_id_1();
+
+	// should not reach here.
+	rc = -10;
+    }
+    PJ_CATCH_ANY {
+        int id = PJ_GET_EXCEPTION();
+	if (id != ID_1) {
+	    PJ_LOG(3,("", "...error: got unexpected exception %d (%s)", 
+		      id, pj_exception_id_name(id)));
+	    if (!rc) rc = -20;
+	}
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return rc;
+
+    /*
+     * Multiple exceptions handlers
+     */
+    PJ_TRY {
+	rc = throw_id_2();
+	// should not reach here.
+	rc = -25;
+    }
+    PJ_CATCH_ANY {
+	switch (PJ_GET_EXCEPTION()) {
+	case ID_1:
+	    if (!rc) rc = -30; break;
+	case ID_2:
+	    if (!rc) rc = 0; break;
+	default:
+	    if (!rc) rc = -40;
+	    break;
+	}
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return rc;
+
+    /*
+     * Test default handler.
+     */
+    PJ_TRY {
+	rc = throw_id_1();
+	// should not reach here
+	rc = -50;
+    }
+    PJ_CATCH_ANY {
+	switch (PJ_GET_EXCEPTION()) {
+	case ID_1:
+	    if (!rc) rc = 0;
+	    break;
+	default:
+	    if (!rc) rc = -60;
+	    break;
+	}
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return rc;
+
+    /*
+     * Nested handlers
+     */
+    PJ_TRY {
+	rc = try_catch_test();
+    }
+    PJ_CATCH_ANY {
+	rc = -70;
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return rc;
+
+    /*
+     * Throwing exception inside handler
+     */
+    rc = -80;
+    PJ_TRY {
+	int rc2;
+	rc2 = throw_in_handler();
+	if (rc2)
+	    rc = rc2;
+    }
+    PJ_CATCH_ANY {
+	if (PJ_GET_EXCEPTION() == ID_2) {
+	    rc = 0;
+	} else {
+	    rc = -90;
+	}
+    }
+    PJ_END;
+
+    if (rc != 0)
+	return rc;
+
+
+    /*
+     * Return from handler. Returning from the function inside a handler
+     * should be okay (though returning from the function inside the
+     * PJ_TRY block IS NOT OKAY!!). We want to test to see if handler
+     * is cleaned up properly, but not sure how to do this.
+     */
+    PJ_TRY {
+	int rc2;
+	rc2 = return_in_handler();
+	if (rc2)
+	    rc = rc2;
+    }
+    PJ_CATCH_ANY {
+	rc = -100;
+    }
+    PJ_END;
+
+
+    return 0;
+}
+
+int exception_test(void)
+{
+    int i, rc;
+    enum { LOOP = 10 };
+
+    for (i=0; i<LOOP; ++i) {
+	if ((rc=test()) != 0) {
+	    PJ_LOG(3,("", "...failed at i=%d (rc=%d)", i, rc));
+	    return rc;
+	}
+    }
+    return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_exception_test;
+#endif	/* INCLUDE_EXCEPTION_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/exception_wrap.cpp b/jni/pjproject-android/pjlib/src/pjlib-test/exception_wrap.cpp
new file mode 100644
index 0000000..9c5b31b
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/exception_wrap.cpp
@@ -0,0 +1,24 @@
+/* $Id: exception_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "exception.c"
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/fifobuf.c b/jni/pjproject-android/pjlib/src/pjlib-test/fifobuf.c
new file mode 100644
index 0000000..89c3ee2
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/fifobuf.c
@@ -0,0 +1,117 @@
+/* $Id: fifobuf.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_fifobuf_test;
+
+#if INCLUDE_FIFOBUF_TEST
+
+#include <pjlib.h>
+
+int fifobuf_test()
+{
+    enum { SIZE = 1024, MAX_ENTRIES = 128, 
+	   MIN_SIZE = 4, MAX_SIZE = 64, 
+	   LOOP=10000 };
+    pj_pool_t *pool;
+    pj_fifobuf_t fifo;
+    unsigned available = SIZE;
+    void *entries[MAX_ENTRIES];
+    void *buffer;
+    int i;
+
+    pool = pj_pool_create(mem, NULL, SIZE+256, 0, NULL);
+    if (!pool)
+	return -10;
+
+    buffer = pj_pool_alloc(pool, SIZE);
+    if (!buffer)
+	return -20;
+
+    pj_fifobuf_init (&fifo, buffer, SIZE);
+    
+    // Test 1
+    for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
+	int size;
+	int c, f;
+	c = i%2;
+	f = (i+1)%2;
+	do {
+	    size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+	    entries[c] = pj_fifobuf_alloc (&fifo, size);
+	} while (entries[c] == 0);
+	if ( i!=0) {
+	    pj_fifobuf_free(&fifo, entries[f]);
+	}
+    }
+    if (entries[(i+1)%2])
+	pj_fifobuf_free(&fifo, entries[(i+1)%2]);
+
+    if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+	pj_assert(0);
+	return -1;
+    }
+
+    // Test 2
+    entries[0] = pj_fifobuf_alloc (&fifo, MIN_SIZE);
+    if (!entries[0]) return -1;
+    for (i=0; i<LOOP*MAX_ENTRIES; ++i) {
+	int size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+	entries[1] = pj_fifobuf_alloc (&fifo, size);
+	if (entries[1])
+	    pj_fifobuf_unalloc(&fifo, entries[1]);
+    }
+    pj_fifobuf_unalloc(&fifo, entries[0]);
+    if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+	pj_assert(0);
+	return -2;
+    }
+
+    // Test 3
+    for (i=0; i<LOOP; ++i) {
+	int count, j;
+	for (count=0; available>=MIN_SIZE+4 && count < MAX_ENTRIES;) {
+	    int size = MIN_SIZE+(pj_rand() % MAX_SIZE);
+	    entries[count] = pj_fifobuf_alloc (&fifo, size);
+	    if (entries[count]) {
+		available -= (size+4);
+		++count;
+	    }
+	}
+	for (j=0; j<count; ++j) {
+	    pj_fifobuf_free (&fifo, entries[j]);
+	}
+	available = SIZE;
+    }
+
+    if (pj_fifobuf_max_size(&fifo) < SIZE-4) {
+	pj_assert(0);
+	return -3;
+    }
+    pj_pool_release(pool);
+    return 0;
+}
+
+#endif	/* INCLUDE_FIFOBUF_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/file.c b/jni/pjproject-android/pjlib/src/pjlib-test/file.c
new file mode 100644
index 0000000..df086e5
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/file.c
@@ -0,0 +1,225 @@
+/* $Id: file.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_FILE_TEST
+
+#define FILENAME                "testfil1.txt"
+#define NEWNAME                 "testfil2.txt"
+#define INCLUDE_FILE_TIME_TEST  0
+
+static char buffer[11] = {'H', 'e', 'l', 'l', 'o', ' ',
+		          'W', 'o', 'r', 'l', 'd' };
+
+static int file_test_internal(void)
+{
+    enum { FILE_MAX_AGE = 1000 };
+    pj_oshandle_t fd = 0;
+    pj_status_t status;
+    char readbuf[sizeof(buffer)+16];
+    pj_file_stat stat;
+    pj_time_val start_time;
+    pj_ssize_t size;
+    pj_off_t pos;
+
+    PJ_LOG(3,("", "..file io test.."));
+
+    /* Get time. */
+    pj_gettimeofday(&start_time);
+
+    /* Delete original file if exists. */
+    if (pj_file_exists(FILENAME))
+        pj_file_delete(FILENAME);
+
+    /*
+     * Write data to the file.
+     */
+    status = pj_file_open(NULL, FILENAME, PJ_O_WRONLY, &fd);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_open() error", status);
+        return -10;
+    }
+
+    size = sizeof(buffer);
+    status = pj_file_write(fd, buffer, &size);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_write() error", status);
+        pj_file_close(fd);
+        return -20;
+    }
+    if (size != sizeof(buffer))
+        return -25;
+
+    status = pj_file_close(fd);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_close() error", status);
+        return -30;
+    }
+
+    /* Check the file existance and size. */
+    if (!pj_file_exists(FILENAME))
+        return -40;
+
+    if (pj_file_size(FILENAME) != sizeof(buffer))
+        return -50;
+
+    /* Get file stat. */
+    status = pj_file_getstat(FILENAME, &stat);
+    if (status != PJ_SUCCESS)
+        return -60;
+
+    /* Check stat size. */
+    if (stat.size != sizeof(buffer))
+        return -70;
+
+#if INCLUDE_FILE_TIME_TEST
+    /* Check file creation time >= start_time. */
+    if (!PJ_TIME_VAL_GTE(stat.ctime, start_time))
+        return -80;
+    /* Check file creation time is not much later. */
+    PJ_TIME_VAL_SUB(stat.ctime, start_time);
+    if (stat.ctime.sec > FILE_MAX_AGE)
+        return -90;
+
+    /* Check file modification time >= start_time. */
+    if (!PJ_TIME_VAL_GTE(stat.mtime, start_time))
+        return -80;
+    /* Check file modification time is not much later. */
+    PJ_TIME_VAL_SUB(stat.mtime, start_time);
+    if (stat.mtime.sec > FILE_MAX_AGE)
+        return -90;
+
+    /* Check file access time >= start_time. */
+    if (!PJ_TIME_VAL_GTE(stat.atime, start_time))
+        return -80;
+    /* Check file access time is not much later. */
+    PJ_TIME_VAL_SUB(stat.atime, start_time);
+    if (stat.atime.sec > FILE_MAX_AGE)
+        return -90;
+#endif
+
+    /*
+     * Re-open the file and read data.
+     */
+    status = pj_file_open(NULL, FILENAME, PJ_O_RDONLY, &fd);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_open() error", status);
+        return -100;
+    }
+
+    size = 0;
+    while (size < (pj_ssize_t)sizeof(readbuf)) {
+        pj_ssize_t read;
+        read = 1;
+        status = pj_file_read(fd, &readbuf[size], &read);
+        if (status != PJ_SUCCESS) {
+	    PJ_LOG(3,("", "...error reading file after %d bytes (error follows)", 
+		      size));
+            app_perror("...error", status);
+            return -110;
+        }
+        if (read == 0) {
+            // EOF
+            break;
+        }
+        size += read;
+    }
+
+    if (size != sizeof(buffer))
+        return -120;
+
+    /*
+    if (!pj_file_eof(fd, PJ_O_RDONLY))
+        return -130;
+     */
+
+    if (pj_memcmp(readbuf, buffer, size) != 0)
+        return -140;
+
+    /* Seek test. */
+    status = pj_file_setpos(fd, 4, PJ_SEEK_SET);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_setpos() error", status);
+        return -141;
+    }
+
+    /* getpos test. */
+    status = pj_file_getpos(fd, &pos);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_getpos() error", status);
+        return -142;
+    }
+    if (pos != 4)
+        return -143;
+
+    status = pj_file_close(fd);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_close() error", status);
+        return -150;
+    }
+
+    /*
+     * Rename test.
+     */
+    status = pj_file_move(FILENAME, NEWNAME);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_move() error", status);
+        return -160;
+    }
+
+    if (pj_file_exists(FILENAME))
+        return -170;
+    if (!pj_file_exists(NEWNAME))
+        return -180;
+
+    if (pj_file_size(NEWNAME) != sizeof(buffer))
+        return -190;
+
+    /* Delete test. */
+    status = pj_file_delete(NEWNAME);
+    if (status != PJ_SUCCESS) {
+        app_perror("...file_delete() error", status);
+        return -200;
+    }
+
+    if (pj_file_exists(NEWNAME))
+        return -210;
+
+    PJ_LOG(3,("", "...success"));
+    return PJ_SUCCESS;
+}
+
+
+int file_test(void)
+{
+    int rc = file_test_internal();
+
+    /* Delete test file if exists. */
+    if (pj_file_exists(FILENAME))
+        pj_file_delete(FILENAME);
+
+    return rc;
+}
+
+#else
+int dummy_file_test;
+#endif
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/hash_test.c b/jni/pjproject-android/pjlib/src/pjlib-test/hash_test.c
new file mode 100644
index 0000000..088107d
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/hash_test.c
@@ -0,0 +1,163 @@
+/* $Id: hash_test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/hash.h>
+#include <pj/rand.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include "test.h"
+
+#if INCLUDE_HASH_TEST
+
+#define HASH_COUNT  31
+
+static int hash_test_with_key(pj_pool_t *pool, unsigned char key)
+{
+    pj_hash_table_t *ht;
+    unsigned value = 0x12345;
+    pj_hash_iterator_t it_buf, *it;
+    unsigned *entry;
+
+    ht = pj_hash_create(pool, HASH_COUNT);
+    if (!ht)
+	return -10;
+
+    pj_hash_set(pool, ht, &key, sizeof(key), 0, &value);
+
+    entry = (unsigned*) pj_hash_get(ht, &key, sizeof(key), NULL);
+    if (!entry)
+	return -20;
+
+    if (*entry != value)
+	return -30;
+
+    if (pj_hash_count(ht) != 1)
+	return -30;
+
+    it = pj_hash_first(ht, &it_buf);
+    if (it == NULL)
+	return -40;
+
+    entry = (unsigned*) pj_hash_this(ht, it);
+    if (!entry)
+	return -50;
+
+    if (*entry != value)
+	return -60;
+
+    it = pj_hash_next(ht, it);
+    if (it != NULL)
+	return -70;
+
+    /* Erase item */
+
+    pj_hash_set(NULL, ht, &key, sizeof(key), 0, NULL);
+
+    if (pj_hash_get(ht, &key, sizeof(key), NULL) != NULL)
+	return -80;
+
+    if (pj_hash_count(ht) != 0)
+	return -90;
+
+    it = pj_hash_first(ht, &it_buf);
+    if (it != NULL)
+	return -100;
+
+    return 0;
+}
+
+
+static int hash_collision_test(pj_pool_t *pool)
+{
+    enum {
+	COUNT = HASH_COUNT * 4
+    };
+    pj_hash_table_t *ht;
+    pj_hash_iterator_t it_buf, *it;
+    unsigned char *values;
+    unsigned i;
+
+    ht = pj_hash_create(pool, HASH_COUNT);
+    if (!ht)
+	return -200;
+
+    values = (unsigned char*) pj_pool_alloc(pool, COUNT);
+
+    for (i=0; i<COUNT; ++i) {
+	values[i] = (unsigned char)i;
+	pj_hash_set(pool, ht, &i, sizeof(i), 0, &values[i]);
+    }
+
+    if (pj_hash_count(ht) != COUNT)
+	return -210;
+
+    for (i=0; i<COUNT; ++i) {
+	unsigned char *entry;
+	entry = (unsigned char*) pj_hash_get(ht, &i, sizeof(i), NULL);
+	if (!entry)
+	    return -220;
+	if (*entry != values[i])
+	    return -230;
+    }
+
+    i = 0;
+    it = pj_hash_first(ht, &it_buf);
+    while (it) {
+	++i;
+	it = pj_hash_next(ht, it);
+    }
+
+    if (i != COUNT)
+	return -240;
+
+    return 0;
+}
+
+
+/*
+ * Hash table test.
+ */
+int hash_test(void)
+{
+    pj_pool_t *pool = pj_pool_create(mem, "hash", 512, 512, NULL);
+    int rc;
+    unsigned i;
+
+    /* Test to fill in each row in the table */
+    for (i=0; i<=HASH_COUNT; ++i) {
+	rc = hash_test_with_key(pool, (unsigned char)i);
+	if (rc != 0) {
+	    pj_pool_release(pool);
+	    return rc;
+	}
+    }
+
+    /* Collision test */
+    rc = hash_collision_test(pool);
+    if (rc != 0) {
+	pj_pool_release(pool);
+	return rc;
+    }
+
+    pj_pool_release(pool);
+    return 0;
+}
+
+#endif	/* INCLUDE_HASH_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/ioq_perf.c b/jni/pjproject-android/pjlib/src/pjlib-test/ioq_perf.c
new file mode 100644
index 0000000..117748f
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/ioq_perf.c
@@ -0,0 +1,572 @@
+/* $Id: ioq_perf.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+#include <pj/compat/high_precision.h>
+
+/**
+ * \page page_pjlib_ioqueue_perf_test Test: I/O Queue Performance
+ *
+ * Test the performance of the I/O queue, using typical producer
+ * consumer test. The test should examine the effect of using multiple
+ * threads on the performance.
+ *
+ * This file is <b>pjlib-test/ioq_perf.c</b>
+ *
+ * \include pjlib-test/ioq_perf.c
+ */
+
+#if INCLUDE_IOQUEUE_PERF_TEST
+
+#ifdef _MSC_VER
+#   pragma warning ( disable: 4204)     // non-constant aggregate initializer
+#endif
+
+#define THIS_FILE	"ioq_perf"
+//#define TRACE_(expr)	PJ_LOG(3,expr)
+#define TRACE_(expr)
+
+
+static pj_bool_t thread_quit_flag;
+static pj_status_t last_error;
+static unsigned last_error_counter;
+
+/* Descriptor for each producer/consumer pair. */
+typedef struct test_item
+{
+    pj_sock_t            server_fd, 
+                         client_fd;
+    pj_ioqueue_t        *ioqueue;
+    pj_ioqueue_key_t    *server_key,
+                        *client_key;
+    pj_ioqueue_op_key_t  recv_op,
+                         send_op;
+    int                  has_pending_send;
+    pj_size_t            buffer_size;
+    char                *outgoing_buffer;
+    char                *incoming_buffer;
+    pj_size_t            bytes_sent, 
+                         bytes_recv;
+} test_item;
+
+/* Callback when data has been read.
+ * Increment item->bytes_recv and ready to read the next data.
+ */
+static void on_read_complete(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key,
+                             pj_ssize_t bytes_read)
+{
+    test_item *item = (test_item*)pj_ioqueue_get_user_data(key);
+    pj_status_t rc;
+    int data_is_available = 1;
+
+    //TRACE_((THIS_FILE, "     read complete, bytes_read=%d", bytes_read));
+
+    do {
+        if (thread_quit_flag)
+            return;
+
+        if (bytes_read < 0) {
+            pj_status_t rc = (pj_status_t)-bytes_read;
+            char errmsg[PJ_ERR_MSG_SIZE];
+
+	    if (rc != last_error) {
+	        //last_error = rc;
+	        pj_strerror(rc, errmsg, sizeof(errmsg));
+	        PJ_LOG(3,(THIS_FILE,"...error: read error, bytes_read=%d (%s)", 
+		          bytes_read, errmsg));
+	        PJ_LOG(3,(THIS_FILE, 
+		          ".....additional info: total read=%u, total sent=%u",
+		          item->bytes_recv, item->bytes_sent));
+	    } else {
+	        last_error_counter++;
+	    }
+            bytes_read = 0;
+
+        } else if (bytes_read == 0) {
+            PJ_LOG(3,(THIS_FILE, "...socket has closed!"));
+        }
+
+        item->bytes_recv += bytes_read;
+    
+        /* To assure that the test quits, even if main thread
+         * doesn't have time to run.
+         */
+        if (item->bytes_recv > item->buffer_size * 10000) 
+	    thread_quit_flag = 1;
+
+        bytes_read = item->buffer_size;
+        rc = pj_ioqueue_recv( key, op_key,
+                              item->incoming_buffer, &bytes_read, 0 );
+
+        if (rc == PJ_SUCCESS) {
+            data_is_available = 1;
+        } else if (rc == PJ_EPENDING) {
+            data_is_available = 0;
+        } else {
+            data_is_available = 0;
+	    if (rc != last_error) {
+	        last_error = rc;
+	        app_perror("...error: read error(1)", rc);
+	    } else {
+	        last_error_counter++;
+	    }
+        }
+
+        if (!item->has_pending_send) {
+            pj_ssize_t sent = item->buffer_size;
+            rc = pj_ioqueue_send(item->client_key, &item->send_op,
+                                 item->outgoing_buffer, &sent, 0);
+            if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+                app_perror("...error: write error", rc);
+            }
+
+            item->has_pending_send = (rc==PJ_EPENDING);
+        }
+
+    } while (data_is_available);
+}
+
+/* Callback when data has been written.
+ * Increment item->bytes_sent and write the next data.
+ */
+static void on_write_complete(pj_ioqueue_key_t *key, 
+                              pj_ioqueue_op_key_t *op_key,
+                              pj_ssize_t bytes_sent)
+{
+    test_item *item = (test_item*) pj_ioqueue_get_user_data(key);
+    
+    //TRACE_((THIS_FILE, "     write complete: sent = %d", bytes_sent));
+
+    if (thread_quit_flag)
+        return;
+
+    item->has_pending_send = 0;
+    item->bytes_sent += bytes_sent;
+
+    if (bytes_sent <= 0) {
+        PJ_LOG(3,(THIS_FILE, "...error: sending stopped. bytes_sent=%d", 
+                  bytes_sent));
+    } 
+    else {
+        pj_status_t rc;
+
+        bytes_sent = item->buffer_size;
+        rc = pj_ioqueue_send( item->client_key, op_key,
+                              item->outgoing_buffer, &bytes_sent, 0);
+        if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+            app_perror("...error: write error", rc);
+        }
+
+        item->has_pending_send = (rc==PJ_EPENDING);
+    }
+}
+
+struct thread_arg
+{
+    int		  id;
+    pj_ioqueue_t *ioqueue;
+    unsigned	  counter;
+};
+
+/* The worker thread. */
+static int worker_thread(void *p)
+{
+    struct thread_arg *arg = (struct thread_arg*) p;
+    const pj_time_val timeout = {0, 100};
+    int rc;
+
+    while (!thread_quit_flag) {
+
+	++arg->counter;
+        rc = pj_ioqueue_poll(arg->ioqueue, &timeout);
+	//TRACE_((THIS_FILE, "     thread: poll returned rc=%d", rc));
+        if (rc < 0) {
+	    char errmsg[PJ_ERR_MSG_SIZE];
+	    pj_strerror(-rc, errmsg, sizeof(errmsg));
+            PJ_LOG(3, (THIS_FILE, 
+		       "...error in pj_ioqueue_poll() in thread %d "
+		       "after %d loop: %s [pj_status_t=%d]", 
+		       arg->id, arg->counter, errmsg, -rc));
+            //return -1;
+        }
+    }
+    return 0;
+}
+
+/* Calculate the bandwidth for the specific test configuration.
+ * The test is simple:
+ *  - create sockpair_cnt number of producer-consumer socket pair.
+ *  - create thread_cnt number of worker threads.
+ *  - each producer will send buffer_size bytes data as fast and
+ *    as soon as it can.
+ *  - each consumer will read buffer_size bytes of data as fast 
+ *    as it could.
+ *  - measure the total bytes received by all consumers during a
+ *    period of time.
+ */
+static int perform_test(pj_bool_t allow_concur,
+			int sock_type, const char *type_name,
+                        unsigned thread_cnt, unsigned sockpair_cnt,
+                        pj_size_t buffer_size, 
+                        pj_size_t *p_bandwidth)
+{
+    enum { MSEC_DURATION = 5000 };
+    pj_pool_t *pool;
+    test_item *items;
+    pj_thread_t **thread;
+    pj_ioqueue_t *ioqueue;
+    pj_status_t rc;
+    pj_ioqueue_callback ioqueue_callback;
+    pj_uint32_t total_elapsed_usec, total_received;
+    pj_highprec_t bandwidth;
+    pj_timestamp start, stop;
+    unsigned i;
+
+    TRACE_((THIS_FILE, "    starting test.."));
+
+    ioqueue_callback.on_read_complete = &on_read_complete;
+    ioqueue_callback.on_write_complete = &on_write_complete;
+
+    thread_quit_flag = 0;
+
+    pool = pj_pool_create(mem, NULL, 4096, 4096, NULL);
+    if (!pool)
+        return -10;
+
+    items = (test_item*) pj_pool_alloc(pool, sockpair_cnt*sizeof(test_item));
+    thread = (pj_thread_t**)
+    	     pj_pool_alloc(pool, thread_cnt*sizeof(pj_thread_t*));
+
+    TRACE_((THIS_FILE, "     creating ioqueue.."));
+    rc = pj_ioqueue_create(pool, sockpair_cnt*2, &ioqueue);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error: unable to create ioqueue", rc);
+        return -15;
+    }
+
+    rc = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_ioqueue_set_default_concurrency()", rc);
+        return -16;
+    }
+
+    /* Initialize each producer-consumer pair. */
+    for (i=0; i<sockpair_cnt; ++i) {
+        pj_ssize_t bytes;
+
+        items[i].ioqueue = ioqueue;
+        items[i].buffer_size = buffer_size;
+        items[i].outgoing_buffer = (char*) pj_pool_alloc(pool, buffer_size);
+        items[i].incoming_buffer = (char*) pj_pool_alloc(pool, buffer_size);
+        items[i].bytes_recv = items[i].bytes_sent = 0;
+
+        /* randomize outgoing buffer. */
+        pj_create_random_string(items[i].outgoing_buffer, buffer_size);
+
+        /* Create socket pair. */
+	TRACE_((THIS_FILE, "      calling socketpair.."));
+        rc = app_socketpair(pj_AF_INET(), sock_type, 0, 
+                            &items[i].server_fd, &items[i].client_fd);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: unable to create socket pair", rc);
+            return -20;
+        }
+
+        /* Register server socket to ioqueue. */
+	TRACE_((THIS_FILE, "      register(1).."));
+        rc = pj_ioqueue_register_sock(pool, ioqueue, 
+                                      items[i].server_fd,
+                                      &items[i], &ioqueue_callback,
+                                      &items[i].server_key);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: registering server socket to ioqueue", rc);
+            return -60;
+        }
+
+        /* Register client socket to ioqueue. */
+	TRACE_((THIS_FILE, "      register(2).."));
+        rc = pj_ioqueue_register_sock(pool, ioqueue, 
+                                      items[i].client_fd,
+                                      &items[i],  &ioqueue_callback,
+                                      &items[i].client_key);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: registering server socket to ioqueue", rc);
+            return -70;
+        }
+
+        /* Start reading. */
+	TRACE_((THIS_FILE, "      pj_ioqueue_recv.."));
+        bytes = items[i].buffer_size;
+        rc = pj_ioqueue_recv(items[i].server_key, &items[i].recv_op,
+                             items[i].incoming_buffer, &bytes,
+			     0);
+        if (rc != PJ_EPENDING) {
+            app_perror("...error: pj_ioqueue_recv", rc);
+            return -73;
+        }
+
+        /* Start writing. */
+	TRACE_((THIS_FILE, "      pj_ioqueue_write.."));
+        bytes = items[i].buffer_size;
+        rc = pj_ioqueue_send(items[i].client_key, &items[i].send_op,
+                             items[i].outgoing_buffer, &bytes, 0);
+        if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+            app_perror("...error: pj_ioqueue_write", rc);
+            return -76;
+        }
+
+        items[i].has_pending_send = (rc==PJ_EPENDING);
+    }
+
+    /* Create the threads. */
+    for (i=0; i<thread_cnt; ++i) {
+	struct thread_arg *arg;
+
+	arg = (struct thread_arg*) pj_pool_zalloc(pool, sizeof(*arg));
+	arg->id = i;
+	arg->ioqueue = ioqueue;
+	arg->counter = 0;
+
+        rc = pj_thread_create( pool, NULL, 
+                               &worker_thread, 
+                               arg, 
+                               PJ_THREAD_DEFAULT_STACK_SIZE, 
+                               PJ_THREAD_SUSPENDED, &thread[i] );
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: unable to create thread", rc);
+            return -80;
+        }
+    }
+
+    /* Mark start time. */
+    rc = pj_get_timestamp(&start);
+    if (rc != PJ_SUCCESS)
+        return -90;
+
+    /* Start the thread. */
+    TRACE_((THIS_FILE, "     resuming all threads.."));
+    for (i=0; i<thread_cnt; ++i) {
+        rc = pj_thread_resume(thread[i]);
+        if (rc != 0)
+            return -100;
+    }
+
+    /* Wait for MSEC_DURATION seconds. 
+     * This should be as simple as pj_thread_sleep(MSEC_DURATION) actually,
+     * but unfortunately it doesn't work when system doesn't employ
+     * timeslicing for threads.
+     */
+    TRACE_((THIS_FILE, "     wait for few seconds.."));
+    do {
+	pj_thread_sleep(1);
+
+	/* Mark end time. */
+	rc = pj_get_timestamp(&stop);
+
+	if (thread_quit_flag) {
+	    TRACE_((THIS_FILE, "      transfer limit reached.."));
+	    break;
+	}
+
+	if (pj_elapsed_usec(&start,&stop)<MSEC_DURATION * 1000) {
+	    TRACE_((THIS_FILE, "      time limit reached.."));
+	    break;
+	}
+
+    } while (1);
+
+    /* Terminate all threads. */
+    TRACE_((THIS_FILE, "     terminating all threads.."));
+    thread_quit_flag = 1;
+
+    for (i=0; i<thread_cnt; ++i) {
+	TRACE_((THIS_FILE, "      join thread %d..", i));
+        pj_thread_join(thread[i]);
+    }
+
+    /* Close all sockets. */
+    TRACE_((THIS_FILE, "     closing all sockets.."));
+    for (i=0; i<sockpair_cnt; ++i) {
+        pj_ioqueue_unregister(items[i].server_key);
+        pj_ioqueue_unregister(items[i].client_key);
+    }
+
+    /* Destroy threads */
+    for (i=0; i<thread_cnt; ++i) {
+        pj_thread_destroy(thread[i]);
+    }
+
+    /* Destroy ioqueue. */
+    TRACE_((THIS_FILE, "     destroying ioqueue.."));
+    pj_ioqueue_destroy(ioqueue);
+
+    /* Calculate actual time in usec. */
+    total_elapsed_usec = pj_elapsed_usec(&start, &stop);
+
+    /* Calculate total bytes received. */
+    total_received = 0;
+    for (i=0; i<sockpair_cnt; ++i) {
+        total_received = (pj_uint32_t)items[i].bytes_recv;
+    }
+
+    /* bandwidth = total_received*1000/total_elapsed_usec */
+    bandwidth = total_received;
+    pj_highprec_mul(bandwidth, 1000);
+    pj_highprec_div(bandwidth, total_elapsed_usec);
+    
+    *p_bandwidth = (pj_uint32_t)bandwidth;
+
+    PJ_LOG(3,(THIS_FILE, "   %.4s    %2d        %2d       %8d KB/s",
+              type_name, thread_cnt, sockpair_cnt,
+              *p_bandwidth));
+
+    /* Done. */
+    pj_pool_release(pool);
+
+    TRACE_((THIS_FILE, "    done.."));
+    return 0;
+}
+
+static int ioqueue_perf_test_imp(pj_bool_t allow_concur)
+{
+    enum { BUF_SIZE = 512 };
+    int i, rc;
+    struct {
+        int         type;
+        const char *type_name;
+        int         thread_cnt;
+        int         sockpair_cnt;
+    } test_param[] = 
+    {
+        { pj_SOCK_DGRAM(), "udp", 1, 1},
+        { pj_SOCK_DGRAM(), "udp", 1, 2},
+        { pj_SOCK_DGRAM(), "udp", 1, 4},
+        { pj_SOCK_DGRAM(), "udp", 1, 8},
+        { pj_SOCK_DGRAM(), "udp", 2, 1},
+        { pj_SOCK_DGRAM(), "udp", 2, 2},
+        { pj_SOCK_DGRAM(), "udp", 2, 4},
+        { pj_SOCK_DGRAM(), "udp", 2, 8},
+        { pj_SOCK_DGRAM(), "udp", 4, 1},
+        { pj_SOCK_DGRAM(), "udp", 4, 2},
+        { pj_SOCK_DGRAM(), "udp", 4, 4},
+        { pj_SOCK_DGRAM(), "udp", 4, 8},
+        { pj_SOCK_DGRAM(), "udp", 4, 16},
+        { pj_SOCK_STREAM(), "tcp", 1, 1},
+        { pj_SOCK_STREAM(), "tcp", 1, 2},
+        { pj_SOCK_STREAM(), "tcp", 1, 4},
+        { pj_SOCK_STREAM(), "tcp", 1, 8},
+        { pj_SOCK_STREAM(), "tcp", 2, 1},
+        { pj_SOCK_STREAM(), "tcp", 2, 2},
+        { pj_SOCK_STREAM(), "tcp", 2, 4},
+        { pj_SOCK_STREAM(), "tcp", 2, 8},
+        { pj_SOCK_STREAM(), "tcp", 4, 1},
+        { pj_SOCK_STREAM(), "tcp", 4, 2},
+        { pj_SOCK_STREAM(), "tcp", 4, 4},
+        { pj_SOCK_STREAM(), "tcp", 4, 8},
+        { pj_SOCK_STREAM(), "tcp", 4, 16},
+/*
+	{ pj_SOCK_DGRAM(), "udp", 32, 1},
+	{ pj_SOCK_DGRAM(), "udp", 32, 1},
+	{ pj_SOCK_DGRAM(), "udp", 32, 1},
+	{ pj_SOCK_DGRAM(), "udp", 32, 1},
+	{ pj_SOCK_DGRAM(), "udp", 1, 32},
+	{ pj_SOCK_DGRAM(), "udp", 1, 32},
+	{ pj_SOCK_DGRAM(), "udp", 1, 32},
+	{ pj_SOCK_DGRAM(), "udp", 1, 32},
+	{ pj_SOCK_STREAM(), "tcp", 32, 1},
+	{ pj_SOCK_STREAM(), "tcp", 32, 1},
+	{ pj_SOCK_STREAM(), "tcp", 32, 1},
+	{ pj_SOCK_STREAM(), "tcp", 32, 1},
+	{ pj_SOCK_STREAM(), "tcp", 1, 32},
+	{ pj_SOCK_STREAM(), "tcp", 1, 32},
+	{ pj_SOCK_STREAM(), "tcp", 1, 32},
+	{ pj_SOCK_STREAM(), "tcp", 1, 32},
+*/
+    };
+    pj_size_t best_bandwidth;
+    int best_index = 0;
+
+    PJ_LOG(3,(THIS_FILE, "   Benchmarking %s ioqueue:", pj_ioqueue_name()));
+    PJ_LOG(3,(THIS_FILE, "   Testing with concurency=%d", allow_concur));
+    PJ_LOG(3,(THIS_FILE, "   ======================================="));
+    PJ_LOG(3,(THIS_FILE, "   Type  Threads  Skt.Pairs      Bandwidth"));
+    PJ_LOG(3,(THIS_FILE, "   ======================================="));
+
+    best_bandwidth = 0;
+    for (i=0; i<(int)(sizeof(test_param)/sizeof(test_param[0])); ++i) {
+        pj_size_t bandwidth;
+
+        rc = perform_test(allow_concur,
+			  test_param[i].type, 
+                          test_param[i].type_name,
+                          test_param[i].thread_cnt, 
+                          test_param[i].sockpair_cnt, 
+                          BUF_SIZE, 
+                          &bandwidth);
+        if (rc != 0)
+            return rc;
+
+        if (bandwidth > best_bandwidth)
+            best_bandwidth = bandwidth, best_index = i;
+
+        /* Give it a rest before next test, to allow system to close the
+	 * sockets properly. 
+	 */
+        pj_thread_sleep(500);
+    }
+
+    PJ_LOG(3,(THIS_FILE, 
+              "   Best: Type=%s Threads=%d, Skt.Pairs=%d, Bandwidth=%u KB/s",
+              test_param[best_index].type_name,
+              test_param[best_index].thread_cnt,
+              test_param[best_index].sockpair_cnt,
+              best_bandwidth));
+    PJ_LOG(3,(THIS_FILE, "   (Note: packet size=%d, total errors=%u)", 
+			 BUF_SIZE, last_error_counter));
+    return 0;
+}
+
+/*
+ * main test entry.
+ */
+int ioqueue_perf_test(void)
+{
+    int rc;
+
+    rc = ioqueue_perf_test_imp(PJ_TRUE);
+    if (rc != 0)
+	return rc;
+
+    rc = ioqueue_perf_test_imp(PJ_FALSE);
+    if (rc != 0)
+	return rc;
+
+    return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_uiq_perf_test;
+#endif  /* INCLUDE_IOQUEUE_PERF_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/ioq_tcp.c b/jni/pjproject-android/pjlib/src/pjlib-test/ioq_tcp.c
new file mode 100644
index 0000000..aa9e1ef
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/ioq_tcp.c
@@ -0,0 +1,972 @@
+/* $Id: ioq_tcp.c 4550 2013-07-02 11:45:57Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_ioqueue_tcp_test Test: I/O Queue (TCP)
+ *
+ * This file provides implementation to test the
+ * functionality of the I/O queue when TCP socket is used.
+ *
+ *
+ * This file is <b>pjlib-test/ioq_tcp.c</b>
+ *
+ * \include pjlib-test/ioq_tcp.c
+ */
+
+
+#if INCLUDE_TCP_IOQUEUE_TEST
+
+#include <pjlib.h>
+
+#if PJ_HAS_TCP
+
+#define THIS_FILE	    "test_tcp"
+#define NON_EXISTANT_PORT   50123
+#define LOOP		    100
+#define BUF_MIN_SIZE	    32
+#define BUF_MAX_SIZE	    2048
+#define SOCK_INACTIVE_MIN   (4-2)
+#define SOCK_INACTIVE_MAX   (PJ_IOQUEUE_MAX_HANDLES - 2)
+#define POOL_SIZE	    (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
+
+static pj_ssize_t	     callback_read_size,
+                             callback_write_size,
+                             callback_accept_status,
+                             callback_connect_status;
+static unsigned		     callback_call_count;
+static pj_ioqueue_key_t     *callback_read_key,
+                            *callback_write_key,
+                            *callback_accept_key,
+                            *callback_connect_key;
+static pj_ioqueue_op_key_t  *callback_read_op,
+                            *callback_write_op,
+                            *callback_accept_op;
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key, 
+                            pj_ioqueue_op_key_t *op_key,
+                            pj_ssize_t bytes_read)
+{
+    callback_read_key = key;
+    callback_read_op = op_key;
+    callback_read_size = bytes_read;
+    callback_call_count++;
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key,
+                             pj_ssize_t bytes_written)
+{
+    callback_write_key = key;
+    callback_write_op = op_key;
+    callback_write_size = bytes_written;
+    callback_call_count++;
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key, 
+                              pj_ioqueue_op_key_t *op_key,
+                              pj_sock_t sock, 
+                              int status)
+{
+    if (sock == PJ_INVALID_SOCKET) {
+
+	if (status != PJ_SUCCESS) {
+	    /* Ignore. Could be blocking error */
+	    app_perror(".....warning: received error in on_ioqueue_accept() callback",
+		       status);
+	} else {
+	    callback_accept_status = -61;
+	    PJ_LOG(3,("", "..... on_ioqueue_accept() callback was given "
+			  "invalid socket and status is %d", status));
+	}
+    } else {
+        pj_sockaddr addr;
+        int client_addr_len;
+
+        client_addr_len = sizeof(addr);
+        status = pj_sock_getsockname(sock, &addr, &client_addr_len);
+        if (status != PJ_SUCCESS) {
+            app_perror("...ERROR in pj_sock_getsockname()", status);
+        }
+
+	callback_accept_key = key;
+	callback_accept_op = op_key;
+	callback_accept_status = status;
+	callback_call_count++;
+    }
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+    callback_connect_key = key;
+    callback_connect_status = status;
+    callback_call_count++;
+}
+
+static pj_ioqueue_callback test_cb = 
+{
+    &on_ioqueue_read,
+    &on_ioqueue_write,
+    &on_ioqueue_accept,
+    &on_ioqueue_connect,
+};
+
+static int send_recv_test(pj_ioqueue_t *ioque,
+			  pj_ioqueue_key_t *skey,
+			  pj_ioqueue_key_t *ckey,
+			  void *send_buf,
+			  void *recv_buf,
+			  pj_ssize_t bufsize,
+			  pj_timestamp *t_elapsed)
+{
+    pj_status_t status;
+    pj_ssize_t bytes;
+    pj_time_val timeout;
+    pj_timestamp t1, t2;
+    int pending_op = 0;
+    pj_ioqueue_op_key_t read_op, write_op;
+
+    // Start reading on the server side.
+    bytes = bufsize;
+    status = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+        app_perror("...pj_ioqueue_recv error", status);
+	return -100;
+    }
+    
+    if (status == PJ_EPENDING)
+        ++pending_op;
+    else {
+        /* Does not expect to return error or immediate data. */
+        return -115;
+    }
+
+    // Randomize send buffer.
+    pj_create_random_string((char*)send_buf, bufsize);
+
+    // Starts send on the client side.
+    bytes = bufsize;
+    status = pj_ioqueue_send(ckey, &write_op, send_buf, &bytes, 0);
+    if (status != PJ_SUCCESS && bytes != PJ_EPENDING) {
+	return -120;
+    }
+    if (status == PJ_EPENDING) {
+	++pending_op;
+    }
+
+    // Begin time.
+    pj_get_timestamp(&t1);
+
+    // Reset indicators
+    callback_read_size = callback_write_size = 0;
+    callback_read_key = callback_write_key = NULL;
+    callback_read_op = callback_write_op = NULL;
+
+    // Poll the queue until we've got completion event in the server side.
+    status = 0;
+    while (pending_op > 0) {
+        timeout.sec = 1; timeout.msec = 0;
+#ifdef PJ_SYMBIAN
+	PJ_UNUSED_ARG(ioque);
+	status = pj_symbianos_poll(-1, 1000);
+#else
+	status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+	if (status > 0) {
+            if (callback_read_size) {
+                if (callback_read_size != bufsize)
+                    return -160;
+                if (callback_read_key != skey)
+                    return -161;
+                if (callback_read_op != &read_op)
+                    return -162;
+            }
+            if (callback_write_size) {
+                if (callback_write_key != ckey)
+                    return -163;
+                if (callback_write_op != &write_op)
+                    return -164;
+            }
+	    pending_op -= status;
+	}
+        if (status == 0) {
+            PJ_LOG(3,("", "...error: timed out"));
+        }
+	if (status < 0) {
+	    return -170;
+	}
+    }
+
+    // Pending op is zero.
+    // Subsequent poll should yield zero too.
+    timeout.sec = timeout.msec = 0;
+#ifdef PJ_SYMBIAN
+    status = pj_symbianos_poll(-1, 1);
+#else
+    status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+    if (status != 0)
+        return -173;
+
+    // End time.
+    pj_get_timestamp(&t2);
+    t_elapsed->u32.lo += (t2.u32.lo - t1.u32.lo);
+
+    // Compare recv buffer with send buffer.
+    if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
+	return -180;
+    }
+
+    // Success
+    return 0;
+}
+
+
+/*
+ * Compliance test for success scenario.
+ */
+static int compliance_test_0(pj_bool_t allow_concur)
+{
+    pj_sock_t ssock=-1, csock0=-1, csock1=-1;
+    pj_sockaddr_in addr, client_addr, rmt_addr;
+    int client_addr_len;
+    pj_pool_t *pool = NULL;
+    char *send_buf, *recv_buf;
+    pj_ioqueue_t *ioque = NULL;
+    pj_ioqueue_key_t *skey=NULL, *ckey0=NULL, *ckey1=NULL;
+    pj_ioqueue_op_key_t accept_op;
+    int bufsize = BUF_MIN_SIZE;
+    int status = -1;
+    int pending_op = 0;
+    pj_timestamp t_elapsed;
+    pj_str_t s;
+    pj_status_t rc;
+
+    // Create pool.
+    pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+    // Allocate buffers for send and receive.
+    send_buf = (char*)pj_pool_alloc(pool, bufsize);
+    recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+    // Create server socket and client socket for connecting
+    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &ssock);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error creating socket", rc);
+        status=-1; goto on_error;
+    }
+
+    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &csock1);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error creating socket", rc);
+	status=-1; goto on_error;
+    }
+
+    // Bind server socket.
+    pj_sockaddr_in_init(&addr, 0, 0);
+    if ((rc=pj_sock_bind(ssock, &addr, sizeof(addr))) != 0 ) {
+        app_perror("...bind error", rc);
+	status=-10; goto on_error;
+    }
+
+    // Get server address.
+    client_addr_len = sizeof(addr);
+    rc = pj_sock_getsockname(ssock, &addr, &client_addr_len);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_sock_getsockname()", rc);
+	status=-15; goto on_error;
+    }
+    addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+    // Create I/O Queue.
+    rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_ioqueue_create()", rc);
+	status=-20; goto on_error;
+    }
+
+    // Concurrency
+    rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_ioqueue_set_default_concurrency()", rc);
+	status=-21; goto on_error;
+    }
+
+    // Register server socket and client socket.
+    rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, &test_cb, &skey);
+    if (rc == PJ_SUCCESS)
+        rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, &test_cb, 
+                                      &ckey1);
+    else
+        ckey1 = NULL;
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
+	status=-23; goto on_error;
+    }
+
+    // Server socket listen().
+    if (pj_sock_listen(ssock, 5)) {
+        app_perror("...ERROR in pj_sock_listen()", rc);
+	status=-25; goto on_error;
+    }
+
+    // Server socket accept()
+    client_addr_len = sizeof(pj_sockaddr_in);
+    status = pj_ioqueue_accept(skey, &accept_op, &csock0, 
+                               &client_addr, &rmt_addr, &client_addr_len);
+    if (status != PJ_EPENDING) {
+        app_perror("...ERROR in pj_ioqueue_accept()", rc);
+	status=-30; goto on_error;
+    }
+    if (status==PJ_EPENDING) {
+	++pending_op;
+    }
+
+    // Client socket connect()
+    status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
+    if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+        app_perror("...ERROR in pj_ioqueue_connect()", rc);
+	status=-40; goto on_error;
+    }
+    if (status==PJ_EPENDING) {
+	++pending_op;
+    }
+
+    // Poll until connected
+    callback_read_size = callback_write_size = 0;
+    callback_accept_status = callback_connect_status = -2;
+    callback_call_count = 0;
+
+    callback_read_key = callback_write_key = 
+        callback_accept_key = callback_connect_key = NULL;
+    callback_accept_op = callback_read_op = callback_write_op = NULL;
+
+    while (pending_op) {
+	pj_time_val timeout = {1, 0};
+
+#ifdef PJ_SYMBIAN
+	callback_call_count = 0;
+	pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
+	status = callback_call_count;
+#else
+	status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+	if (status > 0) {
+            if (callback_accept_status != -2) {
+                if (callback_accept_status != 0) {
+                    status=-41; goto on_error;
+                }
+                if (callback_accept_key != skey) {
+                    status=-42; goto on_error;
+                }
+                if (callback_accept_op != &accept_op) {
+                    status=-43; goto on_error;
+                }
+                callback_accept_status = -2;
+            }
+
+            if (callback_connect_status != -2) {
+                if (callback_connect_status != 0) {
+                    status=-50; goto on_error;
+                }
+                if (callback_connect_key != ckey1) {
+                    status=-51; goto on_error;
+                }
+                callback_connect_status = -2;
+            }
+
+	    if (status > pending_op) {
+		PJ_LOG(3,(THIS_FILE,
+			  "...error: pj_ioqueue_poll() returned %d "
+			  "(only expecting %d)",
+			  status, pending_op));
+		return -52;
+	    }
+	    pending_op -= status;
+
+	    if (pending_op == 0) {
+		status = 0;
+	    }
+	}
+    }
+
+    // There's no pending operation.
+    // When we poll the ioqueue, there must not be events.
+    if (pending_op == 0) {
+        pj_time_val timeout = {1, 0};
+#ifdef PJ_SYMBIAN
+	status = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
+#else
+        status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+        if (status != 0) {
+            status=-60; goto on_error;
+        }
+    }
+
+    // Check accepted socket.
+    if (csock0 == PJ_INVALID_SOCKET) {
+	status = -69;
+        app_perror("...accept() error", pj_get_os_error());
+	goto on_error;
+    }
+
+    // Register newly accepted socket.
+    rc = pj_ioqueue_register_sock(pool, ioque, csock0, NULL, 
+                                  &test_cb, &ckey0);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_ioqueue_register_sock", rc);
+	status = -70;
+	goto on_error;
+    }
+
+    // Test send and receive.
+    t_elapsed.u32.lo = 0;
+    status = send_recv_test(ioque, ckey0, ckey1, send_buf, 
+                            recv_buf, bufsize, &t_elapsed);
+    if (status != 0) {
+	goto on_error;
+    }
+
+    // Success
+    status = 0;
+
+on_error:
+    if (skey != NULL)
+    	pj_ioqueue_unregister(skey);
+    else if (ssock != PJ_INVALID_SOCKET)
+	pj_sock_close(ssock);
+    
+    if (ckey1 != NULL)
+    	pj_ioqueue_unregister(ckey1);
+    else if (csock1 != PJ_INVALID_SOCKET)
+	pj_sock_close(csock1);
+    
+    if (ckey0 != NULL)
+    	pj_ioqueue_unregister(ckey0);
+    else if (csock0 != PJ_INVALID_SOCKET)
+	pj_sock_close(csock0);
+    
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release(pool);
+    return status;
+
+}
+
+/*
+ * Compliance test for failed scenario.
+ * In this case, the client connects to a non-existant service.
+ */
+static int compliance_test_1(pj_bool_t allow_concur)
+{
+    pj_sock_t csock1=PJ_INVALID_SOCKET;
+    pj_sockaddr_in addr;
+    pj_pool_t *pool = NULL;
+    pj_ioqueue_t *ioque = NULL;
+    pj_ioqueue_key_t *ckey1 = NULL;
+    int status = -1;
+    int pending_op = 0;
+    pj_str_t s;
+    pj_status_t rc;
+
+    // Create pool.
+    pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+    // Create I/O Queue.
+    rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+    if (!ioque) {
+	status=-20; goto on_error;
+    }
+
+    // Concurrency
+    rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+    if (rc != PJ_SUCCESS) {
+	status=-21; goto on_error;
+    }
+
+    // Create client socket
+    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &csock1);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_sock_socket()", rc);
+	status=-1; goto on_error;
+    }
+
+    // Register client socket.
+    rc = pj_ioqueue_register_sock(pool, ioque, csock1, NULL, 
+                                  &test_cb, &ckey1);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_ioqueue_register_sock()", rc);
+	status=-23; goto on_error;
+    }
+
+    // Initialize remote address.
+    pj_sockaddr_in_init(&addr, pj_cstr(&s, "127.0.0.1"), NON_EXISTANT_PORT);
+
+    // Client socket connect()
+    status = pj_ioqueue_connect(ckey1, &addr, sizeof(addr));
+    if (status==PJ_SUCCESS) {
+	// unexpectedly success!
+	status = -30;
+	goto on_error;
+    }
+    if (status != PJ_EPENDING) {
+	// success
+    } else {
+	++pending_op;
+    }
+
+    callback_connect_status = -2;
+    callback_connect_key = NULL;
+
+    // Poll until we've got result
+    while (pending_op) {
+	pj_time_val timeout = {1, 0};
+
+#ifdef PJ_SYMBIAN
+	callback_call_count = 0;
+	pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
+	status = callback_call_count;
+#else
+	status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+	if (status > 0) {
+            if (callback_connect_key==ckey1) {
+		if (callback_connect_status == 0) {
+		    // unexpectedly connected!
+		    status = -50;
+		    goto on_error;
+		}
+	    }
+
+	    if (status > pending_op) {
+		PJ_LOG(3,(THIS_FILE,
+			  "...error: pj_ioqueue_poll() returned %d "
+			  "(only expecting %d)",
+			  status, pending_op));
+		return -552;
+	    }
+
+	    pending_op -= status;
+	    if (pending_op == 0) {
+		status = 0;
+	    }
+	}
+    }
+
+    // There's no pending operation.
+    // When we poll the ioqueue, there must not be events.
+    if (pending_op == 0) {
+        pj_time_val timeout = {1, 0};
+#ifdef PJ_SYMBIAN
+	status = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
+#else
+        status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+        if (status != 0) {
+            status=-60; goto on_error;
+        }
+    }
+
+    // Success
+    status = 0;
+
+on_error:
+    if (ckey1 != NULL)
+    	pj_ioqueue_unregister(ckey1);
+    else if (csock1 != PJ_INVALID_SOCKET)
+	pj_sock_close(csock1);
+    
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release(pool);
+    return status;
+}
+
+
+/*
+ * Repeated connect/accept on the same listener socket.
+ */
+static int compliance_test_2(pj_bool_t allow_concur)
+{
+#if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
+    enum { MAX_PAIR = 1, TEST_LOOP = 2 };
+#else
+    enum { MAX_PAIR = 4, TEST_LOOP = 2 };
+#endif
+
+    struct listener
+    {
+	pj_sock_t	     sock;
+	pj_ioqueue_key_t    *key;
+	pj_sockaddr_in	     addr;
+	int		     addr_len;
+    } listener;
+
+    struct server
+    {
+	pj_sock_t	     sock;
+	pj_ioqueue_key_t    *key;
+	pj_sockaddr_in	     local_addr;
+	pj_sockaddr_in	     rem_addr;
+	int		     rem_addr_len;
+	pj_ioqueue_op_key_t  accept_op;
+    } server[MAX_PAIR];
+
+    struct client
+    {
+	pj_sock_t	     sock;
+	pj_ioqueue_key_t    *key;
+    } client[MAX_PAIR];
+
+    pj_pool_t *pool = NULL;
+    char *send_buf, *recv_buf;
+    pj_ioqueue_t *ioque = NULL;
+    int i, bufsize = BUF_MIN_SIZE;
+    int status;
+    int test_loop, pending_op = 0;
+    pj_timestamp t_elapsed;
+    pj_str_t s;
+    pj_status_t rc;
+
+    listener.sock = PJ_INVALID_SOCKET;
+    listener.key = NULL;
+    
+    for (i=0; i<MAX_PAIR; ++i) {
+    	server[i].sock = PJ_INVALID_SOCKET;
+    	server[i].key = NULL;
+    }
+    
+    for (i=0; i<MAX_PAIR; ++i) {
+    	client[i].sock = PJ_INVALID_SOCKET;
+    	client[i].key = NULL;	
+    }
+    
+    // Create pool.
+    pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+
+    // Create I/O Queue.
+    rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_ioqueue_create()", rc);
+	return -10;
+    }
+
+
+    // Concurrency
+    rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_ioqueue_set_default_concurrency()", rc);
+	return -11;
+    }
+
+    // Allocate buffers for send and receive.
+    send_buf = (char*)pj_pool_alloc(pool, bufsize);
+    recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+    // Create listener socket
+    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &listener.sock);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error creating socket", rc);
+        status=-20; goto on_error;
+    }
+
+    // Bind listener socket.
+    pj_sockaddr_in_init(&listener.addr, 0, 0);
+    if ((rc=pj_sock_bind(listener.sock, &listener.addr, sizeof(listener.addr))) != 0 ) {
+        app_perror("...bind error", rc);
+	status=-30; goto on_error;
+    }
+
+    // Get listener address.
+    listener.addr_len = sizeof(listener.addr);
+    rc = pj_sock_getsockname(listener.sock, &listener.addr, &listener.addr_len);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_sock_getsockname()", rc);
+	status=-40; goto on_error;
+    }
+    listener.addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+
+    // Register listener socket.
+    rc = pj_ioqueue_register_sock(pool, ioque, listener.sock, NULL, &test_cb, 
+				  &listener.key);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...ERROR", rc);
+	status=-50; goto on_error;
+    }
+
+
+    // Listener socket listen().
+    if (pj_sock_listen(listener.sock, 5)) {
+        app_perror("...ERROR in pj_sock_listen()", rc);
+	status=-60; goto on_error;
+    }
+
+
+    for (test_loop=0; test_loop < TEST_LOOP; ++test_loop) {
+	// Client connect and server accept.
+	for (i=0; i<MAX_PAIR; ++i) {
+	    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &client[i].sock);
+	    if (rc != PJ_SUCCESS) {
+		app_perror("...error creating socket", rc);
+		status=-70; goto on_error;
+	    }
+
+	    rc = pj_ioqueue_register_sock(pool, ioque, client[i].sock, NULL, 
+					  &test_cb, &client[i].key);
+	    if (rc != PJ_SUCCESS) {
+		app_perror("...error ", rc);
+		status=-80; goto on_error;
+	    }
+
+	    // Server socket accept()
+	    pj_ioqueue_op_key_init(&server[i].accept_op, 
+				   sizeof(server[i].accept_op));
+	    server[i].rem_addr_len = sizeof(pj_sockaddr_in);
+	    status = pj_ioqueue_accept(listener.key, &server[i].accept_op, 
+				       &server[i].sock, &server[i].local_addr, 
+				       &server[i].rem_addr, 
+				       &server[i].rem_addr_len);
+	    if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+		app_perror("...ERROR in pj_ioqueue_accept()", rc);
+		status=-90; goto on_error;
+	    }
+	    if (status==PJ_EPENDING) {
+		++pending_op;
+	    }
+
+
+	    // Client socket connect()
+	    status = pj_ioqueue_connect(client[i].key, &listener.addr, 
+					sizeof(listener.addr));
+	    if (status!=PJ_SUCCESS && status != PJ_EPENDING) {
+		app_perror("...ERROR in pj_ioqueue_connect()", rc);
+		status=-100; goto on_error;
+	    }
+	    if (status==PJ_EPENDING) {
+		++pending_op;
+	    }
+
+	    // Poll until connection of this pair established
+	    while (pending_op) {
+		pj_time_val timeout = {1, 0};
+
+#ifdef PJ_SYMBIAN
+		status = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
+#else
+		status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+		if (status > 0) {
+		    if (status > pending_op) {
+			PJ_LOG(3,(THIS_FILE,
+				  "...error: pj_ioqueue_poll() returned %d "
+				  "(only expecting %d)",
+				  status, pending_op));
+			return -110;
+		    }
+		    pending_op -= status;
+
+		    if (pending_op == 0) {
+			status = 0;
+		    }
+		}
+	    }
+	}
+
+	// There's no pending operation.
+	// When we poll the ioqueue, there must not be events.
+	if (pending_op == 0) {
+	    pj_time_val timeout = {1, 0};
+#ifdef PJ_SYMBIAN
+	    status = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
+#else
+	    status = pj_ioqueue_poll(ioque, &timeout);
+#endif
+	    if (status != 0) {
+		status=-120; goto on_error;
+	    }
+	}
+
+	for (i=0; i<MAX_PAIR; ++i) {
+	    // Check server socket.
+	    if (server[i].sock == PJ_INVALID_SOCKET) {
+		status = -130;
+		app_perror("...accept() error", pj_get_os_error());
+		goto on_error;
+	    }
+
+	    // Check addresses
+	    if (server[i].local_addr.sin_family != pj_AF_INET() ||
+		server[i].local_addr.sin_addr.s_addr == 0 ||
+		server[i].local_addr.sin_port == 0)
+	    {
+		app_perror("...ERROR address not set", rc);
+		status = -140;
+		goto on_error;
+	    }
+
+	    if (server[i].rem_addr.sin_family != pj_AF_INET() ||
+		server[i].rem_addr.sin_addr.s_addr == 0 ||
+		server[i].rem_addr.sin_port == 0)
+	    {
+		app_perror("...ERROR address not set", rc);
+		status = -150;
+		goto on_error;
+	    }
+
+
+	    // Register newly accepted socket.
+	    rc = pj_ioqueue_register_sock(pool, ioque, server[i].sock, NULL,
+					  &test_cb, &server[i].key);
+	    if (rc != PJ_SUCCESS) {
+		app_perror("...ERROR in pj_ioqueue_register_sock", rc);
+		status = -160;
+		goto on_error;
+	    }
+
+	    // Test send and receive.
+	    t_elapsed.u32.lo = 0;
+	    status = send_recv_test(ioque, server[i].key, client[i].key, 
+				    send_buf, recv_buf, bufsize, &t_elapsed);
+	    if (status != 0) {
+		goto on_error;
+	    }
+	}
+
+	// Success
+	status = 0;
+
+	for (i=0; i<MAX_PAIR; ++i) {
+	    if (server[i].key != NULL) {
+		pj_ioqueue_unregister(server[i].key);
+		server[i].key = NULL;
+		server[i].sock = PJ_INVALID_SOCKET;
+	    } else if (server[i].sock != PJ_INVALID_SOCKET) {
+		pj_sock_close(server[i].sock);
+		server[i].sock = PJ_INVALID_SOCKET;
+	    }
+
+	    if (client[i].key != NULL) {
+		pj_ioqueue_unregister(client[i].key);
+		client[i].key = NULL;
+		client[i].sock = PJ_INVALID_SOCKET;
+	    } else if (client[i].sock != PJ_INVALID_SOCKET) {
+		pj_sock_close(client[i].sock);
+		client[i].sock = PJ_INVALID_SOCKET;
+	    }
+	}
+    }
+
+    status = 0;
+
+on_error:
+    for (i=0; i<MAX_PAIR; ++i) {
+	if (server[i].key != NULL) {
+	    pj_ioqueue_unregister(server[i].key);
+	    server[i].key = NULL;
+	    server[i].sock = PJ_INVALID_SOCKET;
+	} else if (server[i].sock != PJ_INVALID_SOCKET) {
+	    pj_sock_close(server[i].sock);
+	    server[i].sock = PJ_INVALID_SOCKET;
+	}
+
+	if (client[i].key != NULL) {
+	    pj_ioqueue_unregister(client[i].key);
+	    client[i].key = NULL;
+	    server[i].sock = PJ_INVALID_SOCKET;
+	} else if (client[i].sock != PJ_INVALID_SOCKET) {
+	    pj_sock_close(client[i].sock);
+	    client[i].sock = PJ_INVALID_SOCKET;
+	}
+    }
+
+    if (listener.key) {
+	pj_ioqueue_unregister(listener.key);
+	listener.key = NULL;
+    } else if (listener.sock != PJ_INVALID_SOCKET) {
+	pj_sock_close(listener.sock);
+	listener.sock = PJ_INVALID_SOCKET;
+    }
+
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release(pool);
+    return status;
+
+}
+
+
+static int tcp_ioqueue_test_impl(pj_bool_t allow_concur)
+{
+    int status;
+
+    PJ_LOG(3,(THIS_FILE, "..testing with concurency=%d", allow_concur));
+
+    PJ_LOG(3, (THIS_FILE, "..%s compliance test 0 (success scenario)",
+	       pj_ioqueue_name()));
+    if ((status=compliance_test_0(allow_concur)) != 0) {
+	PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+	return status;
+    }
+    PJ_LOG(3, (THIS_FILE, "..%s compliance test 1 (failed scenario)",
+               pj_ioqueue_name()));
+    if ((status=compliance_test_1(allow_concur)) != 0) {
+	PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+	return status;
+    }
+
+    PJ_LOG(3, (THIS_FILE, "..%s compliance test 2 (repeated accept)",
+               pj_ioqueue_name()));
+    if ((status=compliance_test_2(allow_concur)) != 0) {
+	PJ_LOG(1, (THIS_FILE, "....FAILED (status=%d)\n", status));
+	return status;
+    }
+
+    return 0;
+}
+
+int tcp_ioqueue_test()
+{
+    int rc;
+
+    rc = tcp_ioqueue_test_impl(PJ_TRUE);
+    if (rc != 0)
+	return rc;
+
+    rc = tcp_ioqueue_test_impl(PJ_FALSE);
+    if (rc != 0)
+	return rc;
+
+    return 0;
+}
+
+#endif	/* PJ_HAS_TCP */
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_uiq_tcp;
+#endif	/* INCLUDE_TCP_IOQUEUE_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/ioq_udp.c b/jni/pjproject-android/pjlib/src/pjlib-test/ioq_udp.c
new file mode 100644
index 0000000..93656ff
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/ioq_udp.c
@@ -0,0 +1,953 @@
+/* $Id: ioq_udp.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_ioqueue_udp_test Test: I/O Queue (UDP)
+ *
+ * This file provides implementation to test the
+ * functionality of the I/O queue when UDP socket is used.
+ *
+ *
+ * This file is <b>pjlib-test/ioq_udp.c</b>
+ *
+ * \include pjlib-test/ioq_udp.c
+ */
+
+
+#if INCLUDE_UDP_IOQUEUE_TEST
+
+#include <pjlib.h>
+
+#include <pj/compat/socket.h>
+
+#define THIS_FILE	    "test_udp"
+#define PORT		    51233
+#define LOOP		    2
+///#define LOOP		    2
+#define BUF_MIN_SIZE	    32
+#define BUF_MAX_SIZE	    2048
+#define SOCK_INACTIVE_MIN   (1)
+#define SOCK_INACTIVE_MAX   (PJ_IOQUEUE_MAX_HANDLES - 2)
+#define POOL_SIZE	    (2*BUF_MAX_SIZE + SOCK_INACTIVE_MAX*128 + 2048)
+
+#undef TRACE_
+#define TRACE_(msg)	    PJ_LOG(3,(THIS_FILE,"....." msg))
+
+#if 0
+#  define TRACE__(args)	    PJ_LOG(3,args)
+#else
+#  define TRACE__(args)
+#endif
+
+
+static pj_ssize_t            callback_read_size,
+                             callback_write_size,
+                             callback_accept_status,
+                             callback_connect_status;
+static pj_ioqueue_key_t     *callback_read_key,
+                            *callback_write_key,
+                            *callback_accept_key,
+                            *callback_connect_key;
+static pj_ioqueue_op_key_t  *callback_read_op,
+                            *callback_write_op,
+                            *callback_accept_op;
+
+static void on_ioqueue_read(pj_ioqueue_key_t *key, 
+                            pj_ioqueue_op_key_t *op_key,
+                            pj_ssize_t bytes_read)
+{
+    callback_read_key = key;
+    callback_read_op = op_key;
+    callback_read_size = bytes_read;
+    TRACE__((THIS_FILE, "     callback_read_key = %p, bytes=%d", 
+	     key, bytes_read));
+}
+
+static void on_ioqueue_write(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key,
+                             pj_ssize_t bytes_written)
+{
+    callback_write_key = key;
+    callback_write_op = op_key;
+    callback_write_size = bytes_written;
+}
+
+static void on_ioqueue_accept(pj_ioqueue_key_t *key, 
+                              pj_ioqueue_op_key_t *op_key,
+                              pj_sock_t sock, int status)
+{
+    PJ_UNUSED_ARG(sock);
+    callback_accept_key = key;
+    callback_accept_op = op_key;
+    callback_accept_status = status;
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+    callback_connect_key = key;
+    callback_connect_status = status;
+}
+
+static pj_ioqueue_callback test_cb = 
+{
+    &on_ioqueue_read,
+    &on_ioqueue_write,
+    &on_ioqueue_accept,
+    &on_ioqueue_connect,
+};
+
+#if defined(PJ_WIN32) || defined(PJ_WIN64)
+#  define S_ADDR S_un.S_addr
+#else
+#  define S_ADDR s_addr
+#endif
+
+/*
+ * compliance_test()
+ * To test that the basic IOQueue functionality works. It will just exchange
+ * data between two sockets.
+ */ 
+static int compliance_test(pj_bool_t allow_concur)
+{
+    pj_sock_t ssock=-1, csock=-1;
+    pj_sockaddr_in addr, dst_addr;
+    int addrlen;
+    pj_pool_t *pool = NULL;
+    char *send_buf, *recv_buf;
+    pj_ioqueue_t *ioque = NULL;
+    pj_ioqueue_key_t *skey = NULL, *ckey = NULL;
+    pj_ioqueue_op_key_t read_op, write_op;
+    int bufsize = BUF_MIN_SIZE;
+    pj_ssize_t bytes;
+    int status = -1;
+    pj_str_t temp;
+    pj_bool_t send_pending, recv_pending;
+    pj_status_t rc;
+
+    pj_set_os_error(PJ_SUCCESS);
+
+    // Create pool.
+    pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+    // Allocate buffers for send and receive.
+    send_buf = (char*)pj_pool_alloc(pool, bufsize);
+    recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+    // Allocate sockets for sending and receiving.
+    TRACE_("creating sockets...");
+    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock);
+    if (rc==PJ_SUCCESS)
+        rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock);
+    else
+        csock = PJ_INVALID_SOCKET;
+    if (rc != PJ_SUCCESS) {
+        app_perror("...ERROR in pj_sock_socket()", rc);
+	status=-1; goto on_error;
+    }
+
+    // Bind server socket.
+    TRACE_("bind socket...");
+    pj_bzero(&addr, sizeof(addr));
+    addr.sin_family = pj_AF_INET();
+    addr.sin_port = pj_htons(PORT);
+    if (pj_sock_bind(ssock, &addr, sizeof(addr))) {
+	status=-10; goto on_error;
+    }
+
+    // Create I/O Queue.
+    TRACE_("create ioqueue...");
+    rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+    if (rc != PJ_SUCCESS) {
+	status=-20; goto on_error;
+    }
+
+    // Set concurrency
+    TRACE_("set concurrency...");
+    rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+    if (rc != PJ_SUCCESS) {
+	status=-21; goto on_error;
+    }
+
+    // Register server and client socket.
+    // We put this after inactivity socket, hopefully this can represent the
+    // worst waiting time.
+    TRACE_("registering first sockets...");
+    rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, 
+			          &test_cb, &skey);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error(10): ioqueue_register error", rc);
+	status=-25; goto on_error;
+    }
+    TRACE_("registering second sockets...");
+    rc = pj_ioqueue_register_sock( pool, ioque, csock, NULL, 
+			           &test_cb, &ckey);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error(11): ioqueue_register error", rc);
+	status=-26; goto on_error;
+    }
+
+    // Randomize send_buf.
+    pj_create_random_string(send_buf, bufsize);
+
+    // Register reading from ioqueue.
+    TRACE_("start recvfrom...");
+    pj_bzero(&addr, sizeof(addr));
+    addrlen = sizeof(addr);
+    bytes = bufsize;
+    rc = pj_ioqueue_recvfrom(skey, &read_op, recv_buf, &bytes, 0,
+			     &addr, &addrlen);
+    if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+        app_perror("...error: pj_ioqueue_recvfrom", rc);
+	status=-28; goto on_error;
+    } else if (rc == PJ_EPENDING) {
+	recv_pending = 1;
+	PJ_LOG(3, (THIS_FILE, 
+		   "......ok: recvfrom returned pending"));
+    } else {
+	PJ_LOG(3, (THIS_FILE, 
+		   "......error: recvfrom returned immediate ok!"));
+	status=-29; goto on_error;
+    }
+
+    // Set destination address to send the packet.
+    TRACE_("set destination address...");
+    temp = pj_str("127.0.0.1");
+    if ((rc=pj_sockaddr_in_init(&dst_addr, &temp, PORT)) != 0) {
+	app_perror("...error: unable to resolve 127.0.0.1", rc);
+	status=-290; goto on_error;
+    }
+
+    // Write must return the number of bytes.
+    TRACE_("start sendto...");
+    bytes = bufsize;
+    rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0, &dst_addr, 
+			   sizeof(dst_addr));
+    if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+        app_perror("...error: pj_ioqueue_sendto", rc);
+	status=-30; goto on_error;
+    } else if (rc == PJ_EPENDING) {
+	send_pending = 1;
+	PJ_LOG(3, (THIS_FILE, 
+		   "......ok: sendto returned pending"));
+    } else {
+	send_pending = 0;
+	PJ_LOG(3, (THIS_FILE, 
+		   "......ok: sendto returned immediate success"));
+    }
+
+    // reset callback variables.
+    callback_read_size = callback_write_size = 0;
+    callback_accept_status = callback_connect_status = -2;
+    callback_read_key = callback_write_key = 
+        callback_accept_key = callback_connect_key = NULL;
+    callback_read_op = callback_write_op = NULL;
+
+    // Poll if pending.
+    while (send_pending || recv_pending) {
+	int rc;
+	pj_time_val timeout = { 5, 0 };
+
+	TRACE_("poll...");
+#ifdef PJ_SYMBIAN
+	rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
+#else
+	rc = pj_ioqueue_poll(ioque, &timeout);
+#endif
+
+	if (rc == 0) {
+	    PJ_LOG(1,(THIS_FILE, "...ERROR: timed out..."));
+	    status=-45; goto on_error;
+        } else if (rc < 0) {
+            app_perror("...ERROR in ioqueue_poll()", -rc);
+	    status=-50; goto on_error;
+	}
+
+	if (callback_read_key != NULL) {
+            if (callback_read_size != bufsize) {
+                status=-61; goto on_error;
+            }
+            if (callback_read_key != skey) {
+                status=-65; goto on_error;
+            }
+            if (callback_read_op != &read_op) {
+                status=-66; goto on_error;
+            }
+
+	    if (pj_memcmp(send_buf, recv_buf, bufsize) != 0) {
+		status=-67; goto on_error;
+	    }
+	    if (addrlen != sizeof(pj_sockaddr_in)) {
+		status=-68; goto on_error;
+	    }
+	    if (addr.sin_family != pj_AF_INET()) {
+		status=-69; goto on_error;
+	    }
+
+
+	    recv_pending = 0;
+	} 
+
+        if (callback_write_key != NULL) {
+            if (callback_write_size != bufsize) {
+                status=-73; goto on_error;
+            }
+            if (callback_write_key != ckey) {
+                status=-75; goto on_error;
+            }
+            if (callback_write_op != &write_op) {
+                status=-76; goto on_error;
+            }
+
+            send_pending = 0;
+	}
+    } 
+    
+    // Success
+    status = 0;
+
+on_error:
+    if (skey)
+    	pj_ioqueue_unregister(skey);
+    else if (ssock != -1)
+	pj_sock_close(ssock);
+    
+    if (ckey)
+    	pj_ioqueue_unregister(ckey);
+    else if (csock != -1)
+	pj_sock_close(csock);
+    
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release(pool);
+    return status;
+
+}
+
+
+static void on_read_complete(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key, 
+                             pj_ssize_t bytes_read)
+{
+    unsigned *p_packet_cnt = (unsigned*) pj_ioqueue_get_user_data(key);
+
+    PJ_UNUSED_ARG(op_key);
+    PJ_UNUSED_ARG(bytes_read);
+
+    (*p_packet_cnt)++;
+}
+
+/*
+ * unregister_test()
+ * Check if callback is still called after socket has been unregistered or 
+ * closed.
+ */ 
+static int unregister_test(pj_bool_t allow_concur)
+{
+    enum { RPORT = 50000, SPORT = 50001 };
+    pj_pool_t *pool;
+    pj_ioqueue_t *ioqueue;
+    pj_sock_t ssock;
+    pj_sock_t rsock;
+    int addrlen;
+    pj_sockaddr_in addr;
+    pj_ioqueue_key_t *key;
+    pj_ioqueue_op_key_t opkey;
+    pj_ioqueue_callback cb;
+    unsigned packet_cnt;
+    char sendbuf[10], recvbuf[10];
+    pj_ssize_t bytes;
+    pj_time_val timeout;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "test", 4000, 4000, NULL);
+    if (!pool) {
+	app_perror("Unable to create pool", PJ_ENOMEM);
+	return -100;
+    }
+
+    status = pj_ioqueue_create(pool, 16, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	app_perror("Error creating ioqueue", status);
+	return -110;
+    }
+
+    // Set concurrency
+    TRACE_("set concurrency...");
+    status = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur);
+    if (status != PJ_SUCCESS) {
+	return -112;
+    }
+
+    /* Create sender socket */
+    status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, SPORT, &ssock);
+    if (status != PJ_SUCCESS) {
+	app_perror("Error initializing socket", status);
+	return -120;
+    }
+
+    /* Create receiver socket. */
+    status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, RPORT, &rsock);
+    if (status != PJ_SUCCESS) {
+	app_perror("Error initializing socket", status);
+	return -130;
+    }
+
+    /* Register rsock to ioqueue. */
+    pj_bzero(&cb, sizeof(cb));
+    cb.on_read_complete = &on_read_complete;
+    packet_cnt = 0;
+    status = pj_ioqueue_register_sock(pool, ioqueue, rsock, &packet_cnt,
+				      &cb, &key);
+    if (status != PJ_SUCCESS) {
+	app_perror("Error registering to ioqueue", status);
+	return -140;
+    }
+
+    /* Init operation key. */
+    pj_ioqueue_op_key_init(&opkey, sizeof(opkey));
+
+    /* Start reading. */
+    bytes = sizeof(recvbuf);
+    status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0);
+    if (status != PJ_EPENDING) {
+	app_perror("Expecting PJ_EPENDING, but got this", status);
+	return -150;
+    }
+
+    /* Init destination address. */
+    addrlen = sizeof(addr);
+    status = pj_sock_getsockname(rsock, &addr, &addrlen);
+    if (status != PJ_SUCCESS) {
+	app_perror("getsockname error", status);
+	return -160;
+    }
+
+    /* Override address with 127.0.0.1, since getsockname will return
+     * zero in the address field.
+     */
+    addr.sin_addr = pj_inet_addr2("127.0.0.1");
+
+    /* Init buffer to send */
+    pj_ansi_strcpy(sendbuf, "Hello0123");
+
+    /* Send one packet. */
+    bytes = sizeof(sendbuf);
+    status = pj_sock_sendto(ssock, sendbuf, &bytes, 0,
+			    &addr, sizeof(addr));
+
+    if (status != PJ_SUCCESS) {
+	app_perror("sendto error", status);
+	return -170;
+    }
+
+    /* Check if packet is received. */
+    timeout.sec = 1; timeout.msec = 0;
+#ifdef PJ_SYMBIAN
+    pj_symbianos_poll(-1, 1000);
+#else
+    pj_ioqueue_poll(ioqueue, &timeout);
+#endif
+
+    if (packet_cnt != 1) {
+	return -180;
+    }
+
+    /* Just to make sure things are settled.. */
+    pj_thread_sleep(100);
+
+    /* Start reading again. */
+    bytes = sizeof(recvbuf);
+    status = pj_ioqueue_recv( key, &opkey, recvbuf, &bytes, 0);
+    if (status != PJ_EPENDING) {
+	app_perror("Expecting PJ_EPENDING, but got this", status);
+	return -190;
+    }
+
+    /* Reset packet counter */
+    packet_cnt = 0;
+
+    /* Send one packet. */
+    bytes = sizeof(sendbuf);
+    status = pj_sock_sendto(ssock, sendbuf, &bytes, 0,
+			    &addr, sizeof(addr));
+
+    if (status != PJ_SUCCESS) {
+	app_perror("sendto error", status);
+	return -200;
+    }
+
+    /* Now unregister and close socket. */
+    pj_ioqueue_unregister(key);
+
+    /* Poll ioqueue. */
+#ifdef PJ_SYMBIAN
+    pj_symbianos_poll(-1, 1000);
+#else
+    timeout.sec = 1; timeout.msec = 0;
+    pj_ioqueue_poll(ioqueue, &timeout);
+#endif
+
+    /* Must NOT receive any packets after socket is closed! */
+    if (packet_cnt > 0) {
+	PJ_LOG(3,(THIS_FILE, "....errror: not expecting to receive packet "
+			     "after socket has been closed"));
+	return -210;
+    }
+
+    /* Success */
+    pj_sock_close(ssock);
+    pj_ioqueue_destroy(ioqueue);
+
+    pj_pool_release(pool);
+
+    return 0;
+}
+
+
+/*
+ * Testing with many handles.
+ * This will just test registering PJ_IOQUEUE_MAX_HANDLES count
+ * of sockets to the ioqueue.
+ */
+static int many_handles_test(pj_bool_t allow_concur)
+{
+    enum { MAX = PJ_IOQUEUE_MAX_HANDLES };
+    pj_pool_t *pool;
+    pj_ioqueue_t *ioqueue;
+    pj_sock_t *sock;
+    pj_ioqueue_key_t **key;
+    pj_status_t rc;
+    int count, i; /* must be signed */
+
+    PJ_LOG(3,(THIS_FILE,"...testing with so many handles"));
+
+    pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+    if (!pool)
+	return PJ_ENOMEM;
+
+    key = (pj_ioqueue_key_t**) 
+    	  pj_pool_alloc(pool, MAX*sizeof(pj_ioqueue_key_t*));
+    sock = (pj_sock_t*) pj_pool_alloc(pool, MAX*sizeof(pj_sock_t));
+    
+    /* Create IOQueue */
+    rc = pj_ioqueue_create(pool, MAX, &ioqueue);
+    if (rc != PJ_SUCCESS || ioqueue == NULL) {
+	app_perror("...error in pj_ioqueue_create", rc);
+	return -10;
+    }
+
+    // Set concurrency
+    rc = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur);
+    if (rc != PJ_SUCCESS) {
+	return -11;
+    }
+
+    /* Register as many sockets. */
+    for (count=0; count<MAX; ++count) {
+	sock[count] = PJ_INVALID_SOCKET;
+	rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[count]);
+	if (rc != PJ_SUCCESS || sock[count] == PJ_INVALID_SOCKET) {
+	    PJ_LOG(3,(THIS_FILE, "....unable to create %d-th socket, rc=%d", 
+				 count, rc));
+	    break;
+	}
+	key[count] = NULL;
+	rc = pj_ioqueue_register_sock(pool, ioqueue, sock[count],
+				      NULL, &test_cb, &key[count]);
+	if (rc != PJ_SUCCESS || key[count] == NULL) {
+	    PJ_LOG(3,(THIS_FILE, "....unable to register %d-th socket, rc=%d", 
+				 count, rc));
+	    return -30;
+	}
+    }
+
+    /* Test complete. */
+
+    /* Now deregister and close all handles. */ 
+
+    /* NOTE for RTEMS:
+     *  It seems that the order of close(sock) is pretty important here.
+     *  If we close the sockets with the same order as when they were created,
+     *  RTEMS doesn't seem to reuse the sockets, thus next socket created
+     *  will have descriptor higher than the last socket created.
+     *  If we close the sockets in the reverse order, then the descriptor will
+     *  get reused.
+     *  This used to cause problem with select ioqueue, since the ioqueue
+     *  always gives FD_SETSIZE for the first select() argument. This ioqueue
+     *  behavior can be changed with setting PJ_SELECT_NEEDS_NFDS macro.
+     */
+    for (i=count-1; i>=0; --i) {
+    ///for (i=0; i<count; ++i) {
+	rc = pj_ioqueue_unregister(key[i]);
+	if (rc != PJ_SUCCESS) {
+	    app_perror("...error in pj_ioqueue_unregister", rc);
+	}
+    }
+
+    rc = pj_ioqueue_destroy(ioqueue);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error in pj_ioqueue_destroy", rc);
+    }
+    
+    pj_pool_release(pool);
+
+    PJ_LOG(3,(THIS_FILE,"....many_handles_test() ok"));
+
+    return 0;
+}
+
+/*
+ * Multi-operation test.
+ */
+
+/*
+ * Benchmarking IOQueue
+ */
+static int bench_test(pj_bool_t allow_concur, int bufsize, 
+		      int inactive_sock_count)
+{
+    pj_sock_t ssock=-1, csock=-1;
+    pj_sockaddr_in addr;
+    pj_pool_t *pool = NULL;
+    pj_sock_t *inactive_sock=NULL;
+    pj_ioqueue_op_key_t *inactive_read_op;
+    char *send_buf, *recv_buf;
+    pj_ioqueue_t *ioque = NULL;
+    pj_ioqueue_key_t *skey, *ckey, *keys[SOCK_INACTIVE_MAX+2];
+    pj_timestamp t1, t2, t_elapsed;
+    int rc=0, i;    /* i must be signed */
+    pj_str_t temp;
+    char errbuf[PJ_ERR_MSG_SIZE];
+
+    TRACE__((THIS_FILE, "   bench test %d", inactive_sock_count));
+
+    // Create pool.
+    pool = pj_pool_create(mem, NULL, POOL_SIZE, 4000, NULL);
+
+    // Allocate buffers for send and receive.
+    send_buf = (char*)pj_pool_alloc(pool, bufsize);
+    recv_buf = (char*)pj_pool_alloc(pool, bufsize);
+
+    // Allocate sockets for sending and receiving.
+    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ssock);
+    if (rc == PJ_SUCCESS) {
+        rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &csock);
+    } else
+        csock = PJ_INVALID_SOCKET;
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_sock_socket()", rc);
+	goto on_error;
+    }
+
+    // Bind server socket.
+    pj_bzero(&addr, sizeof(addr));
+    addr.sin_family = pj_AF_INET();
+    addr.sin_port = pj_htons(PORT);
+    if (pj_sock_bind(ssock, &addr, sizeof(addr)))
+	goto on_error;
+
+    pj_assert(inactive_sock_count+2 <= PJ_IOQUEUE_MAX_HANDLES);
+
+    // Create I/O Queue.
+    rc = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioque);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_ioqueue_create()", rc);
+	goto on_error;
+    }
+
+    // Set concurrency
+    rc = pj_ioqueue_set_default_concurrency(ioque, allow_concur);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_ioqueue_set_default_concurrency()", rc);
+	goto on_error;
+    }
+
+    // Allocate inactive sockets, and bind them to some arbitrary address.
+    // Then register them to the I/O queue, and start a read operation.
+    inactive_sock = (pj_sock_t*)pj_pool_alloc(pool, 
+				    inactive_sock_count*sizeof(pj_sock_t));
+    inactive_read_op = (pj_ioqueue_op_key_t*)pj_pool_alloc(pool,
+                              inactive_sock_count*sizeof(pj_ioqueue_op_key_t));
+    pj_bzero(&addr, sizeof(addr));
+    addr.sin_family = pj_AF_INET();
+    for (i=0; i<inactive_sock_count; ++i) {
+        pj_ssize_t bytes;
+
+	rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &inactive_sock[i]);
+	if (rc != PJ_SUCCESS || inactive_sock[i] < 0) {
+	    app_perror("...error: pj_sock_socket()", rc);
+	    goto on_error;
+	}
+	if ((rc=pj_sock_bind(inactive_sock[i], &addr, sizeof(addr))) != 0) {
+	    pj_sock_close(inactive_sock[i]);
+	    inactive_sock[i] = PJ_INVALID_SOCKET;
+	    app_perror("...error: pj_sock_bind()", rc);
+	    goto on_error;
+	}
+	rc = pj_ioqueue_register_sock(pool, ioque, inactive_sock[i], 
+			              NULL, &test_cb, &keys[i]);
+	if (rc != PJ_SUCCESS) {
+	    pj_sock_close(inactive_sock[i]);
+	    inactive_sock[i] = PJ_INVALID_SOCKET;
+	    app_perror("...error(1): pj_ioqueue_register_sock()", rc);
+	    PJ_LOG(3,(THIS_FILE, "....i=%d", i));
+	    goto on_error;
+	}
+        bytes = bufsize;
+	rc = pj_ioqueue_recv(keys[i], &inactive_read_op[i], recv_buf, &bytes, 0);
+	if (rc != PJ_EPENDING) {
+	    pj_sock_close(inactive_sock[i]);
+	    inactive_sock[i] = PJ_INVALID_SOCKET;
+	    app_perror("...error: pj_ioqueue_read()", rc);
+	    goto on_error;
+	}
+    }
+
+    // Register server and client socket.
+    // We put this after inactivity socket, hopefully this can represent the
+    // worst waiting time.
+    rc = pj_ioqueue_register_sock(pool, ioque, ssock, NULL, 
+			          &test_cb, &skey);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error(2): pj_ioqueue_register_sock()", rc);
+	goto on_error;
+    }
+
+    rc = pj_ioqueue_register_sock(pool, ioque, csock, NULL, 
+			          &test_cb, &ckey);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error(3): pj_ioqueue_register_sock()", rc);
+	goto on_error;
+    }
+
+    // Set destination address to send the packet.
+    pj_sockaddr_in_init(&addr, pj_cstr(&temp, "127.0.0.1"), PORT);
+
+    // Test loop.
+    t_elapsed.u64 = 0;
+    for (i=0; i<LOOP; ++i) {
+	pj_ssize_t bytes;
+        pj_ioqueue_op_key_t read_op, write_op;
+
+	// Randomize send buffer.
+	pj_create_random_string(send_buf, bufsize);
+
+	// Start reading on the server side.
+        bytes = bufsize;
+	rc = pj_ioqueue_recv(skey, &read_op, recv_buf, &bytes, 0);
+	if (rc != PJ_EPENDING) {
+	    app_perror("...error: pj_ioqueue_read()", rc);
+	    break;
+	}
+
+	// Starts send on the client side.
+        bytes = bufsize;
+	rc = pj_ioqueue_sendto(ckey, &write_op, send_buf, &bytes, 0,
+			       &addr, sizeof(addr));
+	if (rc != PJ_SUCCESS && rc != PJ_EPENDING) {
+	    app_perror("...error: pj_ioqueue_write()", rc);
+	    break;
+	}
+	if (rc == PJ_SUCCESS) {
+	    if (bytes < 0) {
+		app_perror("...error: pj_ioqueue_sendto()",(pj_status_t)-bytes);
+		break;
+	    }
+	}
+
+	// Begin time.
+	pj_get_timestamp(&t1);
+
+	// Poll the queue until we've got completion event in the server side.
+        callback_read_key = NULL;
+        callback_read_size = 0;
+	TRACE__((THIS_FILE, "     waiting for key = %p", skey));
+	do {
+	    pj_time_val timeout = { 1, 0 };
+#ifdef PJ_SYMBIAN
+	    rc = pj_symbianos_poll(-1, PJ_TIME_VAL_MSEC(timeout));
+#else
+	    rc = pj_ioqueue_poll(ioque, &timeout);
+#endif
+	    TRACE__((THIS_FILE, "     poll rc=%d", rc));
+	} while (rc >= 0 && callback_read_key != skey);
+
+	// End time.
+	pj_get_timestamp(&t2);
+	t_elapsed.u64 += (t2.u64 - t1.u64);
+
+	if (rc < 0) {
+	    app_perror("   error: pj_ioqueue_poll", -rc);
+	    break;
+	}
+
+	// Compare recv buffer with send buffer.
+	if (callback_read_size != bufsize || 
+	    pj_memcmp(send_buf, recv_buf, bufsize)) 
+	{
+	    rc = -10;
+	    PJ_LOG(3,(THIS_FILE, "   error: size/buffer mismatch"));
+	    break;
+	}
+
+	// Poll until all events are exhausted, before we start the next loop.
+	do {
+	    pj_time_val timeout = { 0, 10 };
+#ifdef PJ_SYMBIAN
+	    PJ_UNUSED_ARG(timeout);
+	    rc = pj_symbianos_poll(-1, 100);
+#else	    
+	    rc = pj_ioqueue_poll(ioque, &timeout);
+#endif
+	} while (rc>0);
+
+	rc = 0;
+    }
+
+    // Print results
+    if (rc == 0) {
+	pj_timestamp tzero;
+	pj_uint32_t usec_delay;
+
+	tzero.u32.hi = tzero.u32.lo = 0;
+	usec_delay = pj_elapsed_usec( &tzero, &t_elapsed);
+
+	PJ_LOG(3, (THIS_FILE, "...%10d %15d  % 9d", 
+	           bufsize, inactive_sock_count, usec_delay));
+
+    } else {
+	PJ_LOG(2, (THIS_FILE, "...ERROR rc=%d (buf:%d, fds:%d)", 
+			      rc, bufsize, inactive_sock_count+2));
+    }
+
+    // Cleaning up.
+    for (i=inactive_sock_count-1; i>=0; --i) {
+	pj_ioqueue_unregister(keys[i]);
+    }
+
+    pj_ioqueue_unregister(skey);
+    pj_ioqueue_unregister(ckey);
+
+
+    pj_ioqueue_destroy(ioque);
+    pj_pool_release( pool);
+    return rc;
+
+on_error:
+    PJ_LOG(1,(THIS_FILE, "...ERROR: %s", 
+	      pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf))));
+    if (ssock)
+	pj_sock_close(ssock);
+    if (csock)
+	pj_sock_close(csock);
+    for (i=0; i<inactive_sock_count && inactive_sock && 
+	      inactive_sock[i]!=PJ_INVALID_SOCKET; ++i) 
+    {
+	pj_sock_close(inactive_sock[i]);
+    }
+    if (ioque != NULL)
+	pj_ioqueue_destroy(ioque);
+    pj_pool_release( pool);
+    return -1;
+}
+
+static int udp_ioqueue_test_imp(pj_bool_t allow_concur)
+{
+    int status;
+    int bufsize, sock_count;
+
+    PJ_LOG(3,(THIS_FILE, "..testing with concurency=%d", allow_concur));
+
+    //goto pass1;
+
+    PJ_LOG(3, (THIS_FILE, "...compliance test (%s)", pj_ioqueue_name()));
+    if ((status=compliance_test(allow_concur)) != 0) {
+	return status;
+    }
+    PJ_LOG(3, (THIS_FILE, "....compliance test ok"));
+
+
+    PJ_LOG(3, (THIS_FILE, "...unregister test (%s)", pj_ioqueue_name()));
+    if ((status=unregister_test(allow_concur)) != 0) {
+	return status;
+    }
+    PJ_LOG(3, (THIS_FILE, "....unregister test ok"));
+
+    if ((status=many_handles_test(allow_concur)) != 0) {
+	return status;
+    }
+    
+    //return 0;
+
+    PJ_LOG(4, (THIS_FILE, "...benchmarking different buffer size:"));
+    PJ_LOG(4, (THIS_FILE, "... note: buf=bytes sent, fds=# of fds, "
+			  "elapsed=in timer ticks"));
+
+//pass1:
+    PJ_LOG(3, (THIS_FILE, "...Benchmarking poll times for %s:", pj_ioqueue_name()));
+    PJ_LOG(3, (THIS_FILE, "...====================================="));
+    PJ_LOG(3, (THIS_FILE, "...Buf.size   #inactive-socks  Time/poll"));
+    PJ_LOG(3, (THIS_FILE, "... (bytes)                    (nanosec)"));
+    PJ_LOG(3, (THIS_FILE, "...====================================="));
+
+    //goto pass2;
+
+    for (bufsize=BUF_MIN_SIZE; bufsize <= BUF_MAX_SIZE; bufsize *= 2) {
+	if ((status=bench_test(allow_concur, bufsize, SOCK_INACTIVE_MIN)) != 0)
+	    return status;
+    }
+//pass2:
+    bufsize = 512;
+    for (sock_count=SOCK_INACTIVE_MIN+2; 
+	 sock_count<=SOCK_INACTIVE_MAX+2; 
+	 sock_count *= 2) 
+    {
+	//PJ_LOG(3,(THIS_FILE, "...testing with %d fds", sock_count));
+	if ((status=bench_test(allow_concur, bufsize, sock_count-2)) != 0)
+	    return status;
+    }
+    return 0;
+}
+
+int udp_ioqueue_test()
+{
+    int rc;
+
+    rc = udp_ioqueue_test_imp(PJ_TRUE);
+    if (rc != 0)
+	return rc;
+
+    rc = udp_ioqueue_test_imp(PJ_FALSE);
+    if (rc != 0)
+	return rc;
+
+    return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_uiq_udp;
+#endif	/* INCLUDE_UDP_IOQUEUE_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/ioq_unreg.c b/jni/pjproject-android/pjlib/src/pjlib-test/ioq_unreg.c
new file mode 100644
index 0000000..c142ef9
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/ioq_unreg.c
@@ -0,0 +1,387 @@
+/* $Id: ioq_unreg.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+#if INCLUDE_IOQUEUE_UNREG_TEST
+/*
+ * This tests the thread safety of ioqueue unregistration operation.
+ */
+
+#include <pj/errno.h>
+#include <pj/ioqueue.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/sock.h>
+#include <pj/compat/socket.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE   "ioq_unreg.c"
+
+
+enum test_method
+{
+    UNREGISTER_IN_APP,
+    UNREGISTER_IN_CALLBACK,
+};
+
+static int thread_quitting;
+static enum test_method test_method;
+static pj_time_val time_to_unregister;
+
+struct sock_data
+{
+    pj_sock_t		 sock;
+    pj_sock_t		 csock;
+    pj_pool_t		*pool;
+    pj_ioqueue_key_t	*key;
+    pj_mutex_t		*mutex;
+    pj_ioqueue_op_key_t	*op_key;
+    char		*buffer;
+    pj_size_t		 bufsize;
+    pj_bool_t		 unregistered;
+    pj_ssize_t		 received;
+} sock_data;
+
+static void on_read_complete(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key, 
+                             pj_ssize_t bytes_read)
+{
+    pj_ssize_t size;
+    char *sendbuf = "Hello world";
+    pj_status_t status;
+
+    if (sock_data.unregistered)
+	return;
+
+    pj_mutex_lock(sock_data.mutex);
+
+    if (sock_data.unregistered) {
+	pj_mutex_unlock(sock_data.mutex);
+	return;
+    }
+
+    if (bytes_read < 0) {
+	if (-bytes_read != PJ_STATUS_FROM_OS(PJ_BLOCKING_ERROR_VAL))
+	    app_perror("ioqueue reported recv error", (pj_status_t)-bytes_read);
+    } else {
+	sock_data.received += bytes_read;
+    }
+
+    if (test_method == UNREGISTER_IN_CALLBACK) {
+	pj_time_val now;
+
+	pj_gettimeofday(&now);
+	if (PJ_TIME_VAL_GTE(now, time_to_unregister)) { 
+	    sock_data.unregistered = 1;
+	    pj_ioqueue_unregister(key);
+	    pj_mutex_unlock(sock_data.mutex);
+	    return;
+	}
+    }
+ 
+    do { 
+	size = sock_data.bufsize;
+	status = pj_ioqueue_recv(key, op_key, sock_data.buffer, &size, 0);
+	if (status != PJ_EPENDING && status != PJ_SUCCESS)
+	    app_perror("recv() error", status);
+
+    } while (status == PJ_SUCCESS);
+
+    pj_mutex_unlock(sock_data.mutex);
+
+    size = pj_ansi_strlen(sendbuf);
+    status = pj_sock_send(sock_data.csock, sendbuf, &size, 0);
+    if (status != PJ_SUCCESS)
+	app_perror("send() error", status);
+
+    size = pj_ansi_strlen(sendbuf);
+    status = pj_sock_send(sock_data.csock, sendbuf, &size, 0);
+    if (status != PJ_SUCCESS)
+	app_perror("send() error", status);
+
+} 
+
+static int worker_thread(void *arg)
+{
+    pj_ioqueue_t *ioqueue = (pj_ioqueue_t*) arg;
+
+    while (!thread_quitting) {
+	pj_time_val timeout = { 0, 20 };
+	pj_ioqueue_poll(ioqueue, &timeout);
+    }
+
+    return 0;
+}
+
+/*
+ * Perform unregistration test.
+ *
+ * This will create ioqueue and register a server socket. Depending
+ * on the test method, either the callback or the main thread will
+ * unregister and destroy the server socket after some period of time.
+ */
+static int perform_unreg_test(pj_ioqueue_t *ioqueue,
+			      pj_pool_t *test_pool,
+			      const char *title, 
+			      pj_bool_t other_socket)
+{
+    enum { WORKER_CNT = 1, MSEC = 500, QUIT_MSEC = 500 };
+    int i;
+    pj_thread_t *thread[WORKER_CNT];
+    struct sock_data osd;
+    pj_ioqueue_callback callback;
+    pj_time_val end_time;
+    pj_status_t status;
+
+
+    /* Sometimes its important to have other sockets registered to
+     * the ioqueue, because when no sockets are registered, the ioqueue
+     * will return from the poll early.
+     */
+    if (other_socket) {
+	status = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 56127, &osd.sock);
+	if (status != PJ_SUCCESS) {
+	    app_perror("Error creating other socket", status);
+	    return -12;
+	}
+
+	pj_bzero(&callback, sizeof(callback));
+	status = pj_ioqueue_register_sock(test_pool, ioqueue, osd.sock,
+					  NULL, &callback, &osd.key);
+	if (status != PJ_SUCCESS) {
+	    app_perror("Error registering other socket", status);
+	    return -13;
+	}
+
+    } else {
+	osd.key = NULL;
+	osd.sock = PJ_INVALID_SOCKET;
+    }
+
+    /* Init both time duration of testing */
+    thread_quitting = 0;
+    pj_gettimeofday(&time_to_unregister);
+    time_to_unregister.msec += MSEC;
+    pj_time_val_normalize(&time_to_unregister);
+
+    end_time = time_to_unregister;
+    end_time.msec += QUIT_MSEC;
+    pj_time_val_normalize(&end_time);
+
+    
+    /* Create polling thread */
+    for (i=0; i<WORKER_CNT; ++i) {
+	status = pj_thread_create(test_pool, "unregtest", &worker_thread,
+				   ioqueue, 0, 0, &thread[i]);
+	if (status != PJ_SUCCESS) {
+	    app_perror("Error creating thread", status);
+	    return -20;
+	}
+    }
+
+    /* Create pair of client/server sockets */
+    status = app_socketpair(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 
+			    &sock_data.sock, &sock_data.csock);
+    if (status != PJ_SUCCESS) {
+	app_perror("app_socketpair error", status);
+	return -30;
+    }
+
+
+    /* Initialize test data */
+    sock_data.pool = pj_pool_create(mem, "sd", 1000, 1000, NULL);
+    sock_data.buffer = (char*) pj_pool_alloc(sock_data.pool, 128);
+    sock_data.bufsize = 128;
+    sock_data.op_key = (pj_ioqueue_op_key_t*) 
+    		       pj_pool_alloc(sock_data.pool, 
+				     sizeof(*sock_data.op_key));
+    sock_data.received = 0;
+    sock_data.unregistered = 0;
+
+    pj_ioqueue_op_key_init(sock_data.op_key, sizeof(*sock_data.op_key));
+
+    status = pj_mutex_create_simple(sock_data.pool, "sd", &sock_data.mutex);
+    if (status != PJ_SUCCESS) {
+	app_perror("create_mutex() error", status);
+	return -35;
+    }
+
+    /* Register socket to ioqueue */
+    pj_bzero(&callback, sizeof(callback));
+    callback.on_read_complete = &on_read_complete;
+    status = pj_ioqueue_register_sock(sock_data.pool, ioqueue, sock_data.sock,
+				      NULL, &callback, &sock_data.key);
+    if (status != PJ_SUCCESS) {
+	app_perror("pj_ioqueue_register error", status);
+	return -40;
+    }
+
+    /* Bootstrap the first send/receive */
+    on_read_complete(sock_data.key, sock_data.op_key, 0);
+
+    /* Loop until test time ends */
+    for (;;) {
+	pj_time_val now, timeout;
+	int n;
+
+	pj_gettimeofday(&now);
+
+	if (test_method == UNREGISTER_IN_APP && 
+	    PJ_TIME_VAL_GTE(now, time_to_unregister) &&
+	    !sock_data.unregistered) 
+	{
+	    sock_data.unregistered = 1;
+	    /* Wait (as much as possible) for callback to complete */
+	    pj_mutex_lock(sock_data.mutex);
+	    pj_mutex_unlock(sock_data.mutex);
+	    pj_ioqueue_unregister(sock_data.key);
+	}
+
+	if (PJ_TIME_VAL_GT(now, end_time) && sock_data.unregistered)
+	    break;
+
+	timeout.sec = 0; timeout.msec = 10;
+	n = pj_ioqueue_poll(ioqueue, &timeout);
+	if (n < 0) {
+	    app_perror("pj_ioqueue_poll error", -n);
+	    pj_thread_sleep(1);
+	}
+    }
+
+    thread_quitting = 1;
+
+    for (i=0; i<WORKER_CNT; ++i) {
+	pj_thread_join(thread[i]);
+	pj_thread_destroy(thread[i]);
+    }
+
+    /* Destroy data */
+    pj_mutex_destroy(sock_data.mutex);
+    pj_pool_release(sock_data.pool);
+    sock_data.pool = NULL;
+
+    if (other_socket) {
+	pj_ioqueue_unregister(osd.key);
+    }
+
+    pj_sock_close(sock_data.csock);
+
+    PJ_LOG(3,(THIS_FILE, "....%s: done (%d KB/s)",
+	      title, sock_data.received * 1000 / MSEC / 1000));
+    return 0;
+}
+
+static int udp_ioqueue_unreg_test_imp(pj_bool_t allow_concur)
+{
+    enum { LOOP = 10 };
+    int i, rc;
+    char title[30];
+    pj_ioqueue_t *ioqueue;
+    pj_pool_t *test_pool;
+	
+    PJ_LOG(3,(THIS_FILE, "..testing with concurency=%d", allow_concur));
+
+    test_method = UNREGISTER_IN_APP;
+
+    test_pool = pj_pool_create(mem, "unregtest", 4000, 4000, NULL);
+
+    rc = pj_ioqueue_create(test_pool, 16, &ioqueue);
+    if (rc != PJ_SUCCESS) {
+	app_perror("Error creating ioqueue", rc);
+	return -10;
+    }
+
+    rc = pj_ioqueue_set_default_concurrency(ioqueue, allow_concur);
+    if (rc != PJ_SUCCESS) {
+	app_perror("Error in pj_ioqueue_set_default_concurrency()", rc);
+	return -12;
+    }
+
+    PJ_LOG(3, (THIS_FILE, "...ioqueue unregister stress test 0/3, unregister in app (%s)", 
+	       pj_ioqueue_name()));
+    for (i=0; i<LOOP; ++i) {
+	pj_ansi_sprintf(title, "repeat %d/%d", i, LOOP);
+	rc = perform_unreg_test(ioqueue, test_pool, title, 0);
+	if (rc != 0)
+	    return rc;
+    }
+
+
+    PJ_LOG(3, (THIS_FILE, "...ioqueue unregister stress test 1/3, unregister in app (%s)",
+	       pj_ioqueue_name()));
+    for (i=0; i<LOOP; ++i) {
+	pj_ansi_sprintf(title, "repeat %d/%d", i, LOOP);
+	rc = perform_unreg_test(ioqueue, test_pool, title, 1);
+	if (rc != 0)
+	    return rc;
+    }
+
+    test_method = UNREGISTER_IN_CALLBACK;
+
+    PJ_LOG(3, (THIS_FILE, "...ioqueue unregister stress test 2/3, unregister in cb (%s)", 
+	       pj_ioqueue_name()));
+    for (i=0; i<LOOP; ++i) {
+	pj_ansi_sprintf(title, "repeat %d/%d", i, LOOP);
+	rc = perform_unreg_test(ioqueue, test_pool, title, 0);
+	if (rc != 0)
+	    return rc;
+    }
+
+
+    PJ_LOG(3, (THIS_FILE, "...ioqueue unregister stress test 3/3, unregister in cb (%s)", 
+	       pj_ioqueue_name()));
+    for (i=0; i<LOOP; ++i) {
+	pj_ansi_sprintf(title, "repeat %d/%d", i, LOOP);
+	rc = perform_unreg_test(ioqueue, test_pool, title, 1);
+	if (rc != 0)
+	    return rc;
+    }
+
+    pj_ioqueue_destroy(ioqueue);
+    pj_pool_release(test_pool);
+
+    return 0;
+}
+
+int udp_ioqueue_unreg_test(void)
+{
+    int rc;
+
+    rc = udp_ioqueue_unreg_test_imp(PJ_TRUE);
+    if (rc != 0)
+    	return rc;
+
+    rc = udp_ioqueue_unreg_test_imp(PJ_FALSE);
+    if (rc != 0)
+	return rc;
+
+    return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_uiq_unreg;
+#endif	/* INCLUDE_IOQUEUE_UNREG_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/list.c b/jni/pjproject-android/pjlib/src/pjlib-test/list.c
new file mode 100644
index 0000000..a354d35
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/list.c
@@ -0,0 +1,227 @@
+/* $Id: list.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_list_test Test: Linked List
+ *
+ * This file provides implementation of \b list_test(). It tests the
+ * functionality of the linked-list API.
+ *
+ * \section list_test_sec Scope of the Test
+ *
+ * API tested:
+ *  - pj_list_init()
+ *  - pj_list_insert_before()
+ *  - pj_list_insert_after()
+ *  - pj_list_merge_last()
+ *  - pj_list_empty()
+ *  - pj_list_insert_nodes_before()
+ *  - pj_list_erase()
+ *  - pj_list_find_node()
+ *  - pj_list_search()
+ *
+ *
+ * This file is <b>pjlib-test/list.c</b>
+ *
+ * \include pjlib-test/list.c
+ */
+
+#if INCLUDE_LIST_TEST
+
+#include <pjlib.h>
+
+typedef struct list_node
+{
+    PJ_DECL_LIST_MEMBER(struct list_node);
+    int value;
+} list_node;
+
+static int compare_node(void *value, const pj_list_type *nd)
+{
+    list_node *node = (list_node*)nd;
+    return ((long)(pj_ssize_t)value == node->value) ? 0 : -1;
+}
+
+#define PJ_SIGNED_ARRAY_SIZE(a)	((int)PJ_ARRAY_SIZE(a))
+
+int list_test()
+{
+    list_node nodes[4];    // must be even number of nodes
+    list_node list;
+    list_node list2;
+    list_node *p;
+    int i; // don't change to unsigned!
+
+    //
+    // Test insert_before().
+    //
+    list.value = (unsigned)-1;
+    pj_list_init(&list);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	nodes[i].value = i;
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    // check.
+    for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+
+    //
+    // Test insert_after()
+    //
+    pj_list_init(&list);
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
+	pj_list_insert_after(&list, &nodes[i]);
+    }
+    // check.
+    for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+
+    //
+    // Test merge_last()
+    //
+    // Init lists
+    pj_list_init(&list);
+    pj_list_init(&list2);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	pj_list_insert_before(&list2, &nodes[i]);
+    }
+    // merge
+    pj_list_merge_last(&list, &list2);
+    // check.
+    for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+    // check list is empty
+    pj_assert( pj_list_empty(&list2) );
+    if (!pj_list_empty(&list2)) {
+	return -1;
+    }
+
+    // 
+    // Check merge_first()
+    //
+    pj_list_init(&list);
+    pj_list_init(&list2);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	pj_list_insert_before(&list2, &nodes[i]);
+    }
+    // merge
+    pj_list_merge_first(&list2, &list);
+    // check (list2).
+    for (i=0, p=list2.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+    // check list is empty
+    pj_assert( pj_list_empty(&list) );
+    if (!pj_list_empty(&list)) {
+	return -1;
+    }
+
+    //
+    // Test insert_nodes_before()
+    //
+    // init list
+    pj_list_init(&list);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes)/2; ++i) {
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    // chain remaining nodes
+    pj_list_init(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)/2+1; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	pj_list_insert_before(&nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2], &nodes[i]);
+    }
+    // insert nodes
+    pj_list_insert_nodes_before(&list, &nodes[PJ_SIGNED_ARRAY_SIZE(nodes)/2]);
+    // check
+    for (i=0, p=list.next; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i, p=p->next) {
+	pj_assert(p->value == i);
+	if (p->value != i) {
+	    return -1;
+	}
+    }
+
+    // erase test.
+    pj_list_init(&list);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	nodes[i].value = i;
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    for (i=PJ_SIGNED_ARRAY_SIZE(nodes)-1; i>=0; --i) {
+	int j;
+	pj_list_erase(&nodes[i]);
+	for (j=0, p=list.next; j<i; ++j, p=p->next) {
+	    pj_assert(p->value == j);
+	    if (p->value != j) {
+		return -1;
+	    }
+	}
+    }
+
+    // find and search
+    pj_list_init(&list);
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	nodes[i].value = i;
+	pj_list_insert_before(&list, &nodes[i]);
+    }
+    for (i=0; i<PJ_SIGNED_ARRAY_SIZE(nodes); ++i) {
+	p = (list_node*) pj_list_find_node(&list, &nodes[i]);
+	pj_assert( p == &nodes[i] );
+	if (p != &nodes[i]) {
+	    return -1;
+	}
+	p = (list_node*) pj_list_search(&list, (void*)(pj_ssize_t)i, 
+					&compare_node);
+	pj_assert( p == &nodes[i] );
+	if (p != &nodes[i]) {
+	    return -1;
+	}
+    }
+    return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_list_test;
+#endif	/* INCLUDE_LIST_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/main.c b/jni/pjproject-android/pjlib/src/pjlib-test/main.c
new file mode 100644
index 0000000..e0241b6
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/main.c
@@ -0,0 +1,107 @@
+/* $Id: main.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/log.h>
+#include <stdio.h>
+
+extern int param_echo_sock_type;
+extern const char *param_echo_server;
+extern int param_echo_port;
+
+
+//#if defined(PJ_WIN32) && PJ_WIN32!=0
+#if 0
+#include <windows.h>
+static void boost(void)
+{
+    SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
+}
+#else
+#define boost()
+#endif
+
+#if defined(PJ_SUNOS) && PJ_SUNOS!=0
+#include <signal.h>
+static void init_signals()
+{
+    struct sigaction act;
+
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = SIG_IGN;
+
+    sigaction(SIGALRM, &act, NULL);
+}
+
+#else
+#define init_signals()
+#endif
+
+int main(int argc, char *argv[])
+{
+    int rc;
+    int interractive = 0;
+
+    boost();
+    init_signals();
+
+    while (argc > 1) {
+        char *arg = argv[--argc];
+
+	if (*arg=='-' && *(arg+1)=='i') {
+	    interractive = 1;
+
+	} else if (*arg=='-' && *(arg+1)=='p') {
+            pj_str_t port = pj_str(argv[--argc]);
+
+            param_echo_port = pj_strtoul(&port);
+
+        } else if (*arg=='-' && *(arg+1)=='s') {
+            param_echo_server = argv[--argc];
+
+        } else if (*arg=='-' && *(arg+1)=='t') {
+            pj_str_t type = pj_str(argv[--argc]);
+            
+            if (pj_stricmp2(&type, "tcp")==0)
+                param_echo_sock_type = pj_SOCK_STREAM();
+            else if (pj_stricmp2(&type, "udp")==0)
+                param_echo_sock_type = pj_SOCK_DGRAM();
+            else {
+                PJ_LOG(3,("", "error: unknown socket type %s", type.ptr));
+                return 1;
+            }
+        }
+    }
+
+    rc = test_main();
+
+    if (interractive) {
+	char s[10];
+	puts("");
+	puts("Press <ENTER> to exit");
+	if (!fgets(s, sizeof(s), stdin))
+	    return rc;
+    }
+
+    return rc;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/main_mod.c b/jni/pjproject-android/pjlib/src/pjlib-test/main_mod.c
new file mode 100644
index 0000000..bb52a6f
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/main_mod.c
@@ -0,0 +1,40 @@
+/* $Id: main_mod.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+int init_module(void)
+{
+    printk(KERN_INFO "PJLIB test module loaded. Starting tests...\n");
+    
+    test_main();
+
+    /* Prevent module from loading. We've finished test anyway.. */
+    return 1;
+}
+
+void cleanup_module(void)
+{
+    printk(KERN_INFO "PJLIB test module unloading...\n");
+}
+
+MODULE_LICENSE("GPL");
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/main_rtems.c b/jni/pjproject-android/pjlib/src/pjlib-test/main_rtems.c
new file mode 100644
index 0000000..b2c8d9b
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/main_rtems.c
@@ -0,0 +1,326 @@
+/* $Id: main_rtems.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * - Many thanks for Zetron, Inc. and Phil Torre <ptorre@zetron.com> for 
+ *   donating this file and the RTEMS port in general!
+ */
+
+#include "test.h"
+
+#include <pj/errno.h>
+#include <pj/string.h>
+#include <pj/sock.h>
+#include <pj/log.h>
+
+extern int param_echo_sock_type;
+extern const char *param_echo_server;
+extern int param_echo_port;
+
+#include <bsp.h>
+
+#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
+#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS    300
+#define CONFIGURE_MAXIMUM_TASKS                     50
+#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES            rtems_resource_unlimited(10)
+#define CONFIGURE_MAXIMUM_SEMAPHORES                rtems_resource_unlimited(10)
+#define CONFIGURE_MAXIMUM_TIMERS                    50
+#define CONFIGURE_MAXIMUM_REGIONS                   3
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_TIMER_DRIVER
+#define CONFIGURE_TICKS_PER_TIMESLICE               2
+//#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+#define CONFIGURE_POSIX_INIT_THREAD_TABLE
+
+
+#define CONFIGURE_MAXIMUM_POSIX_MUTEXES	    rtems_resource_unlimited(16)
+#define CONFIGURE_MAXIMUM_POSIX_CONDITION_VARIABLES rtems_resource_unlimited(5)
+#define CONFIGURE_MAXIMUM_POSIX_SEMAPHORES  rtems_resource_unlimited(16)
+#define CONFIGURE_MAXIMUM_POSIX_TIMERS	    rtems_resource_unlimited(5)
+#define CONFIGURE_MAXIMUM_POSIX_THREADS	    rtems_resource_unlimited(16)
+#define CONFIGURE_MAXIMUM_POSIX_KEYS	    rtems_resource_unlimited(16)
+
+#define CONFIGURE_POSIX_INIT_THREAD_STACK_SIZE	4096
+
+/* Make sure that stack size is at least 4096 */
+#define SZ					(4096-RTEMS_MINIMUM_STACK_SIZE)
+#define CONFIGURE_EXTRA_TASK_STACKS		((SZ)<0 ? 0 : (SZ))
+
+#define CONFIGURE_INIT
+#define STACK_CHECKER_ON
+
+rtems_task Init(rtems_task_argument Argument) ;
+void *POSIX_Init(void *argument);
+
+#include <confdefs.h>
+#include <rtems.h>
+
+/* Any tests that want to build a linked executable for RTEMS must include
+   these headers to get a default config for the network stack. */
+#include <rtems/rtems_bsdnet.h>
+#include "rtems_network_config.h"
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define THIS_FILE   "main_rtems.c"
+
+static void* pjlib_test_main(void* unused);
+static void  initialize_network();
+static void test_sock(void);
+
+static void my_perror(pj_status_t status, const char *title)
+{
+    char err[PJ_ERR_MSG_SIZE];
+
+    pj_strerror(status, err, sizeof(err));
+    printf("%s: %s [%d]\n", title, err, status);
+}
+
+#define TEST(expr)    { int rc;\
+		        /*PJ_LOG(3,(THIS_FILE,"%s", #expr));*/ \
+			/*sleep(1);*/ \
+		        rc=expr; \
+		        if (rc) my_perror(PJ_STATUS_FROM_OS(rc),#expr); }
+
+
+
+//rtems_task Init(rtems_task_argument Argument)
+void *POSIX_Init(void *argument)
+{
+    pthread_attr_t	threadAttr;
+    pthread_t     	theThread;
+    struct sched_param	sched_param;
+    size_t		stack_size;
+    int           	result;
+    char		data[1000];
+    
+
+    memset(data, 1, sizeof(data));
+
+    /* Set the TOD clock, so that gettimeofday() will work */
+    rtems_time_of_day fakeTime = { 2006, 3, 15, 17, 30, 0, 0 };
+
+    if (RTEMS_SUCCESSFUL != rtems_clock_set(&fakeTime))
+    {
+	assert(0);
+    }	
+
+    /* Bring up the network stack so we can run the socket tests. */
+    initialize_network();
+
+    /* Start a POSIX thread for pjlib_test_main(), since that's what it
+     * thinks it is running in. 
+     */
+
+    /* Initialize attribute */
+    TEST( pthread_attr_init(&threadAttr) );
+
+    /* Looks like the rest of the attributes must be fully initialized too,
+     * or otherwise pthread_create will return EINVAL.
+     */
+
+    /* Specify explicit scheduling request */
+    TEST( pthread_attr_setinheritsched(&threadAttr, PTHREAD_EXPLICIT_SCHED));
+
+    /* Timeslicing is needed by thread test, and this is accomplished by
+     * SCHED_RR.
+     */
+    TEST( pthread_attr_setschedpolicy(&threadAttr, SCHED_RR));
+
+    /* Set priority */
+    TEST( pthread_attr_getschedparam(&threadAttr, &sched_param));
+    sched_param.sched_priority = NETWORK_STACK_PRIORITY - 10;
+    TEST( pthread_attr_setschedparam(&threadAttr, &sched_param));
+
+    /* Must have sufficient stack size (large size is needed by
+     * logger, because default settings for logger is to use message buffer
+     * from the stack).
+     */
+    TEST( pthread_attr_getstacksize(&threadAttr, &stack_size));
+    if (stack_size < 8192)
+	TEST( pthread_attr_setstacksize(&threadAttr, 8192));
+
+
+    /* Create the thread for application */
+    result = pthread_create(&theThread, &threadAttr, &pjlib_test_main, NULL);
+    if (result != 0) {
+	my_perror(PJ_STATUS_FROM_OS(result), 
+		  "Error creating pjlib_test_main thread");
+	assert(!"Error creating main thread");
+    } 
+
+    return NULL;
+}
+
+
+
+#define boost()
+#define init_signals()
+
+static void*
+pjlib_test_main(void* unused)
+{
+    int rc;
+
+    /* Drop our priority to below that of the network stack, otherwise
+     * select() tests will fail. */
+    struct sched_param schedParam;
+    int schedPolicy;
+  
+    printf("pjlib_test_main thread started..\n");
+
+    TEST( pthread_getschedparam(pthread_self(), &schedPolicy, &schedParam) );
+
+    schedParam.sched_priority = NETWORK_STACK_PRIORITY - 10;
+
+    TEST( pthread_setschedparam(pthread_self(), schedPolicy, &schedParam) );
+
+    boost();
+    init_signals();
+
+    //my_test_thread("from pjlib_test_main");
+    //test_sock();
+
+    rc = test_main();
+
+    return (void*)rc;
+}
+
+#  include <sys/types.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <arpa/inet.h>
+#  include <unistd.h>
+
+/* 
+ * Send UDP packet to some host. We can then use Ethereal to sniff the packet
+ * to see if this target really transmits UDP packet.
+ */
+static void
+send_udp(const char *target)
+{
+    int sock, rc;
+    struct sockaddr_in addr;
+
+    PJ_LOG(3,("main_rtems.c", "IP addr=%s/%s, gw=%s",
+		DEFAULT_IP_ADDRESS_STRING,
+		DEFAULT_NETMASK_STRING,
+		DEFAULT_GATEWAY_STRING));
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    assert(sock > 0);
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+
+    rc = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
+    assert("bind error" && rc==0);
+
+    addr.sin_addr.s_addr = inet_addr(target);
+    addr.sin_port = htons(4444);
+
+    while(1) {
+	const char *data = "hello";
+
+	rc = sendto(sock, data, 5, 0, (struct sockaddr*)&addr, sizeof(addr));
+	PJ_LOG(3,("main_rtems.c", "pinging %s..(rc=%d)", target, rc));
+    	sleep(1);
+    }
+}
+
+
+static void test_sock(void)
+{
+    int sock;
+    struct sockaddr_in addr;
+    int rc;
+
+    sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sock < 0) {
+	printf("socket() error\n");
+	goto end;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+    addr.sin_port = htons(5000);
+
+    rc = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
+    if (rc != 0) {
+	printf("bind() error %d\n", rc);
+	close(sock);
+	goto end;
+    }
+
+    puts("Bind socket success");
+
+    close(sock);
+
+end:
+    while(1) sleep(1);
+}
+
+/* 
+ * Initialize the network stack and Ethernet driver, using the configuration
+ * in rtems-network-config.h
+ */
+static void
+initialize_network()
+{
+    unsigned32 fd, result;
+    char ip_address_string[] = DEFAULT_IP_ADDRESS_STRING;
+    char netmask_string[] = DEFAULT_NETMASK_STRING;
+    char gateway_string[] = DEFAULT_GATEWAY_STRING;
+
+    // Write the network config files to /etc/hosts and /etc/host.conf
+    result = mkdir("/etc", S_IRWXU | S_IRWXG | S_IRWXO);
+    fd = open("/etc/host.conf", O_RDWR | O_CREAT, 0744);
+    result = write(fd, "hosts,bind\n", 11);
+    result = close(fd);
+    fd = open("/etc/hosts", O_RDWR | O_CREAT, 0744);
+    result = write(fd, "127.0.0.1	localhost\n", 41);
+    result = write(fd, ip_address_string, strlen(ip_address_string));
+    result = write(fd, "	pjsip-test\n", 32); 
+    result = close(fd);
+
+    netdriver_config.ip_address = ip_address_string;
+    netdriver_config.ip_netmask = netmask_string;
+    rtems_bsdnet_config.gateway = gateway_string;
+
+    if (0 != rtems_bsdnet_initialize_network())
+	PJ_LOG(3,(THIS_FILE, "Error: Unable to initialize network stack!"));
+    else
+	PJ_LOG(3,(THIS_FILE, "IP addr=%s/%s, gw=%s", 
+			      ip_address_string,
+			      netmask_string,
+			      gateway_string));
+
+    //rtems_rdbg_initialize();
+    //enterRdbg();
+    //send_udp("192.168.0.1");
+    //test_sock();
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/main_symbian.cpp b/jni/pjproject-android/pjlib/src/pjlib-test/main_symbian.cpp
new file mode 100644
index 0000000..a91b671
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/main_symbian.cpp
@@ -0,0 +1,133 @@
+//Auto-generated file. Please do not modify.
+//#include <e32cmn.h>
+
+//#pragma data_seg(".SYMBIAN")
+//__EMULATOR_IMAGE_HEADER2 (0x1000007a,0x00000000,0x00000000,EPriorityForeground,0x00000000u,0x00000000u,0x00000000,0x00000000,0x00000000,0)
+//#pragma data_seg()
+
+#include "test.h"
+#include <stdlib.h>
+#include <pj/errno.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/unicode.h>
+#include <stdio.h>
+
+#include <e32std.h>
+
+#if 0
+int main()
+{
+    int err = 0;
+    int exp = 0;
+
+    err = test_main();
+    //err = test_main();
+
+    if (err)
+	return err;
+    return exp;
+    //return 0;
+}
+
+#else
+#include <pj/os.h>
+
+#include <e32base.h>
+#include <e32std.h>
+#include <e32cons.h>            // Console
+
+
+
+//  Global Variables
+
+LOCAL_D CConsoleBase* console;  // write all messages to this
+
+
+class MyScheduler : public CActiveScheduler
+{
+public:
+    MyScheduler()
+    {}
+
+    void Error(TInt aError) const;
+};
+
+void MyScheduler::Error(TInt aError) const
+{
+    PJ_UNUSED_ARG(aError);
+}
+
+LOCAL_C void DoStartL()
+    {
+    // Create active scheduler (to run active objects)
+    CActiveScheduler* scheduler = new (ELeave) MyScheduler;
+    CleanupStack::PushL(scheduler);
+    CActiveScheduler::Install(scheduler);
+
+    test_main();
+
+    CActiveScheduler::Install(NULL);
+    CleanupStack::Pop(scheduler);
+    delete scheduler;
+    }
+
+#define WRITE_TO_DEBUG_CONSOLE
+
+#ifdef WRITE_TO_DEBUG_CONSOLE
+#include<e32debug.h>
+#endif
+
+//  Global Functions
+static void log_writer(int level, const char *buf, int len)
+{
+    static wchar_t buf16[PJ_LOG_MAX_SIZE];
+
+    PJ_UNUSED_ARG(level);
+    
+    pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16));
+    buf16[len] = 0;
+    buf16[len+1] = 0;
+    
+    TPtrC16 aBuf((const TUint16*)buf16, (TInt)len);
+    console->Write(aBuf);
+    
+#ifdef WRITE_TO_DEBUG_CONSOLE
+    RDebug::Print(aBuf);
+#endif
+}
+
+
+GLDEF_C TInt E32Main()
+    {
+    // Create cleanup stack
+    __UHEAP_MARK;
+    CTrapCleanup* cleanup = CTrapCleanup::New();
+
+    // Create output console
+    TRAPD(createError, console = Console::NewL(_L("Console"), TSize(KConsFullScreen,KConsFullScreen)));
+    if (createError)
+        return createError;
+
+    pj_log_set_log_func(&log_writer);
+
+    // Run application code inside TRAP harness, wait keypress when terminated
+    TRAPD(mainError, DoStartL());
+    if (mainError)
+        console->Printf(_L(" failed, leave code = %d"), mainError);
+    
+    console->Printf(_L(" [press any key]\n"));
+    console->Getch();
+    
+    delete console;
+    delete cleanup;
+    
+    CloseSTDLIB(); 
+    
+    __UHEAP_MARKEND;
+    
+    return KErrNone;
+    }
+
+#endif	/* if 0 */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/main_win32.c b/jni/pjproject-android/pjlib/src/pjlib-test/main_win32.c
new file mode 100644
index 0000000..fd107c3
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/main_win32.c
@@ -0,0 +1,211 @@
+/* $Id: main_win32.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+#include <pj/string.h>
+#include <pj/unicode.h>
+#include <pj/sock.h>
+#include <pj/log.h>
+
+#define WIN32_LEAN_AND_MEAN
+#define NONAMELESSUNION
+#include <windows.h>
+#include <commctrl.h>
+#include <tchar.h>
+
+#define MAX_LOADSTRING	    100
+#define THIS_FILE	    "main_win32.c"
+
+#define IDC_HELLO_WINCE	    3
+#define ID_LOGWINDOW	    104
+
+
+ATOM			    MyRegisterClass	(HINSTANCE, LPTSTR);
+BOOL			    InitInstance	(HINSTANCE, int);
+LRESULT CALLBACK	    WndProc		(HWND, UINT, WPARAM, LPARAM);
+
+
+extern int		    param_log_decor;	// in test.c
+
+static HINSTANCE	    hInst;
+static HWND		    hwndLog;
+static HFONT		    hFixedFont;
+
+
+static void write_log(int level, const char *data, int len)
+{
+    PJ_DECL_UNICODE_TEMP_BUF(wdata,256);
+
+    PJ_UNUSED_ARG(level);
+    PJ_UNUSED_ARG(len);
+    SendMessage(hwndLog, EM_REPLACESEL, FALSE, 
+		(LPARAM)PJ_STRING_TO_NATIVE(data,wdata,256));
+}
+
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
+		   LPTSTR lpCmdLine, int nCmdShow)
+{
+    MSG msg;
+    
+    PJ_UNUSED_ARG(lpCmdLine);
+    PJ_UNUSED_ARG(hPrevInstance);
+
+    
+    if (!InitInstance (hInstance, nCmdShow))
+	return FALSE;
+    
+    pj_log_set_log_func( &write_log );
+    param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_CR;
+
+    // Run the test!
+    test_main();
+
+    PJ_LOG(3,(THIS_FILE,""));
+    PJ_LOG(3,(THIS_FILE,"Press ESC to quit"));
+
+    // Message loop, waiting to quit.
+    while (GetMessage(&msg, NULL, 0, 0)) {
+	TranslateMessage(&msg);
+	DispatchMessage(&msg);
+    }
+    
+    DeleteObject(hFixedFont);
+    return msg.wParam;
+}
+
+
+#ifdef _CONSOLE
+int main()
+{
+    return WinMain(GetModuleHandle(NULL), NULL, NULL, SW_SHOW);
+}
+#endif
+
+
+ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
+{
+    WNDCLASS	wc;
+    
+    wc.style		= CS_HREDRAW | CS_VREDRAW;
+    wc.lpfnWndProc	= (WNDPROC) WndProc;
+    wc.cbClsExtra	= 0;
+    wc.cbWndExtra	= 0;
+    wc.hInstance	= hInstance;
+    ///wc.hIcon		= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_HELLO_WINCE));
+    wc.hIcon		= NULL;
+    wc.hCursor		= 0;
+    wc.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);
+    wc.lpszMenuName	= 0;
+    wc.lpszClassName	= szWindowClass;
+    
+    return RegisterClass(&wc);
+}
+
+
+BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
+{
+    HWND	hWnd;
+    TCHAR	*szTitle = _T("PJSIP Test");
+    TCHAR	*szWindowClass = _T("PJSIP_TEST");
+    LOGFONT	lf;
+
+
+    memset(&lf, 0, sizeof(lf));
+    lf.lfHeight = 13;
+#if PJ_NATIVE_STRING_IS_UNICODE
+    wcscpy(lf.lfFaceName, _T("Courier New"));
+#else
+    strcpy(lf.lfFaceName, "Lucida Console");
+#endif
+
+    hFixedFont = CreateFontIndirect(&lf);
+    if (!hFixedFont)
+	return FALSE;
+
+    hInst = hInstance;
+    
+    MyRegisterClass(hInstance, szWindowClass);
+    
+    hWnd = CreateWindow(szWindowClass, szTitle, WS_VISIBLE,
+		        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 
+			CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
+    
+    if (!hWnd)
+	return FALSE;
+    
+    ShowWindow(hWnd, nCmdShow);
+    UpdateWindow(hWnd);
+
+    if (hwndLog) {
+	SendMessage(hwndLog, WM_SETFONT, (WPARAM) hFixedFont, (LPARAM) 0);  
+	ShowWindow(hwndLog, TRUE);
+    }
+
+    return TRUE;
+}
+
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    RECT rt;
+    DWORD dwStyle;
+    
+    switch (message) 
+    {
+    case WM_CREATE:
+	// Create text control.
+	GetClientRect(hWnd, &rt);
+	dwStyle = WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | 
+		  WS_BORDER | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL |
+		  ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_READONLY; 
+	hwndLog = CreateWindow( TEXT("edit"),	    // class
+				NULL,		    // window text
+				dwStyle,	    // style
+				0,		    // x-left
+				0,		    // y-top
+				rt.right-rt.left,   // w
+				rt.bottom-rt.top,   // h
+				hWnd,		    // parent
+				(HMENU)ID_LOGWINDOW,// id
+				hInst,		    // instance
+				NULL);		    // NULL for control.
+	break;
+    case WM_ACTIVATE:
+	if (LOWORD(wParam) == WA_INACTIVE)
+	    DestroyWindow(hWnd);
+	break;
+    case WM_CHAR:
+	if (wParam == 27) {
+	    DestroyWindow(hWnd);
+	}
+	break;
+    case WM_CLOSE:
+	DestroyWindow(hWnd);
+	break;
+    case WM_DESTROY:
+	PostQuitMessage(0);
+	break;
+    default:
+	return DefWindowProc(hWnd, message, wParam, lParam);
+    }
+    return 0;
+}
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/mutex.c b/jni/pjproject-android/pjlib/src/pjlib-test/mutex.c
new file mode 100644
index 0000000..0c5fa07
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/mutex.c
@@ -0,0 +1,234 @@
+/* $Id: mutex.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#if INCLUDE_MUTEX_TEST
+
+#undef TRACE_
+//#define TRACE_(x)   PJ_LOG(3,x)
+#define TRACE_(x)
+
+/* Test witn non-recursive mutex. */
+static int simple_mutex_test(pj_pool_t *pool)
+{
+    pj_status_t rc;
+    pj_mutex_t *mutex;
+
+    PJ_LOG(3,("", "...testing simple mutex"));
+    
+    /* Create mutex. */
+    TRACE_(("", "....create mutex"));
+    rc = pj_mutex_create( pool, "", PJ_MUTEX_SIMPLE, &mutex);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_mutex_create", rc);
+	return -10;
+    }
+
+    /* Normal lock/unlock cycle. */
+    TRACE_(("", "....lock mutex"));
+    rc = pj_mutex_lock(mutex);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_mutex_lock", rc);
+	return -20;
+    }
+    TRACE_(("", "....unlock mutex"));
+    rc = pj_mutex_unlock(mutex);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_mutex_unlock", rc);
+	return -30;
+    }
+    
+    /* Lock again. */
+    TRACE_(("", "....lock mutex"));
+    rc = pj_mutex_lock(mutex);
+    if (rc != PJ_SUCCESS) return -40;
+
+    /* Try-lock should fail. It should not deadlocked. */
+    TRACE_(("", "....trylock mutex"));
+    rc = pj_mutex_trylock(mutex);
+    if (rc == PJ_SUCCESS)
+	PJ_LOG(3,("", "...info: looks like simple mutex is recursive"));
+
+    /* Unlock and done. */
+    TRACE_(("", "....unlock mutex"));
+    rc = pj_mutex_unlock(mutex);
+    if (rc != PJ_SUCCESS) return -50;
+
+    TRACE_(("", "....destroy mutex"));
+    rc = pj_mutex_destroy(mutex);
+    if (rc != PJ_SUCCESS) return -60;
+
+    TRACE_(("", "....done"));
+    return PJ_SUCCESS;
+}
+
+
+/* Test with recursive mutex. */
+static int recursive_mutex_test(pj_pool_t *pool)
+{
+    pj_status_t rc;
+    pj_mutex_t *mutex;
+
+    PJ_LOG(3,("", "...testing recursive mutex"));
+
+    /* Create mutex. */
+    TRACE_(("", "....create mutex"));
+    rc = pj_mutex_create( pool, "", PJ_MUTEX_RECURSE, &mutex);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_mutex_create", rc);
+	return -10;
+    }
+
+    /* Normal lock/unlock cycle. */
+    TRACE_(("", "....lock mutex"));
+    rc = pj_mutex_lock(mutex);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_mutex_lock", rc);
+	return -20;
+    }
+    TRACE_(("", "....unlock mutex"));
+    rc = pj_mutex_unlock(mutex);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: pj_mutex_unlock", rc);
+	return -30;
+    }
+    
+    /* Lock again. */
+    TRACE_(("", "....lock mutex"));
+    rc = pj_mutex_lock(mutex);
+    if (rc != PJ_SUCCESS) return -40;
+
+    /* Try-lock should NOT fail. . */
+    TRACE_(("", "....trylock mutex"));
+    rc = pj_mutex_trylock(mutex);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: recursive mutex is not recursive!", rc);
+	return -40;
+    }
+
+    /* Locking again should not fail. */
+    TRACE_(("", "....lock mutex"));
+    rc = pj_mutex_lock(mutex);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: recursive mutex is not recursive!", rc);
+	return -45;
+    }
+
+    /* Unlock several times and done. */
+    TRACE_(("", "....unlock mutex 3x"));
+    rc = pj_mutex_unlock(mutex);
+    if (rc != PJ_SUCCESS) return -50;
+    rc = pj_mutex_unlock(mutex);
+    if (rc != PJ_SUCCESS) return -51;
+    rc = pj_mutex_unlock(mutex);
+    if (rc != PJ_SUCCESS) return -52;
+
+    TRACE_(("", "....destroy mutex"));
+    rc = pj_mutex_destroy(mutex);
+    if (rc != PJ_SUCCESS) return -60;
+
+    TRACE_(("", "....done"));
+    return PJ_SUCCESS;
+}
+
+#if PJ_HAS_SEMAPHORE
+static int semaphore_test(pj_pool_t *pool)
+{
+    pj_sem_t *sem;
+    pj_status_t status;
+
+    PJ_LOG(3,("", "...testing semaphore"));
+
+    status = pj_sem_create(pool, NULL, 0, 1, &sem);
+    if (status != PJ_SUCCESS) {
+	app_perror("...error: pj_sem_create()", status);
+	return -151;
+    }
+
+    status = pj_sem_post(sem);
+    if (status != PJ_SUCCESS) {
+	app_perror("...error: pj_sem_post()", status);
+	pj_sem_destroy(sem);
+	return -153;
+    }
+
+    status = pj_sem_trywait(sem);
+    if (status != PJ_SUCCESS) {
+	app_perror("...error: pj_sem_trywait()", status);
+	pj_sem_destroy(sem);
+	return -156;
+    }
+
+    status = pj_sem_post(sem);
+    if (status != PJ_SUCCESS) {
+	app_perror("...error: pj_sem_post()", status);
+	pj_sem_destroy(sem);
+	return -159;
+    }
+
+    status = pj_sem_wait(sem);
+    if (status != PJ_SUCCESS) {
+	app_perror("...error: pj_sem_wait()", status);
+	pj_sem_destroy(sem);
+	return -161;
+    }
+
+    status = pj_sem_destroy(sem);
+    if (status != PJ_SUCCESS) {
+	app_perror("...error: pj_sem_destroy()", status);
+	return -163;
+    }
+
+    return 0;
+}
+#endif	/* PJ_HAS_SEMAPHORE */
+
+
+int mutex_test(void)
+{
+    pj_pool_t *pool;
+    int rc;
+
+    pool = pj_pool_create(mem, "", 4000, 4000, NULL);
+
+    rc = simple_mutex_test(pool);
+    if (rc != 0)
+	return rc;
+
+    rc = recursive_mutex_test(pool);
+    if (rc != 0)
+	return rc;
+
+#if PJ_HAS_SEMAPHORE
+    rc = semaphore_test(pool);
+    if (rc != 0)
+	return rc;
+#endif
+
+    pj_pool_release(pool);
+
+    return 0;
+}
+
+#else
+int dummy_mutex_test;
+#endif
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/os.c b/jni/pjproject-android/pjlib/src/pjlib-test/os.c
new file mode 100644
index 0000000..87270ff
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/os.c
@@ -0,0 +1,116 @@
+/* $Id: os.c 3841 2011-10-24 09:28:13Z ming $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pj/log.h>
+#include <pj/os.h>
+
+#if INCLUDE_OS_TEST
+static int endianness_test32(void)
+{
+    union t
+    {
+	pj_uint32_t u32;
+	pj_uint16_t u16[2];
+	pj_uint8_t u8[4];
+    } t;
+
+    PJ_LOG(3,("", " Testing endianness.."));
+
+    t.u32 = 0x11223344;
+
+#if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN
+    PJ_LOG(3,("", "   Library is set to little endian"));
+
+#  if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN
+#    error Error: Both PJ_IS_LITTLE_ENDIAN and PJ_IS_BIG_ENDIAN are set!
+#  endif
+
+    if ((t.u16[0] & 0xFFFF) != 0x3344 ||
+	(t.u16[1] & 0xFFFF) != 0x1122)
+    {
+	PJ_LOG(3,("", "   Error: wrong 16bit values 0x%x and 0x%x",
+		      (t.u16[0] & 0xFFFF), (t.u16[1] & 0xFFFF)));
+	return 10;
+    }
+
+    if ((t.u8[0] & 0xFF) != 0x44 ||
+	(t.u8[1] & 0xFF) != 0x33 ||
+	(t.u8[2] & 0xFF) != 0x22 ||
+	(t.u8[3] & 0xFF) != 0x11)
+    {
+	PJ_LOG(3,("", "   Error: wrong 8bit values"));
+	return 12;
+    }
+
+#elif defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN
+    PJ_LOG(3,("", "   Library is set to big endian"));
+
+    if ((t.u16[0] & 0xFFFF) != 0x1122 ||
+	(t.u16[1] & 0xFFFF) != 0x3344)
+    {
+	PJ_LOG(3,("", "   Error: wrong 16bit values 0x%x and 0x%x",
+		      (t.u16[0] & 0xFFFF), (t.u16[1] & 0xFFFF)));
+	return 20;
+    }
+
+    if ((t.u8[0] & 0xFF) != 0x11 ||
+	(t.u8[1] & 0xFF) != 0x22 ||
+	(t.u8[2] & 0xFF) != 0x33 ||
+	(t.u8[3] & 0xFF) != 0x44)
+    {
+	PJ_LOG(3,("", "   Error: wrong 8bit values"));
+	return 22;
+    }
+
+#  if defined(PJ_IS_LITTLE_ENDIAN) && PJ_IS_LITTLE_ENDIAN
+#    error Error: Both PJ_IS_LITTLE_ENDIAN and PJ_IS_BIG_ENDIAN are set!
+#  endif
+
+
+#else
+#    error Error: Endianness is not set properly!
+#endif
+
+    return 0;
+}
+
+int os_test(void)
+{
+    const pj_sys_info *si;
+    int rc = 0;
+
+    PJ_LOG(3,("", " Sys info:"));
+    si = pj_get_sys_info();
+    PJ_LOG(3,("", "   machine:  %s", si->machine.ptr));
+    PJ_LOG(3,("", "   os_name:  %s", si->os_name.ptr));
+    PJ_LOG(3,("", "   os_ver:   0x%x", si->os_ver));
+    PJ_LOG(3,("", "   sdk_name: %s", si->sdk_name.ptr));
+    PJ_LOG(3,("", "   sdk_ver:  0x%x", si->sdk_ver));
+    PJ_LOG(3,("", "   info:     %s", si->info.ptr));
+
+    rc = endianness_test32();
+
+    return rc;
+}
+
+#else
+int dummy_os_var;
+#endif
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/pjlib_test_reg.rss b/jni/pjproject-android/pjlib/src/pjlib-test/pjlib_test_reg.rss
new file mode 100644
index 0000000..dd892e4
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/pjlib_test_reg.rss
@@ -0,0 +1,12 @@
+// Symbian application registration info for pjlib-test

+

+#include <appinfo.rh>

+

+UID2 KUidAppRegistrationResourceFile

+UID3 0xA0000002

+

+RESOURCE APP_REGISTRATION_INFO

+{

+    app_file="pjlib_test";

+}

+

diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/pool.c b/jni/pjproject-android/pjlib/src/pjlib-test/pool.c
new file mode 100644
index 0000000..37fbe0a
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/pool.c
@@ -0,0 +1,322 @@
+/* $Id: pool.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/pool.h>
+#include <pj/pool_buf.h>
+#include <pj/rand.h>
+#include <pj/log.h>
+#include <pj/except.h>
+#include "test.h"
+
+/**
+ * \page page_pjlib_pool_test Test: Pool
+ *
+ * This file provides implementation of \b pool_test(). It tests the
+ * functionality of the memory pool.
+ *
+ *
+ * This file is <b>pjlib-test/pool.c</b>
+ *
+ * \include pjlib-test/pool.c
+ */
+
+
+#if INCLUDE_POOL_TEST
+
+#define SIZE	4096
+
+/* Normally we should throw exception when memory alloc fails.
+ * Here we do nothing so that the flow will go back to original caller,
+ * which will test the result using NULL comparison. Normally caller will
+ * catch the exception instead of checking for NULLs.
+ */
+static void null_callback(pj_pool_t *pool, pj_size_t size)
+{
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(size);
+}
+
+#define GET_FREE(p)	(pj_pool_get_capacity(p)-pj_pool_get_used_size(p))
+
+/* Test that the capacity and used size reported by the pool is correct. 
+ */
+static int capacity_test(void)
+{
+    pj_pool_t *pool = pj_pool_create(mem, NULL, SIZE, 0, &null_callback);
+    pj_size_t freesize;
+
+    PJ_LOG(3,("test", "...capacity_test()"));
+
+    if (!pool)
+	return -200;
+
+    freesize = GET_FREE(pool);
+
+    if (pj_pool_alloc(pool, freesize) == NULL) {
+	PJ_LOG(3,("test", "...error: wrong freesize %u reported",
+			  freesize));
+	pj_pool_release(pool);
+	return -210;
+    }
+
+    pj_pool_release(pool);
+    return 0;
+}
+
+/* Test that the alignment works. */
+static int pool_alignment_test(void)
+{
+    pj_pool_t *pool;
+    void *ptr;
+    enum { MEMSIZE = 64, LOOP = 100 };
+    unsigned i;
+
+    PJ_LOG(3,("test", "...alignment test"));
+
+    pool = pj_pool_create(mem, NULL, PJ_POOL_SIZE+MEMSIZE, MEMSIZE, NULL);
+    if (!pool)
+	return -300;
+
+#define IS_ALIGNED(p)	((((unsigned long)(pj_ssize_t)p) & \
+			   (PJ_POOL_ALIGNMENT-1)) == 0)
+
+    for (i=0; i<LOOP; ++i) {
+	/* Test first allocation */
+	ptr = pj_pool_alloc(pool, 1);
+	if (!IS_ALIGNED(ptr)) {
+	    pj_pool_release(pool);
+	    return -310;
+	}
+
+	/* Test subsequent allocation */
+	ptr = pj_pool_alloc(pool, 1);
+	if (!IS_ALIGNED(ptr)) {
+	    pj_pool_release(pool);
+	    return -320;
+	}
+
+	/* Test allocation after new block is created */
+	ptr = pj_pool_alloc(pool, MEMSIZE*2+1);
+	if (!IS_ALIGNED(ptr)) {
+	    pj_pool_release(pool);
+	    return -330;
+	}
+
+	/* Reset the pool */
+	pj_pool_reset(pool);
+    }
+
+    /* Done */
+    pj_pool_release(pool);
+
+    return 0;
+}
+
+/* Test that the alignment works for pool on buf. */
+static int pool_buf_alignment_test(void)
+{
+    pj_pool_t *pool;
+    char buf[512];
+    void *ptr;
+    enum { LOOP = 100 };
+    unsigned i;
+
+    PJ_LOG(3,("test", "...pool_buf alignment test"));
+
+    pool = pj_pool_create_on_buf(NULL, buf, sizeof(buf));
+    if (!pool)
+	return -400;
+
+    for (i=0; i<LOOP; ++i) {
+	/* Test first allocation */
+	ptr = pj_pool_alloc(pool, 1);
+	if (!IS_ALIGNED(ptr)) {
+	    pj_pool_release(pool);
+	    return -410;
+	}
+
+	/* Test subsequent allocation */
+	ptr = pj_pool_alloc(pool, 1);
+	if (!IS_ALIGNED(ptr)) {
+	    pj_pool_release(pool);
+	    return -420;
+	}
+
+	/* Reset the pool */
+	pj_pool_reset(pool);
+    }
+
+    /* Done */
+    return 0;
+}
+
+/* Test function to drain the pool's space. 
+ */
+static int drain_test(pj_size_t size, pj_size_t increment)
+{
+    pj_pool_t *pool = pj_pool_create(mem, NULL, size, increment, 
+				     &null_callback);
+    pj_size_t freesize;
+    void *p;
+    int status = 0;
+    
+    PJ_LOG(3,("test", "...drain_test(%d,%d)", size, increment));
+
+    if (!pool)
+	return -10;
+
+    /* Get free size */
+    freesize = GET_FREE(pool);
+    if (freesize < 1) {
+    	status=-15; 
+	goto on_error;
+    }
+
+    /* Drain the pool until there's nothing left. */
+    while (freesize > 0) {
+	int size;
+
+	if (freesize > 255)
+	    size = ((pj_rand() & 0x000000FF) + PJ_POOL_ALIGNMENT) & 
+		   ~(PJ_POOL_ALIGNMENT - 1);
+	else
+	    size = (int)freesize;
+
+	p = pj_pool_alloc(pool, size);
+	if (!p) {
+	    status=-20; goto on_error;
+	}
+
+	freesize -= size;
+    }
+
+    /* Check that capacity is zero. */
+    if (GET_FREE(pool) != 0) {
+	PJ_LOG(3,("test", "....error: returned free=%u (expecting 0)",
+		  GET_FREE(pool)));
+	status=-30; goto on_error;
+    }
+
+    /* Try to allocate once more */
+    p = pj_pool_alloc(pool, 257);
+    if (!p) {
+	status=-40; goto on_error;
+    }
+
+    /* Check that capacity is NOT zero. */
+    if (GET_FREE(pool) == 0) {
+	status=-50; goto on_error;
+    }
+
+
+on_error:
+    pj_pool_release(pool);
+    return status;
+}
+
+/* Test the buffer based pool */
+static int pool_buf_test(void)
+{
+    enum { STATIC_BUF_SIZE = 40 };
+    /* 16 is the internal struct in pool_buf */
+    static char buf[ STATIC_BUF_SIZE + sizeof(pj_pool_t) + 
+		     sizeof(pj_pool_block) + 2 * PJ_POOL_ALIGNMENT];
+    pj_pool_t *pool;
+    void *p;
+    PJ_USE_EXCEPTION;
+
+    PJ_LOG(3,("test", "...pool_buf test"));
+
+    pool = pj_pool_create_on_buf("no name", buf, sizeof(buf));
+    if (!pool)
+	return -70;
+
+    /* Drain the pool */
+    PJ_TRY {
+	if ((p=pj_pool_alloc(pool, STATIC_BUF_SIZE/2)) == NULL)
+	    return -75;
+
+	if ((p=pj_pool_alloc(pool, STATIC_BUF_SIZE/2)) == NULL)
+	    return -76;
+    }
+    PJ_CATCH_ANY {
+	return -77;
+    }
+    PJ_END;
+
+    /* On the next alloc, exception should be thrown */
+    PJ_TRY {
+	p = pj_pool_alloc(pool, STATIC_BUF_SIZE);
+	if (p != NULL) {
+	    /* This is unexpected, the alloc should fail */
+	    return -78;
+	}
+    }
+    PJ_CATCH_ANY {
+	/* This is the expected result */
+    }
+    PJ_END;
+
+    /* Done */
+    return 0;
+}
+
+
+int pool_test(void)
+{
+    enum { LOOP = 2 };
+    int loop;
+    int rc;
+
+    rc = capacity_test();
+    if (rc) return rc;
+
+    rc = pool_alignment_test();
+    if (rc) return rc;
+
+    rc = pool_buf_alignment_test();
+    if (rc) return rc;
+
+    for (loop=0; loop<LOOP; ++loop) {
+	/* Test that the pool should grow automaticly. */
+	rc = drain_test(SIZE, SIZE);
+	if (rc != 0) return rc;
+
+	/* Test situation where pool is not allowed to grow. 
+ 	 * We expect the test to return correct error.
+	 */
+	rc = drain_test(SIZE, 0);
+	if (rc != -40) return rc;
+    }
+
+    rc = pool_buf_test();
+    if (rc != 0)
+	return rc;
+
+
+    return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_pool_test;
+#endif	/* INCLUDE_POOL_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/pool_perf.c b/jni/pjproject-android/pjlib/src/pjlib-test/pool_perf.c
new file mode 100644
index 0000000..d5985cb
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/pool_perf.c
@@ -0,0 +1,194 @@
+/* $Id: pool_perf.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+#if INCLUDE_POOL_PERF_TEST
+
+#include <pjlib.h>
+#include <pj/compat/malloc.h>
+
+#if !PJ_HAS_HIGH_RES_TIMER
+# error Need high resolution timer for this test.
+#endif
+
+#define THIS_FILE   "test"
+
+#define LOOP	    10
+#define COUNT	    1024
+static unsigned	    sizes[COUNT];
+static char	   *p[COUNT];
+#define MIN_SIZE    4
+#define MAX_SIZE    512
+static unsigned total_size;
+
+
+static int pool_test_pool()
+{
+    int i;
+    pj_pool_t *pool = pj_pool_create(mem, NULL, total_size + 4*COUNT, 0, NULL);
+    if (!pool)
+	return -1;
+
+    for (i=0; i<COUNT; ++i) {
+	char *p;
+	if ( (p=(char*)pj_pool_alloc(pool, sizes[i])) == NULL) {
+	    PJ_LOG(3,(THIS_FILE,"   error: pool failed to allocate %d bytes",
+		      sizes[i]));
+	    pj_pool_release(pool);
+	    return -1;
+	}
+	*p = '\0';
+    }
+
+    pj_pool_release(pool);
+    return 0;
+}
+
+/* Symbian doesn't have malloc()/free(), so we use new/delete instead */
+//#if defined(PJ_SYMBIAN) && PJ_SYMBIAN != 0
+#if 0
+static int pool_test_malloc_free()
+{
+    int i; /* must be signed */
+
+    for (i=0; i<COUNT; ++i) {
+		p[i] = new char[sizes[i]];
+		if (!p[i]) {
+			PJ_LOG(3,(THIS_FILE,"   error: malloc failed to allocate %d bytes",
+					  sizes[i]));
+			--i;
+			while (i >= 0) {
+				delete [] p[i];
+				--i;
+			}
+			return -1;
+		}
+		*p[i] = '\0';
+    }
+
+    for (i=0; i<COUNT; ++i) {
+    	delete [] p[i];
+    }
+
+    return 0;
+}
+
+#else	/* PJ_SYMBIAN */
+
+static int pool_test_malloc_free()
+{
+    int i; /* must be signed */
+
+    for (i=0; i<COUNT; ++i) {
+	p[i] = (char*)malloc(sizes[i]);
+	if (!p[i]) {
+	    PJ_LOG(3,(THIS_FILE,"   error: malloc failed to allocate %d bytes",
+		      sizes[i]));
+	    --i;
+	    while (i >= 0)
+		free(p[i]), --i;
+	    return -1;
+	}
+	*p[i] = '\0';
+    }
+
+    for (i=0; i<COUNT; ++i) {
+	free(p[i]);
+    }
+
+    return 0;
+}
+
+#endif /* PJ_SYMBIAN */
+
+int pool_perf_test()
+{
+    unsigned i;
+    pj_uint32_t pool_time=0, malloc_time=0, pool_time2=0;
+    pj_timestamp start, end;
+    pj_uint32_t best, worst;
+
+    /* Initialize size of chunks to allocate in for the test. */
+    for (i=0; i<COUNT; ++i) {
+	unsigned aligned_size;
+	sizes[i] = MIN_SIZE + (pj_rand() % MAX_SIZE);
+	aligned_size = sizes[i];
+	if (aligned_size & (PJ_POOL_ALIGNMENT-1))
+	    aligned_size = ((aligned_size + PJ_POOL_ALIGNMENT - 1)) & ~(PJ_POOL_ALIGNMENT - 1);
+	total_size += aligned_size;
+    }
+
+    /* Add some more for pool admin area */
+    total_size += 512;
+
+    PJ_LOG(3, (THIS_FILE, "Benchmarking pool.."));
+
+    /* Warmup */
+    pool_test_pool();
+    pool_test_malloc_free();
+
+    for (i=0; i<LOOP; ++i) {
+	pj_get_timestamp(&start);
+	if (pool_test_pool()) {
+	    return 1;
+	}
+	pj_get_timestamp(&end);
+	pool_time += (end.u32.lo - start.u32.lo);
+
+	pj_get_timestamp(&start);
+	if (pool_test_malloc_free()) {
+	    return 2;
+	}
+	pj_get_timestamp(&end);
+	malloc_time += (end.u32.lo - start.u32.lo);
+
+	pj_get_timestamp(&start);
+	if (pool_test_pool()) {
+	    return 4;
+	}
+	pj_get_timestamp(&end);
+	pool_time2 += (end.u32.lo - start.u32.lo);
+    }
+
+    PJ_LOG(4,(THIS_FILE,"..LOOP count:                        %u",LOOP));
+    PJ_LOG(4,(THIS_FILE,"..number of alloc/dealloc per loop:  %u",COUNT));
+    PJ_LOG(4,(THIS_FILE,"..pool allocation/deallocation time: %u",pool_time));
+    PJ_LOG(4,(THIS_FILE,"..malloc/free time:                  %u",malloc_time));
+    PJ_LOG(4,(THIS_FILE,"..pool again, second invocation:     %u",pool_time2));
+
+    if (pool_time2==0) pool_time2=1;
+    if (pool_time < pool_time2)
+	best = pool_time, worst = pool_time2;
+    else
+	best = pool_time2, worst = pool_time;
+    
+    /* avoid division by zero */
+    if (best==0) best=1;
+    if (worst==0) worst=1;
+
+    PJ_LOG(3, (THIS_FILE, "..pool speedup over malloc best=%dx, worst=%dx", 
+			  (int)(malloc_time/best),
+			  (int)(malloc_time/worst)));
+    return 0;
+}
+
+
+#endif	/* INCLUDE_POOL_PERF_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/pool_wrap.cpp b/jni/pjproject-android/pjlib/src/pjlib-test/pool_wrap.cpp
new file mode 100644
index 0000000..f435ae8
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/pool_wrap.cpp
@@ -0,0 +1,24 @@
+/* $Id: pool_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "pool.c"
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/rand.c b/jni/pjproject-android/pjlib/src/pjlib-test/rand.c
new file mode 100644
index 0000000..3c442d2
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/rand.c
@@ -0,0 +1,54 @@
+/* $Id: rand.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/rand.h>
+#include <pj/log.h>
+#include "test.h"
+
+#if INCLUDE_RAND_TEST
+
+#define COUNT  1024
+static int values[COUNT];
+
+/*
+ * rand_test(), simply generates COUNT number of random number and
+ * check that there's no duplicate numbers.
+ */
+int rand_test(void)
+{
+    int i;
+
+    for (i=0; i<COUNT; ++i) {
+	int j;
+
+	values[i] = pj_rand();
+	for (j=0; j<i; ++j) {
+	    if (values[i] == values[j]) {
+		PJ_LOG(3,("test", "error: duplicate value %d at %d-th index",
+			 values[i], i));
+		return -10;
+	    }
+	}
+    }
+
+    return 0;
+}
+
+#endif	/* INCLUDE_RAND_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/rbtree.c b/jni/pjproject-android/pjlib/src/pjlib-test/rbtree.c
new file mode 100644
index 0000000..601a163
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/rbtree.c
@@ -0,0 +1,168 @@
+/* $Id: rbtree.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+#if INCLUDE_RBTREE_TEST
+
+#include <pjlib.h>
+
+#define LOOP	    32
+#define MIN_COUNT   64
+#define MAX_COUNT   (LOOP * MIN_COUNT)
+#define STRSIZE	    16
+#define THIS_FILE   "rbtree_test"
+
+typedef struct node_key
+{
+    pj_uint32_t hash;
+    char str[STRSIZE];
+} node_key;
+
+static int compare_node(const node_key *k1, const node_key *k2)
+{
+    if (k1->hash == k2->hash) {
+	return strcmp(k1->str, k2->str);
+    } else {
+	return k1->hash	< k2->hash ? -1 : 1;
+    }
+}
+
+void randomize_string(char *str, int len)
+{
+    int i;
+    for (i=0; i<len-1; ++i)
+	str[i] = (char)('a' + pj_rand() % 26);
+    str[len-1] = '\0';
+}
+
+static int test(void)
+{
+    pj_rbtree rb;
+    node_key *key;
+    pj_rbtree_node *node;
+    pj_pool_t *pool;
+    int err=0;
+    int count = MIN_COUNT;
+    int i;
+    unsigned size;
+
+    pj_rbtree_init(&rb, (pj_rbtree_comp*)&compare_node);
+    size = MAX_COUNT*(sizeof(*key)+PJ_RBTREE_NODE_SIZE) + 
+			   PJ_RBTREE_SIZE + PJ_POOL_SIZE;
+    pool = pj_pool_create( mem, "pool", size, 0, NULL);
+    if (!pool) {
+	PJ_LOG(3,("test", "...error: creating pool of %u bytes", size));
+	return -10;
+    }
+
+    key = (node_key *)pj_pool_alloc(pool, MAX_COUNT*sizeof(*key));
+    if (!key)
+	return -20;
+
+    node = (pj_rbtree_node*)pj_pool_alloc(pool, MAX_COUNT*sizeof(*node));
+    if (!node)
+	return -30;
+
+    for (i=0; i<LOOP; ++i) {
+	int j;
+	pj_rbtree_node *prev, *it;
+	pj_timestamp t1, t2, t_setup, t_insert, t_search, t_erase;
+
+	pj_assert(rb.size == 0);
+
+	t_setup.u32.lo = t_insert.u32.lo = t_search.u32.lo = t_erase.u32.lo = 0;
+
+	for (j=0; j<count; j++) {
+	    randomize_string(key[j].str, STRSIZE);
+
+	    pj_get_timestamp(&t1);
+	    node[j].key = &key[j];
+	    node[j].user_data = key[j].str;
+	    key[j].hash = pj_hash_calc(0, key[j].str, PJ_HASH_KEY_STRING);
+	    pj_get_timestamp(&t2);
+	    t_setup.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+	    pj_get_timestamp(&t1);
+	    pj_rbtree_insert(&rb, &node[j]);
+	    pj_get_timestamp(&t2);
+	    t_insert.u32.lo += (t2.u32.lo - t1.u32.lo);
+	}
+
+	pj_assert(rb.size == (unsigned)count);
+
+	// Iterate key, make sure they're sorted.
+	prev = NULL;
+	it = pj_rbtree_first(&rb);
+	while (it) {
+	    if (prev) {
+		if (compare_node((node_key*)prev->key,(node_key*)it->key)>=0) {
+		    ++err;
+		    PJ_LOG(3, (THIS_FILE, "Error: %s >= %s", 
+			       (char*)prev->user_data, (char*)it->user_data));
+		}
+	    }
+	    prev = it;
+	    it = pj_rbtree_next(&rb, it);
+	}
+
+	// Search.
+	for (j=0; j<count; j++) {
+	    pj_get_timestamp(&t1);
+	    it = pj_rbtree_find(&rb, &key[j]);
+	    pj_get_timestamp(&t2);
+	    t_search.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+	    pj_assert(it != NULL);
+	    if (it == NULL)
+		++err;
+	}
+
+	// Erase node.
+	for (j=0; j<count; j++) {
+	    pj_get_timestamp(&t1);
+	    it = pj_rbtree_erase(&rb, &node[j]);
+	    pj_get_timestamp(&t2);
+	    t_erase.u32.lo += (t2.u32.lo - t1.u32.lo);
+	}
+
+	PJ_LOG(4, (THIS_FILE, 
+		"...count:%d, setup:%d, insert:%d, search:%d, erase:%d",
+		count,
+		t_setup.u32.lo / count, t_insert.u32.lo / count,
+		t_search.u32.lo / count, t_erase.u32.lo / count));
+
+	count = 2 * count;
+	if (count > MAX_COUNT)
+	    break;
+    }
+
+    pj_pool_release(pool);
+    return err;
+}
+
+
+int rbtree_test()
+{
+    return test();
+}
+
+#endif	/* INCLUDE_RBTREE_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/rtems_network_config.h b/jni/pjproject-android/pjlib/src/pjlib-test/rtems_network_config.h
new file mode 100644
index 0000000..b2d5652
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/rtems_network_config.h
@@ -0,0 +1,149 @@
+/* $Id: rtems_network_config.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * Thanks Zetron, Inc and Phil Torre <ptorre@zetron.com> for donating PJLIB
+ * port to RTEMS.
+ */
+
+/*
+ * Network configuration
+ * 
+ ************************************************************
+ * EDIT THIS FILE TO REFLECT YOUR NETWORK CONFIGURATION     *
+ * BEFORE RUNNING ANY RTEMS PROGRAMS WHICH USE THE NETWORK! * 
+ ************************************************************
+ *
+ */
+
+#ifndef _RTEMS_NETWORKCONFIG_H_
+#define _RTEMS_NETWORKCONFIG_H_
+
+
+#define DEFAULT_IP_ADDRESS_STRING "192.168.0.2"
+#define DEFAULT_NETMASK_STRING    "255.255.255.0"
+#define DEFAULT_GATEWAY_STRING    "192.168.0.1"
+
+
+
+
+#ifndef RTEMS_BSP_NETWORK_DRIVER_NAME
+#warning "RTEMS_BSP_NETWORK_DRIVER_NAME is not defined"
+#define RTEMS_BSP_NETWORK_DRIVER_NAME "no_network1"
+#endif
+
+#ifndef RTEMS_BSP_NETWORK_DRIVER_ATTACH
+#warning "RTEMS_BSP_NETWORK_DRIVER_ATTACH is not defined"
+#define RTEMS_BSP_NETWORK_DRIVER_ATTACH 0
+#endif
+
+#define NETWORK_STACK_PRIORITY 128
+/* #define RTEMS_USE_BOOTP */
+
+/* #define RTEMS_USE_LOOPBACK */
+
+#include <bsp.h>
+
+/*
+ * Define RTEMS_SET_ETHERNET_ADDRESS if you want to specify the
+ * Ethernet address here.  If RTEMS_SET_ETHERNET_ADDRESS is not
+ * defined the driver will choose an address.
+ */
+// NOTE:  The address below is a dummy address that should only ever
+// be used for testing on a private network.  DO NOT LET A PRODUCT
+// CONTAINING THIS ETHERNET ADDRESS OUT INTO THE FIELD!
+//#define RTEMS_SET_ETHERNET_ADDRESS
+#if (defined (RTEMS_SET_ETHERNET_ADDRESS))
+static char ethernet_address[6] = { 0x00, 0x80, 0x7F, 0x22, 0x61, 0x77 };
+#endif
+
+#define RTEMS_USE_LOOPBACK 
+#ifdef RTEMS_USE_LOOPBACK 
+/*
+ * Loopback interface
+ */
+extern int rtems_bsdnet_loopattach(struct rtems_bsdnet_ifconfig* dummy, int unused);
+static struct rtems_bsdnet_ifconfig loopback_config = {
+	"lo0",				/* name */
+	rtems_bsdnet_loopattach,	/* attach function */
+	NULL,				/* link to next interface */
+	"127.0.0.1",			/* IP address */
+	"255.0.0.0",			/* IP net mask */
+};
+#endif
+
+/*
+ * Default network interface
+ */
+static struct rtems_bsdnet_ifconfig netdriver_config = {
+	RTEMS_BSP_NETWORK_DRIVER_NAME,		/* name */
+	RTEMS_BSP_NETWORK_DRIVER_ATTACH,	/* attach function */
+
+#ifdef RTEMS_USE_LOOPBACK 
+	&loopback_config,		/* link to next interface */
+#else
+	NULL,				/* No more interfaces */
+#endif
+
+#if (defined (RTEMS_USE_BOOTP))
+	NULL,				/* BOOTP supplies IP address */
+	NULL,				/* BOOTP supplies IP net mask */
+#else
+	"192.168.0.33",			/* IP address */
+	"255.255.255.0",		/* IP net mask */
+#endif /* !RTEMS_USE_BOOTP */
+
+#if (defined (RTEMS_SET_ETHERNET_ADDRESS))
+	ethernet_address,               /* Ethernet hardware address */
+#else
+	NULL,                           /* Driver supplies hardware address */
+#endif
+	0				/* Use default driver parameters */
+};
+
+/*
+ * Network configuration
+ */
+struct rtems_bsdnet_config rtems_bsdnet_config = {
+	&netdriver_config,
+
+#if (defined (RTEMS_USE_BOOTP))
+	rtems_bsdnet_do_bootp,
+#else
+	NULL,
+#endif
+
+	NETWORK_STACK_PRIORITY,		/* Default network task priority */
+	1048576,			/* Default mbuf capacity */
+	1048576,			/* Default mbuf cluster capacity */
+
+#if (!defined (RTEMS_USE_BOOTP))
+	"testnode",		/* Host name */
+	"example.org",		/* Domain name */
+	"192.168.6.9",		/* Gateway */
+	"192.168.7.41",		/* Log host */
+	{"198.137.231.1" },	/* Name server(s) */
+	{"207.202.190.162" },	/* NTP server(s) */
+#endif /* !RTEMS_USE_BOOTP */
+
+};
+
+#endif	/* _RTEMS_NETWORKCONFIG_H_ */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/select.c b/jni/pjproject-android/pjlib/src/pjlib-test/select.c
new file mode 100644
index 0000000..d7d891a
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/select.c
@@ -0,0 +1,221 @@
+/* $Id: select.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_select_test Test: Socket Select()
+ *
+ * This file provides implementation of \b select_test(). It tests the
+ * functionality of the pj_sock_select() API.
+ *
+ *
+ * This file is <b>pjlib-test/select.c</b>
+ *
+ * \include pjlib-test/select.c
+ */
+
+
+#if INCLUDE_SELECT_TEST
+
+#include <pj/sock.h>
+#include <pj/sock_select.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/assert.h>
+#include <pj/os.h>
+#include <pj/errno.h>
+
+enum
+{
+    READ_FDS,
+    WRITE_FDS,
+    EXCEPT_FDS
+};
+
+#define UDP_PORT    51232
+#define THIS_FILE   "select_test"
+
+/*
+ * do_select()
+ *
+ * Perform pj_sock_select() and find out which sockets
+ * are signalled.
+ */    
+static int do_select( pj_sock_t sock1, pj_sock_t sock2,
+		      int setcount[])
+{
+    pj_fd_set_t fds[3];
+    pj_time_val timeout;
+    int i, n;
+    
+    for (i=0; i<3; ++i) {
+        PJ_FD_ZERO(&fds[i]);
+        PJ_FD_SET(sock1, &fds[i]);
+        PJ_FD_SET(sock2, &fds[i]);
+        setcount[i] = 0;
+    }
+
+    timeout.sec = 1;
+    timeout.msec = 0;
+
+    n = pj_sock_select(PJ_IOQUEUE_MAX_HANDLES, &fds[0], &fds[1], &fds[2],
+		       &timeout);
+    if (n < 0)
+        return n;
+    if (n == 0)
+        return 0;
+
+    for (i=0; i<3; ++i) {
+        if (PJ_FD_ISSET(sock1, &fds[i]))
+            setcount[i]++;
+        if (PJ_FD_ISSET(sock2, &fds[i]))
+	    setcount[i]++;
+    }
+
+    return n;
+}
+
+/*
+ * select_test()
+ *
+ * Test main entry.
+ */
+int select_test()
+{
+    pj_sock_t udp1=PJ_INVALID_SOCKET, udp2=PJ_INVALID_SOCKET;
+    pj_sockaddr_in udp_addr;
+    int status;
+    int setcount[3];
+    pj_str_t s;
+    const char data[] = "hello";
+    const int datalen = 5;
+    pj_ssize_t sent, received;
+    char buf[10];
+    pj_status_t rc;
+
+    PJ_LOG(3, (THIS_FILE, "...Testing simple UDP select()"));
+    
+    // Create two UDP sockets.
+    rc = pj_sock_socket( pj_AF_INET(), pj_SOCK_DGRAM(), 0, &udp1);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error: unable to create socket", rc);
+	status=-10; goto on_return;
+    }
+    rc = pj_sock_socket( pj_AF_INET(), pj_SOCK_DGRAM(), 0, &udp2);
+    if (udp2 == PJ_INVALID_SOCKET) {
+        app_perror("...error: unable to create socket", rc);
+	status=-20; goto on_return;
+    }
+
+    // Bind one of the UDP socket.
+    pj_bzero(&udp_addr, sizeof(udp_addr));
+    udp_addr.sin_family = pj_AF_INET();
+    udp_addr.sin_port = UDP_PORT;
+    udp_addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+
+    if (pj_sock_bind(udp2, &udp_addr, sizeof(udp_addr))) {
+	status=-30; goto on_return;
+    }
+
+    // Send data.
+    sent = datalen;
+    rc = pj_sock_sendto(udp1, data, &sent, 0, &udp_addr, sizeof(udp_addr));
+    if (rc != PJ_SUCCESS || sent != datalen) {
+        app_perror("...error: sendto() error", rc);
+	status=-40; goto on_return;
+    }
+
+    // Sleep a bit. See http://trac.pjsip.org/repos/ticket/890
+    pj_thread_sleep(10);
+
+    // Check that socket is marked as reable.
+    // Note that select() may also report that sockets are writable.
+    status = do_select(udp1, udp2, setcount);
+    if (status < 0) {
+	char errbuf[128];
+        pj_strerror(pj_get_netos_error(), errbuf, sizeof(errbuf));
+	PJ_LOG(1,(THIS_FILE, "...error: %s", errbuf));
+	status=-50; goto on_return;
+    }
+    if (status == 0) {
+	status=-60; goto on_return;
+    }
+
+    if (setcount[READ_FDS] != 1) {
+	status=-70; goto on_return;
+    }
+    if (setcount[WRITE_FDS] != 0) {
+	if (setcount[WRITE_FDS] == 2) {
+	    PJ_LOG(3,(THIS_FILE, "...info: system reports writable sockets"));
+	} else {
+	    status=-80; goto on_return;
+	}
+    } else {
+	PJ_LOG(3,(THIS_FILE, 
+		  "...info: system doesn't report writable sockets"));
+    }
+    if (setcount[EXCEPT_FDS] != 0) {
+	status=-90; goto on_return;
+    }
+
+    // Read the socket to clear readable sockets.
+    received = sizeof(buf);
+    rc = pj_sock_recv(udp2, buf, &received, 0);
+    if (rc != PJ_SUCCESS || received != 5) {
+	status=-100; goto on_return;
+    }
+    
+    status = 0;
+
+    // Test timeout on the read part.
+    // This won't necessarily return zero, as select() may report that
+    // sockets are writable.
+    setcount[0] = setcount[1] = setcount[2] = 0;
+    status = do_select(udp1, udp2, setcount);
+    if (status != 0 && status != setcount[WRITE_FDS]) {
+	PJ_LOG(3,(THIS_FILE, "...error: expecting timeout but got %d sks set",
+			     status));
+	PJ_LOG(3,(THIS_FILE, "          rdset: %d, wrset: %d, exset: %d",
+			     setcount[0], setcount[1], setcount[2]));
+	status = -110; goto on_return;
+    }
+    if (setcount[READ_FDS] != 0) {
+	PJ_LOG(3,(THIS_FILE, "...error: readable socket not expected"));
+	status = -120; goto on_return;
+    }
+
+    status = 0;
+
+on_return:
+    if (udp1 != PJ_INVALID_SOCKET)
+	pj_sock_close(udp1);
+    if (udp2 != PJ_INVALID_SOCKET)
+	pj_sock_close(udp2);
+    return status;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_select_test;
+#endif	/* INCLUDE_SELECT_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/sleep.c b/jni/pjproject-android/pjlib/src/pjlib-test/sleep.c
new file mode 100644
index 0000000..03bef7d
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/sleep.c
@@ -0,0 +1,224 @@
+/* $Id: sleep.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_sleep_test Test: Sleep, Time, and Timestamp
+ *
+ * This file provides implementation of \b sleep_test().
+ *
+ * \section sleep_test_sec Scope of the Test
+ *
+ * This tests:
+ *  - whether pj_thread_sleep() works.
+ *  - whether pj_gettimeofday() works.
+ *  - whether pj_get_timestamp() and friends works.
+ *
+ * API tested:
+ *  - pj_thread_sleep()
+ *  - pj_gettimeofday()
+ *  - PJ_TIME_VAL_SUB()
+ *  - PJ_TIME_VAL_LTE()
+ *  - pj_get_timestamp()
+ *  - pj_get_timestamp_freq() (implicitly)
+ *  - pj_elapsed_time()
+ *  - pj_elapsed_usec()
+ *
+ *
+ * This file is <b>pjlib-test/sleep.c</b>
+ *
+ * \include pjlib-test/sleep.c
+ */
+
+#if INCLUDE_SLEEP_TEST
+
+#include <pjlib.h>
+
+#define THIS_FILE   "sleep_test"
+
+static int simple_sleep_test(void)
+{
+    enum { COUNT = 10 };
+    int i;
+    pj_status_t rc;
+    
+    PJ_LOG(3,(THIS_FILE, "..will write messages every 1 second:"));
+    
+    for (i=0; i<COUNT; ++i) {
+	pj_time_val tv;
+	pj_parsed_time pt;
+
+	rc = pj_thread_sleep(1000);
+	if (rc != PJ_SUCCESS) {
+	    app_perror("...error: pj_thread_sleep()", rc);
+	    return -10;
+	}
+
+	rc = pj_gettimeofday(&tv);
+	if (rc != PJ_SUCCESS) {
+	    app_perror("...error: pj_gettimeofday()", rc);
+	    return -11;
+	}
+
+	pj_time_decode(&tv, &pt);
+
+	PJ_LOG(3,(THIS_FILE, 
+		  "...%04d-%02d-%02d %02d:%02d:%02d.%03d",
+		  pt.year, pt.mon, pt.day,
+		  pt.hour, pt.min, pt.sec, pt.msec));
+
+    }
+
+    return 0;
+}
+
+static int sleep_duration_test(void)
+{
+    enum { MIS = 20};
+    unsigned duration[] = { 2000, 1000, 500, 200, 100 };
+    unsigned i;
+    pj_status_t rc;
+
+    PJ_LOG(3,(THIS_FILE, "..running sleep duration test"));
+
+    /* Test pj_thread_sleep() and pj_gettimeofday() */
+    for (i=0; i<PJ_ARRAY_SIZE(duration); ++i) {
+        pj_time_val start, stop;
+	pj_uint32_t msec;
+
+        /* Mark start of test. */
+        rc = pj_gettimeofday(&start);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: pj_gettimeofday()", rc);
+            return -10;
+        }
+
+        /* Sleep */
+        rc = pj_thread_sleep(duration[i]);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: pj_thread_sleep()", rc);
+            return -20;
+        }
+
+        /* Mark end of test. */
+        rc = pj_gettimeofday(&stop);
+
+        /* Calculate duration (store in stop). */
+        PJ_TIME_VAL_SUB(stop, start);
+
+	/* Convert to msec. */
+	msec = PJ_TIME_VAL_MSEC(stop);
+
+	/* Check if it's within range. */
+	if (msec < duration[i] * (100-MIS)/100 ||
+	    msec > duration[i] * (100+MIS)/100)
+	{
+	    PJ_LOG(3,(THIS_FILE, 
+		      "...error: slept for %d ms instead of %d ms "
+		      "(outside %d%% err window)",
+		      msec, duration[i], MIS));
+	    return -30;
+	}
+    }
+
+
+    /* Test pj_thread_sleep() and pj_get_timestamp() and friends */
+    for (i=0; i<PJ_ARRAY_SIZE(duration); ++i) {
+	pj_time_val t1, t2;
+        pj_timestamp start, stop;
+	pj_uint32_t msec;
+
+	pj_thread_sleep(0);
+
+        /* Mark start of test. */
+        rc = pj_get_timestamp(&start);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: pj_get_timestamp()", rc);
+            return -60;
+        }
+
+	/* ..also with gettimeofday() */
+	pj_gettimeofday(&t1);
+
+        /* Sleep */
+        rc = pj_thread_sleep(duration[i]);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error: pj_thread_sleep()", rc);
+            return -70;
+        }
+
+        /* Mark end of test. */
+        pj_get_timestamp(&stop);
+
+	/* ..also with gettimeofday() */
+	pj_gettimeofday(&t2);
+
+	/* Compare t1 and t2. */
+	if (PJ_TIME_VAL_LT(t2, t1)) {
+	    PJ_LOG(3,(THIS_FILE, "...error: t2 is less than t1!!"));
+	    return -75;
+	}
+
+        /* Get elapsed time in msec */
+        msec = pj_elapsed_msec(&start, &stop);
+
+	/* Check if it's within range. */
+	if (msec < duration[i] * (100-MIS)/100 ||
+	    msec > duration[i] * (100+MIS)/100)
+	{
+	    PJ_LOG(3,(THIS_FILE, 
+		      "...error: slept for %d ms instead of %d ms "
+		      "(outside %d%% err window)",
+		      msec, duration[i], MIS));
+	    PJ_TIME_VAL_SUB(t2, t1);
+	    PJ_LOG(3,(THIS_FILE, 
+		      "...info: gettimeofday() reported duration is "
+		      "%d msec",
+		      PJ_TIME_VAL_MSEC(t2)));
+
+	    return -76;
+	}
+    }
+
+    /* All done. */
+    return 0;
+}
+
+int sleep_test()
+{
+    int rc;
+
+    rc = simple_sleep_test();
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    rc = sleep_duration_test();
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_sleep_test;
+#endif  /* INCLUDE_SLEEP_TEST */
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/sock.c b/jni/pjproject-android/pjlib/src/pjlib-test/sock.c
new file mode 100644
index 0000000..330fb97
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/sock.c
@@ -0,0 +1,877 @@
+/* $Id: sock.c 3841 2011-10-24 09:28:13Z ming $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjlib.h>
+#include "test.h"
+
+
+/**
+ * \page page_pjlib_sock_test Test: Socket
+ *
+ * This file provides implementation of \b sock_test(). It tests the
+ * various aspects of the socket API.
+ *
+ * \section sock_test_scope_sec Scope of the Test
+ *
+ * The scope of the test:
+ *  - verify the validity of the address structs.
+ *  - verify that address manipulation API works.
+ *  - simple socket creation and destruction.
+ *  - simple socket send/recv and sendto/recvfrom.
+ *  - UDP connect()
+ *  - send/recv big data.
+ *  - all for both UDP and TCP.
+ *
+ * The APIs tested in this test:
+ *  - pj_inet_aton()
+ *  - pj_inet_ntoa()
+ *  - pj_inet_pton()  (only if IPv6 is enabled)
+ *  - pj_inet_ntop()  (only if IPv6 is enabled)
+ *  - pj_gethostname()
+ *  - pj_sock_socket()
+ *  - pj_sock_close()
+ *  - pj_sock_send()
+ *  - pj_sock_sendto()
+ *  - pj_sock_recv()
+ *  - pj_sock_recvfrom()
+ *  - pj_sock_bind()
+ *  - pj_sock_connect()
+ *  - pj_sock_listen()
+ *  - pj_sock_accept()
+ *  - pj_gethostbyname()
+ *
+ *
+ * This file is <b>pjlib-test/sock.c</b>
+ *
+ * \include pjlib-test/sock.c
+ */
+
+#if INCLUDE_SOCK_TEST
+
+#define UDP_PORT	51234
+#define TCP_PORT        (UDP_PORT+10)
+#define BIG_DATA_LEN	8192
+#define ADDRESS		"127.0.0.1"
+
+static char bigdata[BIG_DATA_LEN];
+static char bigbuffer[BIG_DATA_LEN];
+
+/* Macro for checking the value of "sin_len" member of sockaddr
+ * (it must always be zero).
+ */
+#if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0
+#   define CHECK_SA_ZERO_LEN(addr, ret) \
+	if (((pj_addr_hdr*)(addr))->sa_zero_len != 0) \
+	    return ret
+#else
+#   define CHECK_SA_ZERO_LEN(addr, ret)
+#endif
+
+
+static int format_test(void)
+{
+    pj_str_t s = pj_str(ADDRESS);
+    unsigned char *p;
+    pj_in_addr addr;
+    char zero[64];
+    pj_sockaddr_in addr2;
+    const pj_str_t *hostname;
+    const unsigned char A[] = {127, 0, 0, 1};
+
+    PJ_LOG(3,("test", "...format_test()"));
+    
+    /* pj_inet_aton() */
+    if (pj_inet_aton(&s, &addr) != 1)
+	return -10;
+    
+    /* Check the result. */
+    p = (unsigned char*)&addr;
+    if (p[0]!=A[0] || p[1]!=A[1] || p[2]!=A[2] || p[3]!=A[3]) {
+	PJ_LOG(3,("test", "  error: mismatched address. p0=%d, p1=%d, "
+			  "p2=%d, p3=%d", p[0] & 0xFF, p[1] & 0xFF, 
+			   p[2] & 0xFF, p[3] & 0xFF));
+	return -15;
+    }
+
+    /* pj_inet_ntoa() */
+    p = (unsigned char*) pj_inet_ntoa(addr);
+    if (!p)
+	return -20;
+
+    if (pj_strcmp2(&s, (char*)p) != 0)
+	return -22;
+
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
+    /* pj_inet_pton() */
+    /* pj_inet_ntop() */
+    {
+	const pj_str_t s_ipv4 = pj_str("127.0.0.1");
+	const pj_str_t s_ipv6 = pj_str("fe80::2ff:83ff:fe7c:8b42");
+	char buf_ipv4[PJ_INET_ADDRSTRLEN];
+	char buf_ipv6[PJ_INET6_ADDRSTRLEN];
+	pj_in_addr ipv4;
+	pj_in6_addr ipv6;
+
+	if (pj_inet_pton(pj_AF_INET(), &s_ipv4, &ipv4) != PJ_SUCCESS)
+	    return -24;
+
+	p = (unsigned char*)&ipv4;
+	if (p[0]!=A[0] || p[1]!=A[1] || p[2]!=A[2] || p[3]!=A[3]) {
+	    return -25;
+	}
+
+	if (pj_inet_pton(pj_AF_INET6(), &s_ipv6, &ipv6) != PJ_SUCCESS)
+	    return -26;
+
+	p = (unsigned char*)&ipv6;
+	if (p[0] != 0xfe || p[1] != 0x80 || p[2] != 0 || p[3] != 0 ||
+	    p[4] != 0 || p[5] != 0 || p[6] != 0 || p[7] != 0 ||
+	    p[8] != 0x02 || p[9] != 0xff || p[10] != 0x83 || p[11] != 0xff ||
+	    p[12]!=0xfe || p[13]!=0x7c || p[14] != 0x8b || p[15]!=0x42)
+	{
+	    return -27;
+	}
+
+	if (pj_inet_ntop(pj_AF_INET(), &ipv4, buf_ipv4, sizeof(buf_ipv4)) != PJ_SUCCESS)
+	    return -28;
+	if (pj_stricmp2(&s_ipv4, buf_ipv4) != 0)
+	    return -29;
+
+	if (pj_inet_ntop(pj_AF_INET6(), &ipv6, buf_ipv6, sizeof(buf_ipv6)) != PJ_SUCCESS)
+	    return -30;
+	if (pj_stricmp2(&s_ipv6, buf_ipv6) != 0)
+	    return -31;
+    }
+
+#endif	/* PJ_HAS_IPV6 */
+
+    /* Test that pj_sockaddr_in_init() initialize the whole structure, 
+     * including sin_zero.
+     */
+    pj_sockaddr_in_init(&addr2, 0, 1000);
+    pj_bzero(zero, sizeof(zero));
+    if (pj_memcmp(addr2.sin_zero, zero, sizeof(addr2.sin_zero)) != 0)
+	return -35;
+
+    /* pj_gethostname() */
+    hostname = pj_gethostname();
+    if (!hostname || !hostname->ptr || !hostname->slen)
+	return -40;
+
+    PJ_LOG(3,("test", "....hostname is %.*s", 
+	      (int)hostname->slen, hostname->ptr));
+
+    /* pj_gethostaddr() */
+
+    /* Various constants */
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+    if (PJ_AF_INET==0xFFFF) return -5500;
+    if (PJ_AF_INET6==0xFFFF) return -5501;
+    
+    /* 0xFFFF could be a valid SOL_SOCKET (e.g: on some Win or Mac) */
+    //if (PJ_SOL_SOCKET==0xFFFF) return -5503;
+    
+    if (PJ_SOL_IP==0xFFFF) return -5502;
+    if (PJ_SOL_TCP==0xFFFF) return -5510;
+    if (PJ_SOL_UDP==0xFFFF) return -5520;
+    if (PJ_SOL_IPV6==0xFFFF) return -5530;
+
+    if (PJ_SO_TYPE==0xFFFF) return -5540;
+    if (PJ_SO_RCVBUF==0xFFFF) return -5550;
+    if (PJ_SO_SNDBUF==0xFFFF) return -5560;
+    if (PJ_TCP_NODELAY==0xFFFF) return -5570;
+    if (PJ_SO_REUSEADDR==0xFFFF) return -5580;
+
+    if (PJ_MSG_OOB==0xFFFF) return -5590;
+    if (PJ_MSG_PEEK==0xFFFF) return -5600;
+#endif
+
+    return 0;
+}
+
+static int parse_test(void)
+{
+#define IPv4	1
+#define IPv6	2
+
+    struct test_t {
+	const char  *input;
+	int	     result_af;
+	const char  *result_ip;
+	pj_uint16_t  result_port;
+    };
+    struct test_t valid_tests[] = 
+    {
+	/* IPv4 */
+	{ "10.0.0.1:80", IPv4, "10.0.0.1", 80},
+	{ "10.0.0.1", IPv4, "10.0.0.1", 0},
+	{ "10.0.0.1:", IPv4, "10.0.0.1", 0},
+	{ "10.0.0.1:0", IPv4, "10.0.0.1", 0},
+	{ ":80", IPv4, "0.0.0.0", 80},
+	{ ":", IPv4, "0.0.0.0", 0},
+#if !PJ_SYMBIAN
+	{ "localhost", IPv4, "127.0.0.1", 0},
+	{ "localhost:", IPv4, "127.0.0.1", 0},
+	{ "localhost:80", IPv4, "127.0.0.1", 80},
+#endif
+
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
+	{ "fe::01:80", IPv6, "fe::01:80", 0},
+	{ "[fe::01]:80", IPv6, "fe::01", 80},
+	{ "fe::01", IPv6, "fe::01", 0},
+	{ "[fe::01]", IPv6, "fe::01", 0},
+	{ "fe::01:", IPv6, "fe::01", 0},
+	{ "[fe::01]:", IPv6, "fe::01", 0},
+	{ "::", IPv6, "::0", 0},
+	{ "[::]", IPv6, "::", 0},
+	{ ":::", IPv6, "::", 0},
+	{ "[::]:", IPv6, "::", 0},
+	{ ":::80", IPv6, "::", 80},
+	{ "[::]:80", IPv6, "::", 80},
+#endif
+    };
+    struct test_t invalid_tests[] = 
+    {
+	/* IPv4 */
+	{ "10.0.0.1:abcd", IPv4},   /* port not numeric */
+	{ "10.0.0.1:-1", IPv4},	    /* port contains illegal character */
+	{ "10.0.0.1:123456", IPv4}, /* port too big	*/
+	{ "1.2.3.4.5:80", IPv4},    /* invalid IP */
+	{ "10:0:80", IPv4},	    /* hostname has colon */
+
+#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6
+	{ "[fe::01]:abcd", IPv6},   /* port not numeric */
+	{ "[fe::01]:-1", IPv6},	    /* port contains illegal character */
+	{ "[fe::01]:123456", IPv6}, /* port too big	*/
+	{ "fe::01:02::03:04:80", IPv6},	    /* invalid IP */
+	{ "[fe::01:02::03:04]:80", IPv6},   /* invalid IP */
+	{ "[fe:01", IPv6},	    /* Unterminated bracket */
+#endif
+    };
+
+    unsigned i;
+
+    PJ_LOG(3,("test", "...IP address parsing"));
+
+    for (i=0; i<PJ_ARRAY_SIZE(valid_tests); ++i) {
+	pj_status_t status;
+	pj_str_t input;
+	pj_sockaddr addr, result;
+
+	switch (valid_tests[i].result_af) {
+	case IPv4:
+	    valid_tests[i].result_af = PJ_AF_INET;
+	    break;
+	case IPv6:
+	    valid_tests[i].result_af = PJ_AF_INET6;
+	    break;
+	default:
+	    pj_assert(!"Invalid AF!");
+	    continue;
+	}
+
+	/* Try parsing with PJ_AF_UNSPEC */
+	status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, 
+				   pj_cstr(&input, valid_tests[i].input), 
+				   &addr);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(1,("test", ".... failed when parsing %s (i=%d)", 
+		      valid_tests[i].input, i));
+	    return -10;
+	}
+
+	/* Check "sin_len" member of parse result */
+	CHECK_SA_ZERO_LEN(&addr, -20);
+
+	/* Build the correct result */
+	status = pj_sockaddr_init(valid_tests[i].result_af,
+				  &result,
+				  pj_cstr(&input, valid_tests[i].result_ip), 
+				  valid_tests[i].result_port);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(1,("test", ".... error building IP address %s", 
+		      valid_tests[i].input));
+	    return -30;
+	}
+
+	/* Compare the result */
+	if (pj_sockaddr_cmp(&addr, &result) != 0) {
+	    PJ_LOG(1,("test", ".... parsed result mismatched for %s", 
+		      valid_tests[i].input));
+	    return -40;
+	}
+
+	/* Parse again with the specified af */
+	status = pj_sockaddr_parse(valid_tests[i].result_af, 0, 
+				   pj_cstr(&input, valid_tests[i].input), 
+				   &addr);
+	if (status != PJ_SUCCESS) {
+	    PJ_LOG(1,("test", ".... failed when parsing %s", 
+		      valid_tests[i].input));
+	    return -50;
+	}
+
+	/* Check "sin_len" member of parse result */
+	CHECK_SA_ZERO_LEN(&addr, -55);
+
+	/* Compare the result again */
+	if (pj_sockaddr_cmp(&addr, &result) != 0) {
+	    PJ_LOG(1,("test", ".... parsed result mismatched for %s", 
+		      valid_tests[i].input));
+	    return -60;
+	}
+    }
+
+    for (i=0; i<PJ_ARRAY_SIZE(invalid_tests); ++i) {
+	pj_status_t status;
+	pj_str_t input;
+	pj_sockaddr addr;
+
+	switch (invalid_tests[i].result_af) {
+	case IPv4:
+	    invalid_tests[i].result_af = PJ_AF_INET;
+	    break;
+	case IPv6:
+	    invalid_tests[i].result_af = PJ_AF_INET6;
+	    break;
+	default:
+	    pj_assert(!"Invalid AF!");
+	    continue;
+	}
+
+	/* Try parsing with PJ_AF_UNSPEC */
+	status = pj_sockaddr_parse(PJ_AF_UNSPEC, 0, 
+				   pj_cstr(&input, invalid_tests[i].input), 
+				   &addr);
+	if (status == PJ_SUCCESS) {
+	    PJ_LOG(1,("test", ".... expecting failure when parsing %s", 
+		      invalid_tests[i].input));
+	    return -100;
+	}
+    }
+
+    return 0;
+}
+
+static int purity_test(void)
+{
+    PJ_LOG(3,("test", "...purity_test()"));
+
+#if defined(PJ_SOCKADDR_HAS_LEN) && PJ_SOCKADDR_HAS_LEN!=0
+    /* Check on "sin_len" member of sockaddr */
+    {
+	const pj_str_t str_ip = {"1.1.1.1", 7};
+	pj_sockaddr addr[16];
+	pj_addrinfo ai[16];
+	unsigned cnt;
+	pj_status_t rc;
+
+	/* pj_enum_ip_interface() */
+	cnt = PJ_ARRAY_SIZE(addr);
+	rc = pj_enum_ip_interface(pj_AF_UNSPEC(), &cnt, addr);
+	if (rc == PJ_SUCCESS) {
+	    while (cnt--)
+		CHECK_SA_ZERO_LEN(&addr[cnt], -10);
+	}
+
+	/* pj_gethostip() on IPv4 */
+	rc = pj_gethostip(pj_AF_INET(), &addr[0]);
+	if (rc == PJ_SUCCESS)
+	    CHECK_SA_ZERO_LEN(&addr[0], -20);
+
+	/* pj_gethostip() on IPv6 */
+	rc = pj_gethostip(pj_AF_INET6(), &addr[0]);
+	if (rc == PJ_SUCCESS)
+	    CHECK_SA_ZERO_LEN(&addr[0], -30);
+
+	/* pj_getdefaultipinterface() on IPv4 */
+	rc = pj_getdefaultipinterface(pj_AF_INET(), &addr[0]);
+	if (rc == PJ_SUCCESS)
+	    CHECK_SA_ZERO_LEN(&addr[0], -40);
+
+	/* pj_getdefaultipinterface() on IPv6 */
+	rc = pj_getdefaultipinterface(pj_AF_INET6(), &addr[0]);
+	if (rc == PJ_SUCCESS)
+	    CHECK_SA_ZERO_LEN(&addr[0], -50);
+
+	/* pj_getaddrinfo() on a host name */
+	cnt = PJ_ARRAY_SIZE(ai);
+	rc = pj_getaddrinfo(pj_AF_UNSPEC(), pj_gethostname(), &cnt, ai);
+	if (rc == PJ_SUCCESS) {
+	    while (cnt--)
+		CHECK_SA_ZERO_LEN(&ai[cnt].ai_addr, -60);
+	}
+
+	/* pj_getaddrinfo() on an IP address */
+	cnt = PJ_ARRAY_SIZE(ai);
+	rc = pj_getaddrinfo(pj_AF_UNSPEC(), &str_ip, &cnt, ai);
+	if (rc == PJ_SUCCESS) {
+	    pj_assert(cnt == 1);
+	    CHECK_SA_ZERO_LEN(&ai[0].ai_addr, -70);
+	}
+    }
+#endif
+
+    return 0;
+}
+
+static int simple_sock_test(void)
+{
+    int types[2];
+    pj_sock_t sock;
+    int i;
+    pj_status_t rc = PJ_SUCCESS;
+
+    types[0] = pj_SOCK_STREAM();
+    types[1] = pj_SOCK_DGRAM();
+
+    PJ_LOG(3,("test", "...simple_sock_test()"));
+
+    for (i=0; i<(int)(sizeof(types)/sizeof(types[0])); ++i) {
+	
+	rc = pj_sock_socket(pj_AF_INET(), types[i], 0, &sock);
+	if (rc != PJ_SUCCESS) {
+	    app_perror("...error: unable to create socket", rc);
+	    break;
+	} else {
+	    rc = pj_sock_close(sock);
+	    if (rc != 0) {
+		app_perror("...error: close socket", rc);
+		break;
+	    }
+	}
+    }
+    return rc;
+}
+
+
+static int send_recv_test(int sock_type,
+                          pj_sock_t ss, pj_sock_t cs,
+			  pj_sockaddr_in *dstaddr, pj_sockaddr_in *srcaddr, 
+			  int addrlen)
+{
+    enum { DATA_LEN = 16 };
+    char senddata[DATA_LEN+4], recvdata[DATA_LEN+4];
+    pj_ssize_t sent, received, total_received;
+    pj_status_t rc;
+
+    TRACE_(("test", "....create_random_string()"));
+    pj_create_random_string(senddata, DATA_LEN);
+    senddata[DATA_LEN-1] = '\0';
+
+    /*
+     * Test send/recv small data.
+     */
+    TRACE_(("test", "....sendto()"));
+    if (dstaddr) {
+        sent = DATA_LEN;
+	rc = pj_sock_sendto(cs, senddata, &sent, 0, dstaddr, addrlen);
+	if (rc != PJ_SUCCESS || sent != DATA_LEN) {
+	    app_perror("...sendto error", rc);
+	    rc = -140; goto on_error;
+	}
+    } else {
+        sent = DATA_LEN;
+	rc = pj_sock_send(cs, senddata, &sent, 0);
+	if (rc != PJ_SUCCESS || sent != DATA_LEN) {
+	    app_perror("...send error", rc);
+	    rc = -145; goto on_error;
+	}
+    }
+
+    TRACE_(("test", "....recv()"));
+    if (srcaddr) {
+	pj_sockaddr_in addr;
+	int srclen = sizeof(addr);
+	
+	pj_bzero(&addr, sizeof(addr));
+
+        received = DATA_LEN;
+	rc = pj_sock_recvfrom(ss, recvdata, &received, 0, &addr, &srclen);
+	if (rc != PJ_SUCCESS || received != DATA_LEN) {
+	    app_perror("...recvfrom error", rc);
+	    rc = -150; goto on_error;
+	}
+	if (srclen != addrlen)
+	    return -151;
+	if (pj_sockaddr_cmp(&addr, srcaddr) != 0) {
+	    char srcaddr_str[32], addr_str[32];
+	    strcpy(srcaddr_str, pj_inet_ntoa(srcaddr->sin_addr));
+	    strcpy(addr_str, pj_inet_ntoa(addr.sin_addr));
+	    PJ_LOG(3,("test", "...error: src address mismatch (original=%s, "
+			      "recvfrom addr=%s)", 
+			      srcaddr_str, addr_str));
+	    return -152;
+	}
+	
+    } else {
+        /* Repeat recv() until all data is received.
+         * This applies only for non-UDP of course, since for UDP
+         * we would expect all data to be received in one packet.
+         */
+        total_received = 0;
+        do {
+            received = DATA_LEN-total_received;
+	    rc = pj_sock_recv(ss, recvdata+total_received, &received, 0);
+	    if (rc != PJ_SUCCESS) {
+	        app_perror("...recv error", rc);
+	        rc = -155; goto on_error;
+	    }
+            if (received <= 0) {
+                PJ_LOG(3,("", "...error: socket has closed! (received=%d)",
+                          received));
+                rc = -156; goto on_error;
+            }
+	    if (received != DATA_LEN-total_received) {
+                if (sock_type != pj_SOCK_STREAM()) {
+	            PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+                              DATA_LEN-total_received, received));
+	            rc = -157; goto on_error;
+                }
+	    }
+            total_received += received;
+        } while (total_received < DATA_LEN);
+    }
+
+    TRACE_(("test", "....memcmp()"));
+    if (pj_memcmp(senddata, recvdata, DATA_LEN) != 0) {
+	PJ_LOG(3,("","...error: received data mismatch "
+		     "(got:'%s' expecting:'%s'",
+		     recvdata, senddata));
+	rc = -160; goto on_error;
+    }
+
+    /*
+     * Test send/recv big data.
+     */
+    TRACE_(("test", "....sendto()"));
+    if (dstaddr) {
+        sent = BIG_DATA_LEN;
+	rc = pj_sock_sendto(cs, bigdata, &sent, 0, dstaddr, addrlen);
+	if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) {
+	    app_perror("...sendto error", rc);
+	    rc = -161; goto on_error;
+	}
+    } else {
+        sent = BIG_DATA_LEN;
+	rc = pj_sock_send(cs, bigdata, &sent, 0);
+	if (rc != PJ_SUCCESS || sent != BIG_DATA_LEN) {
+	    app_perror("...send error", rc);
+	    rc = -165; goto on_error;
+	}
+    }
+
+    TRACE_(("test", "....recv()"));
+
+    /* Repeat recv() until all data is received.
+     * This applies only for non-UDP of course, since for UDP
+     * we would expect all data to be received in one packet.
+     */
+    total_received = 0;
+    do {
+        received = BIG_DATA_LEN-total_received;
+	rc = pj_sock_recv(ss, bigbuffer+total_received, &received, 0);
+	if (rc != PJ_SUCCESS) {
+	    app_perror("...recv error", rc);
+	    rc = -170; goto on_error;
+	}
+        if (received <= 0) {
+            PJ_LOG(3,("", "...error: socket has closed! (received=%d)",
+                      received));
+            rc = -173; goto on_error;
+        }
+	if (received != BIG_DATA_LEN-total_received) {
+            if (sock_type != pj_SOCK_STREAM()) {
+	        PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+                          BIG_DATA_LEN-total_received, received));
+	        rc = -176; goto on_error;
+            }
+	}
+        total_received += received;
+    } while (total_received < BIG_DATA_LEN);
+
+    TRACE_(("test", "....memcmp()"));
+    if (pj_memcmp(bigdata, bigbuffer, BIG_DATA_LEN) != 0) {
+        PJ_LOG(3,("", "...error: received data has been altered!"));
+	rc = -180; goto on_error;
+    }
+    
+    rc = 0;
+
+on_error:
+    return rc;
+}
+
+static int udp_test(void)
+{
+    pj_sock_t cs = PJ_INVALID_SOCKET, ss = PJ_INVALID_SOCKET;
+    pj_sockaddr_in dstaddr, srcaddr;
+    pj_str_t s;
+    pj_status_t rc = 0, retval;
+
+    PJ_LOG(3,("test", "...udp_test()"));
+
+    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &ss);
+    if (rc != 0) {
+	app_perror("...error: unable to create socket", rc);
+	return -100;
+    }
+
+    rc = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &cs);
+    if (rc != 0)
+	return -110;
+
+    /* Bind server socket. */
+    pj_bzero(&dstaddr, sizeof(dstaddr));
+    dstaddr.sin_family = pj_AF_INET();
+    dstaddr.sin_port = pj_htons(UDP_PORT);
+    dstaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS));
+    
+    if ((rc=pj_sock_bind(ss, &dstaddr, sizeof(dstaddr))) != 0) {
+	app_perror("...bind error udp:"ADDRESS, rc);
+	rc = -120; goto on_error;
+    }
+
+    /* Bind client socket. */
+    pj_bzero(&srcaddr, sizeof(srcaddr));
+    srcaddr.sin_family = pj_AF_INET();
+    srcaddr.sin_port = pj_htons(UDP_PORT-1);
+    srcaddr.sin_addr = pj_inet_addr(pj_cstr(&s, ADDRESS));
+
+    if ((rc=pj_sock_bind(cs, &srcaddr, sizeof(srcaddr))) != 0) {
+	app_perror("...bind error", rc);
+	rc = -121; goto on_error;
+    }
+	    
+    /* Test send/recv, with sendto */
+    rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, &dstaddr, NULL, 
+                        sizeof(dstaddr));
+    if (rc != 0)
+	goto on_error;
+
+    /* Test send/recv, with sendto and recvfrom */
+    rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, &dstaddr, 
+                        &srcaddr, sizeof(dstaddr));
+    if (rc != 0)
+	goto on_error;
+
+    /* Disable this test on Symbian since UDP connect()/send() failed
+     * with S60 3rd edition (including MR2).
+     * See http://www.pjsip.org/trac/ticket/264
+     */    
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+    /* connect() the sockets. */
+    rc = pj_sock_connect(cs, &dstaddr, sizeof(dstaddr));
+    if (rc != 0) {
+	app_perror("...connect() error", rc);
+	rc = -122; goto on_error;
+    }
+
+    /* Test send/recv with send() */
+    rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, NULL, NULL, 0);
+    if (rc != 0)
+	goto on_error;
+
+    /* Test send/recv with send() and recvfrom */
+    rc = send_recv_test(pj_SOCK_DGRAM(), ss, cs, NULL, &srcaddr, 
+                        sizeof(srcaddr));
+    if (rc != 0)
+	goto on_error;
+#endif
+
+on_error:
+    retval = rc;
+    if (cs != PJ_INVALID_SOCKET) {
+        rc = pj_sock_close(cs);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error in closing socket", rc);
+            return -1000;
+        }
+    }
+    if (ss != PJ_INVALID_SOCKET) {
+        rc = pj_sock_close(ss);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...error in closing socket", rc);
+            return -1010;
+        }
+    }
+
+    return retval;
+}
+
+static int tcp_test(void)
+{
+    pj_sock_t cs, ss;
+    pj_status_t rc = 0, retval;
+
+    PJ_LOG(3,("test", "...tcp_test()"));
+
+    rc = app_socketpair(pj_AF_INET(), pj_SOCK_STREAM(), 0, &ss, &cs);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error: app_socketpair():", rc);
+        return -2000;
+    }
+
+    /* Test send/recv with send() and recv() */
+    retval = send_recv_test(pj_SOCK_STREAM(), ss, cs, NULL, NULL, 0);
+
+    rc = pj_sock_close(cs);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error in closing socket", rc);
+        return -2000;
+    }
+
+    rc = pj_sock_close(ss);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error in closing socket", rc);
+        return -2010;
+    }
+
+    return retval;
+}
+
+static int ioctl_test(void)
+{
+    return 0;
+}
+
+static int gethostbyname_test(void)
+{
+    pj_str_t host;
+    pj_hostent he;
+    pj_status_t status;
+
+    /* Testing pj_gethostbyname() with invalid host */
+    host = pj_str("an-invalid-host-name");
+    status = pj_gethostbyname(&host, &he);
+
+    /* Must return failure! */
+    if (status == PJ_SUCCESS)
+	return -20100;
+    else
+	return 0;
+}
+
+#if 0
+#include "../pj/os_symbian.h"
+static int connect_test()
+{
+	RSocketServ rSockServ;
+	RSocket rSock;
+	TInetAddr inetAddr;
+	TRequestStatus reqStatus;
+	char buffer[16];
+	TPtrC8 data((const TUint8*)buffer, (TInt)sizeof(buffer));
+ 	int rc;
+	
+	rc = rSockServ.Connect();
+	if (rc != KErrNone)
+		return rc;
+	
+	rc = rSock.Open(rSockServ, KAfInet, KSockDatagram, KProtocolInetUdp);
+    	if (rc != KErrNone) 
+    	{    		
+    		rSockServ.Close();
+    		return rc;
+    	}
+   	
+    	inetAddr.Init(KAfInet);
+    	inetAddr.Input(_L("127.0.0.1"));
+    	inetAddr.SetPort(80);
+    	
+    	rSock.Connect(inetAddr, reqStatus);
+    	User::WaitForRequest(reqStatus);
+
+    	if (reqStatus != KErrNone) {
+		rSock.Close();
+    		rSockServ.Close();
+		return rc;
+    	}
+    
+    	rSock.Send(data, 0, reqStatus);
+    	User::WaitForRequest(reqStatus);
+    	
+    	if (reqStatus!=KErrNone) {
+    		rSock.Close();
+    		rSockServ.Close();
+    		return rc;
+    	}
+    	
+    	rSock.Close();
+    	rSockServ.Close();
+	return KErrNone;
+}
+#endif
+
+int sock_test()
+{
+    int rc;
+    
+    pj_create_random_string(bigdata, BIG_DATA_LEN);
+
+// Enable this to demonstrate the error witn S60 3rd Edition MR2
+#if 0
+    rc = connect_test();
+    if (rc != 0)
+    	return rc;
+#endif
+    
+    rc = format_test();
+    if (rc != 0)
+	return rc;
+
+    rc = parse_test();
+    if (rc != 0)
+	return rc;
+
+    rc = purity_test();
+    if (rc != 0)
+	return rc;
+
+    rc = gethostbyname_test();
+    if (rc != 0)
+	return rc;
+
+    rc = simple_sock_test();
+    if (rc != 0)
+	return rc;
+
+    rc = ioctl_test();
+    if (rc != 0)
+	return rc;
+
+    rc = udp_test();
+    if (rc != 0)
+	return rc;
+
+    rc = tcp_test();
+    if (rc != 0)
+	return rc;
+
+    return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_sock_test;
+#endif	/* INCLUDE_SOCK_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/sock_perf.c b/jni/pjproject-android/pjlib/src/pjlib-test/sock_perf.c
new file mode 100644
index 0000000..796d9f1
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/sock_perf.c
@@ -0,0 +1,190 @@
+/* $Id: sock_perf.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+#include <pj/compat/high_precision.h>
+
+
+/**
+ * \page page_pjlib_sock_perf_test Test: Socket Performance
+ *
+ * Test the performance of the socket communication. This will perform
+ * simple producer-consumer type of test, where we calculate how long
+ * does it take to send certain number of packets from producer to
+ * consumer.
+ *
+ * This file is <b>pjlib-test/sock_perf.c</b>
+ *
+ * \include pjlib-test/sock_perf.c
+ */
+
+#if INCLUDE_SOCK_PERF_TEST
+
+/*
+ * sock_producer_consumer()
+ *
+ * Simple producer-consumer benchmarking. Send loop number of
+ * buf_size size packets as fast as possible.
+ */
+static int sock_producer_consumer(int sock_type,
+                                  pj_size_t buf_size,
+                                  unsigned loop, 
+                                  unsigned *p_bandwidth)
+{
+    pj_sock_t consumer, producer;
+    pj_pool_t *pool;
+    char *outgoing_buffer, *incoming_buffer;
+    pj_timestamp start, stop;
+    unsigned i;
+    pj_highprec_t elapsed, bandwidth;
+    pj_highprec_t total_received;
+    pj_status_t rc;
+
+    /* Create pool. */
+    pool = pj_pool_create(mem, NULL, 4096, 4096, NULL);
+    if (!pool)
+        return -10;
+
+    /* Create producer-consumer pair. */
+    rc = app_socketpair(pj_AF_INET(), sock_type, 0, &consumer, &producer);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error: create socket pair", rc);
+        return -20;
+    }
+
+    /* Create buffers. */
+    outgoing_buffer = (char*) pj_pool_alloc(pool, buf_size);
+    incoming_buffer = (char*) pj_pool_alloc(pool, buf_size);
+
+    /* Start loop. */
+    pj_get_timestamp(&start);
+    total_received = 0;
+    for (i=0; i<loop; ++i) {
+        pj_ssize_t sent, part_received, received;
+	pj_time_val delay;
+
+        sent = buf_size;
+        rc = pj_sock_send(producer, outgoing_buffer, &sent, 0);
+        if (rc != PJ_SUCCESS || sent != (pj_ssize_t)buf_size) {
+            app_perror("...error: send()", rc);
+            return -61;
+        }
+
+        /* Repeat recv() until all data is part_received.
+         * This applies only for non-UDP of course, since for UDP
+         * we would expect all data to be part_received in one packet.
+         */
+        received = 0;
+        do {
+            part_received = buf_size-received;
+	    rc = pj_sock_recv(consumer, incoming_buffer+received, 
+			      &part_received, 0);
+	    if (rc != PJ_SUCCESS) {
+	        app_perror("...recv error", rc);
+	        return -70;
+	    }
+            if (part_received <= 0) {
+                PJ_LOG(3,("", "...error: socket has closed (part_received=%d)!",
+                          part_received));
+                return -73;
+            }
+	    if ((pj_size_t)part_received != buf_size-received) {
+                if (sock_type != pj_SOCK_STREAM()) {
+	            PJ_LOG(3,("", "...error: expecting %u bytes, got %u bytes",
+                              buf_size-received, part_received));
+	            return -76;
+                }
+	    }
+            received += part_received;
+        } while ((pj_size_t)received < buf_size);
+
+	total_received += received;
+
+	/* Stop test if it's been runnign for more than 10 secs. */
+	pj_get_timestamp(&stop);
+	delay = pj_elapsed_time(&start, &stop);
+	if (delay.sec > 10)
+	    break;
+    }
+
+    /* Stop timer. */
+    pj_get_timestamp(&stop);
+
+    elapsed = pj_elapsed_usec(&start, &stop);
+
+    /* bandwidth = total_received * 1000 / elapsed */
+    bandwidth = total_received;
+    pj_highprec_mul(bandwidth, 1000);
+    pj_highprec_div(bandwidth, elapsed);
+    
+    *p_bandwidth = (pj_uint32_t)bandwidth;
+
+    /* Close sockets. */
+    pj_sock_close(consumer);
+    pj_sock_close(producer);
+
+    /* Done */
+    pj_pool_release(pool);
+
+    return 0;
+}
+
+/*
+ * sock_perf_test()
+ *
+ * Main test entry.
+ */
+int sock_perf_test(void)
+{
+    enum { LOOP = 64 * 1024 };
+    int rc;
+    unsigned bandwidth;
+
+    PJ_LOG(3,("", "...benchmarking socket "
+                  "(2 sockets, packet=512, single threaded):"));
+
+    /* Disable this test on Symbian since UDP connect()/send() failed
+     * with S60 3rd edition (including MR2).
+     * See http://www.pjsip.org/trac/ticket/264
+     */    
+#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0
+    /* Benchmarking UDP */
+    rc = sock_producer_consumer(pj_SOCK_DGRAM(), 512, LOOP, &bandwidth);
+    if (rc != 0) return rc;
+    PJ_LOG(3,("", "....bandwidth UDP = %d KB/s", bandwidth));
+#endif
+
+    /* Benchmarking TCP */
+    rc = sock_producer_consumer(pj_SOCK_STREAM(), 512, LOOP, &bandwidth);
+    if (rc != 0) return rc;
+    PJ_LOG(3,("", "....bandwidth TCP = %d KB/s", bandwidth));
+
+    return rc;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_sock_perf_test;
+#endif  /* INCLUDE_SOCK_PERF_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/ssl_sock.c b/jni/pjproject-android/pjlib/src/pjlib-test/ssl_sock.c
new file mode 100644
index 0000000..0bf89b7
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/ssl_sock.c
@@ -0,0 +1,1448 @@
+/* $Id: ssl_sock.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+
+#define CERT_DIR		    "../build/"
+#define CERT_CA_FILE		    CERT_DIR "cacert.pem"
+#define CERT_FILE		    CERT_DIR "cacert.pem"
+#define CERT_PRIVKEY_FILE	    CERT_DIR "privkey.pem"
+#define CERT_PRIVKEY_PASS	    ""
+
+
+#if INCLUDE_SSLSOCK_TEST
+
+/* Global vars */
+static int clients_num;
+
+struct send_key {
+    pj_ioqueue_op_key_t	op_key;
+};
+
+
+static int get_cipher_list(void) {
+    pj_status_t status;
+    pj_ssl_cipher ciphers[100];
+    unsigned cipher_num;
+    unsigned i;
+
+    cipher_num = PJ_ARRAY_SIZE(ciphers);
+    status = pj_ssl_cipher_get_availables(ciphers, &cipher_num);
+    if (status != PJ_SUCCESS) {
+	app_perror("...FAILED to get available ciphers", status);
+	return status;
+    }
+
+    PJ_LOG(3, ("", "...Found %u ciphers:", cipher_num));
+    for (i = 0; i < cipher_num; ++i) {
+	const char* st;
+	st = pj_ssl_cipher_name(ciphers[i]);
+	if (st == NULL)
+	    st = "[Unknown]";
+
+	PJ_LOG(3, ("", "...%3u: 0x%08x=%s", i+1, ciphers[i], st));
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+struct test_state
+{
+    pj_pool_t	   *pool;	    /* pool				    */
+    pj_ioqueue_t   *ioqueue;	    /* ioqueue				    */
+    pj_bool_t	    is_server;	    /* server role flag			    */
+    pj_bool_t	    is_verbose;	    /* verbose flag, e.g: cert info	    */
+    pj_bool_t	    echo;	    /* echo received data		    */
+    pj_status_t	    err;	    /* error flag			    */
+    pj_size_t	    sent;	    /* bytes sent			    */
+    pj_size_t	    recv;	    /* bytes received			    */
+    pj_uint8_t	    read_buf[256];  /* read buffer			    */
+    pj_bool_t	    done;	    /* test done flag			    */
+    char	   *send_str;	    /* data to send once connected	    */
+    pj_size_t	    send_str_len;   /* send data length			    */
+    pj_bool_t	    check_echo;	    /* flag to compare sent & echoed data   */
+    const char	   *check_echo_ptr; /* pointer/cursor for comparing data    */
+    struct send_key send_key;	    /* send op key			    */
+};
+
+static void dump_ssl_info(const pj_ssl_sock_info *si)
+{
+    const char *tmp_st;
+
+    /* Print cipher name */
+    tmp_st = pj_ssl_cipher_name(si->cipher);
+    if (tmp_st == NULL)
+	tmp_st = "[Unknown]";
+    PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
+
+    /* Print remote certificate info and verification result */
+    if (si->remote_cert_info && si->remote_cert_info->subject.info.slen) 
+    {
+	char buf[2048];
+	const char *verif_msgs[32];
+	unsigned verif_msg_cnt;
+
+	/* Dump remote TLS certificate info */
+	PJ_LOG(3, ("", ".....Remote certificate info:"));
+	pj_ssl_cert_info_dump(si->remote_cert_info, "  ", buf, sizeof(buf));
+	PJ_LOG(3,("", "\n%s", buf));
+
+	/* Dump remote TLS certificate verification result */
+	verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
+	pj_ssl_cert_get_verify_status_strings(si->verify_status,
+					      verif_msgs, &verif_msg_cnt);
+	PJ_LOG(3,("", ".....Remote certificate verification result: %s",
+		  (verif_msg_cnt == 1? verif_msgs[0]:"")));
+	if (verif_msg_cnt > 1) {
+	    unsigned i;
+	    for (i = 0; i < verif_msg_cnt; ++i)
+		PJ_LOG(3,("", "..... - %s", verif_msgs[i]));
+	}
+    }
+}
+
+
+static pj_bool_t ssl_on_connect_complete(pj_ssl_sock_t *ssock,
+					 pj_status_t status)
+{
+    struct test_state *st = (struct test_state*) 
+		    	    pj_ssl_sock_get_user_data(ssock);
+    void *read_buf[1];
+    pj_ssl_sock_info info;
+    char buf1[64], buf2[64];
+
+    if (status != PJ_SUCCESS) {
+	app_perror("...ERROR ssl_on_connect_complete()", status);
+	goto on_return;
+    }
+
+    status = pj_ssl_sock_get_info(ssock, &info);
+    if (status != PJ_SUCCESS) {
+	app_perror("...ERROR pj_ssl_sock_get_info()", status);
+	goto on_return;
+    }
+
+    pj_sockaddr_print((pj_sockaddr_t*)&info.local_addr, buf1, sizeof(buf1), 1);
+    pj_sockaddr_print((pj_sockaddr_t*)&info.remote_addr, buf2, sizeof(buf2), 1);
+    PJ_LOG(3, ("", "...Connected %s -> %s!", buf1, buf2));
+
+    if (st->is_verbose)
+	dump_ssl_info(&info);
+
+    /* Start reading data */
+    read_buf[0] = st->read_buf;
+    status = pj_ssl_sock_start_read2(ssock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
+    if (status != PJ_SUCCESS) {
+	app_perror("...ERROR pj_ssl_sock_start_read2()", status);
+	goto on_return;
+    }
+
+    /* Start sending data */
+    while (st->sent < st->send_str_len) {
+	pj_ssize_t size;
+
+	size = st->send_str_len - st->sent;
+	status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, 
+				  st->send_str + st->sent, &size, 0);
+	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	    app_perror("...ERROR pj_ssl_sock_send()", status);
+	    goto on_return;
+	}
+
+	if (status == PJ_SUCCESS)
+	    st->sent += size;
+	else
+	    break;
+    }
+
+on_return:
+    st->err = status;
+
+    if (st->err != PJ_SUCCESS) {
+	pj_ssl_sock_close(ssock);
+	clients_num--;
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+
+static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock,
+					pj_ssl_sock_t *newsock,
+					const pj_sockaddr_t *src_addr,
+					int src_addr_len)
+{
+    struct test_state *parent_st = (struct test_state*) 
+				   pj_ssl_sock_get_user_data(ssock);
+    struct test_state *st;
+    void *read_buf[1];
+    pj_ssl_sock_info info;
+    char buf[64];
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(src_addr_len);
+
+    /* Duplicate parent test state to newly accepted test state */
+    st = (struct test_state*)pj_pool_zalloc(parent_st->pool, sizeof(struct test_state));
+    *st = *parent_st;
+    pj_ssl_sock_set_user_data(newsock, st);
+
+    status = pj_ssl_sock_get_info(newsock, &info);
+    if (status != PJ_SUCCESS) {
+	app_perror("...ERROR pj_ssl_sock_get_info()", status);
+	goto on_return;
+    }
+
+    pj_sockaddr_print(src_addr, buf, sizeof(buf), 1);
+    PJ_LOG(3, ("", "...Accepted connection from %s", buf));
+
+    if (st->is_verbose)
+	dump_ssl_info(&info);
+
+    /* Start reading data */
+    read_buf[0] = st->read_buf;
+    status = pj_ssl_sock_start_read2(newsock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
+    if (status != PJ_SUCCESS) {
+	app_perror("...ERROR pj_ssl_sock_start_read2()", status);
+	goto on_return;
+    }
+
+    /* Start sending data */
+    while (st->sent < st->send_str_len) {
+	pj_ssize_t size;
+
+	size = st->send_str_len - st->sent;
+	status = pj_ssl_sock_send(newsock, (pj_ioqueue_op_key_t*)&st->send_key, 
+				  st->send_str + st->sent, &size, 0);
+	if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	    app_perror("...ERROR pj_ssl_sock_send()", status);
+	    goto on_return;
+	}
+
+	if (status == PJ_SUCCESS)
+	    st->sent += size;
+	else
+	    break;
+    }
+
+on_return:
+    st->err = status;
+
+    if (st->err != PJ_SUCCESS) {
+	pj_ssl_sock_close(newsock);
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t ssl_on_data_read(pj_ssl_sock_t *ssock,
+				  void *data,
+				  pj_size_t size,
+				  pj_status_t status,
+				  pj_size_t *remainder)
+{
+    struct test_state *st = (struct test_state*) 
+			     pj_ssl_sock_get_user_data(ssock);
+
+    PJ_UNUSED_ARG(remainder);
+    PJ_UNUSED_ARG(data);
+
+    if (size > 0) {
+	pj_size_t consumed;
+
+	/* Set random remainder */
+	*remainder = pj_rand() % 100;
+
+	/* Apply zero remainder if:
+	 * - remainder is less than size, or
+	 * - connection closed/error
+	 * - echo/check_eco set
+	 */
+	if (*remainder > size || status != PJ_SUCCESS || st->echo || st->check_echo)
+	    *remainder = 0;
+
+	consumed = size - *remainder;
+	st->recv += consumed;
+
+	//printf("%.*s", consumed, (char*)data);
+
+	pj_memmove(data, (char*)data + consumed, *remainder);
+
+	/* Echo data when specified to */
+	if (st->echo) {
+	    pj_ssize_t size_ = consumed;
+	    status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, data, &size_, 0);
+	    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+		app_perror("...ERROR pj_ssl_sock_send()", status);
+		goto on_return;
+	    }
+
+	    if (status == PJ_SUCCESS)
+		st->sent += size_;
+	}
+
+	/* Verify echoed data when specified to */
+	if (st->check_echo) {
+	    if (!st->check_echo_ptr)
+		st->check_echo_ptr = st->send_str;
+
+	    if (pj_memcmp(st->check_echo_ptr, data, consumed)) {
+		status = PJ_EINVAL;
+		app_perror("...ERROR echoed data not exact", status);
+		goto on_return;
+	    }
+	    st->check_echo_ptr += consumed;
+
+	    /* Echo received completely */
+	    if (st->send_str_len == st->recv) {
+		pj_ssl_sock_info info;
+		char buf[64];
+
+		status = pj_ssl_sock_get_info(ssock, &info);
+		if (status != PJ_SUCCESS) {
+		    app_perror("...ERROR pj_ssl_sock_get_info()", status);
+		    goto on_return;
+		}
+
+		pj_sockaddr_print((pj_sockaddr_t*)&info.local_addr, buf, sizeof(buf), 1);
+		PJ_LOG(3, ("", "...%s successfully recv %d bytes echo", buf, st->recv));
+		st->done = PJ_TRUE;
+	    }
+	}
+    }
+
+    if (status != PJ_SUCCESS) {
+	if (status == PJ_EEOF) {
+	    status = PJ_SUCCESS;
+	    st->done = PJ_TRUE;
+	} else {
+	    app_perror("...ERROR ssl_on_data_read()", status);
+	}
+    }
+
+on_return:
+    st->err = status;
+
+    if (st->err != PJ_SUCCESS || st->done) {
+	pj_ssl_sock_close(ssock);
+	if (!st->is_server)
+	    clients_num--;
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t ssl_on_data_sent(pj_ssl_sock_t *ssock,
+				  pj_ioqueue_op_key_t *op_key,
+				  pj_ssize_t sent)
+{
+    struct test_state *st = (struct test_state*)
+			     pj_ssl_sock_get_user_data(ssock);
+    PJ_UNUSED_ARG(op_key);
+
+    if (sent < 0) {
+	st->err = (pj_status_t)-sent;
+    } else {
+	st->sent += sent;
+
+	/* Send more if any */
+	while (st->sent < st->send_str_len) {
+	    pj_ssize_t size;
+	    pj_status_t status;
+
+	    size = st->send_str_len - st->sent;
+	    status = pj_ssl_sock_send(ssock, (pj_ioqueue_op_key_t*)&st->send_key, 
+				      st->send_str + st->sent, &size, 0);
+	    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+		app_perror("...ERROR pj_ssl_sock_send()", status);
+		st->err = status;
+		break;
+	    }
+
+	    if (status == PJ_SUCCESS)
+		st->sent += size;
+	    else
+		break;
+	}
+    }
+
+    if (st->err != PJ_SUCCESS) {
+	pj_ssl_sock_close(ssock);
+	if (!st->is_server)
+	    clients_num--;
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+#define HTTP_REQ		"GET / HTTP/1.0\r\n\r\n";
+#define HTTP_SERVER_ADDR	"trac.pjsip.org"
+#define HTTP_SERVER_PORT	443
+
+static int https_client_test(unsigned ms_timeout)
+{
+    pj_pool_t *pool = NULL;
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_timer_heap_t *timer = NULL;
+    pj_ssl_sock_t *ssock = NULL;
+    pj_ssl_sock_param param;
+    pj_status_t status;
+    struct test_state state = {0};
+    pj_sockaddr local_addr, rem_addr;
+    pj_str_t tmp_st;
+
+    pool = pj_pool_create(mem, "https_get", 256, 256, NULL);
+
+    status = pj_ioqueue_create(pool, 4, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_timer_heap_create(pool, 4, &timer);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    state.pool = pool;
+    state.send_str = HTTP_REQ;
+    state.send_str_len = pj_ansi_strlen(state.send_str);
+    state.is_verbose = PJ_TRUE;
+
+    pj_ssl_sock_param_default(&param);
+    param.cb.on_connect_complete = &ssl_on_connect_complete;
+    param.cb.on_data_read = &ssl_on_data_read;
+    param.cb.on_data_sent = &ssl_on_data_sent;
+    param.ioqueue = ioqueue;
+    param.user_data = &state;
+    param.server_name = pj_str((char*)HTTP_SERVER_ADDR);
+    param.timer_heap = timer;
+    param.timeout.sec = 0;
+    param.timeout.msec = ms_timeout;
+    param.proto = PJ_SSL_SOCK_PROTO_SSL23;
+    pj_time_val_normalize(&param.timeout);
+
+    status = pj_ssl_sock_create(pool, &param, &ssock);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    pj_sockaddr_init(PJ_AF_INET, &local_addr, pj_strset2(&tmp_st, "0.0.0.0"), 0);
+    pj_sockaddr_init(PJ_AF_INET, &rem_addr, pj_strset2(&tmp_st, HTTP_SERVER_ADDR), HTTP_SERVER_PORT);
+    status = pj_ssl_sock_start_connect(ssock, pool, &local_addr, &rem_addr, sizeof(rem_addr));
+    if (status == PJ_SUCCESS) {
+	ssl_on_connect_complete(ssock, PJ_SUCCESS);
+    } else if (status == PJ_EPENDING) {
+	status = PJ_SUCCESS;
+    } else {
+	goto on_return;
+    }
+
+    /* Wait until everything has been sent/received */
+    while (state.err == PJ_SUCCESS && !state.done) {
+#ifdef PJ_SYMBIAN
+	pj_symbianos_poll(-1, 1000);
+#else
+	pj_time_val delay = {0, 100};
+	pj_ioqueue_poll(ioqueue, &delay);
+	pj_timer_heap_poll(timer, &delay);
+#endif
+    }
+
+    if (state.err) {
+	status = state.err;
+	goto on_return;
+    }
+
+    PJ_LOG(3, ("", "...Done!"));
+    PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state.sent, state.recv));
+
+on_return:
+    if (ssock && !state.err && !state.done) 
+	pj_ssl_sock_close(ssock);
+    if (ioqueue)
+	pj_ioqueue_destroy(ioqueue);
+    if (timer)
+	pj_timer_heap_destroy(timer);
+    if (pool)
+	pj_pool_release(pool);
+
+    return status;
+}
+
+
+static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
+		     pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher,
+		     pj_bool_t req_client_cert, pj_bool_t client_provide_cert)
+{
+    pj_pool_t *pool = NULL;
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_ssl_sock_t *ssock_serv = NULL;
+    pj_ssl_sock_t *ssock_cli = NULL;
+    pj_ssl_sock_param param;
+    struct test_state state_serv = { 0 };
+    struct test_state state_cli = { 0 };
+    pj_sockaddr addr, listen_addr;
+    pj_ssl_cipher ciphers[1];
+    pj_ssl_cert_t *cert = NULL;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "ssl_echo", 256, 256, NULL);
+
+    status = pj_ioqueue_create(pool, 4, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    pj_ssl_sock_param_default(&param);
+    param.cb.on_accept_complete = &ssl_on_accept_complete;
+    param.cb.on_connect_complete = &ssl_on_connect_complete;
+    param.cb.on_data_read = &ssl_on_data_read;
+    param.cb.on_data_sent = &ssl_on_data_sent;
+    param.ioqueue = ioqueue;
+    param.ciphers = ciphers;
+
+    /* Init default bind address */
+    {
+	pj_str_t tmp_st;
+	pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+    }
+
+    /* === SERVER === */
+    param.proto = srv_proto;
+    param.user_data = &state_serv;
+    param.ciphers_num = (srv_cipher == -1)? 0 : 1;
+    param.require_client_cert = req_client_cert;
+    ciphers[0] = srv_cipher;
+
+    state_serv.pool = pool;
+    state_serv.echo = PJ_TRUE;
+    state_serv.is_server = PJ_TRUE;
+    state_serv.is_verbose = PJ_TRUE;
+
+    status = pj_ssl_sock_create(pool, &param, &ssock_serv);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Set server cert */
+    {
+	pj_str_t tmp1, tmp2, tmp3, tmp4;
+
+	status = pj_ssl_cert_load_from_files(pool, 
+					     pj_strset2(&tmp1, (char*)CERT_CA_FILE), 
+					     pj_strset2(&tmp2, (char*)CERT_FILE), 
+					     pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), 
+					     pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), 
+					     &cert);
+	if (status != PJ_SUCCESS) {
+	    goto on_return;
+	}
+
+	status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
+	if (status != PJ_SUCCESS) {
+	    goto on_return;
+	}
+    }
+
+    status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Get listener address */
+    {
+	pj_ssl_sock_info info;
+
+	pj_ssl_sock_get_info(ssock_serv, &info);
+	pj_sockaddr_cp(&listen_addr, &info.local_addr);
+    }
+
+    /* === CLIENT === */
+    param.proto = cli_proto;
+    param.user_data = &state_cli;
+    param.ciphers_num = (cli_cipher == -1)? 0 : 1;
+    ciphers[0] = cli_cipher;
+
+    state_cli.pool = pool;
+    state_cli.check_echo = PJ_TRUE;
+    state_cli.is_verbose = PJ_TRUE;
+
+    {
+	pj_time_val now;
+
+	pj_gettimeofday(&now);
+	pj_srand((unsigned)now.sec);
+	state_cli.send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024;
+    }
+    state_cli.send_str = (char*)pj_pool_alloc(pool, state_cli.send_str_len);
+    {
+	unsigned i;
+	for (i = 0; i < state_cli.send_str_len; ++i)
+	    state_cli.send_str[i] = (char)(pj_rand() % 256);
+    }
+
+    status = pj_ssl_sock_create(pool, &param, &ssock_cli);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Set cert for client */
+    {
+
+	if (!client_provide_cert) {
+	    pj_str_t tmp1, tmp2;
+
+	    pj_strset2(&tmp1, (char*)CERT_CA_FILE);
+	    pj_strset2(&tmp2, NULL);
+	    status = pj_ssl_cert_load_from_files(pool, 
+						 &tmp1, &tmp2, &tmp2, &tmp2,
+						 &cert);
+	    if (status != PJ_SUCCESS) {
+		goto on_return;
+	    }
+	}
+
+	status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert);
+	if (status != PJ_SUCCESS) {
+	    goto on_return;
+	}
+    }
+
+    status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
+    if (status == PJ_SUCCESS) {
+	ssl_on_connect_complete(ssock_cli, PJ_SUCCESS);
+    } else if (status == PJ_EPENDING) {
+	status = PJ_SUCCESS;
+    } else {
+	goto on_return;
+    }
+
+    /* Wait until everything has been sent/received or error */
+    while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done)
+    {
+#ifdef PJ_SYMBIAN
+	pj_symbianos_poll(-1, 1000);
+#else
+	pj_time_val delay = {0, 100};
+	pj_ioqueue_poll(ioqueue, &delay);
+#endif
+    }
+
+    /* Clean up sockets */
+    {
+	pj_time_val delay = {0, 100};
+	while (pj_ioqueue_poll(ioqueue, &delay) > 0);
+    }
+
+    if (state_serv.err || state_cli.err) {
+	if (state_serv.err != PJ_SUCCESS)
+	    status = state_serv.err;
+	else
+	    status = state_cli.err;
+
+	goto on_return;
+    }
+
+    PJ_LOG(3, ("", "...Done!"));
+    PJ_LOG(3, ("", ".....Sent/recv: %d/%d bytes", state_cli.sent, state_cli.recv));
+
+on_return:
+    if (ssock_serv)
+	pj_ssl_sock_close(ssock_serv);
+    if (ssock_cli && !state_cli.err && !state_cli.done) 
+	pj_ssl_sock_close(ssock_cli);
+    if (ioqueue)
+	pj_ioqueue_destroy(ioqueue);
+    if (pool)
+	pj_pool_release(pool);
+
+    return status;
+}
+
+
+static pj_bool_t asock_on_data_read(pj_activesock_t *asock,
+				    void *data,
+				    pj_size_t size,
+				    pj_status_t status,
+				    pj_size_t *remainder)
+{
+    struct test_state *st = (struct test_state*)
+			     pj_activesock_get_user_data(asock);
+
+    PJ_UNUSED_ARG(data);
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(remainder);
+
+    if (status != PJ_SUCCESS) {
+	if (status == PJ_EEOF) {
+	    status = PJ_SUCCESS;
+	    st->done = PJ_TRUE;
+	} else {
+	    app_perror("...ERROR asock_on_data_read()", status);
+	}
+    }
+
+    st->err = status;
+
+    if (st->err != PJ_SUCCESS || st->done) {
+	pj_activesock_close(asock);
+	if (!st->is_server)
+	    clients_num--;
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+
+static pj_bool_t asock_on_connect_complete(pj_activesock_t *asock,
+					   pj_status_t status)
+{
+    struct test_state *st = (struct test_state*)
+			     pj_activesock_get_user_data(asock);
+
+    if (status == PJ_SUCCESS) {
+	void *read_buf[1];
+
+	/* Start reading data */
+	read_buf[0] = st->read_buf;
+	status = pj_activesock_start_read2(asock, st->pool, sizeof(st->read_buf), (void**)read_buf, 0);
+	if (status != PJ_SUCCESS) {
+	    app_perror("...ERROR pj_ssl_sock_start_read2()", status);
+	}
+    }
+
+    st->err = status;
+
+    if (st->err != PJ_SUCCESS) {
+	pj_activesock_close(asock);
+	if (!st->is_server)
+	    clients_num--;
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t asock_on_accept_complete(pj_activesock_t *asock,
+					  pj_sock_t newsock,
+					  const pj_sockaddr_t *src_addr,
+					  int src_addr_len)
+{
+    struct test_state *st;
+    void *read_buf[1];
+    pj_activesock_t *new_asock;
+    pj_activesock_cb asock_cb = { 0 };
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(src_addr_len);
+
+    st = (struct test_state*) pj_activesock_get_user_data(asock);
+
+    asock_cb.on_data_read = &asock_on_data_read;
+    status = pj_activesock_create(st->pool, newsock, pj_SOCK_STREAM(), NULL, 
+				  st->ioqueue, &asock_cb, st, &new_asock);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Start reading data */
+    read_buf[0] = st->read_buf;
+    status = pj_activesock_start_read2(new_asock, st->pool, 
+				       sizeof(st->read_buf), 
+				       (void**)read_buf, 0);
+    if (status != PJ_SUCCESS) {
+	app_perror("...ERROR pj_ssl_sock_start_read2()", status);
+    }
+
+on_return:
+    st->err = status;
+
+    if (st->err != PJ_SUCCESS)
+	pj_activesock_close(new_asock);
+
+    return PJ_TRUE;
+}
+
+
+/* Raw TCP socket try to connect to SSL socket server, once
+ * connection established, it will just do nothing, SSL socket
+ * server should be able to close the connection after specified
+ * timeout period (set ms_timeout to 0 to disable timer).
+ */
+static int client_non_ssl(unsigned ms_timeout)
+{
+    pj_pool_t *pool = NULL;
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_timer_heap_t *timer = NULL;
+    pj_ssl_sock_t *ssock_serv = NULL;
+    pj_activesock_t *asock_cli = NULL;
+    pj_activesock_cb asock_cb = { 0 };
+    pj_sock_t sock = PJ_INVALID_SOCKET;
+    pj_ssl_sock_param param;
+    struct test_state state_serv = { 0 };
+    struct test_state state_cli = { 0 };
+    pj_sockaddr listen_addr;
+    pj_ssl_cert_t *cert = NULL;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "ssl_accept_raw_tcp", 256, 256, NULL);
+
+    status = pj_ioqueue_create(pool, 4, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_timer_heap_create(pool, 4, &timer);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Set cert */
+    {
+	pj_str_t tmp1, tmp2, tmp3, tmp4;
+	status = pj_ssl_cert_load_from_files(pool, 
+					     pj_strset2(&tmp1, (char*)CERT_CA_FILE), 
+					     pj_strset2(&tmp2, (char*)CERT_FILE), 
+					     pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), 
+					     pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), 
+					     &cert);
+	if (status != PJ_SUCCESS) {
+	    goto on_return;
+	}
+    }
+
+    pj_ssl_sock_param_default(&param);
+    param.cb.on_accept_complete = &ssl_on_accept_complete;
+    param.cb.on_data_read = &ssl_on_data_read;
+    param.cb.on_data_sent = &ssl_on_data_sent;
+    param.ioqueue = ioqueue;
+    param.timer_heap = timer;
+    param.timeout.sec = 0;
+    param.timeout.msec = ms_timeout;
+    pj_time_val_normalize(&param.timeout);
+
+    /* SERVER */
+    param.user_data = &state_serv;
+    state_serv.pool = pool;
+    state_serv.is_server = PJ_TRUE;
+    state_serv.is_verbose = PJ_TRUE;
+
+    status = pj_ssl_sock_create(pool, &param, &ssock_serv);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Init bind address */
+    {
+	pj_str_t tmp_st;
+	pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+    }
+
+    status = pj_ssl_sock_start_accept(ssock_serv, pool, &listen_addr, pj_sockaddr_get_len(&listen_addr));
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Update listener address */
+    {
+	pj_ssl_sock_info info;
+
+	pj_ssl_sock_get_info(ssock_serv, &info);
+	pj_sockaddr_cp(&listen_addr, &info.local_addr);
+    }
+
+    /* CLIENT */
+    state_cli.pool = pool;
+    status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    asock_cb.on_connect_complete = &asock_on_connect_complete;
+    asock_cb.on_data_read = &asock_on_data_read;
+    status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL, 
+				  ioqueue, &asock_cb, &state_cli, &asock_cli);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_activesock_start_connect(asock_cli, pool, (pj_sockaddr_t*)&listen_addr, 
+					 pj_sockaddr_get_len(&listen_addr));
+    if (status == PJ_SUCCESS) {
+	asock_on_connect_complete(asock_cli, PJ_SUCCESS);
+    } else if (status == PJ_EPENDING) {
+	status = PJ_SUCCESS;
+    } else {
+	goto on_return;
+    }
+
+    /* Wait until everything has been sent/received or error */
+    while (!state_serv.err && !state_cli.err && !state_serv.done && !state_cli.done)
+    {
+#ifdef PJ_SYMBIAN
+	pj_symbianos_poll(-1, 1000);
+#else
+	pj_time_val delay = {0, 100};
+	pj_ioqueue_poll(ioqueue, &delay);
+	pj_timer_heap_poll(timer, &delay);
+#endif
+    }
+
+    if (state_serv.err || state_cli.err) {
+	if (state_serv.err != PJ_SUCCESS)
+	    status = state_serv.err;
+	else
+	    status = state_cli.err;
+
+	goto on_return;
+    }
+
+    PJ_LOG(3, ("", "...Done!"));
+
+on_return:
+    if (ssock_serv)
+	pj_ssl_sock_close(ssock_serv);
+    if (asock_cli && !state_cli.err && !state_cli.done)
+	pj_activesock_close(asock_cli);
+    if (timer)
+	pj_timer_heap_destroy(timer);
+    if (ioqueue)
+	pj_ioqueue_destroy(ioqueue);
+    if (pool)
+	pj_pool_release(pool);
+
+    return status;
+}
+
+
+/* SSL socket try to connect to raw TCP socket server, once
+ * connection established, SSL socket will try to perform SSL
+ * handshake. SSL client socket should be able to close the
+ * connection after specified timeout period (set ms_timeout to 
+ * 0 to disable timer).
+ */
+static int server_non_ssl(unsigned ms_timeout)
+{
+    pj_pool_t *pool = NULL;
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_timer_heap_t *timer = NULL;
+    pj_activesock_t *asock_serv = NULL;
+    pj_ssl_sock_t *ssock_cli = NULL;
+    pj_activesock_cb asock_cb = { 0 };
+    pj_sock_t sock = PJ_INVALID_SOCKET;
+    pj_ssl_sock_param param;
+    struct test_state state_serv = { 0 };
+    struct test_state state_cli = { 0 };
+    pj_sockaddr addr, listen_addr;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "ssl_connect_raw_tcp", 256, 256, NULL);
+
+    status = pj_ioqueue_create(pool, 4, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_timer_heap_create(pool, 4, &timer);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* SERVER */
+    state_serv.pool = pool;
+    state_serv.ioqueue = ioqueue;
+
+    status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Init bind address */
+    {
+	pj_str_t tmp_st;
+	pj_sockaddr_init(PJ_AF_INET, &listen_addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+    }
+
+    status = pj_sock_bind(sock, (pj_sockaddr_t*)&listen_addr, 
+			  pj_sockaddr_get_len((pj_sockaddr_t*)&listen_addr));
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_sock_listen(sock, PJ_SOMAXCONN);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    asock_cb.on_accept_complete = &asock_on_accept_complete;
+    status = pj_activesock_create(pool, sock, pj_SOCK_STREAM(), NULL, 
+				  ioqueue, &asock_cb, &state_serv, &asock_serv);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_activesock_start_accept(asock_serv, pool);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Update listener address */
+    {
+	int addr_len;
+
+	addr_len = sizeof(listen_addr);
+	pj_sock_getsockname(sock, (pj_sockaddr_t*)&listen_addr, &addr_len);
+    }
+
+    /* CLIENT */
+    pj_ssl_sock_param_default(&param);
+    param.cb.on_connect_complete = &ssl_on_connect_complete;
+    param.cb.on_data_read = &ssl_on_data_read;
+    param.cb.on_data_sent = &ssl_on_data_sent;
+    param.ioqueue = ioqueue;
+    param.timer_heap = timer;
+    param.timeout.sec = 0;
+    param.timeout.msec = ms_timeout;
+    pj_time_val_normalize(&param.timeout);
+    param.user_data = &state_cli;
+
+    state_cli.pool = pool;
+    state_cli.is_server = PJ_FALSE;
+    state_cli.is_verbose = PJ_TRUE;
+
+    status = pj_ssl_sock_create(pool, &param, &ssock_cli);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Init default bind address */
+    {
+	pj_str_t tmp_st;
+	pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+    }
+
+    status = pj_ssl_sock_start_connect(ssock_cli, pool, 
+				       (pj_sockaddr_t*)&addr, 
+				       (pj_sockaddr_t*)&listen_addr, 
+				       pj_sockaddr_get_len(&listen_addr));
+    if (status != PJ_EPENDING) {
+	goto on_return;
+    }
+
+    /* Wait until everything has been sent/received or error */
+    while ((!state_serv.err && !state_serv.done) || (!state_cli.err && !state_cli.done))
+    {
+#ifdef PJ_SYMBIAN
+	pj_symbianos_poll(-1, 1000);
+#else
+	pj_time_val delay = {0, 100};
+	pj_ioqueue_poll(ioqueue, &delay);
+	pj_timer_heap_poll(timer, &delay);
+#endif
+    }
+
+    if (state_serv.err || state_cli.err) {
+	if (state_cli.err != PJ_SUCCESS)
+	    status = state_cli.err;
+	else
+	    status = state_serv.err;
+
+	goto on_return;
+    }
+
+    PJ_LOG(3, ("", "...Done!"));
+
+on_return:
+    if (asock_serv)
+	pj_activesock_close(asock_serv);
+    if (ssock_cli && !state_cli.err && !state_cli.done)
+	pj_ssl_sock_close(ssock_cli);
+    if (timer)
+	pj_timer_heap_destroy(timer);
+    if (ioqueue)
+	pj_ioqueue_destroy(ioqueue);
+    if (pool)
+	pj_pool_release(pool);
+
+    return status;
+}
+
+
+/* Test will perform multiple clients trying to connect to single server.
+ * Once SSL connection established, echo test will be performed.
+ */
+static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
+{
+    pj_pool_t *pool = NULL;
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_timer_heap_t *timer = NULL;
+    pj_ssl_sock_t *ssock_serv = NULL;
+    pj_ssl_sock_t **ssock_cli = NULL;
+    pj_ssl_sock_param param;
+    struct test_state state_serv = { 0 };
+    struct test_state *state_cli = NULL;
+    pj_sockaddr addr, listen_addr;
+    pj_ssl_cert_t *cert = NULL;
+    pj_status_t status;
+    unsigned i, cli_err = 0;
+    pj_size_t tot_sent = 0, tot_recv = 0;
+    pj_time_val start;
+
+    pool = pj_pool_create(mem, "ssl_perf", 256, 256, NULL);
+
+    status = pj_ioqueue_create(pool, PJ_IOQUEUE_MAX_HANDLES, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_timer_heap_create(pool, PJ_IOQUEUE_MAX_HANDLES, &timer);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Set cert */
+    {
+	pj_str_t tmp1, tmp2, tmp3, tmp4;
+
+	status = pj_ssl_cert_load_from_files(pool, 
+					     pj_strset2(&tmp1, (char*)CERT_CA_FILE), 
+					     pj_strset2(&tmp2, (char*)CERT_FILE), 
+					     pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE), 
+					     pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS), 
+					     &cert);
+	if (status != PJ_SUCCESS) {
+	    goto on_return;
+	}
+    }
+
+    pj_ssl_sock_param_default(&param);
+    param.cb.on_accept_complete = &ssl_on_accept_complete;
+    param.cb.on_connect_complete = &ssl_on_connect_complete;
+    param.cb.on_data_read = &ssl_on_data_read;
+    param.cb.on_data_sent = &ssl_on_data_sent;
+    param.ioqueue = ioqueue;
+    param.timer_heap = timer;
+    param.timeout.sec = 0;
+    param.timeout.msec = ms_handshake_timeout;
+    pj_time_val_normalize(&param.timeout);
+
+    /* Init default bind address */
+    {
+	pj_str_t tmp_st;
+	pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
+    }
+
+    /* SERVER */
+    param.user_data = &state_serv;
+
+    state_serv.pool = pool;
+    state_serv.echo = PJ_TRUE;
+    state_serv.is_server = PJ_TRUE;
+
+    status = pj_ssl_sock_create(pool, &param, &ssock_serv);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
+    if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Get listening address for clients to connect to */
+    {
+	pj_ssl_sock_info info;
+	char buf[64];
+
+	pj_ssl_sock_get_info(ssock_serv, &info);
+	pj_sockaddr_cp(&listen_addr, &info.local_addr);
+
+	pj_sockaddr_print((pj_sockaddr_t*)&listen_addr, buf, sizeof(buf), 1);
+	PJ_LOG(3, ("", "...Listener ready at %s", buf));
+    }
+
+
+    /* CLIENTS */
+    clients_num = clients;
+    param.timeout.sec = 0;
+    param.timeout.msec = 0;
+
+    /* Init random seed */
+    {
+	pj_time_val now;
+
+	pj_gettimeofday(&now);
+	pj_srand((unsigned)now.sec);
+    }
+
+    /* Allocate SSL socket pointers and test state */
+    ssock_cli = (pj_ssl_sock_t**)pj_pool_calloc(pool, clients, sizeof(pj_ssl_sock_t*));
+    state_cli = (struct test_state*)pj_pool_calloc(pool, clients, sizeof(struct test_state));
+
+    /* Get start timestamp */
+    pj_gettimeofday(&start);
+
+    /* Setup clients */
+    for (i = 0; i < clients; ++i) {
+	param.user_data = &state_cli[i];
+
+	state_cli[i].pool = pool;
+	state_cli[i].check_echo = PJ_TRUE;
+	state_cli[i].send_str_len = (pj_rand() % 5 + 1) * 1024 + pj_rand() % 1024;
+	state_cli[i].send_str = (char*)pj_pool_alloc(pool, state_cli[i].send_str_len);
+	{
+	    unsigned j;
+	    for (j = 0; j < state_cli[i].send_str_len; ++j)
+		state_cli[i].send_str[j] = (char)(pj_rand() % 256);
+	}
+
+	status = pj_ssl_sock_create(pool, &param, &ssock_cli[i]);
+	if (status != PJ_SUCCESS) {
+	    app_perror("...ERROR pj_ssl_sock_create()", status);
+	    cli_err++;
+	    clients_num--;
+	    continue;
+	}
+
+	status = pj_ssl_sock_start_connect(ssock_cli[i], pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
+	if (status == PJ_SUCCESS) {
+	    ssl_on_connect_complete(ssock_cli[i], PJ_SUCCESS);
+	} else if (status == PJ_EPENDING) {
+	    status = PJ_SUCCESS;
+	} else {
+	    app_perror("...ERROR pj_ssl_sock_create()", status);
+	    pj_ssl_sock_close(ssock_cli[i]);
+	    ssock_cli[i] = NULL;
+	    clients_num--;
+	    cli_err++;
+	    continue;
+	}
+
+	/* Give chance to server to accept this client */
+	{
+	    unsigned n = 5;
+
+#ifdef PJ_SYMBIAN
+	    while(n && pj_symbianos_poll(-1, 1000))
+		n--;
+#else
+	    pj_time_val delay = {0, 100};
+	    while(n && pj_ioqueue_poll(ioqueue, &delay) > 0)
+		n--;
+#endif
+	}
+    }
+
+    /* Wait until everything has been sent/received or error */
+    while (clients_num)
+    {
+#ifdef PJ_SYMBIAN
+	pj_symbianos_poll(-1, 1000);
+#else
+	pj_time_val delay = {0, 100};
+	pj_ioqueue_poll(ioqueue, &delay);
+	pj_timer_heap_poll(timer, &delay);
+#endif
+    }
+
+    /* Clean up sockets */
+    {
+	pj_time_val delay = {0, 500};
+	while (pj_ioqueue_poll(ioqueue, &delay) > 0);
+    }
+
+    if (state_serv.err != PJ_SUCCESS) {
+	status = state_serv.err;
+	goto on_return;
+    }
+
+    PJ_LOG(3, ("", "...Done!"));
+
+    /* SSL setup and data transfer duration */
+    {
+	pj_time_val stop;
+	
+	pj_gettimeofday(&stop);
+	PJ_TIME_VAL_SUB(stop, start);
+
+	PJ_LOG(3, ("", ".....Setup & data transfer duration: %d.%03ds", stop.sec, stop.msec));
+    }
+
+    /* Check clients status */
+    for (i = 0; i < clients; ++i) {
+	if (state_cli[i].err != PJ_SUCCESS)
+	    cli_err++;
+
+	tot_sent += state_cli[1].sent;
+	tot_recv += state_cli[1].recv;
+    }
+
+    PJ_LOG(3, ("", ".....Clients: %d (%d errors)", clients, cli_err));
+    PJ_LOG(3, ("", ".....Total sent/recv: %d/%d bytes", tot_sent, tot_recv));
+
+on_return:
+    if (ssock_serv) 
+	pj_ssl_sock_close(ssock_serv);
+
+    for (i = 0; i < clients; ++i) {
+	if (ssock_cli[i] && !state_cli[i].err && !state_cli[i].done)
+	    pj_ssl_sock_close(ssock_cli[i]);
+    }
+    if (ioqueue)
+	pj_ioqueue_destroy(ioqueue);
+    if (pool)
+	pj_pool_release(pool);
+
+    return status;
+}
+
+#if 0 && (!defined(PJ_SYMBIAN) || PJ_SYMBIAN==0)
+pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool);
+static int ossl_test_send_buf()
+{
+    pj_pool_t *pool;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "send_buf", 256, 256, NULL);
+    status = pj_ssl_sock_ossl_test_send_buf(pool);
+    pj_pool_release(pool);
+    return status;
+}
+#else
+static int ossl_test_send_buf()
+{
+    return 0;
+}
+#endif
+
+int ssl_sock_test(void)
+{
+    int ret;
+
+    PJ_LOG(3,("", "..test ossl send buf"));
+    ret = ossl_test_send_buf();
+    if (ret != 0)
+	return ret;
+
+    PJ_LOG(3,("", "..get cipher list test"));
+    ret = get_cipher_list();
+    if (ret != 0)
+	return ret;
+
+    PJ_LOG(3,("", "..https client test"));
+    ret = https_client_test(30000);
+    // Ignore test result as internet connection may not be available.
+    //if (ret != 0)
+	//return ret;
+
+#ifndef PJ_SYMBIAN
+   
+    /* On Symbian platforms, SSL socket is implemented using CSecureSocket, 
+     * and it hasn't supported server mode, so exclude the following tests,
+     * which require SSL server, for now.
+     */
+
+    PJ_LOG(3,("", "..echo test w/ TLSv1 and PJ_TLS_RSA_WITH_DES_CBC_SHA cipher"));
+    ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1, 
+		    PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_DES_CBC_SHA, 
+		    PJ_FALSE, PJ_FALSE);
+    if (ret != 0)
+	return ret;
+
+    PJ_LOG(3,("", "..echo test w/ SSLv23 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher"));
+    ret = echo_test(PJ_SSL_SOCK_PROTO_SSL23, PJ_SSL_SOCK_PROTO_SSL23, 
+		    PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
+		    PJ_FALSE, PJ_FALSE);
+    if (ret != 0)
+	return ret;
+
+    PJ_LOG(3,("", "..echo test w/ incompatible proto"));
+    ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_SSL3, 
+		    PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_DES_CBC_SHA,
+		    PJ_FALSE, PJ_FALSE);
+    if (ret == 0)
+	return PJ_EBUG;
+
+    PJ_LOG(3,("", "..echo test w/ incompatible ciphers"));
+    ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT, 
+		    PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
+		    PJ_FALSE, PJ_FALSE);
+    if (ret == 0)
+	return PJ_EBUG;
+
+    PJ_LOG(3,("", "..echo test w/ client cert required but not provided"));
+    ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT, 
+		    PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
+		    PJ_TRUE, PJ_FALSE);
+    if (ret == 0)
+	return PJ_EBUG;
+
+    PJ_LOG(3,("", "..echo test w/ client cert required and provided"));
+    ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT, 
+		    PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
+		    PJ_TRUE, PJ_TRUE);
+    if (ret != 0)
+	return ret;
+
+    PJ_LOG(3,("", "..performance test"));
+    ret = perf_test(PJ_IOQUEUE_MAX_HANDLES/2 - 1, 0);
+    if (ret != 0)
+	return ret;
+
+    PJ_LOG(3,("", "..client non-SSL (handshake timeout 5 secs)"));
+    ret = client_non_ssl(5000);
+    /* PJ_TIMEDOUT won't be returned as accepted socket is deleted silently */
+    if (ret != 0)
+	return ret;
+
+#endif
+
+    PJ_LOG(3,("", "..server non-SSL (handshake timeout 5 secs)"));
+    ret = server_non_ssl(5000);
+    if (ret != PJ_ETIMEDOUT)
+	return ret;
+
+    return 0;
+}
+
+#else	/* INCLUDE_SSLSOCK_TEST */
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_ssl_sock_test;
+#endif	/* INCLUDE_SSLSOCK_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/string.c b/jni/pjproject-android/pjlib/src/pjlib-test/string.c
new file mode 100644
index 0000000..b7f73b9
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/string.c
@@ -0,0 +1,438 @@
+/* $Id: string.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include "test.h"
+
+#define THIS_FILE	"string.c"
+
+/**
+ * \page page_pjlib_string_test Test: String
+ *
+ * This file provides implementation of \b string_test(). It tests the
+ * functionality of the string API.
+ *
+ * \section sleep_test_sec Scope of the Test
+ *
+ * API tested:
+ *  - pj_str()
+ *  - pj_strcmp()
+ *  - pj_strcmp2()
+ *  - pj_stricmp()
+ *  - pj_strlen()
+ *  - pj_strncmp()
+ *  - pj_strnicmp()
+ *  - pj_strchr()
+ *  - pj_strdup()
+ *  - pj_strdup2()
+ *  - pj_strcpy()
+ *  - pj_strcat()
+ *  - pj_strtrim()
+ *  - pj_utoa()
+ *  - pj_strtoul()
+ *  - pj_strtoul2()
+ *  - pj_create_random_string()
+ *  - ... and mode..
+ *
+ * This file is <b>pjlib-test/string.c</b>
+ *
+ * \include pjlib-test/string.c
+ */
+
+#if INCLUDE_STRING_TEST
+
+#ifdef _MSC_VER
+#   pragma warning(disable: 4204)
+#endif
+
+#define HELLO_WORLD	"Hello World"
+#define HELLO_WORLD_LEN	11
+#define JUST_HELLO	"Hello"
+#define JUST_HELLO_LEN	5
+#define UL_VALUE	3456789012UL
+
+#if 1
+/* See if both integers have the same sign */
+PJ_INLINE(int) cmp(const char *expr, int i, int j)
+{
+    int r = !((i>0 && j>0) || (i<0 && j<0) || (i==0 && j==0));
+    if (r) {
+	PJ_LOG(3,(THIS_FILE,"   error: %s: expecting %d, got %d", expr, j, i));
+    }
+    return r;
+}
+#else
+/* For strict comparison, must be equal */
+PJ_INLINE(int) cmp(const char *expr, int i, int j)
+{
+    PJ_UNUSED_ARG(expr);
+    return i!=j;
+}
+#endif
+
+#define C(expr, res)	cmp(#expr, expr, res)
+
+static int stricmp_test(void)
+{
+/* This specificly tests and benchmark pj_stricmp(), pj_stricmp_alnum().
+ * In addition, it also tests pj_stricmp2(), pj_strnicmp(), and 
+ * pj_strnicmp2().
+ */
+#define STRTEST(res,res2,S1,S2,code)	\
+	    do { \
+		s1.ptr=S1; s1.slen=(S1)?len:0; \
+		s2.ptr=S2; s2.slen=(S2)?len:0; \
+		pj_get_timestamp(&t1); \
+	        if (C(pj_stricmp(&s1,&s2),res)) return code; \
+		pj_get_timestamp(&t2); \
+		pj_sub_timestamp(&t2, &t1); \
+		pj_add_timestamp(&e1, &t2); \
+		pj_get_timestamp(&t1); \
+	        if (C(pj_stricmp_alnum(&s1,&s2),res)) return code-1; \
+		pj_get_timestamp(&t2); \
+		pj_sub_timestamp(&t2, &t1); \
+		pj_add_timestamp(&e2, &t2); \
+		if (C(pj_stricmp2(&s1,S2),res2)) return code*10; \
+		if (C(pj_strnicmp(&s1,&s2,len),res)) return code*100; \
+		if (C(pj_strnicmp2(&s1,S2,len),res)) return code*1000; \
+	    } while (0)
+
+    char *buf;
+    pj_str_t s1, s2;
+    pj_timestamp t1, t2, e1, e2, zero;
+    pj_uint32_t c1, c2;
+    int len;
+
+    e1.u32.hi = e1.u32.lo = e2.u32.hi = e2.u32.lo = 0;
+
+    pj_thread_sleep(0);
+
+#define SNULL 0
+
+    /* Compare empty strings. */
+    len=0;
+    STRTEST( 0, 0, "","",-500);
+    STRTEST( 0, 0, SNULL,"",-502);
+    STRTEST( 0, 0, "",SNULL,-504);
+    STRTEST( 0, 0, SNULL,SNULL,-506);
+    STRTEST( 0, -1, "hello","world",-508);
+
+    /* equal, length=1 
+     * use buffer to simulate non-aligned string.
+     */
+    buf = "a""A";
+    len=1;
+    STRTEST( 0,  -1, "a",buf+0,-510);
+    STRTEST( 0,  0, "a",buf+1,-512);
+    STRTEST(-1, -1, "O", "P", -514);
+    STRTEST(-1, -1, SNULL, "a", -516);
+    STRTEST( 1,  1, "a", SNULL, -518);
+
+    /* equal, length=2 
+     * use buffer to simulate non-aligned string.
+     */
+    buf = "aa""Aa""aA""AA";
+    len=2;
+    STRTEST( 0, -1, "aa",buf+0,-520);
+    STRTEST( 0, -1, "aa",buf+2,-522);
+    STRTEST( 0, -1, "aa",buf+4,-524);
+    STRTEST( 0, 0, "aa",buf+6,-524);
+
+    /* equal, length=3 
+     * use buffer to simulate non-aligned string.
+     */
+    buf = "aaa""Aaa""aAa""aaA""AAa""aAA""AaA""AAA";
+    len=3;
+    STRTEST( 0, -1, "aaa",buf+0,-530);
+    STRTEST( 0, -1, "aaa",buf+3,-532);
+    STRTEST( 0, -1, "aaa",buf+6,-534);
+    STRTEST( 0, -1, "aaa",buf+9,-536);
+    STRTEST( 0, -1, "aaa",buf+12,-538);
+    STRTEST( 0, -1, "aaa",buf+15,-540);
+    STRTEST( 0, -1, "aaa",buf+18,-542);
+    STRTEST( 0, 0, "aaa",buf+21,-534);
+
+    /* equal, length=4 */
+    len=4;
+    STRTEST( 0, 0, "aaaa","aaaa",-540);
+    STRTEST( 0, 0, "aaaa","Aaaa",-542);
+    STRTEST( 0, 0, "aaaa","aAaa",-544);
+    STRTEST( 0, 0, "aaaa","aaAa",-546);
+    STRTEST( 0, 0, "aaaa","aaaA",-548);
+    STRTEST( 0, 0, "aaaa","AAaa",-550);
+    STRTEST( 0, 0, "aaaa","aAAa",-552);
+    STRTEST( 0, 0, "aaaa","aaAA",-554);
+    STRTEST( 0, 0, "aaaa","AaAa",-556);
+    STRTEST( 0, 0, "aaaa","aAaA",-558);
+    STRTEST( 0, 0, "aaaa","AaaA",-560);
+    STRTEST( 0, 0, "aaaa","AAAa",-562);
+    STRTEST( 0, 0, "aaaa","aAAA",-564);
+    STRTEST( 0, 0, "aaaa","AAaA",-566);
+    STRTEST( 0, 0, "aaaa","AaAA",-568);
+    STRTEST( 0, 0, "aaaa","AAAA",-570);
+
+    /* equal, length=5 */
+    buf = "aaaAa""AaaaA""AaAaA""AAAAA";
+    len=5;
+    STRTEST( 0, -1, "aaaaa",buf+0,-580);
+    STRTEST( 0, -1, "aaaaa",buf+5,-582);
+    STRTEST( 0, -1, "aaaaa",buf+10,-584);
+    STRTEST( 0, 0, "aaaaa",buf+15,-586);
+
+    /* not equal, length=1 */
+    len=1;
+    STRTEST( -1, -1, "a", "b", -600);
+
+    /* not equal, length=2 */
+    buf = "ab""ba";
+    len=2;
+    STRTEST( -1, -1, "aa", buf+0, -610);
+    STRTEST( -1, -1, "aa", buf+2, -612);
+
+    /* not equal, length=3 */
+    buf = "aab""aba""baa";
+    len=3;
+    STRTEST( -1, -1, "aaa", buf+0, -620);
+    STRTEST( -1, -1, "aaa", buf+3, -622);
+    STRTEST( -1, -1, "aaa", buf+6, -624);
+
+    /* not equal, length=4 */
+    buf = "aaab""aaba""abaa""baaa";
+    len=4;
+    STRTEST( -1, -1, "aaaa", buf+0, -630);
+    STRTEST( -1, -1, "aaaa", buf+4, -632);
+    STRTEST( -1, -1, "aaaa", buf+8, -634);
+    STRTEST( -1, -1, "aaaa", buf+12, -636);
+
+    /* not equal, length=5 */
+    buf="aaaab""aaaba""aabaa""abaaa""baaaa";
+    len=5;
+    STRTEST( -1, -1, "aaaaa", buf+0, -640);
+    STRTEST( -1, -1, "aaaaa", buf+5, -642);
+    STRTEST( -1, -1, "aaaaa", buf+10, -644);
+    STRTEST( -1, -1, "aaaaa", buf+15, -646);
+    STRTEST( -1, -1, "aaaaa", buf+20, -648);
+
+    zero.u32.hi = zero.u32.lo = 0;
+    c1 = pj_elapsed_cycle(&zero, &e1);
+    c2 = pj_elapsed_cycle(&zero, &e2);
+
+    if (c1 < c2) {
+	PJ_LOG(3,("", "  info: pj_stricmp_alnum is slower than pj_stricmp!"));
+	//return -700;
+    }
+
+    /* Avoid division by zero */
+    if (c2 == 0) c2=1;
+    
+    PJ_LOG(3, ("", "  time: stricmp=%u, stricmp_alnum=%u (speedup=%d.%02dx)", 
+		   c1, c2,
+		   (c1 * 100 / c2) / 100,
+		   (c1 * 100 / c2) % 100));
+    return 0;
+#undef STRTEST
+}
+
+/* This tests pj_strcmp(), pj_strcmp2(), pj_strncmp(), pj_strncmp2() */
+static int strcmp_test(void)
+{
+#define STR_TEST(res,S1,S2,code)    \
+	    do { \
+		s1.ptr=S1; s1.slen=S1?len:0; \
+		s2.ptr=S2; s2.slen=S2?len:0; \
+	        if (C(pj_strcmp(&s1,&s2),res)) return code; \
+		if (C(pj_strcmp2(&s1,S2),res)) return code-1; \
+		if (C(pj_strncmp(&s1,&s2,len),res)) return code-2; \
+		if (C(pj_strncmp2(&s1,S2,len),res)) return code-3; \
+	    } while (0)
+
+    pj_str_t s1, s2;
+    int len;
+    
+    /* Test with length == 0 */
+    len=0;
+    STR_TEST(0, "", "", -400);
+    STR_TEST(0, SNULL, "", -405);
+    STR_TEST(0, "", SNULL, -410);
+    STR_TEST(0, SNULL, SNULL, -415);
+    STR_TEST(0, "hello", "", -420);
+    STR_TEST(0, "hello", SNULL, -425);
+
+    /* Test with length != 0 */
+    len = 2;
+    STR_TEST(0, "12", "12", -430);
+    STR_TEST(1, "12", "1", -435);
+    STR_TEST(-1, "1", "12", -440);
+    STR_TEST(-1, SNULL, "12", -445);
+    STR_TEST(1, "12", SNULL, -450);
+
+    return 0;
+
+#undef STR_TEST
+}
+
+int string_test(void)
+{
+    const pj_str_t hello_world = { HELLO_WORLD, HELLO_WORLD_LEN };
+    const pj_str_t just_hello = { JUST_HELLO, JUST_HELLO_LEN };
+    pj_str_t s1, s2, s3, s4, s5;
+    enum { RCOUNT = 10, RLEN = 16 };
+    pj_str_t random[RCOUNT];
+    pj_pool_t *pool;
+    int i;
+
+    pool = pj_pool_create(mem, SNULL, 4096, 0, SNULL);
+    if (!pool) return -5;
+    
+    /* 
+     * pj_str(), pj_strcmp(), pj_stricmp(), pj_strlen(), 
+     * pj_strncmp(), pj_strchr() 
+     */
+    s1 = pj_str(HELLO_WORLD);
+    if (pj_strcmp(&s1, &hello_world) != 0)
+	return -10;
+    if (pj_stricmp(&s1, &hello_world) != 0)
+	return -20;
+    if (pj_strcmp(&s1, &just_hello) <= 0)
+	return -30;
+    if (pj_stricmp(&s1, &just_hello) <= 0)
+	return -40;
+    if (pj_strlen(&s1) != strlen(HELLO_WORLD))
+	return -50;
+    if (pj_strncmp(&s1, &hello_world, 5) != 0)
+	return -60;
+    if (pj_strnicmp(&s1, &hello_world, 5) != 0)
+	return -70;
+    if (pj_strchr(&s1, HELLO_WORLD[1]) != s1.ptr+1)
+	return -80;
+
+    /* 
+     * pj_strdup() 
+     */
+    if (!pj_strdup(pool, &s2, &s1))
+	return -100;
+    if (pj_strcmp(&s1, &s2) != 0)
+	return -110;
+    
+    /* 
+     * pj_strcpy(), pj_strcat() 
+     */
+    s3.ptr = (char*) pj_pool_alloc(pool, 256);
+    if (!s3.ptr) 
+	return -200;
+    pj_strcpy(&s3, &s2);
+    pj_strcat(&s3, &just_hello);
+
+    if (pj_strcmp2(&s3, HELLO_WORLD JUST_HELLO) != 0)
+	return -210;
+
+    /* 
+     * pj_strdup2(), pj_strtrim(). 
+     */
+    pj_strdup2(pool, &s4, " " HELLO_WORLD "\t ");
+    pj_strtrim(&s4);
+    if (pj_strcmp2(&s4, HELLO_WORLD) != 0)
+	return -250;
+
+    /* 
+     * pj_utoa() 
+     */
+    s5.ptr = (char*) pj_pool_alloc(pool, 16);
+    if (!s5.ptr)
+	return -270;
+    s5.slen = pj_utoa(UL_VALUE, s5.ptr);
+
+    /* 
+     * pj_strtoul() 
+     */
+    if (pj_strtoul(&s5) != UL_VALUE)
+	return -280;
+
+    /*
+     * pj_strtoul2()
+     */
+    s5 = pj_str("123456");
+
+    pj_strtoul2(&s5, SNULL, 10);	/* Crash test */
+
+    if (pj_strtoul2(&s5, &s4, 10) != 123456UL)
+	return -290;
+    if (s4.slen != 0)
+	return -291;
+    if (pj_strtoul2(&s5, &s4, 16) != 0x123456UL)
+	return -292;
+
+    s5 = pj_str("0123ABCD");
+    if (pj_strtoul2(&s5, &s4, 10) != 123)
+	return -293;
+    if (s4.slen != 4)
+	return -294;
+    if (s4.ptr == SNULL || *s4.ptr != 'A')
+	return -295;
+    if (pj_strtoul2(&s5, &s4, 16) != 0x123ABCDUL)
+	return -296;
+    if (s4.slen != 0)
+	return -297;
+
+    /* 
+     * pj_create_random_string() 
+     * Check that no duplicate strings are returned.
+     */
+    for (i=0; i<RCOUNT; ++i) {
+	int j;
+	
+	random[i].ptr = (char*) pj_pool_alloc(pool, RLEN);
+	if (!random[i].ptr)
+	    return -320;
+
+        random[i].slen = RLEN;
+	pj_create_random_string(random[i].ptr, RLEN);
+
+	for (j=0; j<i; ++j) {
+	    if (pj_strcmp(&random[i], &random[j])==0)
+		return -330;
+	}
+    }
+
+    /* Done. */
+    pj_pool_release(pool);
+
+    /* Case sensitive comparison test. */
+    i = strcmp_test();
+    if (i != 0)
+	return i;
+
+    /* Caseless comparison test. */
+    i = stricmp_test();
+    if (i != 0)
+	return i;
+
+    return 0;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_string_test;
+#endif	/* INCLUDE_STRING_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/test.c b/jni/pjproject-android/pjlib/src/pjlib-test/test.c
new file mode 100644
index 0000000..5c6f57d
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/test.c
@@ -0,0 +1,234 @@
+/* $Id: test.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+#ifdef _MSC_VER
+#  pragma warning(disable:4127)
+#endif
+
+#define DO_TEST(test)	do { \
+			    PJ_LOG(3, ("test", "Running %s...", #test));  \
+			    rc = test; \
+			    PJ_LOG(3, ("test",  \
+				       "%s(%d)",  \
+				       (rc ? "..ERROR" : "..success"), rc)); \
+			    if (rc!=0) goto on_return; \
+			} while (0)
+
+
+pj_pool_factory *mem;
+
+int param_echo_sock_type;
+const char *param_echo_server = ECHO_SERVER_ADDRESS;
+int param_echo_port = ECHO_SERVER_START_PORT;
+int param_log_decor = PJ_LOG_HAS_NEWLINE | PJ_LOG_HAS_TIME | 
+		      PJ_LOG_HAS_MICRO_SEC;
+
+int null_func()
+{
+    return 0;
+}
+
+int test_inner(void)
+{
+    pj_caching_pool caching_pool;
+    const char *filename;
+    int line;
+    int rc = 0;
+
+    mem = &caching_pool.factory;
+
+    pj_log_set_level(3);
+    pj_log_set_decor(param_log_decor);
+
+    rc = pj_init();
+    if (rc != 0) {
+	app_perror("pj_init() error!!", rc);
+	return rc;
+    }
+    
+    //pj_dump_config();
+    pj_caching_pool_init( &caching_pool, NULL, 0 );
+
+#if INCLUDE_ERRNO_TEST
+    DO_TEST( errno_test() );
+#endif
+
+#if INCLUDE_EXCEPTION_TEST
+    DO_TEST( exception_test() );
+#endif
+
+#if INCLUDE_OS_TEST
+    DO_TEST( os_test() );
+#endif
+
+#if INCLUDE_RAND_TEST
+    DO_TEST( rand_test() );
+#endif
+
+#if INCLUDE_LIST_TEST
+    DO_TEST( list_test() );
+#endif
+
+#if INCLUDE_POOL_TEST
+    DO_TEST( pool_test() );
+#endif
+
+#if INCLUDE_POOL_PERF_TEST
+    DO_TEST( pool_perf_test() );
+#endif
+
+#if INCLUDE_STRING_TEST
+    DO_TEST( string_test() );
+#endif
+    
+#if INCLUDE_FIFOBUF_TEST
+    DO_TEST( fifobuf_test() );
+#endif
+
+#if INCLUDE_RBTREE_TEST
+    DO_TEST( rbtree_test() );
+#endif
+
+#if INCLUDE_HASH_TEST
+    DO_TEST( hash_test() );
+#endif
+
+#if INCLUDE_TIMESTAMP_TEST
+    DO_TEST( timestamp_test() );
+#endif
+
+#if INCLUDE_ATOMIC_TEST
+    DO_TEST( atomic_test() );
+#endif
+
+#if INCLUDE_MUTEX_TEST
+    DO_TEST( mutex_test() );
+#endif
+
+#if INCLUDE_TIMER_TEST
+    DO_TEST( timer_test() );
+#endif
+
+#if INCLUDE_SLEEP_TEST
+    DO_TEST( sleep_test() );
+#endif
+
+#if INCLUDE_THREAD_TEST
+    DO_TEST( thread_test() );
+#endif
+
+#if INCLUDE_SOCK_TEST
+    DO_TEST( sock_test() );
+#endif
+
+#if INCLUDE_SOCK_PERF_TEST
+    DO_TEST( sock_perf_test() );
+#endif
+
+#if INCLUDE_SELECT_TEST
+    DO_TEST( select_test() );
+#endif
+
+#if INCLUDE_UDP_IOQUEUE_TEST
+    DO_TEST( udp_ioqueue_test() );
+#endif
+
+#if PJ_HAS_TCP && INCLUDE_TCP_IOQUEUE_TEST
+    DO_TEST( tcp_ioqueue_test() );
+#endif
+
+#if INCLUDE_IOQUEUE_PERF_TEST
+    DO_TEST( ioqueue_perf_test() );
+#endif
+
+#if INCLUDE_IOQUEUE_UNREG_TEST
+    DO_TEST( udp_ioqueue_unreg_test() );
+#endif
+
+#if INCLUDE_ACTIVESOCK_TEST
+    DO_TEST( activesock_test() );
+#endif
+
+#if INCLUDE_FILE_TEST
+    DO_TEST( file_test() );
+#endif
+
+#if INCLUDE_SSLSOCK_TEST
+    DO_TEST( ssl_sock_test() );
+#endif
+
+#if INCLUDE_ECHO_SERVER
+    //echo_server();
+    //echo_srv_sync();
+    udp_echo_srv_ioqueue();
+
+#elif INCLUDE_ECHO_CLIENT
+    if (param_echo_sock_type == 0)
+        param_echo_sock_type = pj_SOCK_DGRAM();
+
+    echo_client( param_echo_sock_type, 
+                 param_echo_server, 
+                 param_echo_port);
+#endif
+
+    goto on_return;
+
+on_return:
+
+    pj_caching_pool_destroy( &caching_pool );
+
+    PJ_LOG(3,("test", ""));
+ 
+    pj_thread_get_stack_info(pj_thread_this(), &filename, &line);
+    PJ_LOG(3,("test", "Stack max usage: %u, deepest: %s:%u", 
+	              pj_thread_get_stack_max_usage(pj_thread_this()),
+		      filename, line));
+    if (rc == 0)
+	PJ_LOG(3,("test", "Looks like everything is okay!.."));
+    else
+	PJ_LOG(3,("test", "Test completed with error(s)"));
+    
+    pj_shutdown();
+    
+    return 0;
+}
+
+#include <pj/sock.h>
+
+int test_main(void)
+{
+    int i;
+    PJ_USE_EXCEPTION;
+
+    i = pj_AF_INET();
+
+    PJ_TRY {
+        return test_inner();
+    }
+    PJ_CATCH_ANY {
+        int id = PJ_GET_EXCEPTION();
+        PJ_LOG(3,("test", "FATAL: unhandled exception id %d (%s)", 
+                  id, pj_exception_id_name(id)));
+    }
+    PJ_END;
+
+    return -1;
+}
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/test.h b/jni/pjproject-android/pjlib/src/pjlib-test/test.h
new file mode 100644
index 0000000..de69ea4
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/test.h
@@ -0,0 +1,129 @@
+/* $Id: test.h 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __PJLIB_TEST_H__
+#define __PJLIB_TEST_H__
+
+#include <pj/types.h>
+
+#define GROUP_LIBC                  1
+#define GROUP_OS                    1
+#define GROUP_DATA_STRUCTURE        1
+#define GROUP_NETWORK               1
+#if defined(PJ_SYMBIAN)
+#   define GROUP_FILE               0
+#else
+#   define GROUP_FILE               1
+#endif
+
+#define INCLUDE_ERRNO_TEST          GROUP_LIBC
+#define INCLUDE_TIMESTAMP_TEST      GROUP_OS
+#define INCLUDE_EXCEPTION_TEST	    GROUP_LIBC
+#define INCLUDE_RAND_TEST	    GROUP_LIBC
+#define INCLUDE_LIST_TEST	    GROUP_DATA_STRUCTURE
+#define INCLUDE_HASH_TEST	    GROUP_DATA_STRUCTURE
+#define INCLUDE_POOL_TEST	    GROUP_LIBC
+#define INCLUDE_POOL_PERF_TEST	    GROUP_LIBC
+#define INCLUDE_STRING_TEST	    GROUP_DATA_STRUCTURE
+#define INCLUDE_FIFOBUF_TEST	    0	// GROUP_DATA_STRUCTURE
+#define INCLUDE_RBTREE_TEST	    GROUP_DATA_STRUCTURE
+#define INCLUDE_TIMER_TEST	    GROUP_DATA_STRUCTURE
+#define INCLUDE_ATOMIC_TEST         GROUP_OS
+#define INCLUDE_MUTEX_TEST	    (PJ_HAS_THREADS && GROUP_OS)
+#define INCLUDE_SLEEP_TEST          GROUP_OS
+#define INCLUDE_OS_TEST             GROUP_OS
+#define INCLUDE_THREAD_TEST         (PJ_HAS_THREADS && GROUP_OS)
+#define INCLUDE_SOCK_TEST	    GROUP_NETWORK
+#define INCLUDE_SOCK_PERF_TEST	    GROUP_NETWORK
+#define INCLUDE_SELECT_TEST	    GROUP_NETWORK
+#define INCLUDE_UDP_IOQUEUE_TEST    GROUP_NETWORK
+#define INCLUDE_TCP_IOQUEUE_TEST    GROUP_NETWORK
+#define INCLUDE_ACTIVESOCK_TEST	    GROUP_NETWORK
+#define INCLUDE_SSLSOCK_TEST	    (PJ_HAS_SSL_SOCK && GROUP_NETWORK)
+#define INCLUDE_IOQUEUE_PERF_TEST   (PJ_HAS_THREADS && GROUP_NETWORK)
+#define INCLUDE_IOQUEUE_UNREG_TEST  (PJ_HAS_THREADS && GROUP_NETWORK)
+#define INCLUDE_FILE_TEST           GROUP_FILE
+
+#define INCLUDE_ECHO_SERVER         0
+#define INCLUDE_ECHO_CLIENT         0
+
+
+#define ECHO_SERVER_MAX_THREADS     2
+#define ECHO_SERVER_START_PORT      65000
+#define ECHO_SERVER_ADDRESS         "compaq.home"
+#define ECHO_SERVER_DURATION_MSEC   (60*60*1000)
+
+#define ECHO_CLIENT_MAX_THREADS     6
+
+PJ_BEGIN_DECL
+
+extern int errno_test(void);
+extern int timestamp_test(void);
+extern int exception_test(void);
+extern int rand_test(void);
+extern int list_test(void);
+extern int hash_test(void);
+extern int os_test(void);
+extern int pool_test(void);
+extern int pool_perf_test(void);
+extern int string_test(void);
+extern int fifobuf_test(void);
+extern int timer_test(void);
+extern int rbtree_test(void);
+extern int atomic_test(void);
+extern int mutex_test(void);
+extern int sleep_test(void);
+extern int thread_test(void);
+extern int sock_test(void);
+extern int sock_perf_test(void);
+extern int select_test(void);
+extern int udp_ioqueue_test(void);
+extern int udp_ioqueue_unreg_test(void);
+extern int tcp_ioqueue_test(void);
+extern int ioqueue_perf_test(void);
+extern int activesock_test(void);
+extern int file_test(void);
+extern int ssl_sock_test(void);
+
+extern int echo_server(void);
+extern int echo_client(int sock_type, const char *server, int port);
+
+extern int echo_srv_sync(void);
+extern int udp_echo_srv_ioqueue(void);
+extern int echo_srv_common_loop(pj_atomic_t *bytes_counter);
+
+
+extern pj_pool_factory *mem;
+
+extern int          test_main(void);
+extern void         app_perror(const char *msg, pj_status_t err);
+extern pj_status_t  app_socket(int family, int type, int proto, int port,
+                               pj_sock_t *ptr_sock);
+extern pj_status_t  app_socketpair(int family, int type, int protocol,
+                                   pj_sock_t *server, pj_sock_t *client);
+extern int	    null_func(void);
+
+//#define TRACE_(expr) PJ_LOG(3,expr)
+#define TRACE_(expr)
+#define HALT(msg)   { PJ_LOG(3,(THIS_FILE,"%s halted",msg)); for(;;) sleep(1); }
+
+PJ_END_DECL
+
+#endif	/* __PJLIB_TEST_H__ */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/test_wrap.cpp b/jni/pjproject-android/pjlib/src/pjlib-test/test_wrap.cpp
new file mode 100644
index 0000000..7341204
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/test_wrap.cpp
@@ -0,0 +1,24 @@
+/* $Id: test_wrap.cpp 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2009-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+/*
+ * This file is a C++ wrapper, see ticket #886 for details.
+ */
+
+#include "test.c"
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/thread.c b/jni/pjproject-android/pjlib/src/pjlib-test/thread.c
new file mode 100644
index 0000000..976317f
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/thread.c
@@ -0,0 +1,336 @@
+/* $Id: thread.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_thread_test Test: Thread Test
+ *
+ * This file contains \a thread_test() definition.
+ *
+ * \section thread_test_scope_sec Scope of Test
+ * This tests:
+ *  - whether PJ_THREAD_SUSPENDED flag works.
+ *  - whether multithreading works.
+ *  - whether thread timeslicing works, and threads have equal
+ *    time-slice proportion.
+ * 
+ * APIs tested:
+ *  - pj_thread_create()
+ *  - pj_thread_register()
+ *  - pj_thread_this()
+ *  - pj_thread_get_name()
+ *  - pj_thread_destroy()
+ *  - pj_thread_resume()
+ *  - pj_thread_sleep()
+ *  - pj_thread_join()
+ *  - pj_thread_destroy()
+ *
+ *
+ * This file is <b>pjlib-test/thread.c</b>
+ *
+ * \include pjlib-test/thread.c
+ */
+#if INCLUDE_THREAD_TEST
+
+#include <pjlib.h>
+
+#define THIS_FILE   "thread_test"
+
+static volatile int quit_flag=0;
+
+#if 0
+#   define TRACE__(args)	PJ_LOG(3,args)
+#else
+#   define TRACE__(args)
+#endif
+
+
+/*
+ * The thread's entry point.
+ *
+ * Each of the thread mainly will just execute the loop which
+ * increments a variable.
+ */
+static void* thread_proc(pj_uint32_t *pcounter)
+{
+    /* Test that pj_thread_register() works. */
+    pj_thread_desc desc;
+    pj_thread_t *this_thread;
+    unsigned id;
+    pj_status_t rc;
+
+    id = *pcounter;
+    TRACE__((THIS_FILE, "     thread %d running..", id));
+
+    pj_bzero(desc, sizeof(desc));
+
+    rc = pj_thread_register("thread", desc, &this_thread);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error in pj_thread_register", rc);
+        return NULL;
+    }
+
+    /* Test that pj_thread_this() works */
+    this_thread = pj_thread_this();
+    if (this_thread == NULL) {
+        PJ_LOG(3,(THIS_FILE, "...error: pj_thread_this() returns NULL!"));
+        return NULL;
+    }
+
+    /* Test that pj_thread_get_name() works */
+    if (pj_thread_get_name(this_thread) == NULL) {
+        PJ_LOG(3,(THIS_FILE, "...error: pj_thread_get_name() returns NULL!"));
+        return NULL;
+    }
+
+    /* Main loop */
+    for (;!quit_flag;) {
+	(*pcounter)++;
+        //Must sleep if platform doesn't do time-slicing.
+	//pj_thread_sleep(0);
+    }
+
+    TRACE__((THIS_FILE, "     thread %d quitting..", id));
+    return NULL;
+}
+
+/*
+ * simple_thread()
+ */
+static int simple_thread(const char *title, unsigned flags)
+{
+    pj_pool_t *pool;
+    pj_thread_t *thread;
+    pj_status_t rc;
+    pj_uint32_t counter = 0;
+ 
+    PJ_LOG(3,(THIS_FILE, "..%s", title));
+    
+    pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+    if (!pool)
+	return -1000;
+
+    quit_flag = 0;
+
+    TRACE__((THIS_FILE, "    Creating thread 0.."));
+    rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc,
+			  &counter,
+			  PJ_THREAD_DEFAULT_STACK_SIZE,
+			  flags,
+			  &thread);
+
+    if (rc != PJ_SUCCESS) {
+	app_perror("...error: unable to create thread", rc);
+	return -1010;
+    }
+
+    TRACE__((THIS_FILE, "    Main thread waiting.."));
+    pj_thread_sleep(1500);
+    TRACE__((THIS_FILE, "    Main thread resuming.."));
+
+    if (flags & PJ_THREAD_SUSPENDED) {
+
+	/* Check that counter is still zero */
+	if (counter != 0) {
+	    PJ_LOG(3,(THIS_FILE, "...error: thread is not suspended"));
+	    return -1015;
+	}
+	
+	rc = pj_thread_resume(thread);
+	if (rc != PJ_SUCCESS) {
+	    app_perror("...error: resume thread error", rc);
+	    return -1020;
+	}
+    }
+    
+    PJ_LOG(3,(THIS_FILE, "..waiting for thread to quit.."));
+
+    pj_thread_sleep(1500);
+
+    quit_flag = 1;
+    pj_thread_join(thread);
+
+    pj_pool_release(pool);
+
+    if (counter == 0) {
+	PJ_LOG(3,(THIS_FILE, "...error: thread is not running"));
+	return -1025;
+    }
+    
+    PJ_LOG(3,(THIS_FILE, "...%s success", title));
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * timeslice_test()
+ */
+static int timeslice_test(void)
+{
+    enum { NUM_THREADS = 4 };
+    pj_pool_t *pool;
+    pj_uint32_t counter[NUM_THREADS], lowest, highest, diff;
+    pj_thread_t *thread[NUM_THREADS];
+    unsigned i;
+    pj_status_t rc;
+
+    quit_flag = 0;
+
+    pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+    if (!pool)
+        return -10;
+
+    PJ_LOG(3,(THIS_FILE, "..timeslice testing with %d threads", NUM_THREADS));
+
+    /* Create all threads in suspended mode. */
+    for (i=0; i<NUM_THREADS; ++i) {
+        counter[i] = i;
+        rc = pj_thread_create(pool, "thread", (pj_thread_proc*)&thread_proc, 
+			      &counter[i], 
+                              PJ_THREAD_DEFAULT_STACK_SIZE, 
+                              PJ_THREAD_SUSPENDED, 
+                              &thread[i]);
+        if (rc!=PJ_SUCCESS) {
+            app_perror("...ERROR in pj_thread_create()", rc);
+            return -20;
+        }
+    }
+
+    /* Sleep for 1 second.
+     * The purpose of this is to test whether all threads are suspended.
+     */
+    TRACE__((THIS_FILE, "    Main thread waiting.."));
+    pj_thread_sleep(1000);
+    TRACE__((THIS_FILE, "    Main thread resuming.."));
+
+    /* Check that all counters are still zero. */
+    for (i=0; i<NUM_THREADS; ++i) {
+        if (counter[i] > i) {
+            PJ_LOG(3,(THIS_FILE, "....ERROR! Thread %d-th is not suspended!", 
+		      i));
+            return -30;
+        }
+    }
+
+    /* Now resume all threads. */
+    for (i=0; i<NUM_THREADS; ++i) {
+	TRACE__((THIS_FILE, "    Resuming thread %d [%p]..", i, thread[i]));
+        rc = pj_thread_resume(thread[i]);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...ERROR in pj_thread_resume()", rc);
+            return -40;
+        }
+    }
+
+    /* Main thread sleeps for some time to allow threads to run. 
+     * The longer we sleep, the more accurate the calculation will be,
+     * but it'll make user waits for longer for the test to finish.
+     */
+    TRACE__((THIS_FILE, "    Main thread waiting (5s).."));
+    pj_thread_sleep(5000);
+    TRACE__((THIS_FILE, "    Main thread resuming.."));
+
+    /* Signal all threads to quit. */
+    quit_flag = 1;
+
+    /* Wait until all threads quit, then destroy. */
+    for (i=0; i<NUM_THREADS; ++i) {
+	TRACE__((THIS_FILE, "    Main thread joining thread %d [%p]..", 
+			    i, thread[i]));
+        rc = pj_thread_join(thread[i]);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...ERROR in pj_thread_join()", rc);
+            return -50;
+        }
+	TRACE__((THIS_FILE, "    Destroying thread %d [%p]..", i, thread[i]));
+        rc = pj_thread_destroy(thread[i]);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...ERROR in pj_thread_destroy()", rc);
+            return -60;
+        }
+    }
+
+    TRACE__((THIS_FILE, "    Main thread calculating time slices.."));
+
+    /* Now examine the value of the counters.
+     * Check that all threads had equal proportion of processing.
+     */
+    lowest = 0xFFFFFFFF;
+    highest = 0;
+    for (i=0; i<NUM_THREADS; ++i) {
+        if (counter[i] < lowest)
+            lowest = counter[i];
+        if (counter[i] > highest)
+            highest = counter[i];
+    }
+
+    /* Check that all threads are running. */
+    if (lowest < 2) {
+        PJ_LOG(3,(THIS_FILE, "...ERROR: not all threads were running!"));
+        return -70;
+    }
+
+    /* The difference between lowest and higest should be lower than 50%.
+     */
+    diff = (highest-lowest)*100 / ((highest+lowest)/2);
+    if ( diff >= 50) {
+        PJ_LOG(3,(THIS_FILE, 
+		  "...ERROR: thread didn't have equal timeslice!"));
+        PJ_LOG(3,(THIS_FILE, 
+		  ".....lowest counter=%u, highest counter=%u, diff=%u%%",
+                  lowest, highest, diff));
+        return -80;
+    } else {
+        PJ_LOG(3,(THIS_FILE, 
+                  "...info: timeslice diff between lowest & highest=%u%%",
+                  diff));
+    }
+
+    pj_pool_release(pool);
+    return 0;
+}
+
+int thread_test(void)
+{
+    int rc;
+
+    rc = simple_thread("simple thread test", 0);
+    if (rc != PJ_SUCCESS)
+	return rc;
+    
+    rc = simple_thread("suspended thread test", PJ_THREAD_SUSPENDED);
+    if (rc != PJ_SUCCESS)
+	return rc;
+    
+    rc = timeslice_test();
+    if (rc != PJ_SUCCESS)
+	return rc;
+
+    return rc;
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_thread_test;
+#endif	/* INCLUDE_THREAD_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/timer.c b/jni/pjproject-android/pjlib/src/pjlib-test/timer.c
new file mode 100644
index 0000000..9dd09b7
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/timer.c
@@ -0,0 +1,197 @@
+/* $Id: timer.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+
+/**
+ * \page page_pjlib_timer_test Test: Timer
+ *
+ * This file provides implementation of \b timer_test(). It tests the
+ * functionality of the timer heap.
+ *
+ *
+ * This file is <b>pjlib-test/timer.c</b>
+ *
+ * \include pjlib-test/timer.c
+ */
+
+
+#if INCLUDE_TIMER_TEST
+
+#include <pjlib.h>
+
+#define LOOP		16
+#define MIN_COUNT	250
+#define MAX_COUNT	(LOOP * MIN_COUNT)
+#define MIN_DELAY	2
+#define	D		(MAX_COUNT / 32000)
+#define DELAY		(D < MIN_DELAY ? MIN_DELAY : D)
+#define THIS_FILE	"timer_test"
+
+
+static void timer_callback(pj_timer_heap_t *ht, pj_timer_entry *e)
+{
+    PJ_UNUSED_ARG(ht);
+    PJ_UNUSED_ARG(e);
+}
+
+static int test_timer_heap(void)
+{
+    int i, j;
+    pj_timer_entry *entry;
+    pj_pool_t *pool;
+    pj_timer_heap_t *timer;
+    pj_time_val delay;
+    pj_status_t rc;    int err=0;
+    pj_size_t size;
+    unsigned count;
+
+    size = pj_timer_heap_mem_size(MAX_COUNT)+MAX_COUNT*sizeof(pj_timer_entry);
+    pool = pj_pool_create( mem, NULL, size, 4000, NULL);
+    if (!pool) {
+	PJ_LOG(3,("test", "...error: unable to create pool of %u bytes",
+		  size));
+	return -10;
+    }
+
+    entry = (pj_timer_entry*)pj_pool_calloc(pool, MAX_COUNT, sizeof(*entry));
+    if (!entry)
+	return -20;
+
+    for (i=0; i<MAX_COUNT; ++i) {
+	entry[i].cb = &timer_callback;
+    }
+    rc = pj_timer_heap_create(pool, MAX_COUNT, &timer);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error: unable to create timer heap", rc);
+	return -30;
+    }
+
+    count = MIN_COUNT;
+    for (i=0; i<LOOP; ++i) {
+	int early = 0;
+	int done=0;
+	int cancelled=0;
+	int rc;
+	pj_timestamp t1, t2, t_sched, t_cancel, t_poll;
+	pj_time_val now, expire;
+
+	pj_gettimeofday(&now);
+	pj_srand(now.sec);
+	t_sched.u32.lo = t_cancel.u32.lo = t_poll.u32.lo = 0;
+
+	// Register timers
+	for (j=0; j<(int)count; ++j) {
+	    delay.sec = pj_rand() % DELAY;
+	    delay.msec = pj_rand() % 1000;
+
+	    // Schedule timer
+	    pj_get_timestamp(&t1);
+	    rc = pj_timer_heap_schedule(timer, &entry[j], &delay);
+	    if (rc != 0)
+		return -40;
+	    pj_get_timestamp(&t2);
+
+	    t_sched.u32.lo += (t2.u32.lo - t1.u32.lo);
+
+	    // Poll timers.
+	    pj_get_timestamp(&t1);
+	    rc = pj_timer_heap_poll(timer, NULL);
+	    pj_get_timestamp(&t2);
+	    if (rc > 0) {
+		t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+		early += rc;
+	    }
+	}
+
+	// Set the time where all timers should finish
+	pj_gettimeofday(&expire);
+	delay.sec = DELAY; 
+	delay.msec = 0;
+	PJ_TIME_VAL_ADD(expire, delay);
+
+	// Wait unfil all timers finish, cancel some of them.
+	do {
+	    int index = pj_rand() % count;
+	    pj_get_timestamp(&t1);
+	    rc = pj_timer_heap_cancel(timer, &entry[index]);
+	    pj_get_timestamp(&t2);
+	    if (rc > 0) {
+		cancelled += rc;
+		t_cancel.u32.lo += (t2.u32.lo - t1.u32.lo);
+	    }
+
+	    pj_gettimeofday(&now);
+
+	    pj_get_timestamp(&t1);
+#if defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
+	    /* On Symbian, we must use OS poll (Active Scheduler poll) since 
+	     * timer is implemented using Active Object.
+	     */
+	    rc = 0;
+	    while (pj_symbianos_poll(-1, 0))
+		++rc;
+#else
+	    rc = pj_timer_heap_poll(timer, NULL);
+#endif
+	    pj_get_timestamp(&t2);
+	    if (rc > 0) {
+		done += rc;
+		t_poll.u32.lo += (t2.u32.lo - t1.u32.lo);
+	    }
+
+	} while (PJ_TIME_VAL_LTE(now, expire)&&pj_timer_heap_count(timer) > 0);
+
+	if (pj_timer_heap_count(timer)) {
+	    PJ_LOG(3, (THIS_FILE, "ERROR: %d timers left", 
+		       pj_timer_heap_count(timer)));
+	    ++err;
+	}
+	t_sched.u32.lo /= count; 
+	t_cancel.u32.lo /= count;
+	t_poll.u32.lo /= count;
+	PJ_LOG(4, (THIS_FILE, 
+	        "...ok (count:%d, early:%d, cancelled:%d, "
+		"sched:%d, cancel:%d poll:%d)", 
+		count, early, cancelled, t_sched.u32.lo, t_cancel.u32.lo,
+		t_poll.u32.lo));
+
+	count = count * 2;
+	if (count > MAX_COUNT)
+	    break;
+    }
+
+    pj_pool_release(pool);
+    return err;
+}
+
+
+int timer_test()
+{
+    return test_timer_heap();
+}
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_timer_test;
+#endif	/* INCLUDE_TIMER_TEST */
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/timestamp.c b/jni/pjproject-android/pjlib/src/pjlib-test/timestamp.c
new file mode 100644
index 0000000..3b04000
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/timestamp.c
@@ -0,0 +1,235 @@
+/* $Id: timestamp.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/rand.h>
+
+
+/**
+ * \page page_pjlib_timestamp_test Test: Timestamp
+ *
+ * This file provides implementation of timestamp_test()
+ *
+ * \section timestamp_test_sec Scope of the Test
+ *
+ * This tests whether timestamp API works.
+ *
+ * API tested:
+ *  - pj_get_timestamp_freq()
+ *  - pj_get_timestamp()
+ *  - pj_elapsed_usec()
+ *  - PJ_LOG()
+ *
+ *
+ * This file is <b>pjlib-test/timestamp.c</b>
+ *
+ * \include pjlib-test/timestamp.c
+ */
+
+#if INCLUDE_TIMESTAMP_TEST
+
+#define THIS_FILE   "timestamp"
+
+static int timestamp_accuracy()
+{
+    pj_timestamp freq, t1, t2;
+    pj_time_val tv1, tv2, tvtmp;
+    pj_int64_t msec, tics;
+    pj_int64_t diff;
+
+    PJ_LOG(3,(THIS_FILE, "...testing frequency accuracy (pls wait)"));
+
+    pj_get_timestamp_freq(&freq);
+
+    /* Get the start time */
+    pj_gettimeofday(&tvtmp);
+    do {
+	pj_gettimeofday(&tv1);
+    } while (PJ_TIME_VAL_EQ(tvtmp, tv1));
+    pj_get_timestamp(&t1);
+
+    /* Sleep for 10 seconds */
+    pj_thread_sleep(10000);
+
+    /* Get end time */
+    pj_gettimeofday(&tvtmp);
+    do {
+	pj_gettimeofday(&tv2);
+    } while (PJ_TIME_VAL_EQ(tvtmp, tv2));
+    pj_get_timestamp(&t2);
+
+    /* Get the elapsed time */
+    PJ_TIME_VAL_SUB(tv2, tv1);
+    msec = PJ_TIME_VAL_MSEC(tv2);
+
+    /* Check that the frequency match the elapsed time */
+    tics = t2.u64 - t1.u64;
+    diff = tics - (msec * freq.u64 / 1000);
+    if (diff < 0)
+	diff = -diff;
+
+    /* Only allow 1 msec mismatch */
+    if (diff > (pj_int64_t)(freq.u64 / 1000)) {
+	PJ_LOG(3,(THIS_FILE, "....error: timestamp drifted by %d usec after "
+			     "%d msec", 
+			     (pj_uint32_t)(diff * 1000000 / freq.u64), 
+			     msec));
+	return -2000;
+
+    /* Otherwise just print warning if timestamp drifted by >1 usec */
+    } else if (diff > (pj_int64_t)(freq.u64 / 1000000)) {
+	PJ_LOG(3,(THIS_FILE, "....warning: timestamp drifted by %d usec after "
+			     "%d msec", 
+			     (pj_uint32_t)(diff * 1000000 / freq.u64), 
+			     msec));
+    } else {
+	PJ_LOG(3,(THIS_FILE, "....good. Timestamp is accurate down to"
+			     " nearest usec."));
+    }
+
+    return 0;
+}
+
+
+int timestamp_test(void)
+{
+    enum { CONSECUTIVE_LOOP = 100 };
+    volatile unsigned i;
+    pj_timestamp freq, t1, t2;
+    pj_time_val tv1, tv2;
+    unsigned elapsed;
+    pj_status_t rc;
+
+    PJ_LOG(3,(THIS_FILE, "...Testing timestamp (high res time)"));
+    
+    /* Get and display timestamp frequency. */
+    if ((rc=pj_get_timestamp_freq(&freq)) != PJ_SUCCESS) {
+	app_perror("...ERROR: get timestamp freq", rc);
+	return -1000;
+    }
+
+    PJ_LOG(3,(THIS_FILE, "....frequency: hiword=%lu loword=%lu", 
+			freq.u32.hi, freq.u32.lo));
+
+    PJ_LOG(3,(THIS_FILE, "...checking if time can run backwards (pls wait).."));
+
+    /*
+     * Check if consecutive readings should yield timestamp value
+     * that is bigger than previous value.
+     * First we get the first timestamp.
+     */
+    rc = pj_get_timestamp(&t1);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...ERROR: pj_get_timestamp", rc);
+	return -1001;
+    }
+    rc = pj_gettimeofday(&tv1);
+    if (rc != PJ_SUCCESS) {
+	app_perror("...ERROR: pj_gettimeofday", rc);
+	return -1002;
+    }
+    for (i=0; i<CONSECUTIVE_LOOP; ++i) {
+        
+        pj_thread_sleep(pj_rand() % 100);
+
+	rc = pj_get_timestamp(&t2);
+	if (rc != PJ_SUCCESS) {
+	    app_perror("...ERROR: pj_get_timestamp", rc);
+	    return -1003;
+	}
+	rc = pj_gettimeofday(&tv2);
+	if (rc != PJ_SUCCESS) {
+	    app_perror("...ERROR: pj_gettimeofday", rc);
+	    return -1004;
+	}
+
+	/* compare t2 with t1, expecting t2 >= t1. */
+	if (t2.u32.hi < t1.u32.hi ||
+	    (t2.u32.hi == t1.u32.hi && t2.u32.lo < t1.u32.lo))
+	{
+	    PJ_LOG(3,(THIS_FILE, "...ERROR: timestamp run backwards!"));
+	    return -1005;
+	}
+
+	/* compare tv2 with tv1, expecting tv2 >= tv1. */
+	if (PJ_TIME_VAL_LT(tv2, tv1)) {
+	    PJ_LOG(3,(THIS_FILE, "...ERROR: time run backwards!"));
+	    return -1006;
+	}
+    }
+
+    /* 
+     * Simple test to time some loop. 
+     */
+    PJ_LOG(3,(THIS_FILE, "....testing simple 1000000 loop"));
+
+
+    /* Mark start time. */
+    if ((rc=pj_get_timestamp(&t1)) != PJ_SUCCESS) {
+	app_perror("....error: cat't get timestamp", rc);
+	return -1010;
+    }
+
+    /* Loop.. */
+    for (i=0; i<1000000; ++i) {
+	/* Try to do something so that smart compilers wont
+	 * remove this silly loop.
+	 */
+	null_func();
+    }
+
+    pj_thread_sleep(0);
+
+    /* Mark end time. */
+    pj_get_timestamp(&t2);
+
+    /* Get elapsed time in usec. */
+    elapsed = pj_elapsed_usec(&t1, &t2);
+    PJ_LOG(3,(THIS_FILE, "....elapsed: %u usec", (unsigned)elapsed));
+
+    /* See if elapsed time is "reasonable". 
+     * This should be good even on 50Mhz embedded powerpc.
+     */
+    if (elapsed < 1 || elapsed > 1000000) {
+	PJ_LOG(3,(THIS_FILE, "....error: elapsed time outside window (%u, "
+			     "t1.u32.hi=%u, t1.u32.lo=%u, "
+			     "t2.u32.hi=%u, t2.u32.lo=%u)",
+			     elapsed, 
+			     t1.u32.hi, t1.u32.lo, t2.u32.hi, t2.u32.lo));
+	return -1030;
+    }
+
+    /* Testing time/timestamp accuracy */
+    rc = timestamp_accuracy();
+    if (rc != 0)
+	return rc;
+
+    return 0;
+}
+
+
+#else
+/* To prevent warning about "translation unit is empty"
+ * when this test is disabled. 
+ */
+int dummy_timestamp_test;
+#endif	/* INCLUDE_TIMESTAMP_TEST */
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c b/jni/pjproject-android/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
new file mode 100644
index 0000000..bd70165
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/udp_echo_srv_ioqueue.c
@@ -0,0 +1,214 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjlib.h>
+#include "test.h"
+
+static pj_ioqueue_key_t *key;
+static pj_atomic_t *total_bytes;
+static pj_bool_t thread_quit_flag;
+
+struct op_key
+{
+    pj_ioqueue_op_key_t  op_key_;
+    struct op_key       *peer;
+    char                *buffer;
+    pj_size_t            size;
+    int                  is_pending;
+    pj_status_t          last_err;
+    pj_sockaddr_in       addr;
+    int                  addrlen;
+};
+
+static void on_read_complete(pj_ioqueue_key_t *key, 
+                             pj_ioqueue_op_key_t *op_key, 
+                             pj_ssize_t bytes_received)
+{
+    pj_status_t rc;
+    struct op_key *recv_rec = (struct op_key *)op_key;
+
+    for (;;) {
+        struct op_key *send_rec = recv_rec->peer;
+        recv_rec->is_pending = 0;
+
+        if (bytes_received < 0) {
+            if (-bytes_received != recv_rec->last_err) {
+                recv_rec->last_err = (pj_status_t)-bytes_received;
+                app_perror("...error receiving data", recv_rec->last_err);
+            }
+        } else if (bytes_received == 0) {
+            /* note: previous error, or write callback */
+        } else {
+            pj_atomic_add(total_bytes, (pj_atomic_value_t)bytes_received);
+
+            if (!send_rec->is_pending) {
+                pj_ssize_t sent = bytes_received;
+                pj_memcpy(send_rec->buffer, recv_rec->buffer, bytes_received);
+                pj_memcpy(&send_rec->addr, &recv_rec->addr, recv_rec->addrlen);
+                send_rec->addrlen = recv_rec->addrlen;
+                rc = pj_ioqueue_sendto(key, &send_rec->op_key_, 
+                                       send_rec->buffer, &sent, 0,
+                                       &send_rec->addr, send_rec->addrlen);
+                send_rec->is_pending = (rc==PJ_EPENDING);
+
+                if (rc!=PJ_SUCCESS && rc!=PJ_EPENDING) {
+                    app_perror("...send error(1)", rc);
+                }
+            }
+        }
+
+        if (!send_rec->is_pending) {
+            bytes_received = recv_rec->size;
+            rc = pj_ioqueue_recvfrom(key, &recv_rec->op_key_, 
+                                     recv_rec->buffer, &bytes_received, 0,
+                                     &recv_rec->addr, &recv_rec->addrlen);
+            recv_rec->is_pending = (rc==PJ_EPENDING);
+            if (rc == PJ_SUCCESS) {
+                /* fall through next loop. */
+            } else if (rc == PJ_EPENDING) {
+                /* quit callback. */
+                break;
+            } else {
+                /* error */
+                app_perror("...recv error", rc);
+                recv_rec->last_err = rc;
+
+                bytes_received = 0;
+                /* fall through next loop. */
+            }
+        } else {
+            /* recv will be done when write completion callback is called. */
+            break;
+        }
+    }
+}
+
+static void on_write_complete(pj_ioqueue_key_t *key, 
+                              pj_ioqueue_op_key_t *op_key, 
+                              pj_ssize_t bytes_sent)
+{
+    struct op_key *send_rec = (struct op_key*)op_key;
+
+    if (bytes_sent <= 0) {
+        pj_status_t rc = (pj_status_t)-bytes_sent;
+        if (rc != send_rec->last_err) {
+            send_rec->last_err = rc;
+            app_perror("...send error(2)", rc);
+        }
+    }
+
+    send_rec->is_pending = 0;
+    on_read_complete(key, &send_rec->peer->op_key_, 0);
+}
+
+static int worker_thread(void *arg)
+{
+    pj_ioqueue_t *ioqueue = (pj_ioqueue_t*) arg;
+    struct op_key read_op, write_op;
+    char recv_buf[512], send_buf[512];
+    pj_ssize_t length;
+    pj_status_t rc;
+
+    read_op.peer = &write_op;
+    read_op.is_pending = 0;
+    read_op.last_err = 0;
+    read_op.buffer = recv_buf;
+    read_op.size = sizeof(recv_buf);
+    read_op.addrlen = sizeof(read_op.addr);
+
+    write_op.peer = &read_op;
+    write_op.is_pending = 0;
+    write_op.last_err = 0;
+    write_op.buffer = send_buf;
+    write_op.size = sizeof(send_buf);
+
+    length = sizeof(recv_buf);
+    rc = pj_ioqueue_recvfrom(key, &read_op.op_key_, recv_buf, &length, 0,
+                             &read_op.addr, &read_op.addrlen);
+    if (rc == PJ_SUCCESS) {
+        read_op.is_pending = 1;
+        on_read_complete(key, &read_op.op_key_, length);
+    }
+    
+    while (!thread_quit_flag) {
+        pj_time_val timeout;
+        timeout.sec = 0; timeout.msec = 10;
+        rc = pj_ioqueue_poll(ioqueue, &timeout);
+    }
+    return 0;
+}
+
+int udp_echo_srv_ioqueue(void)
+{
+    pj_pool_t *pool;
+    pj_sock_t sock;
+    pj_ioqueue_t *ioqueue;
+    pj_ioqueue_callback callback;
+    int i;
+    pj_thread_t *thread[ECHO_SERVER_MAX_THREADS];
+    pj_status_t rc;
+
+    pj_bzero(&callback, sizeof(callback));
+    callback.on_read_complete = &on_read_complete;
+    callback.on_write_complete = &on_write_complete;
+
+    pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+    if (!pool)
+        return -10;
+
+    rc = pj_ioqueue_create(pool, 2, &ioqueue);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...pj_ioqueue_create error", rc);
+        return -20;
+    }
+
+    rc = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, 
+                    ECHO_SERVER_START_PORT, &sock);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...app_socket error", rc);
+        return -30;
+    }
+
+    rc = pj_ioqueue_register_sock(pool, ioqueue, sock, NULL,
+                                  &callback, &key);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error registering socket", rc);
+        return -40;
+    }
+
+    rc = pj_atomic_create(pool, 0, &total_bytes);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...error creating atomic variable", rc);
+        return -45;
+    }
+
+    for (i=0; i<ECHO_SERVER_MAX_THREADS; ++i) {
+        rc = pj_thread_create(pool, NULL, &worker_thread, ioqueue,
+                              PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+                              &thread[i]);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...create thread error", rc);
+            return -50;
+        }
+    }
+
+    echo_srv_common_loop(total_bytes);
+
+    return 0;
+}
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/udp_echo_srv_sync.c b/jni/pjproject-android/pjlib/src/pjlib-test/udp_echo_srv_sync.c
new file mode 100644
index 0000000..bb6337a
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/udp_echo_srv_sync.c
@@ -0,0 +1,167 @@
+/* $Id: udp_echo_srv_sync.c 4537 2013-06-19 06:47:43Z riza $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+static pj_atomic_t *total_bytes;
+static pj_bool_t thread_quit_flag = 0;
+
+static int worker_thread(void *arg)
+{
+    pj_sock_t    sock = (pj_sock_t)arg;
+    char         buf[512];
+    pj_status_t  last_recv_err = PJ_SUCCESS, last_write_err = PJ_SUCCESS;
+
+    while (!thread_quit_flag) {
+        pj_ssize_t len;
+        pj_status_t rc;
+        pj_sockaddr_in addr;
+        int addrlen;
+
+        len = sizeof(buf);
+        addrlen = sizeof(addr);
+        rc = pj_sock_recvfrom(sock, buf, &len, 0, &addr, &addrlen);
+        if (rc != 0) {
+            if (rc != last_recv_err) {
+                app_perror("...recv error", rc);
+                last_recv_err = rc;
+            }
+            continue;
+        }
+
+	pj_atomic_add(total_bytes, (pj_atomic_value_t)len);
+
+        rc = pj_sock_sendto(sock, buf, &len, 0, &addr, addrlen);
+        if (rc != PJ_SUCCESS) {
+            if (rc != last_write_err) {
+                app_perror("...send error", rc);
+                last_write_err = rc;
+            }
+            continue;
+        }
+    }
+    return 0;
+}
+
+
+int echo_srv_sync(void)
+{
+    pj_pool_t *pool;
+    pj_sock_t sock;
+    pj_thread_t *thread[ECHO_SERVER_MAX_THREADS];
+    pj_status_t rc;
+    int i;
+
+    pool = pj_pool_create(mem, NULL, 4000, 4000, NULL);
+    if (!pool)
+        return -5;
+
+    rc = pj_atomic_create(pool, 0, &total_bytes);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...unable to create atomic_var", rc);
+        return -6;
+    }
+
+    rc = app_socket(pj_AF_INET(), pj_SOCK_DGRAM(),0, ECHO_SERVER_START_PORT, &sock);
+    if (rc != PJ_SUCCESS) {
+        app_perror("...socket error", rc);
+        return -10;
+    }
+
+    for (i=0; i<ECHO_SERVER_MAX_THREADS; ++i) {
+        rc = pj_thread_create(pool, NULL, &worker_thread, (void*)sock,
+                              PJ_THREAD_DEFAULT_STACK_SIZE, 0,
+                              &thread[i]);
+        if (rc != PJ_SUCCESS) {
+            app_perror("...unable to create thread", rc);
+            return -20;
+        }
+    }
+
+    PJ_LOG(3,("", "...UDP echo server running with %d threads at port %d",
+                  ECHO_SERVER_MAX_THREADS, ECHO_SERVER_START_PORT));
+    PJ_LOG(3,("", "...Press Ctrl-C to abort"));
+
+    echo_srv_common_loop(total_bytes);
+    return 0;
+}
+
+
+int echo_srv_common_loop(pj_atomic_t *bytes_counter)
+{
+    pj_highprec_t last_received, avg_bw, highest_bw;
+    pj_time_val last_print;
+    unsigned count;
+    const char *ioqueue_name;
+
+    last_received = 0;
+    pj_gettimeofday(&last_print);
+    avg_bw = highest_bw = 0;
+    count = 0;
+
+    ioqueue_name = pj_ioqueue_name();
+
+    for (;;) {
+        pj_highprec_t received, cur_received, bw;
+        unsigned msec;
+        pj_time_val now, duration;
+
+        pj_thread_sleep(1000);
+
+        received = cur_received = pj_atomic_get(bytes_counter);
+        cur_received = cur_received - last_received;
+
+        pj_gettimeofday(&now);
+        duration = now;
+        PJ_TIME_VAL_SUB(duration, last_print);
+        msec = PJ_TIME_VAL_MSEC(duration);
+        
+        bw = cur_received;
+        pj_highprec_mul(bw, 1000);
+        pj_highprec_div(bw, msec);
+
+        last_print = now;
+        last_received = received;
+
+        avg_bw = avg_bw + bw;
+        count++;
+
+        PJ_LOG(3,("", "%s UDP (%d threads): %u KB/s (avg=%u KB/s) %s", 
+		  ioqueue_name,
+                  ECHO_SERVER_MAX_THREADS, 
+                  (unsigned)(bw / 1000),
+                  (unsigned)(avg_bw / count / 1000),
+                  (count==20 ? "<ses avg>" : "")));
+
+        if (count==20) {
+            if (avg_bw/count > highest_bw)
+                highest_bw = avg_bw/count;
+
+            count = 0;
+            avg_bw = 0;
+
+            PJ_LOG(3,("", "Highest average bandwidth=%u KB/s",
+                          (unsigned)(highest_bw/1000)));
+        }
+    }
+    PJ_UNREACHED(return 0;)
+}
+
+
diff --git a/jni/pjproject-android/pjlib/src/pjlib-test/util.c b/jni/pjproject-android/pjlib/src/pjlib-test/util.c
new file mode 100644
index 0000000..32aa938
--- /dev/null
+++ b/jni/pjproject-android/pjlib/src/pjlib-test/util.c
@@ -0,0 +1,140 @@
+/* $Id: util.c 3553 2011-05-05 06:14:19Z nanang $ */
+/* 
+ * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
+ * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include "test.h"
+#include <pjlib.h>
+
+#define THIS_FILE "util.c"
+
+void app_perror(const char *msg, pj_status_t rc)
+{
+    char errbuf[PJ_ERR_MSG_SIZE];
+
+    PJ_CHECK_STACK();
+
+    pj_strerror(rc, errbuf, sizeof(errbuf));
+    PJ_LOG(3,("test", "%s: [pj_status_t=%d] %s", msg, rc, errbuf));
+}
+
+#define SERVER 0
+#define CLIENT 1
+
+pj_status_t app_socket(int family, int type, int proto, int port,
+                       pj_sock_t *ptr_sock)
+{
+    pj_sockaddr_in addr;
+    pj_sock_t sock;
+    pj_status_t rc;
+
+    rc = pj_sock_socket(family, type, proto, &sock);
+    if (rc != PJ_SUCCESS)
+        return rc;
+
+    pj_bzero(&addr, sizeof(addr));
+    addr.sin_family = (pj_uint16_t)family;
+    addr.sin_port = (short)(port!=-1 ? pj_htons((pj_uint16_t)port) : 0);
+    rc = pj_sock_bind(sock, &addr, sizeof(addr));
+    if (rc != PJ_SUCCESS)
+        return rc;
+    
+#if PJ_HAS_TCP
+    if (type == pj_SOCK_STREAM()) {
+        rc = pj_sock_listen(sock, 5);
+        if (rc != PJ_SUCCESS)
+            return rc;
+    }
+#endif
+
+    *ptr_sock = sock;
+    return PJ_SUCCESS;
+}
+
+pj_status_t app_socketpair(int family, int type, int protocol,
+                           pj_sock_t *serverfd, pj_sock_t *clientfd)
+{
+    int i;
+    static unsigned short port = 11000;
+    pj_sockaddr_in addr;
+    pj_str_t s;
+    pj_status_t rc = 0;
+    pj_sock_t sock[2];
+
+    /* Create both sockets. */
+    for (i=0; i<2; ++i) {
+        rc = pj_sock_socket(family, type, protocol, &sock[i]);
+        if (rc != PJ_SUCCESS) {
+            if (i==1)
+                pj_sock_close(sock[0]);
+            return rc;
+        }
+    }
+
+    /* Retry bind */
+    pj_bzero(&addr, sizeof(addr));
+    addr.sin_family = pj_AF_INET();
+    for (i=0; i<5; ++i) {
+        addr.sin_port = pj_htons(port++);
+        rc = pj_sock_bind(sock[SERVER], &addr, sizeof(addr));
+        if (rc == PJ_SUCCESS)
+            break;
+    }
+
+    if (rc != PJ_SUCCESS)
+        goto on_error;
+
+    /* For TCP, listen the socket. */
+#if PJ_HAS_TCP
+    if (type == pj_SOCK_STREAM()) {
+        rc = pj_sock_listen(sock[SERVER], PJ_SOMAXCONN);
+        if (rc != PJ_SUCCESS)
+            goto on_error;
+    }
+#endif
+
+    /* Connect client socket. */
+    addr.sin_addr = pj_inet_addr(pj_cstr(&s, "127.0.0.1"));
+    rc = pj_sock_connect(sock[CLIENT], &addr, sizeof(addr));
+    if (rc != PJ_SUCCESS)
+        goto on_error;
+
+    /* For TCP, must accept(), and get the new socket. */
+#if PJ_HAS_TCP
+    if (type == pj_SOCK_STREAM()) {
+        pj_sock_t newserver;
+
+        rc = pj_sock_accept(sock[SERVER], &newserver, NULL, NULL);
+        if (rc != PJ_SUCCESS)
+            goto on_error;
+
+        /* Replace server socket with new socket. */
+        pj_sock_close(sock[SERVER]);
+        sock[SERVER] = newserver;
+    }
+#endif
+
+    *serverfd = sock[SERVER];
+    *clientfd = sock[CLIENT];
+
+    return rc;
+
+on_error:
+    for (i=0; i<2; ++i)
+        pj_sock_close(sock[i]);
+    return rc;
+}