* #36737: switch back to svn repo, remove assert in sip_transaction.c
diff --git a/jni/pjproject-android/.svn/pristine/c8/c848a87a144cbcfa440fbb078b9cf1daef599bca.svn-base b/jni/pjproject-android/.svn/pristine/c8/c848a87a144cbcfa440fbb078b9cf1daef599bca.svn-base
new file mode 100644
index 0000000..7cc9920
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c848a87a144cbcfa440fbb078b9cf1daef599bca.svn-base
@@ -0,0 +1,63 @@
+/* $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 "test.h"
+#include <pj/string.h>
+
+#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
+
+#define boost()
+
+int main(int argc, char *argv[])
+{
+    int rc;
+
+    PJ_UNUSED_ARG(argc);
+    PJ_UNUSED_ARG(argv);
+
+    boost();
+    init_signals();
+
+    rc = test_main();
+
+    if (argc==2 && pj_ansi_strcmp(argv[1], "-i")==0) {
+	char s[10];
+
+	puts("Press ENTER to quit");
+	if (fgets(s, sizeof(s), stdin) == NULL)
+	    return rc;
+    }
+
+    return rc;
+}
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8500ba78b4fc9de1d5e3abc6f3ab02a63676a9e.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8500ba78b4fc9de1d5e3abc6f3ab02a63676a9e.svn-base
new file mode 100644
index 0000000..cefec10
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8500ba78b4fc9de1d5e3abc6f3ab02a63676a9e.svn-base
@@ -0,0 +1,43 @@
+/* $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/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/.svn/pristine/c8/c86c1c59d11fb99c125c5d36b8e477fe51a05ae2.svn-base b/jni/pjproject-android/.svn/pristine/c8/c86c1c59d11fb99c125c5d36b8e477fe51a05ae2.svn-base
new file mode 100644
index 0000000..5b68ead
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c86c1c59d11fb99c125c5d36b8e477fe51a05ae2.svn-base
@@ -0,0 +1,117 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2010 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 
+ */
+#ifndef __PJLIB_UTIL_CLI_CONSOLE_H__
+#define __PJLIB_UTIL_CLI_CONSOLE_H__
+
+/**
+ * @file cli_console.h
+ * @brief Command Line Interface Console Front End API
+ */
+
+#include <pjlib-util/cli_imp.h>
+
+
+PJ_BEGIN_DECL
+
+/**
+ * @ingroup PJLIB_UTIL_CLI_IMP
+ * @{
+ *
+ */
+
+
+/**
+ * This structure contains various options for CLI console front-end.
+ * Application must call pj_cli_console_cfg_default() to initialize
+ * this structure with its default values.
+ */
+typedef struct pj_cli_console_cfg
+{
+    /**
+     * Default log verbosity level for the session.
+     *
+     * Default value: PJ_CLI_CONSOLE_LOG_LEVEL
+     */
+    int log_level;
+
+    /**
+     * Specify text message as a prompt string to user.
+     *
+     * Default: empty
+     */
+    pj_str_t prompt_str;
+
+    /**
+     * Specify the command to quit the application.
+     *
+     * Default: empty
+     */
+    pj_str_t quit_command;
+
+} pj_cli_console_cfg;
+
+
+/**
+ * Initialize pj_cli_console_cfg with its default values.
+ *
+ * @param param		The structure to be initialized.
+ */
+PJ_DECL(void) pj_cli_console_cfg_default(pj_cli_console_cfg *param);
+
+
+/**
+ * Create a console front-end for the specified CLI application, and return
+ * the only session instance for the console front end. On Windows operating
+ * system, this may open a new console window.
+ *
+ * @param cli		The CLI application instance.
+ * @param param		Optional console CLI parameters. If this value is
+ * 			NULL, default parameters will be used.
+ * @param p_sess	Pointer to receive the session instance for the
+ * 			console front end.
+ * @param p_fe		Optional pointer to receive the front-end instance
+ * 			of the console front-end just created.
+ *
+ * @return		PJ_SUCCESS on success, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_cli_console_create(pj_cli_t *cli,
+					   const pj_cli_console_cfg *param,
+					   pj_cli_sess **p_sess,
+					   pj_cli_front_end **p_fe);
+
+/**
+ * Retrieve a cmdline from console stdin and process the input accordingly.
+ *
+ * @param sess		The CLI session.
+ * @param buf		Pointer to receive the buffer.
+ * @param maxlen	Maximum length to read.
+ *
+ * @return		PJ_SUCCESS if an input was read
+ */
+PJ_DECL(pj_status_t) pj_cli_console_process(pj_cli_sess *sess, 
+					    char *buf,
+					    unsigned maxlen);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJLIB_UTIL_CLI_CONSOLE_H__ */
diff --git a/jni/pjproject-android/.svn/pristine/c8/c87a184e8b8dfa874bd63777fe622b76e18d31b2.svn-base b/jni/pjproject-android/.svn/pristine/c8/c87a184e8b8dfa874bd63777fe622b76e18d31b2.svn-base
new file mode 100644
index 0000000..4dd73e0
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c87a184e8b8dfa874bd63777fe622b76e18d31b2.svn-base
@@ -0,0 +1,36 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+sdp = \
+"""
+v=0
+o=- 0 0 IN IP4 127.0.0.1
+s=pjmedia
+c=IN IP4 127.0.0.1
+t=0 0
+m=audio 4000 RTP/AVP 0 101
+a=rtpmap:0 PCMU/8000
+a=sendrecv
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-15
+"""
+
+# RFC 4028 Section 9:
+#   If the incoming request contains a Supported header field with a
+#   value 'timer' but does not contain a Session-Expires header, it means
+#   that the UAS is indicating support for timers but is not requesting
+#   one.  The UAS may request a session timer in the 2XX response by
+#   including a Session-Expires header field.  The value MUST NOT be set
+#   to a duration lower than the value in the Min-SE header field in the
+#   request, if it is present.
+   
+pjsua_args = "--null-audio --auto-answer 200 --use-timer 2 --timer-min-se 90 --timer-se 1800"
+extra_headers = "Supported: timer\n"
+include = ["Session-Expires: .*;refresher=.*"]
+exclude = []
+sendto_cfg = sip.SendtoCfg("Session Timer initiated by UAS", pjsua_args, sdp, 200, 
+			   extra_headers=extra_headers,
+			   resp_inc=include, resp_exc=exclude) 
+			   
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8b3d783969c6a925a1d8af5b2844c354e6f046c.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8b3d783969c6a925a1d8af5b2844c354e6f046c.svn-base
new file mode 100644
index 0000000..70c8b68
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8b3d783969c6a925a1d8af5b2844c354e6f046c.svn-base
@@ -0,0 +1,15 @@
+@ECHO OFF
+
+REM Bldmake-generated batch file - ABLD.BAT
+REM ** DO NOT EDIT **
+
+perl -S ABLD.PL "\Users\Teluu\pjsip\trunk\pjsip-apps\src\pjsua\symbian\group\\" %1 %2 %3 %4 %5 %6 %7 %8 %9
+if errorlevel==1 goto CheckPerl
+goto End
+
+:CheckPerl
+perl -v >NUL
+if errorlevel==1 echo Is Perl, version 5.003_07 or later, installed?
+goto End
+
+:End
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8ba373748c0a69fd1014b41ac12639cbcde1c13.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8ba373748c0a69fd1014b41ac12639cbcde1c13.svn-base
new file mode 100644
index 0000000..df6155a
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8ba373748c0a69fd1014b41ac12639cbcde1c13.svn-base
@@ -0,0 +1,370 @@
+/* $Id$ */
+/* 
+ * 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 __PJ_TIMER_H__
+#define __PJ_TIMER_H__
+
+/**
+ * @file timer.h
+ * @brief Timer Heap
+ */
+
+#include <pj/types.h>
+#include <pj/lock.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJ_TIMER Timer Heap Management.
+ * @ingroup PJ_MISC
+ * @brief
+ * The timer scheduling implementation here is based on ACE library's 
+ * ACE_Timer_Heap, with only little modification to suit our library's style
+ * (I even left most of the comments in the original source).
+ *
+ * To quote the original quote in ACE_Timer_Heap_T class:
+ *
+ *      This implementation uses a heap-based callout queue of
+ *      absolute times.  Therefore, in the average and worst case,
+ *      scheduling, canceling, and expiring timers is O(log N) (where
+ *      N is the total number of timers).  In addition, we can also
+ *      preallocate as many \a ACE_Timer_Nodes as there are slots in
+ *      the heap.  This allows us to completely remove the need for
+ *      dynamic memory allocation, which is important for real-time
+ *      systems.
+ *
+ * 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>
+ *
+ * @{
+ *
+ * \section pj_timer_examples_sec Examples
+ *
+ * For some examples on how to use the timer heap, please see the link below.
+ *
+ *  - \ref page_pjlib_timer_test
+ */
+
+
+/**
+ * The type for internal timer ID.
+ */
+typedef int pj_timer_id_t;
+
+/** 
+ * Forward declaration for pj_timer_entry. 
+ */
+struct pj_timer_entry;
+
+/**
+ * The type of callback function to be called by timer scheduler when a timer
+ * has expired.
+ *
+ * @param timer_heap    The timer heap.
+ * @param entry         Timer entry which timer's has expired.
+ */
+typedef void pj_timer_heap_callback(pj_timer_heap_t *timer_heap,
+				    struct pj_timer_entry *entry);
+
+
+/**
+ * This structure represents an entry to the timer.
+ */
+typedef struct pj_timer_entry
+{
+    /** 
+     * User data to be associated with this entry. 
+     * Applications normally will put the instance of object that
+     * owns the timer entry in this field.
+     */
+    void *user_data;
+
+    /** 
+     * Arbitrary ID assigned by the user/owner of this entry. 
+     * Applications can use this ID to distinguish multiple
+     * timer entries that share the same callback and user_data.
+     */
+    int id;
+
+    /** 
+     * Callback to be called when the timer expires. 
+     */
+    pj_timer_heap_callback *cb;
+
+    /** 
+     * Internal unique timer ID, which is assigned by the timer heap. 
+     * Application should not touch this ID.
+     */
+    pj_timer_id_t _timer_id;
+
+    /** 
+     * The future time when the timer expires, which the value is updated
+     * by timer heap when the timer is scheduled.
+     */
+    pj_time_val _timer_value;
+
+    /**
+     * Internal: the group lock used by this entry, set when
+     * pj_timer_heap_schedule_w_lock() is used.
+     */
+    pj_grp_lock_t *_grp_lock;
+
+#if PJ_TIMER_DEBUG
+    const char	*src_file;
+    int		 src_line;
+#endif
+} pj_timer_entry;
+
+
+/**
+ * Calculate memory size required to create a timer heap.
+ *
+ * @param count     Number of timer entries to be supported.
+ * @return          Memory size requirement in bytes.
+ */
+PJ_DECL(pj_size_t) pj_timer_heap_mem_size(pj_size_t count);
+
+/**
+ * Create a timer heap.
+ *
+ * @param pool      The pool where allocations in the timer heap will be 
+ *                  allocated. The timer heap will dynamicly allocate 
+ *                  more storate from the pool if the number of timer 
+ *                  entries registered is more than the size originally 
+ *                  requested when calling this function.
+ * @param count     The maximum number of timer entries to be supported 
+ *                  initially. If the application registers more entries 
+ *                  during runtime, then the timer heap will resize.
+ * @param ht        Pointer to receive the created timer heap.
+ *
+ * @return          PJ_SUCCESS, or the appropriate error code.
+ */
+PJ_DECL(pj_status_t) pj_timer_heap_create( pj_pool_t *pool,
+					   pj_size_t count,
+                                           pj_timer_heap_t **ht);
+
+/**
+ * Destroy the timer heap.
+ *
+ * @param ht        The timer heap.
+ */
+PJ_DECL(void) pj_timer_heap_destroy( pj_timer_heap_t *ht );
+
+
+/**
+ * Set lock object to be used by the timer heap. By default, the timer heap
+ * uses dummy synchronization.
+ *
+ * @param ht        The timer heap.
+ * @param lock      The lock object to be used for synchronization.
+ * @param auto_del  If nonzero, the lock object will be destroyed when
+ *                  the timer heap is destroyed.
+ */
+PJ_DECL(void) pj_timer_heap_set_lock( pj_timer_heap_t *ht,
+                                      pj_lock_t *lock,
+                                      pj_bool_t auto_del );
+
+/**
+ * Set maximum number of timed out entries to process in a single poll.
+ *
+ * @param ht        The timer heap.
+ * @param count     Number of entries.
+ *
+ * @return          The old number.
+ */
+PJ_DECL(unsigned) pj_timer_heap_set_max_timed_out_per_poll(pj_timer_heap_t *ht,
+                                                           unsigned count );
+
+/**
+ * Initialize a timer entry. Application should call this function at least
+ * once before scheduling the entry to the timer heap, to properly initialize
+ * the timer entry.
+ *
+ * @param entry     The timer entry to be initialized.
+ * @param id        Arbitrary ID assigned by the user/owner of this entry.
+ *                  Applications can use this ID to distinguish multiple
+ *                  timer entries that share the same callback and user_data.
+ * @param user_data User data to be associated with this entry. 
+ *                  Applications normally will put the instance of object that
+ *                  owns the timer entry in this field.
+ * @param cb        Callback function to be called when the timer elapses.
+ *
+ * @return          The timer entry itself.
+ */
+PJ_DECL(pj_timer_entry*) pj_timer_entry_init( pj_timer_entry *entry,
+                                              int id,
+                                              void *user_data,
+                                              pj_timer_heap_callback *cb );
+
+/**
+ * Queries whether a timer entry is currently running.
+ *
+ * @param entry     The timer entry to query.
+ *
+ * @return          PJ_TRUE if the timer is running.  PJ_FALSE if not.
+ */
+PJ_DECL(pj_bool_t) pj_timer_entry_running( pj_timer_entry *entry );
+
+/**
+ * Schedule a timer entry which will expire AFTER the specified delay.
+ *
+ * @param ht        The timer heap.
+ * @param entry     The entry to be registered. 
+ * @param delay     The interval to expire.
+ * @return          PJ_SUCCESS, or the appropriate error code.
+ */
+#if PJ_TIMER_DEBUG
+#  define pj_timer_heap_schedule(ht,e,d) \
+			pj_timer_heap_schedule_dbg(ht,e,d,__FILE__,__LINE__)
+
+  PJ_DECL(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);
+#else
+PJ_DECL(pj_status_t) pj_timer_heap_schedule( pj_timer_heap_t *ht,
+					     pj_timer_entry *entry, 
+					     const pj_time_val *delay);
+#endif	/* PJ_TIMER_DEBUG */
+
+/**
+ * Schedule a timer entry which will expire AFTER the specified delay, and
+ * increment the reference counter of the group lock while the timer entry
+ * is active. The group lock reference counter will automatically be released
+ * after the timer callback is called or when the timer is cancelled.
+ *
+ * @param ht        The timer heap.
+ * @param entry     The entry to be registered.
+ * @param id_val    The value to be set to the "id" field of the timer entry
+ * 		    once the timer is scheduled.
+ * @param delay     The interval to expire.
+ * @param grp_lock  The group lock.
+ *
+ * @return          PJ_SUCCESS, or the appropriate error code.
+ */
+#if PJ_TIMER_DEBUG
+#  define pj_timer_heap_schedule_w_grp_lock(ht,e,d,id,g) \
+	pj_timer_heap_schedule_w_grp_lock_dbg(ht,e,d,id,g,__FILE__,__LINE__)
+
+  PJ_DECL(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);
+#else
+PJ_DECL(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);
+#endif	/* PJ_TIMER_DEBUG */
+
+
+/**
+ * Cancel a previously registered timer. This will also decrement the
+ * reference counter of the group lock associated with the timer entry,
+ * if the entry was scheduled with one.
+ *
+ * @param ht        The timer heap.
+ * @param entry     The entry to be cancelled.
+ * @return          The number of timer cancelled, which should be one if the
+ *                  entry has really been registered, or zero if no timer was
+ *                  cancelled.
+ */
+PJ_DECL(int) pj_timer_heap_cancel( pj_timer_heap_t *ht,
+				   pj_timer_entry *entry);
+
+/**
+ * Cancel only if the previously registered timer is active. This will
+ * also decrement the reference counter of the group lock associated
+ * with the timer entry, if the entry was scheduled with one. In any
+ * case, set the "id" to the specified value.
+ *
+ * @param ht        The timer heap.
+ * @param entry     The entry to be cancelled.
+ * @param id_val    Value to be set to "id"
+ *
+ * @return          The number of timer cancelled, which should be one if the
+ *                  entry has really been registered, or zero if no timer was
+ *                  cancelled.
+ */
+PJ_DECL(int) pj_timer_heap_cancel_if_active(pj_timer_heap_t *ht,
+                                            pj_timer_entry *entry,
+                                            int id_val);
+
+/**
+ * Get the number of timer entries.
+ *
+ * @param ht        The timer heap.
+ * @return          The number of timer entries.
+ */
+PJ_DECL(pj_size_t) pj_timer_heap_count( pj_timer_heap_t *ht );
+
+/**
+ * Get the earliest time registered in the timer heap. The timer heap
+ * MUST have at least one timer being scheduled (application should use
+ * #pj_timer_heap_count() before calling this function).
+ *
+ * @param ht        The timer heap.
+ * @param timeval   The time deadline of the earliest timer entry.
+ *
+ * @return          PJ_SUCCESS, or PJ_ENOTFOUND if no entry is scheduled.
+ */
+PJ_DECL(pj_status_t) pj_timer_heap_earliest_time( pj_timer_heap_t *ht, 
+					          pj_time_val *timeval);
+
+/**
+ * Poll the timer heap, check for expired timers and call the callback for
+ * each of the expired timers.
+ *
+ * Note: polling the timer heap is not necessary in Symbian. Please see
+ * @ref PJ_SYMBIAN_OS for more info.
+ *
+ * @param ht         The timer heap.
+ * @param next_delay If this parameter is not NULL, it will be filled up with
+ *		     the time delay until the next timer elapsed, or 
+ *		     PJ_MAXINT32 in the sec part if no entry exist.
+ *
+ * @return           The number of timers expired.
+ */
+PJ_DECL(unsigned) pj_timer_heap_poll( pj_timer_heap_t *ht, 
+                                      pj_time_val *next_delay);
+
+#if PJ_TIMER_DEBUG
+/**
+ * Dump timer heap entries.
+ *
+ * @param ht	    The timer heap.
+ */
+PJ_DECL(void) pj_timer_heap_dump(pj_timer_heap_t *ht);
+#endif
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif	/* __PJ_TIMER_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8bb8cc339f400fd82fc0d272632dd1fdebe81f9.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8bb8cc339f400fd82fc0d272632dd1fdebe81f9.svn-base
new file mode 100644
index 0000000..c54c0c4
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8bb8cc339f400fd82fc0d272632dd1fdebe81f9.svn-base
@@ -0,0 +1,64 @@
+# $Id$
+# Useful constants
+
+
+##########################
+# MENU OUTPUT
+#
+
+
+##########################
+# EVENTS
+#
+
+# Text to expect when there is incoming call
+EVENT_INCOMING_CALL = "Press .* answer"
+
+
+##########################
+# CALL STATES
+#
+
+# Call state is CALLING
+STATE_CALLING = "state.*CALLING"
+# Call state is EARLY
+STATE_EARLY = "state.*EARLY"
+# Call state is CONFIRMED
+STATE_CONFIRMED = "state.*CONFIRMED"
+# Call state is DISCONNECTED
+STATE_DISCONNECTED = "Call .* DISCONNECTED"
+
+# Media call is put on-hold
+MEDIA_HOLD = "Call [0-9]+ media [0-9]+ .*, status is .* hold"
+# Media call is active
+MEDIA_ACTIVE = "Call [0-9]+ media [0-9]+ .*, status is Active"
+#MEDIA_ACTIVE = "Media for call [0-9]+ is active"
+# RX_DTMF
+RX_DTMF = "Incoming DTMF on call [0-9]+: "
+
+##########################
+# MEDIA
+#
+
+# Connecting/disconnecting ports
+MEDIA_CONN_PORT_SUCCESS = "Port \d+ \(.+\) transmitting to port"
+MEDIA_DISCONN_PORT_SUCCESS = "Port \d+ \(.+\) stop transmitting to port"
+
+# Filename to play / record
+MEDIA_PLAY_FILE = "--play-file\s+(\S+)"
+MEDIA_REC_FILE = "--rec-file\s+(\S+)"
+
+##########################
+# MISC
+#
+
+# The command prompt
+PROMPT = ">>>"
+# When pjsua has been destroyed
+DESTROYED = "PJSUA destroyed"
+# Assertion failure
+ASSERT = "Assertion failed"
+# Stdout refresh text
+STDOUT_REFRESH = "XXSTDOUT_REFRESHXX"
+
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8d9de641a1648d486c7069e43d61bc35f39c572.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8d9de641a1648d486c7069e43d61bc35f39c572.svn-base
new file mode 100644
index 0000000..659d3a4
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8d9de641a1648d486c7069e43d61bc35f39c572.svn-base
@@ -0,0 +1,88 @@
+/* $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 
+ */
+#ifndef __PJ_COMPAT_CC_MSVC_H__
+#define __PJ_COMPAT_CC_MSVC_H__
+
+/**
+ * @file cc_msvc.h
+ * @brief Describes Microsoft Visual C compiler specifics.
+ */
+
+#ifndef _MSC_VER
+#  error "This header file is only for Visual C compiler!"
+#endif
+
+#define PJ_CC_NAME	    "msvc"
+#define PJ_CC_VER_1	    (_MSC_VER/100)
+#define PJ_CC_VER_2	    (_MSC_VER%100)
+#define PJ_CC_VER_3	    0
+
+/* Disable CRT deprecation warnings. */
+#if PJ_CC_VER_1 >= 8 && !defined(_CRT_SECURE_NO_DEPRECATE)
+#   define _CRT_SECURE_NO_DEPRECATE
+#endif
+#if PJ_CC_VER_1 >= 8 && !defined(_CRT_SECURE_NO_WARNINGS)
+#   define _CRT_SECURE_NO_WARNINGS
+    /* The above doesn't seem to work, at least on VS2005, so lets use
+     * this construct as well.
+     */
+#   pragma warning(disable: 4996)
+#endif
+
+#pragma warning(disable: 4127) // conditional expression is constant
+#pragma warning(disable: 4611) // not wise to mix setjmp with C++
+#pragma warning(disable: 4514) // unref. inline function has been removed
+#ifdef NDEBUG
+#  pragma warning(disable: 4702) // unreachable code
+#  pragma warning(disable: 4710) // function is not inlined.
+#  pragma warning(disable: 4711) // function selected for auto inline expansion
+#endif
+
+#ifdef __cplusplus
+#  define PJ_INLINE_SPECIFIER	inline
+#else
+#  define PJ_INLINE_SPECIFIER	static __inline
+#endif
+
+#define PJ_EXPORT_DECL_SPECIFIER    __declspec(dllexport)
+#define PJ_EXPORT_DEF_SPECIFIER	    __declspec(dllexport)
+#define PJ_IMPORT_DECL_SPECIFIER    __declspec(dllimport)
+
+#define PJ_THREAD_FUNC	
+#define PJ_NORETURN		__declspec(noreturn)
+#define PJ_ATTR_NORETURN	
+#define PJ_ATTR_MAY_ALIAS	
+
+#define PJ_HAS_INT64	1
+
+typedef __int64 pj_int64_t;
+typedef unsigned __int64 pj_uint64_t;
+
+#define PJ_INT64(val)		val##i64
+#define PJ_UINT64(val)		val##ui64
+#define PJ_INT64_FMT		"I64"
+
+#define PJ_UNREACHED(x)	    	
+
+#define PJ_ALIGN_DATA(declaration, alignment) __declspec(align(alignment)) declaration
+
+
+#endif	/* __PJ_COMPAT_CC_MSVC_H__ */
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8e190c7112b7d38394ee4eb6e6bba1d51390c76.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8e190c7112b7d38394ee4eb6e6bba1d51390c76.svn-base
new file mode 100644
index 0000000..30725b5
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8e190c7112b7d38394ee4eb6e6bba1d51390c76.svn-base
@@ -0,0 +1,1933 @@
+/* $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 <pjsua-lib/pjsua.h>
+#include "pjsua_app_common.h"
+
+#define THIS_FILE	"pjsua_app_legacy.c"
+
+static pj_bool_t	cmd_echo;
+
+/*
+ * Print buddy list.
+ */
+static void print_buddy_list()
+{
+    pjsua_buddy_id ids[64];
+    int i;
+    unsigned count = PJ_ARRAY_SIZE(ids);
+
+    puts("Buddy list:");
+
+    pjsua_enum_buddies(ids, &count);
+
+    if (count == 0)
+	puts(" -none-");
+    else {
+	for (i=0; i<(int)count; ++i) {
+	    pjsua_buddy_info info;
+
+	    if (pjsua_buddy_get_info(ids[i], &info) != PJ_SUCCESS)
+		continue;
+
+	    printf(" [%2d] <%.*s>  %.*s\n", 
+		    ids[i]+1, 
+		    (int)info.status_text.slen,
+		    info.status_text.ptr, 
+		    (int)info.uri.slen,
+		    info.uri.ptr);
+	}
+    }
+    puts("");
+}
+
+/*
+ * Input URL.
+ */
+static void ui_input_url(const char *title, char *buf, pj_size_t len, 
+			 input_result *result)
+{
+    result->nb_result = PJSUA_APP_NO_NB;
+    result->uri_result = NULL;
+
+    print_buddy_list();
+
+    printf("Choices:\n"
+	   "   0         For current dialog.\n"
+	   "  -1         All %d buddies in buddy list\n"
+	   "  [1 -%2d]    Select from buddy list\n"
+	   "  URL        An URL\n"
+	   "  <Enter>    Empty input (or 'q') to cancel\n"
+	   , pjsua_get_buddy_count(), pjsua_get_buddy_count());
+    printf("%s: ", title);
+
+    fflush(stdout);
+    if (fgets(buf, (int)len, stdin) == NULL)
+	return;
+    len = strlen(buf);
+
+    /* Left trim */
+    while (pj_isspace(*buf)) {
+	++buf;
+	--len;
+    }
+
+    /* Remove trailing newlines */
+    while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
+	buf[--len] = '\0';
+
+    if (len == 0 || buf[0]=='q')
+	return;
+
+    if (pj_isdigit(*buf) || *buf=='-') {
+	
+	unsigned i;
+	
+	if (*buf=='-')
+	    i = 1;
+	else
+	    i = 0;
+
+	for (; i<len; ++i) {
+	    if (!pj_isdigit(buf[i])) {
+		puts("Invalid input");
+		return;
+	    }
+	}
+
+	result->nb_result = my_atoi(buf);
+
+	if (result->nb_result >= 0 && 
+	    result->nb_result <= (int)pjsua_get_buddy_count()) 
+	{
+	    return;
+	}
+	if (result->nb_result == -1)
+	    return;
+
+	puts("Invalid input");
+	result->nb_result = PJSUA_APP_NO_NB;
+	return;
+
+    } else {
+	pj_status_t status;
+
+	if ((status=pjsua_verify_url(buf)) != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Invalid URL", status);
+	    return;
+	}
+
+	result->uri_result = buf;
+    }
+}
+
+static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
+{
+    char *p;
+
+    printf("%s (empty to cancel): ", title); fflush(stdout);
+    if (fgets(buf, (int)len, stdin) == NULL)
+	return PJ_FALSE;
+
+    /* Remove trailing newlines. */
+    for (p=buf; ; ++p) {
+	if (*p=='\r' || *p=='\n') *p='\0';
+	else if (!*p) break;
+    }
+
+    if (!*buf)
+	return PJ_FALSE;
+    
+    return PJ_TRUE;
+}
+
+/*
+ * Print account status.
+ */
+static void print_acc_status(int acc_id)
+{
+    char buf[80];
+    pjsua_acc_info info;
+
+    pjsua_acc_get_info(acc_id, &info);
+
+    if (!info.has_registration) {
+	pj_ansi_snprintf(buf, sizeof(buf), "%.*s", 
+			 (int)info.status_text.slen,
+			 info.status_text.ptr);
+
+    } else {
+	pj_ansi_snprintf(buf, sizeof(buf),
+			 "%d/%.*s (expires=%d)",
+			 info.status,
+			 (int)info.status_text.slen,
+			 info.status_text.ptr,
+			 info.expires);
+
+    }
+
+    printf(" %c[%2d] %.*s: %s\n", (acc_id==current_acc?'*':' '),
+	   acc_id,  (int)info.acc_uri.slen, info.acc_uri.ptr, buf);
+    printf("       Online status: %.*s\n", 
+	(int)info.online_status_text.slen,
+	info.online_status_text.ptr);
+}
+
+/*
+ * Show a bit of help.
+ */
+static void keystroke_help()
+{
+    pjsua_acc_id acc_ids[16];
+    unsigned count = PJ_ARRAY_SIZE(acc_ids);
+    int i;
+
+    printf(">>>>\n");
+
+    pjsua_enum_accs(acc_ids, &count);
+
+    printf("Account list:\n");
+    for (i=0; i<(int)count; ++i)
+	print_acc_status(acc_ids[i]);
+
+    print_buddy_list();
+    
+    //puts("Commands:");
+    puts("+=============================================================================+");
+    puts("|       Call Commands:         |   Buddy, IM & Presence:  |     Account:      |");
+    puts("|                              |                          |                   |");
+    puts("|  m  Make new call            | +b  Add new buddy       .| +a  Add new accnt |");
+    puts("|  M  Make multiple calls      | -b  Delete buddy         | -a  Delete accnt. |");
+    puts("|  a  Answer call              |  i  Send IM              | !a  Modify accnt. |");
+    puts("|  h  Hangup call  (ha=all)    |  s  Subscribe presence   | rr  (Re-)register |");
+    puts("|  H  Hold call                |  u  Unsubscribe presence | ru  Unregister    |");
+    puts("|  v  re-inVite (release hold) |  t  ToGgle Online status |  >  Cycle next ac.|");
+    puts("|  U  send UPDATE              |  T  Set online status    |  <  Cycle prev ac.|");
+    puts("| ],[ Select next/prev call    +--------------------------+-------------------+");
+    puts("|  x  Xfer call                |      Media Commands:     |  Status & Config: |");
+    puts("|  X  Xfer with Replaces       |                          |                   |");
+    puts("|  #  Send RFC 2833 DTMF       | cl  List ports           |  d  Dump status   |");
+    puts("|  *  Send DTMF with INFO      | cc  Connect port         | dd  Dump detailed |");
+    puts("| dq  Dump curr. call quality  | cd  Disconnect port      | dc  Dump config   |");
+    puts("|                              |  V  Adjust audio Volume  |  f  Save config   |");
+    puts("|  S  Send arbitrary REQUEST   | Cp  Codec priorities     |                   |");
+    puts("+-----------------------------------------------------------------------------+");
+#if PJSUA_HAS_VIDEO
+    puts("| Video: \"vid help\" for more info                                             |");
+    puts("+-----------------------------------------------------------------------------+");
+#endif
+    puts("|  q  QUIT   L  ReLoad   sleep MS   echo [0|1|txt]     n: detect NAT type     |");
+    puts("+=============================================================================+");
+
+    i = pjsua_call_get_count();
+    printf("You have %d active call%s\n", i, (i>1?"s":""));
+
+    if (current_call != PJSUA_INVALID_ID) {
+	pjsua_call_info ci;
+	if (pjsua_call_get_info(current_call, &ci)==PJ_SUCCESS)
+	    printf("Current call id=%d to %.*s [%.*s]\n", current_call,
+		   (int)ci.remote_info.slen, ci.remote_info.ptr,
+		   (int)ci.state_text.slen, ci.state_text.ptr);
+    }
+}
+
+/* Help screen for video */
+#if PJSUA_HAS_VIDEO
+static void vid_show_help()
+{
+    pj_bool_t vid_enabled = (app_config.vid.vid_cnt > 0);
+
+    puts("+=============================================================================+");
+    puts("|                            Video commands:                                  |");
+    puts("|                                                                             |");
+    puts("| vid help                  Show this help screen                             |");
+    puts("| vid enable|disable        Enable or disable video in next offer/answer      |");
+    puts("| vid acc show              Show current account video settings               |");
+    puts("| vid acc autorx on|off     Automatically show incoming video on/off          |");
+    puts("| vid acc autotx on|off     Automatically offer video on/off                  |");
+    puts("| vid acc cap ID            Set default capture device for current acc        |");
+    puts("| vid acc rend ID           Set default renderer device for current acc       |");
+    puts("| vid call rx on|off N      Enable/disable video RX for stream N in curr call |");
+    puts("| vid call tx on|off N      Enable/disable video TX for stream N in curr call |");
+    puts("| vid call add              Add video stream for current call                 |");
+    puts("| vid call enable|disable N Enable/disable stream #N in current call          |");
+    puts("| vid call cap N ID         Set capture dev ID for stream #N in current call  |");
+    puts("| vid dev list              List all video devices                            |");
+    puts("| vid dev refresh           Refresh video device list                         |");
+    puts("| vid dev prev on|off ID    Enable/disable preview for specified device ID    |");
+    puts("| vid codec list            List video codecs                                 |");
+    puts("| vid codec prio ID PRIO    Set codec ID priority to PRIO                     |");
+    puts("| vid codec fps ID NUM DEN  Set codec ID framerate to (NUM/DEN) fps           |");
+    puts("| vid codec bw ID AVG MAX   Set codec ID bitrate to AVG & MAX kbps            |");
+    puts("| vid codec size ID W H     Set codec ID size/resolution to W x H             |");
+    puts("| vid win list              List all active video windows                     |");
+    puts("| vid win arrange           Auto arrange windows                              |");
+    puts("| vid win show|hide ID      Show/hide the specified video window ID           |");
+    puts("| vid win move ID X Y       Move window ID to position X,Y                    |");
+    puts("| vid win resize ID w h     Resize window ID to the specified width, height   |");
+    puts("+=============================================================================+");
+    printf("| Video will be %s in the next offer/answer %s                            |\n",
+	   (vid_enabled? "enabled" : "disabled"), (vid_enabled? " " : ""));
+    puts("+=============================================================================+");
+}
+
+static void vid_handle_menu(char *menuin)
+{
+    char *argv[8];
+    int argc = 0;
+
+    /* Tokenize */
+    argv[argc] = strtok(menuin, " \t\r\n");
+    while (argv[argc] && *argv[argc]) {
+	argc++;
+	argv[argc] = strtok(NULL, " \t\r\n");
+    }
+
+    if (argc == 1 || strcmp(argv[1], "help")==0) {
+	vid_show_help();
+    } else if (argc == 2 && (strcmp(argv[1], "enable")==0 ||
+			     strcmp(argv[1], "disable")==0))
+    {
+	pj_bool_t enabled = (strcmp(argv[1], "enable")==0);
+	app_config.vid.vid_cnt = (enabled ? 1 : 0);
+	PJ_LOG(3,(THIS_FILE, "Video will be %s in next offer/answer",
+		  (enabled?"enabled":"disabled")));
+    } else if (strcmp(argv[1], "acc")==0) {
+	pjsua_acc_config acc_cfg;
+	pj_bool_t changed = PJ_FALSE;
+	pj_pool_t *tmp_pool = pjsua_pool_create("tmp-pjsua", 1000, 1000);
+
+	pjsua_acc_get_config(current_acc, tmp_pool, &acc_cfg);
+
+	if (argc == 3 && strcmp(argv[2], "show")==0) {
+	    app_config_show_video(current_acc, &acc_cfg);
+	} else if (argc == 4 && strcmp(argv[2], "autorx")==0) {
+	    int on = (strcmp(argv[3], "on")==0);
+	    acc_cfg.vid_in_auto_show = on;
+	    changed = PJ_TRUE;
+	} else if (argc == 4 && strcmp(argv[2], "autotx")==0) {
+	    int on = (strcmp(argv[3], "on")==0);
+	    acc_cfg.vid_out_auto_transmit = on;
+	    changed = PJ_TRUE;
+	} else if (argc == 4 && strcmp(argv[2], "cap")==0) {
+	    int dev = atoi(argv[3]);
+	    acc_cfg.vid_cap_dev = dev;
+	    changed = PJ_TRUE;
+	} else if (argc == 4 && strcmp(argv[2], "rend")==0) {
+	    int dev = atoi(argv[3]);
+	    acc_cfg.vid_rend_dev = dev;
+	    changed = PJ_TRUE;
+	} else {
+	    pj_pool_release(tmp_pool);
+	    goto on_error;
+	}
+
+	if (changed) {
+	    pj_status_t status = pjsua_acc_modify(current_acc, &acc_cfg);
+	    if (status != PJ_SUCCESS)
+		PJ_PERROR(1,(THIS_FILE, status, "Error modifying account %d",
+			     current_acc));
+	}
+	pj_pool_release(tmp_pool);
+
+    } else if (strcmp(argv[1], "call")==0) {
+	pjsua_call_vid_strm_op_param param;
+	pj_status_t status = PJ_SUCCESS;
+
+	pjsua_call_vid_strm_op_param_default(&param);
+
+	if (argc == 5 && strcmp(argv[2], "rx")==0) {
+	    pjsua_stream_info si;
+	    pj_bool_t on = (strcmp(argv[3], "on") == 0);
+
+	    param.med_idx = atoi(argv[4]);
+	    if (pjsua_call_get_stream_info(current_call, param.med_idx, &si) ||
+		si.type != PJMEDIA_TYPE_VIDEO)
+	    {
+		PJ_PERROR(1,(THIS_FILE, PJ_EINVAL, "Invalid stream"));
+		return;
+	    }
+
+	    if (on) param.dir = (si.info.vid.dir | PJMEDIA_DIR_DECODING);
+	    else param.dir = (si.info.vid.dir & PJMEDIA_DIR_ENCODING);
+
+	    status = pjsua_call_set_vid_strm(current_call,
+	                                     PJSUA_CALL_VID_STRM_CHANGE_DIR,
+	                                     &param);
+	}
+	else if (argc == 5 && strcmp(argv[2], "tx")==0) {
+	    pj_bool_t on = (strcmp(argv[3], "on") == 0);
+	    pjsua_call_vid_strm_op op = on? PJSUA_CALL_VID_STRM_START_TRANSMIT :
+					    PJSUA_CALL_VID_STRM_STOP_TRANSMIT;
+
+	    param.med_idx = atoi(argv[4]);
+
+	    status = pjsua_call_set_vid_strm(current_call, op, &param);
+	}
+	else if (argc == 3 && strcmp(argv[2], "add")==0) {
+	    status = pjsua_call_set_vid_strm(current_call,
+	                                     PJSUA_CALL_VID_STRM_ADD, NULL);
+	}
+	else if (argc >= 3 && 
+		 (strcmp(argv[2], "disable")==0 || strcmp(argv[2], "enable")==0))
+	{
+	    pj_bool_t enable = (strcmp(argv[2], "enable") == 0);
+	    pjsua_call_vid_strm_op op = enable? PJSUA_CALL_VID_STRM_CHANGE_DIR :
+						PJSUA_CALL_VID_STRM_REMOVE;
+
+	    param.med_idx = argc >= 4? atoi(argv[3]) : -1;
+	    param.dir = PJMEDIA_DIR_ENCODING_DECODING;
+	    status = pjsua_call_set_vid_strm(current_call, op, &param);
+	}
+	else if (argc >= 3 && strcmp(argv[2], "cap")==0) {
+	    param.med_idx = argc >= 4? atoi(argv[3]) : -1;
+	    param.cap_dev = argc >= 5? atoi(argv[4]) : PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+	    status = pjsua_call_set_vid_strm(current_call,
+	                                     PJSUA_CALL_VID_STRM_CHANGE_CAP_DEV,
+	                                     &param);
+	} else
+	    goto on_error;
+
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(1,(THIS_FILE, status, "Error modifying video stream"));
+	}
+
+    } else if (argc >= 3 && strcmp(argv[1], "dev")==0) {
+	if (strcmp(argv[2], "list")==0) {
+	    vid_list_devs();
+	} else if (strcmp(argv[2], "refresh")==0) {
+	    pjmedia_vid_dev_refresh();
+	} else if (strcmp(argv[2], "prev")==0) {
+	    if (argc != 5) {
+		goto on_error;
+	    } else {
+		pj_bool_t on = (strcmp(argv[3], "on") == 0);
+		int dev_id = atoi(argv[4]);
+		if (on) {
+                    pjsua_vid_preview_param param;
+
+                    pjsua_vid_preview_param_default(&param);
+                    param.wnd_flags = PJMEDIA_VID_DEV_WND_BORDER |
+                                      PJMEDIA_VID_DEV_WND_RESIZABLE;
+		    pjsua_vid_preview_start(dev_id, &param);
+		    arrange_window(pjsua_vid_preview_get_win(dev_id));
+		} else {
+		    pjsua_vid_win_id wid;
+		    wid = pjsua_vid_preview_get_win(dev_id);
+		    if (wid != PJSUA_INVALID_ID) {
+			/* Preview window hiding once it is stopped is
+			 * responsibility of app */
+			pjsua_vid_win_set_show(wid, PJ_FALSE);
+			pjsua_vid_preview_stop(dev_id);
+		    }
+		}
+	    }
+	} else
+	    goto on_error;
+    } else if (strcmp(argv[1], "win")==0) {
+	pj_status_t status = PJ_SUCCESS;
+
+	if (argc==3 && strcmp(argv[2], "list")==0) {
+	    pjsua_vid_win_id wids[PJSUA_MAX_VID_WINS];
+	    unsigned i, cnt = PJ_ARRAY_SIZE(wids);
+
+	    pjsua_vid_enum_wins(wids, &cnt);
+
+	    PJ_LOG(3,(THIS_FILE, "Found %d video windows:", cnt));
+	    PJ_LOG(3,(THIS_FILE, "WID show    pos       size"));
+	    PJ_LOG(3,(THIS_FILE, "------------------------------"));
+	    for (i = 0; i < cnt; ++i) {
+		pjsua_vid_win_info wi;
+		pjsua_vid_win_get_info(wids[i], &wi);
+		PJ_LOG(3,(THIS_FILE, "%3d   %c  (%d,%d)  %dx%d",
+			  wids[i], (wi.show?'Y':'N'), wi.pos.x, wi.pos.y,
+			  wi.size.w, wi.size.h));
+	    }
+	} else if (argc==4 && (strcmp(argv[2], "show")==0 ||
+			       strcmp(argv[2], "hide")==0))
+	{
+	    pj_bool_t show = (strcmp(argv[2], "show")==0);
+	    pjsua_vid_win_id wid = atoi(argv[3]);
+	    status = pjsua_vid_win_set_show(wid, show);
+	} else if (argc==6 && strcmp(argv[2], "move")==0) {
+	    pjsua_vid_win_id wid = atoi(argv[3]);
+	    pjmedia_coord pos;
+
+	    pos.x = atoi(argv[4]);
+	    pos.y = atoi(argv[5]);
+	    status = pjsua_vid_win_set_pos(wid, &pos);
+	} else if (argc==6 && strcmp(argv[2], "resize")==0) {
+	    pjsua_vid_win_id wid = atoi(argv[3]);
+	    pjmedia_rect_size size;
+
+	    size.w = atoi(argv[4]);
+	    size.h = atoi(argv[5]);
+	    status = pjsua_vid_win_set_size(wid, &size);
+	} else if (argc==3 && strcmp(argv[2], "arrange")==0) {
+	    arrange_window(PJSUA_INVALID_ID);
+	} else
+	    goto on_error;
+
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(1,(THIS_FILE, status, "Window operation error"));
+	}
+
+    } else if (strcmp(argv[1], "codec")==0) {
+	pjsua_codec_info ci[PJMEDIA_CODEC_MGR_MAX_CODECS];
+	unsigned count = PJ_ARRAY_SIZE(ci);
+	pj_status_t status;
+
+	if (argc==3 && strcmp(argv[2], "list")==0) {
+	    status = pjsua_vid_enum_codecs(ci, &count);
+	    if (status != PJ_SUCCESS) {
+		PJ_PERROR(1,(THIS_FILE, status, "Error enumerating codecs"));
+	    } else {
+		unsigned i;
+		PJ_LOG(3,(THIS_FILE, "Found %d video codecs:", count));
+		PJ_LOG(3,(THIS_FILE, "codec id      prio  fps    bw(kbps)   size"));
+		PJ_LOG(3,(THIS_FILE, "------------------------------------------"));
+		for (i=0; i<count; ++i) {
+		    pjmedia_vid_codec_param cp;
+		    pjmedia_video_format_detail *vfd;
+
+		    status = pjsua_vid_codec_get_param(&ci[i].codec_id, &cp);
+		    if (status != PJ_SUCCESS)
+			continue;
+
+		    vfd = pjmedia_format_get_video_format_detail(&cp.enc_fmt,
+								 PJ_TRUE);
+		    PJ_LOG(3,(THIS_FILE, "%.*s%.*s %3d %7.2f  %4d/%4d  %dx%d", 
+			      (int)ci[i].codec_id.slen, ci[i].codec_id.ptr,
+			      13-(int)ci[i].codec_id.slen, "                ",
+			      ci[i].priority,
+			      (vfd->fps.num*1.0/vfd->fps.denum),
+			      vfd->avg_bps/1000, vfd->max_bps/1000,
+			      vfd->size.w, vfd->size.h));
+		}
+	    }
+	} else if (argc==5 && strcmp(argv[2], "prio")==0) {
+	    pj_str_t cid;
+	    int prio;
+	    cid = pj_str(argv[3]);
+	    prio = atoi(argv[4]);
+	    status = pjsua_vid_codec_set_priority(&cid, (pj_uint8_t)prio);
+	    if (status != PJ_SUCCESS)
+		PJ_PERROR(1,(THIS_FILE, status, "Set codec priority error"));
+	} else if (argc==6 && strcmp(argv[2], "fps")==0) {
+	    pjmedia_vid_codec_param cp;
+	    pj_str_t cid;
+	    int M, N;
+	    cid = pj_str(argv[3]);
+	    M = atoi(argv[4]);
+	    N = atoi(argv[5]);
+	    status = pjsua_vid_codec_get_param(&cid, &cp);
+	    if (status == PJ_SUCCESS) {
+		cp.enc_fmt.det.vid.fps.num = M;
+		cp.enc_fmt.det.vid.fps.denum = N;
+		status = pjsua_vid_codec_set_param(&cid, &cp);
+	    }
+	    if (status != PJ_SUCCESS)
+		PJ_PERROR(1,(THIS_FILE, status, "Set codec framerate error"));
+	} else if (argc==6 && strcmp(argv[2], "bw")==0) {
+	    pjmedia_vid_codec_param cp;
+	    pj_str_t cid;
+	    int M, N;
+	    cid = pj_str(argv[3]);
+	    M = atoi(argv[4]);
+	    N = atoi(argv[5]);
+	    status = pjsua_vid_codec_get_param(&cid, &cp);
+	    if (status == PJ_SUCCESS) {
+		cp.enc_fmt.det.vid.avg_bps = M * 1000;
+		cp.enc_fmt.det.vid.max_bps = N * 1000;
+		status = pjsua_vid_codec_set_param(&cid, &cp);
+	    }
+	    if (status != PJ_SUCCESS)
+		PJ_PERROR(1,(THIS_FILE, status, "Set codec bitrate error"));
+	} else if (argc==6 && strcmp(argv[2], "size")==0) {
+	    pjmedia_vid_codec_param cp;
+	    pj_str_t cid;
+	    int M, N;
+	    cid = pj_str(argv[3]);
+	    M = atoi(argv[4]);
+	    N = atoi(argv[5]);
+	    status = pjsua_vid_codec_get_param(&cid, &cp);
+	    if (status == PJ_SUCCESS) {
+		cp.enc_fmt.det.vid.size.w = M;
+		cp.enc_fmt.det.vid.size.h = N;
+		status = pjsua_vid_codec_set_param(&cid, &cp);
+	    }
+	    if (status != PJ_SUCCESS)
+		PJ_PERROR(1,(THIS_FILE, status, "Set codec size error"));
+	} else
+	    goto on_error;
+    } else
+	goto on_error;
+
+    return;
+
+on_error:
+    PJ_LOG(1,(THIS_FILE, "Invalid command, use 'vid help'"));
+}
+
+#endif /* PJSUA_HAS_VIDEO */
+
+/** UI Command **/
+static void ui_make_new_call()
+{    
+    char buf[128];
+    pjsua_msg_data msg_data;
+    input_result result;
+    pj_str_t tmp;
+
+    printf("(You currently have %d calls)\n", pjsua_call_get_count());
+    
+    ui_input_url("Make call", buf, sizeof(buf), &result);
+    if (result.nb_result != PJSUA_APP_NO_NB) {
+
+	if (result.nb_result == -1 || result.nb_result == 0) {
+	    puts("You can't do that with make call!");
+	    return;
+	} else {
+	    pjsua_buddy_info binfo;
+	    pjsua_buddy_get_info(result.nb_result-1, &binfo);
+	    tmp.ptr = buf;
+	    pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
+	}
+
+    } else if (result.uri_result) {
+	tmp = pj_str(result.uri_result);
+    } else {
+	tmp.slen = 0;
+    }
+
+    pjsua_msg_data_init(&msg_data);
+    TEST_MULTIPART(&msg_data);
+    pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL, 
+			 &msg_data, &current_call);
+}
+
+static void ui_make_multi_call()
+{
+    char menuin[32];
+    int count;
+    char buf[128];
+    input_result result;
+    pj_str_t tmp;
+    int i;
+
+    printf("(You currently have %d calls)\n", pjsua_call_get_count());
+
+    if (!simple_input("Number of calls", menuin, sizeof(menuin)))
+	return;
+
+    count = my_atoi(menuin);
+    if (count < 1)
+	return;
+
+    ui_input_url("Make call", buf, sizeof(buf), &result);
+    if (result.nb_result != PJSUA_APP_NO_NB) {
+	pjsua_buddy_info binfo;
+	if (result.nb_result == -1 || result.nb_result == 0) {
+	    puts("You can't do that with make call!");
+	    return;
+	}
+	pjsua_buddy_get_info(result.nb_result-1, &binfo);
+	tmp.ptr = buf;
+	pj_strncpy(&tmp, &binfo.uri, sizeof(buf));
+    } else {
+	tmp = pj_str(result.uri_result);
+    }
+
+    for (i=0; i<my_atoi(menuin); ++i) {
+	pj_status_t status;
+
+	status = pjsua_call_make_call(current_acc, &tmp, &call_opt, NULL,
+	    NULL, NULL);
+	if (status != PJ_SUCCESS)
+	    break;
+    }
+}
+
+static void ui_detect_nat_type()
+{
+    int i = pjsua_detect_nat_type();
+    if (i != PJ_SUCCESS)
+	pjsua_perror(THIS_FILE, "Error", i);
+}
+
+static void ui_send_instant_message()
+{
+    char *uri = NULL;
+    /* i is for call index to send message, if any */
+    int i = -1;
+    input_result result;
+    char buf[128];
+    char text[128];
+    pj_str_t tmp;
+
+    /* Input destination. */
+    ui_input_url("Send IM to", buf, sizeof(buf), &result);
+    if (result.nb_result != PJSUA_APP_NO_NB) {
+
+	if (result.nb_result == -1) {
+	    puts("You can't send broadcast IM like that!");
+	    return;
+
+	} else if (result.nb_result == 0) {
+	    i = current_call;
+	} else {
+	    pjsua_buddy_info binfo;
+	    pjsua_buddy_get_info(result.nb_result-1, &binfo);
+	    tmp.ptr = buf;
+	    pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
+	    uri = buf;
+	}
+
+    } else if (result.uri_result) {
+	uri = result.uri_result;
+    }
+
+
+    /* Send typing indication. */
+    if (i != -1)
+	pjsua_call_send_typing_ind(i, PJ_TRUE, NULL);
+    else {
+	pj_str_t tmp_uri = pj_str(uri);
+	pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE, NULL);
+    }
+
+    /* Input the IM . */
+    if (!simple_input("Message", text, sizeof(text))) {
+	/*
+	* Cancelled.
+	* Send typing notification too, saying we're not typing.
+	*/
+	if (i != -1)
+	    pjsua_call_send_typing_ind(i, PJ_FALSE, NULL);
+	else {
+	    pj_str_t tmp_uri = pj_str(uri);
+	    pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE, NULL);
+	}
+	return;
+    }
+
+    tmp = pj_str(text);
+
+    /* Send the IM */
+    if (i != -1)
+	pjsua_call_send_im(i, NULL, &tmp, NULL, NULL);
+    else {
+	pj_str_t tmp_uri = pj_str(uri);
+	pjsua_im_send(current_acc, &tmp_uri, NULL, &tmp, NULL, NULL);
+    }
+}
+
+static void ui_answer_call()
+{
+    pjsua_call_info call_info;
+    char buf[128];
+    pjsua_msg_data msg_data;
+
+    if (current_call != -1) {
+	pjsua_call_get_info(current_call, &call_info);
+    } else {
+	/* Make compiler happy */
+	call_info.role = PJSIP_ROLE_UAC;
+	call_info.state = PJSIP_INV_STATE_DISCONNECTED;
+    }
+
+    if (current_call == -1 || 
+	call_info.role != PJSIP_ROLE_UAS ||
+	call_info.state >= PJSIP_INV_STATE_CONNECTING)
+    {
+	puts("No pending incoming call");
+	fflush(stdout);
+	return;
+
+    } else {
+	int st_code;
+	char contact[120];
+	pj_str_t hname = { "Contact", 7 };
+	pj_str_t hvalue;
+	pjsip_generic_string_hdr hcontact;
+
+	if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
+	    return;
+
+	st_code = my_atoi(buf);
+	if (st_code < 100)
+	    return;
+
+	pjsua_msg_data_init(&msg_data);
+
+	if (st_code/100 == 3) {
+	    if (!simple_input("Enter URL to be put in Contact", 
+		contact, sizeof(contact)))
+		return;
+	    hvalue = pj_str(contact);
+	    pjsip_generic_string_hdr_init2(&hcontact, &hname, &hvalue);
+
+	    pj_list_push_back(&msg_data.hdr_list, &hcontact);
+	}
+
+	/*
+	* Must check again!
+	* Call may have been disconnected while we're waiting for 
+	* keyboard input.
+	*/
+	if (current_call == -1) {
+	    puts("Call has been disconnected");
+	    fflush(stdout);
+	    return;
+	}
+
+	pjsua_call_answer2(current_call, &call_opt, st_code, NULL, &msg_data);
+    }    
+}
+
+static void ui_hangup_call(char menuin[])
+{
+    if (current_call == -1) {
+	puts("No current call");
+	fflush(stdout);
+	return;
+
+    } else if (menuin[1] == 'a') {
+	/* Hangup all calls */
+	pjsua_call_hangup_all();
+    } else {
+	/* Hangup current calls */
+	pjsua_call_hangup(current_call, 0, NULL, NULL);
+    }
+}
+
+static void ui_cycle_dialog(char menuin[])
+{
+    if (menuin[0] == ']') {
+	find_next_call();
+
+    } else {
+	find_prev_call();
+    }
+
+    if (current_call != -1) {
+	pjsua_call_info call_info;
+
+	pjsua_call_get_info(current_call, &call_info);
+	PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s", 
+	    (int)call_info.remote_info.slen, 
+	    call_info.remote_info.ptr));
+
+    } else {
+	PJ_LOG(3,(THIS_FILE,"No current dialog"));
+    }
+}
+
+static void ui_cycle_account()
+{
+    int i;
+    char buf[128];
+
+    if (!simple_input("Enter account ID to select", buf, sizeof(buf)))
+	return;
+
+    i = my_atoi(buf);
+    if (pjsua_acc_is_valid(i)) {
+	pjsua_acc_set_default(i);
+	PJ_LOG(3,(THIS_FILE, "Current account changed to %d", i));
+    } else {
+	PJ_LOG(3,(THIS_FILE, "Invalid account id %d", i));
+    }
+}
+
+static void ui_add_buddy()
+{
+    char buf[128];
+    pjsua_buddy_config buddy_cfg;
+    pjsua_buddy_id buddy_id;
+    pj_status_t status;
+
+    if (!simple_input("Enter buddy's URI:", buf, sizeof(buf)))
+	return;
+
+    if (pjsua_verify_url(buf) != PJ_SUCCESS) {
+	printf("Invalid URI '%s'\n", buf);
+	return;
+    }
+
+    pj_bzero(&buddy_cfg, sizeof(pjsua_buddy_config));
+
+    buddy_cfg.uri = pj_str(buf);
+    buddy_cfg.subscribe = PJ_TRUE;
+
+    status = pjsua_buddy_add(&buddy_cfg, &buddy_id);
+    if (status == PJ_SUCCESS) {
+	printf("New buddy '%s' added at index %d\n",
+	    buf, buddy_id+1);
+    }
+}
+
+static void ui_add_account(pjsua_transport_config *rtp_cfg)
+{
+    char id[80], registrar[80], realm[80], uname[80], passwd[30];
+    pjsua_acc_config acc_cfg;
+    pj_status_t status;
+
+    if (!simple_input("Your SIP URL:", id, sizeof(id)))
+	return;
+    if (!simple_input("URL of the registrar:", registrar, sizeof(registrar)))
+	return;
+    if (!simple_input("Auth Realm:", realm, sizeof(realm)))
+	return;
+    if (!simple_input("Auth Username:", uname, sizeof(uname)))
+	return;
+    if (!simple_input("Auth Password:", passwd, sizeof(passwd)))
+	return;
+
+    pjsua_acc_config_default(&acc_cfg);
+    acc_cfg.id = pj_str(id);
+    acc_cfg.reg_uri = pj_str(registrar);
+    acc_cfg.cred_count = 1;
+    acc_cfg.cred_info[0].scheme = pj_str("Digest");
+    acc_cfg.cred_info[0].realm = pj_str(realm);
+    acc_cfg.cred_info[0].username = pj_str(uname);
+    acc_cfg.cred_info[0].data_type = 0;
+    acc_cfg.cred_info[0].data = pj_str(passwd);
+
+    acc_cfg.rtp_cfg = *rtp_cfg;    
+    app_config_init_video(&acc_cfg);
+
+    status = pjsua_acc_add(&acc_cfg, PJ_TRUE, NULL);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error adding new account", status);
+    }
+}
+
+static void ui_delete_buddy()
+{
+    char buf[128];
+    int i;
+
+    if (!simple_input("Enter buddy ID to delete", buf, sizeof(buf)))
+	return;
+
+    i = my_atoi(buf) - 1;
+
+    if (!pjsua_buddy_is_valid(i)) {
+	printf("Invalid buddy id %d\n", i);
+    } else {
+	pjsua_buddy_del(i);
+	printf("Buddy %d deleted\n", i);
+    }
+}
+
+static void ui_delete_account()
+{
+    char buf[128];
+    int i;
+
+    if (!simple_input("Enter account ID to delete", buf, sizeof(buf)))
+	return;
+
+    i = my_atoi(buf);
+
+    if (!pjsua_acc_is_valid(i)) {
+	printf("Invalid account id %d\n", i);
+    } else {
+	pjsua_acc_del(i);
+	printf("Account %d deleted\n", i);
+    }
+}
+
+static void ui_call_hold()
+{
+    if (current_call != -1) {		
+	pjsua_call_set_hold(current_call, NULL);
+    } else {
+	PJ_LOG(3,(THIS_FILE, "No current call"));
+    }
+}
+
+static void ui_call_reinvite()
+{
+    call_opt.flag |= PJSUA_CALL_UNHOLD;
+    pjsua_call_reinvite2(current_call, &call_opt, NULL);
+}
+
+static void ui_send_update()
+{
+    if (current_call != -1) {		
+	pjsua_call_update2(current_call, &call_opt, NULL);
+    } else {
+	PJ_LOG(3,(THIS_FILE, "No current call"));
+    }
+}
+
+/*
+ * Change codec priorities.
+ */
+static void ui_manage_codec_prio()
+{
+    pjsua_codec_info c[32];
+    unsigned i, count = PJ_ARRAY_SIZE(c);
+    char input[32];
+    char *codec, *prio;
+    pj_str_t id;
+    int new_prio;
+    pj_status_t status;
+
+    printf("List of audio codecs:\n");
+    pjsua_enum_codecs(c, &count);
+    for (i=0; i<count; ++i) {
+	printf("  %d\t%.*s\n", c[i].priority, (int)c[i].codec_id.slen,
+			       c[i].codec_id.ptr);
+    }
+
+#if PJSUA_HAS_VIDEO
+    puts("");
+    printf("List of video codecs:\n");
+    pjsua_vid_enum_codecs(c, &count);
+    for (i=0; i<count; ++i) {
+	printf("  %d\t%.*s%s%.*s\n", c[i].priority,
+				     (int)c[i].codec_id.slen,
+				     c[i].codec_id.ptr,
+				     c[i].desc.slen? " - ":"",
+				     (int)c[i].desc.slen,
+				     c[i].desc.ptr);
+    }
+#endif
+
+    puts("");
+    puts("Enter codec id and its new priority (e.g. \"speex/16000 200\", "
+	 """\"H263 200\"),");
+    puts("or empty to cancel.");
+
+    printf("Codec name (\"*\" for all) and priority: ");
+    if (fgets(input, sizeof(input), stdin) == NULL)
+	return;
+    if (input[0]=='\r' || input[0]=='\n') {
+	puts("Done");
+	return;
+    }
+
+    codec = strtok(input, " \t\r\n");
+    prio = strtok(NULL, " \r\n");
+
+    if (!codec || !prio) {
+	puts("Invalid input");
+	return;
+    }
+
+    new_prio = atoi(prio);
+    if (new_prio < 0) 
+	new_prio = 0;
+    else if (new_prio > PJMEDIA_CODEC_PRIO_HIGHEST) 
+	new_prio = PJMEDIA_CODEC_PRIO_HIGHEST;
+
+    status = pjsua_codec_set_priority(pj_cstr(&id, codec), 
+				      (pj_uint8_t)new_prio);
+#if PJSUA_HAS_VIDEO
+    if (status != PJ_SUCCESS) {
+	status = pjsua_vid_codec_set_priority(pj_cstr(&id, codec), 
+					      (pj_uint8_t)new_prio);
+    }
+#endif
+    if (status != PJ_SUCCESS)
+	pjsua_perror(THIS_FILE, "Error setting codec priority", status);
+}
+
+static void ui_call_transfer(pj_bool_t no_refersub)
+{
+    if (current_call == -1) {
+	PJ_LOG(3,(THIS_FILE, "No current call"));
+    } else {
+	int call = current_call;
+	char buf[128];
+	pjsip_generic_string_hdr refer_sub;
+	pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
+	pj_str_t STR_FALSE = { "false", 5 };
+	pjsua_call_info ci;
+	input_result result;
+	pjsua_msg_data msg_data;
+
+	pjsua_call_get_info(current_call, &ci);
+	printf("Transfering current call [%d] %.*s\n", current_call,
+	       (int)ci.remote_info.slen, ci.remote_info.ptr);
+
+	ui_input_url("Transfer to URL", buf, sizeof(buf), &result);
+
+	/* Check if call is still there. */
+
+	if (call != current_call) {
+	    puts("Call has been disconnected");
+	    return;
+	}
+
+	pjsua_msg_data_init(&msg_data);
+	if (no_refersub) {
+	    /* Add Refer-Sub: false in outgoing REFER request */
+	    pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB,
+		&STR_FALSE);
+	    pj_list_push_back(&msg_data.hdr_list, &refer_sub);
+	}
+	if (result.nb_result != PJSUA_APP_NO_NB) {
+	    if (result.nb_result == -1 || result.nb_result == 0)
+		puts("You can't do that with transfer call!");
+	    else {
+		pjsua_buddy_info binfo;
+		pjsua_buddy_get_info(result.nb_result-1, &binfo);
+		pjsua_call_xfer( current_call, &binfo.uri, &msg_data);
+	    }
+
+	} else if (result.uri_result) {
+	    pj_str_t tmp;
+	    tmp = pj_str(result.uri_result);
+	    pjsua_call_xfer( current_call, &tmp, &msg_data);
+	}
+    }
+}
+
+static void ui_call_transfer_replaces(pj_bool_t no_refersub)
+{        
+    if (current_call == -1) {
+	PJ_LOG(3,(THIS_FILE, "No current call"));
+    } else {
+	int call = current_call;
+	int dst_call;
+	pjsip_generic_string_hdr refer_sub;
+	pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
+	pj_str_t STR_FALSE = { "false", 5 };
+	pjsua_call_id ids[PJSUA_MAX_CALLS];
+	pjsua_call_info ci;
+	pjsua_msg_data msg_data;
+	char buf[128];
+	unsigned i, count;
+
+	count = PJ_ARRAY_SIZE(ids);
+	pjsua_enum_calls(ids, &count);
+
+	if (count <= 1) {
+	    puts("There are no other calls");
+	    return;
+	}
+
+	pjsua_call_get_info(current_call, &ci);
+	printf("Transfer call [%d] %.*s to one of the following:\n",
+	       current_call,
+	       (int)ci.remote_info.slen, ci.remote_info.ptr);
+
+	for (i=0; i<count; ++i) {
+	    pjsua_call_info call_info;
+
+	    if (ids[i] == call)
+		return;
+
+	    pjsua_call_get_info(ids[i], &call_info);
+	    printf("%d  %.*s [%.*s]\n",
+		ids[i],
+		(int)call_info.remote_info.slen,
+		call_info.remote_info.ptr,
+		(int)call_info.state_text.slen,
+		call_info.state_text.ptr);
+	}
+
+	if (!simple_input("Enter call number to be replaced", buf, sizeof(buf)))
+	    return;
+
+	dst_call = my_atoi(buf);
+
+	/* Check if call is still there. */
+
+	if (call != current_call) {
+	    puts("Call has been disconnected");
+	    return;
+	}
+
+	/* Check that destination call is valid. */
+	if (dst_call == call) {
+	    puts("Destination call number must not be the same "
+		"as the call being transfered");
+	    return;
+	}
+	if (dst_call >= PJSUA_MAX_CALLS) {
+	    puts("Invalid destination call number");
+	    return;
+	}
+	if (!pjsua_call_is_active(dst_call)) {
+	    puts("Invalid destination call number");
+	    return;
+	}
+
+	pjsua_msg_data_init(&msg_data);
+	if (no_refersub) {
+	    /* Add Refer-Sub: false in outgoing REFER request */
+	    pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB, 
+					   &STR_FALSE);
+	    pj_list_push_back(&msg_data.hdr_list, &refer_sub);
+	}
+
+	pjsua_call_xfer_replaces(call, dst_call, 
+				 PJSUA_XFER_NO_REQUIRE_REPLACES, 
+				 &msg_data);
+    }
+}
+
+static void ui_send_dtmf_2833()
+{
+    if (current_call == -1) {
+	PJ_LOG(3,(THIS_FILE, "No current call"));
+    } else if (!pjsua_call_has_media(current_call)) {
+	PJ_LOG(3,(THIS_FILE, "Media is not established yet!"));
+    } else {
+	pj_str_t digits;
+	int call = current_call;
+	pj_status_t status;
+	char buf[128];
+
+	if (!simple_input("DTMF strings to send (0-9*#A-B)", buf, 
+	    sizeof(buf)))
+	{
+	    return;
+	}
+
+	if (call != current_call) {
+	    puts("Call has been disconnected");
+	    return;
+	}
+
+	digits = pj_str(buf);
+	status = pjsua_call_dial_dtmf(current_call, &digits);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to send DTMF", status);
+	} else {
+	    puts("DTMF digits enqueued for transmission");
+	}
+    }
+}
+
+static void ui_send_dtmf_info()
+{
+    if (current_call == -1) {
+	PJ_LOG(3,(THIS_FILE, "No current call"));
+    } else {
+	const pj_str_t SIP_INFO = pj_str("INFO");
+	pj_str_t digits;
+	int call = current_call;
+	int i;
+	pj_status_t status;
+	char buf[128];
+
+	if (!simple_input("DTMF strings to send (0-9*#A-B)", buf, 
+	    sizeof(buf)))
+	{
+	    return;
+	}
+
+	if (call != current_call) {
+	    puts("Call has been disconnected");
+	    return;
+	}
+
+	digits = pj_str(buf);
+	for (i=0; i<digits.slen; ++i) {
+	    char body[80];
+	    pjsua_msg_data msg_data;
+
+	    pjsua_msg_data_init(&msg_data);
+	    msg_data.content_type = pj_str("application/dtmf-relay");
+
+	    pj_ansi_snprintf(body, sizeof(body),
+		"Signal=%c\r\n"
+		"Duration=160",
+		buf[i]);
+	    msg_data.msg_body = pj_str(body);
+
+	    status = pjsua_call_send_request(current_call, &SIP_INFO, 
+		&msg_data);
+	    if (status != PJ_SUCCESS) {
+		return;
+	    }
+	}
+    }
+}
+
+static void ui_send_arbitrary_request()
+{
+    char text[128];
+    char buf[128];
+    char *uri;
+    input_result result;
+    pj_str_t tmp;
+
+    if (pjsua_acc_get_count() == 0) {
+	puts("Sorry, need at least one account configured");
+	return;
+    }
+
+    puts("Send arbitrary request to remote host");
+
+    /* Input METHOD */
+    if (!simple_input("Request method:",text,sizeof(text)))
+	return;
+
+    /* Input destination URI */
+    uri = NULL;
+    ui_input_url("Destination URI", buf, sizeof(buf), &result);
+    if (result.nb_result != PJSUA_APP_NO_NB) {
+
+	if (result.nb_result == -1) {
+	    puts("Sorry you can't do that!");
+	    return;
+	} else if (result.nb_result == 0) {
+	    uri = NULL;
+	    if (current_call == PJSUA_INVALID_ID) {
+		puts("No current call");
+		return;
+	    }
+	} else {	    
+	    pjsua_buddy_info binfo;
+	    pjsua_buddy_get_info(result.nb_result-1, &binfo);
+	    tmp.ptr = buf;
+	    pj_strncpy_with_null(&tmp, &binfo.uri, sizeof(buf));
+	    uri = buf;
+	}
+
+    } else if (result.uri_result) {
+	uri = result.uri_result;
+    } else {
+	return;
+    }
+
+    if (uri) {
+	tmp = pj_str(uri);
+	send_request(text, &tmp);
+    } else {
+	/* If you send call control request using this method
+	* (such requests includes BYE, CANCEL, etc.), it will
+	* not go well with the call state, so don't do it
+	* unless it's for testing.
+	*/
+	pj_str_t method = pj_str(text);
+	pjsua_call_send_request(current_call, &method, NULL);
+    }	    
+}
+
+static void ui_echo(char menuin[])
+{
+    if (pj_ansi_strnicmp(menuin, "echo", 4)==0) {
+	pj_str_t tmp;
+
+	tmp.ptr = menuin+5;
+	tmp.slen = pj_ansi_strlen(menuin)-6;
+
+	if (tmp.slen < 1) {
+	    puts("Usage: echo [0|1]");
+	    return;
+	}
+	cmd_echo = *tmp.ptr != '0' || tmp.slen!=1;
+    }
+}
+
+static void ui_sleep(char menuin[])
+{
+    if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
+	pj_str_t tmp;
+	int delay;
+
+	tmp.ptr = menuin+6;
+	tmp.slen = pj_ansi_strlen(menuin)-7;
+
+	if (tmp.slen < 1) {
+	    puts("Usage: sleep MSEC");
+	    return;
+	}
+
+	delay = pj_strtoul(&tmp);
+	if (delay < 0) delay = 0;
+	pj_thread_sleep(delay);		
+    }
+}
+
+static void ui_subscribe(char menuin[])
+{
+    char buf[128];
+    input_result result;
+
+    ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result);
+    if (result.nb_result != PJSUA_APP_NO_NB) {
+	if (result.nb_result == -1) {
+	    int i, count;
+	    count = pjsua_get_buddy_count();
+	    for (i=0; i<count; ++i)
+		pjsua_buddy_subscribe_pres(i, menuin[0]=='s');
+	} else if (result.nb_result == 0) {
+	    puts("Sorry, can only subscribe to buddy's presence, "
+		"not from existing call");
+	} else {
+	    pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s'));
+	}
+
+    } else if (result.uri_result) {
+	puts("Sorry, can only subscribe to buddy's presence, "
+	    "not arbitrary URL (for now)");
+    }
+}
+
+static void ui_register(char menuin[])
+{
+    switch (menuin[1]) {
+    case 'r':
+	/*
+	* Re-Register.
+	*/
+	pjsua_acc_set_registration(current_acc, PJ_TRUE);
+	break;
+    case 'u':
+	/*
+	* Unregister
+	*/
+	pjsua_acc_set_registration(current_acc, PJ_FALSE);
+	break;
+    }
+}
+
+static void ui_toggle_state()
+{
+    pjsua_acc_info acc_info;
+
+    pjsua_acc_get_info(current_acc, &acc_info);
+    acc_info.online_status = !acc_info.online_status;
+    pjsua_acc_set_online_status(current_acc, acc_info.online_status);
+    printf("Setting %s online status to %s\n",
+	   acc_info.acc_uri.ptr,
+	   (acc_info.online_status?"online":"offline"));
+}
+
+/*
+ * Change extended online status.
+ */
+static void ui_change_online_status()
+{
+    char menuin[32];
+    pj_bool_t online_status;
+    pjrpid_element elem;
+    int i, choice;
+
+    enum {
+	AVAILABLE, BUSY, OTP, IDLE, AWAY, BRB, OFFLINE, OPT_MAX
+    };
+
+    struct opt {
+	int id;
+	char *name;
+    } opts[] = {
+	{ AVAILABLE, "Available" },
+	{ BUSY, "Busy"},
+	{ OTP, "On the phone"},
+	{ IDLE, "Idle"},
+	{ AWAY, "Away"},
+	{ BRB, "Be right back"},
+	{ OFFLINE, "Offline"}
+    };
+
+    printf("\n"
+	   "Choices:\n");
+    for (i=0; i<PJ_ARRAY_SIZE(opts); ++i) {
+	printf("  %d  %s\n", opts[i].id+1, opts[i].name);
+    }
+
+    if (!simple_input("Select status", menuin, sizeof(menuin)))
+	return;
+
+    choice = atoi(menuin) - 1;
+    if (choice < 0 || choice >= OPT_MAX) {
+	puts("Invalid selection");
+	return;
+    }
+
+    pj_bzero(&elem, sizeof(elem));
+    elem.type = PJRPID_ELEMENT_TYPE_PERSON;
+
+    online_status = PJ_TRUE;
+
+    switch (choice) {
+    case AVAILABLE:
+	break;
+    case BUSY:
+	elem.activity = PJRPID_ACTIVITY_BUSY;
+	elem.note = pj_str("Busy");
+	break;
+    case OTP:
+	elem.activity = PJRPID_ACTIVITY_BUSY;
+	elem.note = pj_str("On the phone");
+	break;
+    case IDLE:
+	elem.activity = PJRPID_ACTIVITY_UNKNOWN;
+	elem.note = pj_str("Idle");
+	break;
+    case AWAY:
+	elem.activity = PJRPID_ACTIVITY_AWAY;
+	elem.note = pj_str("Away");
+	break;
+    case BRB:
+	elem.activity = PJRPID_ACTIVITY_UNKNOWN;
+	elem.note = pj_str("Be right back");
+	break;
+    case OFFLINE:
+	online_status = PJ_FALSE;
+	break;
+    }
+
+    pjsua_acc_set_online_status2(current_acc, online_status, &elem);
+}
+
+/*
+ * List the ports in conference bridge
+ */
+static void ui_conf_list()
+{
+    unsigned i, count;
+    pjsua_conf_port_id id[PJSUA_MAX_CALLS];
+
+    printf("Conference ports:\n");
+
+    count = PJ_ARRAY_SIZE(id);
+    pjsua_enum_conf_ports(id, &count);
+
+    for (i=0; i<count; ++i) {
+	char txlist[PJSUA_MAX_CALLS*4+10];
+	unsigned j;
+	pjsua_conf_port_info info;
+
+	pjsua_conf_get_port_info(id[i], &info);
+
+	txlist[0] = '\0';
+	for (j=0; j<info.listener_cnt; ++j) {
+	    char s[10];
+	    pj_ansi_snprintf(s, sizeof(s), "#%d ", info.listeners[j]);
+	    pj_ansi_strcat(txlist, s);
+	}
+	printf("Port #%02d[%2dKHz/%dms/%d] %20.*s  transmitting to: %s\n", 
+	       info.slot_id, 
+	       info.clock_rate/1000,
+	       info.samples_per_frame*1000/info.channel_count/info.clock_rate,
+	       info.channel_count,
+	       (int)info.name.slen, 
+	       info.name.ptr,
+	       txlist);
+
+    }
+    puts("");
+}
+
+static void ui_conf_connect(char menuin[])
+{
+    char tmp[10], src_port[10], dst_port[10];
+    pj_status_t status;
+    int cnt;
+    const char *src_title, *dst_title;
+
+    cnt = sscanf(menuin, "%s %s %s", tmp, src_port, dst_port);
+
+    if (cnt != 3) {
+	ui_conf_list();
+
+	src_title = (menuin[1]=='c'? "Connect src port #":
+				     "Disconnect src port #");
+	dst_title = (menuin[1]=='c'? "To dst port #":"From dst port #");
+
+	if (!simple_input(src_title, src_port, sizeof(src_port)))
+	    return;
+
+	if (!simple_input(dst_title, dst_port, sizeof(dst_port)))
+	    return;
+    }
+
+    if (menuin[1]=='c') {
+	status = pjsua_conf_connect(my_atoi(src_port), my_atoi(dst_port));
+    } else {
+	status = pjsua_conf_disconnect(my_atoi(src_port), my_atoi(dst_port));
+    }
+    if (status == PJ_SUCCESS) {
+	puts("Success");
+    } else {
+	puts("ERROR!!");
+    }
+}
+
+static void ui_adjust_volume()
+{
+    char buf[128];
+    char text[128];
+    sprintf(buf, "Adjust mic level: [%4.1fx] ", app_config.mic_level);
+    if (simple_input(buf,text,sizeof(text))) {
+	char *err;
+	app_config.mic_level = (float)strtod(text, &err);
+	pjsua_conf_adjust_rx_level(0, app_config.mic_level);
+    }
+    sprintf(buf, "Adjust speaker level: [%4.1fx] ", app_config.speaker_level);
+    if (simple_input(buf,text,sizeof(text))) {
+	char *err;
+	app_config.speaker_level = (float)strtod(text, &err);
+	pjsua_conf_adjust_tx_level(0, app_config.speaker_level);
+    }
+}
+
+static void ui_dump_call_quality()
+{
+    if (current_call != PJSUA_INVALID_ID) {
+	log_call_dump(current_call);
+    } else {
+	PJ_LOG(3,(THIS_FILE, "No current call"));
+    }
+}
+
+static void ui_dump_configuration()
+{
+    char settings[2000];
+    int len;
+
+    len = write_settings(&app_config, settings, sizeof(settings));
+    if (len < 1)
+	PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
+    else
+	PJ_LOG(3,(THIS_FILE, "Dumping configuration (%d bytes):\n%s\n",	
+		  len, settings));	
+}
+
+static void ui_write_settings()
+{
+    char settings[2000];
+    int len;
+    char buf[128];
+
+    len = write_settings(&app_config, settings, sizeof(settings));
+    if (len < 1)
+	PJ_LOG(1,(THIS_FILE, "Error: not enough buffer"));
+    else {
+	pj_oshandle_t fd;
+	pj_status_t status;
+
+	status = pj_file_open(app_config.pool, buf, PJ_O_WRONLY, &fd);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to open file", status);
+	} else {
+	    pj_ssize_t size = len;
+	    pj_file_write(fd, settings, &size);
+	    pj_file_close(fd);
+
+	    printf("Settings successfully written to '%s'\n", buf);
+	}
+    }
+}
+
+/*
+ * Dump application states.
+ */
+static void ui_app_dump(pj_bool_t detail)
+{
+    pjsua_dump(detail);
+}
+
+static void ui_call_redirect(char menuin[])
+{
+    if (current_call == PJSUA_INVALID_ID) {
+	PJ_LOG(3,(THIS_FILE, "No current call"));
+    } else {
+	if (!pjsua_call_is_active(current_call)) {
+	    PJ_LOG(1,(THIS_FILE, "Call %d has gone", current_call));
+	} else if (menuin[1] == 'a') {
+	    pjsua_call_process_redirect(current_call, 
+		PJSIP_REDIRECT_ACCEPT_REPLACE);
+	} else if (menuin[1] == 'A') {
+	    pjsua_call_process_redirect(current_call, 
+		PJSIP_REDIRECT_ACCEPT);
+	} else if (menuin[1] == 'r') {
+	    pjsua_call_process_redirect(current_call,
+		PJSIP_REDIRECT_REJECT);
+	} else {
+	    pjsua_call_process_redirect(current_call,
+		PJSIP_REDIRECT_STOP);
+	}
+    }
+}
+
+/*
+ * Main "user interface" loop.
+ */
+void legacy_main()
+{    
+    char menuin[80];    
+    char buf[128];    
+
+    keystroke_help();
+
+    for (;;) {
+
+	printf(">>> ");
+	fflush(stdout);
+
+	if (fgets(menuin, sizeof(menuin), stdin) == NULL) {
+	    /* 
+	     * Be friendly to users who redirect commands into
+	     * program, when file ends, resume with kbd.
+	     * If exit is desired end script with q for quit
+	     */
+ 	    /* Reopen stdin/stdout/stderr to /dev/console */
+#if ((defined(PJ_WIN32) && PJ_WIN32!=0) || \
+     (defined(PJ_WIN64) && PJ_WIN64!=0)) && \
+  (!defined(PJ_WIN32_WINCE) || PJ_WIN32_WINCE==0)
+	    if (freopen ("CONIN$", "r", stdin) == NULL) {
+#else
+	    if (1) {
+#endif
+		puts("Cannot switch back to console from file redirection");
+		menuin[0] = 'q';
+		menuin[1] = '\0';
+	    } else {
+		puts("Switched back to console from file redirection");
+		continue;
+	    }
+	}
+
+	if (cmd_echo) {
+	    printf("%s", menuin);
+	}
+
+	/* Update call setting */
+	pjsua_call_setting_default(&call_opt);
+	call_opt.aud_cnt = app_config.aud_cnt;
+	call_opt.vid_cnt = app_config.vid.vid_cnt;
+
+	switch (menuin[0]) {
+
+	case 'm':
+	    /* Make call! : */
+	    ui_make_new_call();
+	    break;
+
+	case 'M':
+	    /* Make multiple calls! : */
+	    ui_make_multi_call();
+	    break;
+
+	case 'n':
+	    ui_detect_nat_type();
+	    break;
+
+	case 'i':
+	    /* Send instant messaeg */
+	    ui_send_instant_message();
+	    break;
+
+	case 'a':
+	    ui_answer_call();
+	    break;
+
+	case 'h':
+	    ui_hangup_call(menuin);
+	    break;
+
+	case ']':
+	case '[':
+	    /*
+	     * Cycle next/prev dialog.
+	     */
+	    ui_cycle_dialog(menuin);
+	    break;
+
+	case '>':
+	case '<':
+	    ui_cycle_account();
+	    break;
+
+	case '+':
+	    if (menuin[1] == 'b') {
+		ui_add_buddy();
+	    } else if (menuin[1] == 'a') {
+		ui_add_account(&app_config.rtp_cfg);
+	    } else {
+		printf("Invalid input %s\n", menuin);
+	    }
+	    break;
+
+	case '-':
+	    if (menuin[1] == 'b') {
+		ui_delete_buddy();
+	    } else if (menuin[1] == 'a') {
+		ui_delete_account();
+	    } else {
+		printf("Invalid input %s\n", menuin);
+	    }
+	    break;
+
+	case 'H':
+	    /*
+	     * Hold call.
+	     */
+	    ui_call_hold();
+	    break;
+
+	case 'v':	    
+#if PJSUA_HAS_VIDEO
+	    if (menuin[1]=='i' && menuin[2]=='d' && menuin[3]==' ') {
+		vid_handle_menu(menuin);
+	    } else
+#endif
+	    if (current_call != -1) {
+		/*
+		 * re-INVITE
+		 */
+		ui_call_reinvite();
+	    } else {
+		PJ_LOG(3,(THIS_FILE, "No current call"));
+	    }
+	    break;
+
+	case 'U':
+	    /*
+	     * Send UPDATE
+	     */
+	    ui_send_update();
+	    break;
+
+	case 'C':
+	    if (menuin[1] == 'p') {
+		ui_manage_codec_prio();
+	    }
+	    break;
+
+	case 'x':
+	    /*
+	     * Transfer call.
+	     */
+	    ui_call_transfer(app_config.no_refersub);
+	    break;
+
+	case 'X':
+	    /*
+	     * Transfer call with replaces.
+	     */
+	    ui_call_transfer_replaces(app_config.no_refersub);
+	    break;
+
+	case '#':
+	    /*
+	     * Send DTMF strings.
+	     */
+	    ui_send_dtmf_2833();
+	    break;
+
+	case '*':
+	    /* Send DTMF with INFO */
+	    ui_send_dtmf_info();
+	    break;
+
+	case 'S':
+	    /*
+	     * Send arbitrary request
+	     */
+	    ui_send_arbitrary_request();
+	    break;
+
+	case 'e':
+	    ui_echo(menuin);
+	    break;
+
+	case 's':
+	    if (pj_ansi_strnicmp(menuin, "sleep", 5)==0) {
+		ui_sleep(menuin);
+		break;
+	    }
+	    /* Continue below */
+
+	case 'u':
+	    /*
+	     * Subscribe/unsubscribe presence.
+	     */
+	    ui_subscribe(menuin);
+	    break;
+
+	case 'r':
+	    ui_register(menuin);
+	    break;
+	    
+	case 't':
+	    ui_toggle_state();
+	    break;
+
+	case 'T':
+	    ui_change_online_status();
+	    break;
+
+	case 'c':
+	    switch (menuin[1]) {
+	    case 'l':
+		ui_conf_list();
+		break;
+	    case 'c':
+	    case 'd':
+		ui_conf_connect(menuin);
+		break;
+	    }
+	    break;
+
+	case 'V':
+	    /* Adjust audio volume */	    
+	    ui_adjust_volume();
+	    break;
+
+	case 'd':
+	    if (menuin[1] == 'c') {
+		ui_dump_configuration();	
+	    } else if (menuin[1] == 'q') {
+		ui_dump_call_quality();
+	    } else {		
+		ui_app_dump(menuin[1]=='d');
+	    }
+	    break;
+
+	case 'f':
+	    if (simple_input("Enter output filename", buf, sizeof(buf))) {
+		ui_write_settings();		
+	    }
+	    break;
+
+	case 'L':   /* Restart */
+	case 'q':
+	    legacy_on_stopped(menuin[0]=='L');
+	    goto on_exit;
+
+	case 'R':	    
+	    ui_call_redirect(menuin);
+	    break;
+
+	default:
+	    if (menuin[0] != '\n' && menuin[0] != '\r') {
+		printf("Invalid input %s", menuin);
+	    }
+	    keystroke_help();
+	    break;
+	}
+    }
+
+on_exit:
+    ;
+}
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8e50eadcbf6176950210349e5a9338d3d66ee0d.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8e50eadcbf6176950210349e5a9338d3d66ee0d.svn-base
new file mode 100644
index 0000000..ab22c5e
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8e50eadcbf6176950210349e5a9338d3d66ee0d.svn-base
@@ -0,0 +1,772 @@
+/* $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 "test.h"
+#include <pjsip.h>
+#include <pjlib.h>
+
+#define THIS_FILE   "transport_test.c"
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic testing for transport, to make sure that basic
+ * attributes have been initialized properly.
+ */
+int generic_transport_test(pjsip_transport *tp)
+{
+    PJ_LOG(3,(THIS_FILE, "  structure test..."));
+
+    /* Check that local address name is valid. */
+    {
+	struct pj_in_addr addr;
+
+	/* Note: inet_aton() returns non-zero if addr is valid! */
+	if (pj_inet_aton(&tp->local_name.host, &addr) != 0) {
+	    if (addr.s_addr==PJ_INADDR_ANY || addr.s_addr==PJ_INADDR_NONE) {
+		PJ_LOG(3,(THIS_FILE, "   Error: invalid address name"));
+		return -420;
+	    }
+	} else {
+	    /* It's okay. local_name.host may be a hostname instead of
+	     * IP address.
+	     */
+	}
+    }
+
+    /* Check that port is valid. */
+    if (tp->local_name.port <= 0) {
+	return -430;
+    }
+
+    /* Check length of address (for now we only check against sockaddr_in). */
+    if (tp->addr_len != sizeof(pj_sockaddr_in))
+	return -440;
+
+    /* Check type. */
+    if (tp->key.type == PJSIP_TRANSPORT_UNSPECIFIED)
+	return -450;
+
+    /* That's it. */
+    return PJ_SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/* 
+ * Send/receive test.
+ *
+ * This test sends a request to loopback address; as soon as request is 
+ * received, response will be sent, and time is recorded.
+ *
+ * The main purpose is to test that the basic transport functionalities works,
+ * before we continue with more complicated tests.
+ */
+#define FROM_HDR    "Bob <sip:bob@example.com>"
+#define CONTACT_HDR "Bob <sip:bob@127.0.0.1>"
+#define CALL_ID_HDR "SendRecv-Test"
+#define CSEQ_VALUE  100
+#define BODY	    "Hello World!"
+
+static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata);
+
+/* Flag to indicate message has been received
+ * (or failed to send)
+ */
+#define NO_STATUS   -2
+static int send_status = NO_STATUS;
+static int recv_status = NO_STATUS;
+static pj_timestamp my_send_time, my_recv_time;
+
+/* Module to receive messages for this test. */
+static pjsip_module my_module = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Transport-Test", 14},		/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_TSX_LAYER-1,	/* Priority		*/
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    &my_on_rx_request,			/* on_rx_request()	*/
+    &my_on_rx_response,			/* on_rx_response()	*/
+    NULL,				/* on_tsx_state()	*/
+};
+
+
+static pj_bool_t my_on_rx_request(pjsip_rx_data *rdata)
+{
+    /* Check that this is our request. */
+    if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
+	/* It is! */
+	/* Send response. */
+	pjsip_tx_data *tdata;
+	pjsip_response_addr res_addr;
+	pj_status_t status;
+
+	status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
+	if (status != PJ_SUCCESS) {
+	    recv_status = status;
+	    return PJ_TRUE;
+	}
+	status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
+	if (status != PJ_SUCCESS) {
+	    recv_status = status;
+	    pjsip_tx_data_dec_ref(tdata);
+	    return PJ_TRUE;
+	}
+	status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
+	if (status != PJ_SUCCESS) {
+	    recv_status = status;
+	    pjsip_tx_data_dec_ref(tdata);
+	    return PJ_TRUE;
+	}
+	return PJ_TRUE;
+    }
+    
+    /* Not ours. */
+    return PJ_FALSE;
+}
+
+static pj_bool_t my_on_rx_response(pjsip_rx_data *rdata)
+{
+    if (pj_strcmp2(&rdata->msg_info.cid->id, CALL_ID_HDR) == 0) {
+	pj_get_timestamp(&my_recv_time);
+	recv_status = PJ_SUCCESS;
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+/* Transport callback. */
+static void send_msg_callback(pjsip_send_state *stateless_data,
+			      pj_ssize_t sent, pj_bool_t *cont)
+{
+    PJ_UNUSED_ARG(stateless_data);
+
+    if (sent < 1) {
+	/* Obtain the error code. */
+	send_status = (int)-sent;
+    } else {
+	send_status = PJ_SUCCESS;
+    }
+
+    /* Don't want to continue. */
+    *cont = PJ_FALSE;
+}
+
+
+/* Test that we receive loopback message. */
+int transport_send_recv_test( pjsip_transport_type_e tp_type,
+			      pjsip_transport *ref_tp,
+			      char *target_url,
+			      int *p_usec_rtt)
+{
+    pj_bool_t msg_log_enabled;
+    pj_status_t status;
+    pj_str_t target, from, to, contact, call_id, body;
+    pjsip_method method;
+    pjsip_tx_data *tdata;
+    pj_time_val timeout;
+
+    PJ_UNUSED_ARG(tp_type);
+    PJ_UNUSED_ARG(ref_tp);
+
+    PJ_LOG(3,(THIS_FILE, "  single message round-trip test..."));
+
+    /* Register out test module to receive the message (if necessary). */
+    if (my_module.id == -1) {
+	status = pjsip_endpt_register_module( endpt, &my_module );
+	if (status != PJ_SUCCESS) {
+	    app_perror("   error: unable to register module", status);
+	    return -500;
+	}
+    }
+
+    /* Disable message logging. */
+    msg_log_enabled = msg_logger_set_enabled(0);
+
+    /* Create a request message. */
+    target = pj_str(target_url);
+    from = pj_str(FROM_HDR);
+    to = pj_str(target_url);
+    contact = pj_str(CONTACT_HDR);
+    call_id = pj_str(CALL_ID_HDR);
+    body = pj_str(BODY);
+
+    pjsip_method_set(&method, PJSIP_OPTIONS_METHOD);
+    status = pjsip_endpt_create_request( endpt, &method, &target, &from, &to,
+					 &contact, &call_id, CSEQ_VALUE, 
+					 &body, &tdata );
+    if (status != PJ_SUCCESS) {
+	app_perror("   error: unable to create request", status);
+	return -510;
+    }
+
+    /* Reset statuses */
+    send_status = recv_status = NO_STATUS;
+
+    /* Start time. */
+    pj_get_timestamp(&my_send_time);
+
+    /* Send the message (statelessly). */
+    PJ_LOG(5,(THIS_FILE, "Sending request to %.*s", 
+			 (int)target.slen, target.ptr));
+    status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL,
+					         &send_msg_callback);
+    if (status != PJ_SUCCESS) {
+	/* Immediate error! */
+	pjsip_tx_data_dec_ref(tdata);
+	send_status = status;
+    }
+
+    /* Set the timeout (2 seconds from now) */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 2;
+
+    /* Loop handling events until we get status */
+    do {
+	pj_time_val now;
+	pj_time_val poll_interval = { 0, 10 };
+
+	pj_gettimeofday(&now);
+	if (PJ_TIME_VAL_GTE(now, timeout)) {
+	    PJ_LOG(3,(THIS_FILE, "   error: timeout in send/recv test"));
+	    status = -540;
+	    goto on_return;
+	}
+
+	if (send_status!=NO_STATUS && send_status!=PJ_SUCCESS) {
+	    app_perror("   error sending message", send_status);
+	    status = -550;
+	    goto on_return;
+	}
+
+	if (recv_status!=NO_STATUS && recv_status!=PJ_SUCCESS) {
+	    app_perror("   error receiving message", recv_status);
+	    status = -560;
+	    goto on_return;
+	}
+
+	if (send_status!=NO_STATUS && recv_status!=NO_STATUS) {
+	    /* Success! */
+	    break;
+	}
+
+	pjsip_endpt_handle_events(endpt, &poll_interval);
+
+    } while (1);
+
+    if (status == PJ_SUCCESS) {
+	unsigned usec_rt;
+	usec_rt = pj_elapsed_usec(&my_send_time, &my_recv_time);
+
+	PJ_LOG(3,(THIS_FILE, "    round-trip = %d usec", usec_rt));
+
+	*p_usec_rtt = usec_rt;
+    }
+
+    /* Restore message logging. */
+    msg_logger_set_enabled(msg_log_enabled);
+
+    status = PJ_SUCCESS;
+
+on_return:
+    return status;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/* 
+ * Multithreaded round-trip test
+ *
+ * This test will spawn multiple threads, each of them send a request. As soon
+ * as request is received, response will be sent, and time is recorded.
+ *
+ * The main purpose of this test is to ensure there's no crash when multiple
+ * threads are sending/receiving messages.
+ *
+ */
+static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata);
+static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata);
+
+static pjsip_module rt_module = 
+{
+    NULL, NULL,				/* prev and next	*/
+    { "Transport-RT-Test", 17},		/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_TSX_LAYER-1,	/* Priority		*/
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    &rt_on_rx_request,			/* on_rx_request()	*/
+    &rt_on_rx_response,			/* on_rx_response()	*/
+    NULL,				/* tsx_handler()	*/
+};
+
+static struct
+{
+    pj_thread_t *thread;
+    pj_timestamp send_time;
+    pj_timestamp total_rt_time;
+    int sent_request_count, recv_response_count;
+    pj_str_t call_id;
+    pj_timer_entry timeout_timer;
+    pj_timer_entry tx_timer;
+    pj_mutex_t *mutex;
+} rt_test_data[16];
+
+static char	 rt_target_uri[64];
+static pj_bool_t rt_stop;
+static pj_str_t  rt_call_id;
+
+static pj_bool_t rt_on_rx_request(pjsip_rx_data *rdata)
+{
+    if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) {
+	pjsip_tx_data *tdata;
+	pjsip_response_addr res_addr;
+	pj_status_t status;
+
+	status = pjsip_endpt_create_response( endpt, rdata, 200, NULL, &tdata);
+	if (status != PJ_SUCCESS) {
+	    app_perror("    error creating response", status);
+	    return PJ_TRUE;
+	}
+	status = pjsip_get_response_addr( tdata->pool, rdata, &res_addr);
+	if (status != PJ_SUCCESS) {
+	    app_perror("    error in get response address", status);
+	    pjsip_tx_data_dec_ref(tdata);
+	    return PJ_TRUE;
+	}
+	status = pjsip_endpt_send_response( endpt, &res_addr, tdata, NULL, NULL);
+	if (status != PJ_SUCCESS) {
+	    app_perror("    error sending response", status);
+	    pjsip_tx_data_dec_ref(tdata);
+	    return PJ_TRUE;
+	}
+	return PJ_TRUE;
+	
+    }
+    return PJ_FALSE;
+}
+
+static pj_status_t rt_send_request(int thread_id)
+{
+    pj_status_t status;
+    pj_str_t target, from, to, contact, call_id;
+    pjsip_tx_data *tdata;
+    pj_time_val timeout_delay;
+
+    pj_mutex_lock(rt_test_data[thread_id].mutex);
+
+    /* Create a request message. */
+    target = pj_str(rt_target_uri);
+    from = pj_str(FROM_HDR);
+    to = pj_str(rt_target_uri);
+    contact = pj_str(CONTACT_HDR);
+    call_id = rt_test_data[thread_id].call_id;
+
+    status = pjsip_endpt_create_request( endpt, &pjsip_options_method, 
+					 &target, &from, &to,
+					 &contact, &call_id, -1, 
+					 NULL, &tdata );
+    if (status != PJ_SUCCESS) {
+	app_perror("    error: unable to create request", status);
+	pj_mutex_unlock(rt_test_data[thread_id].mutex);
+	return -610;
+    }
+
+    /* Start time. */
+    pj_get_timestamp(&rt_test_data[thread_id].send_time);
+
+    /* Send the message (statelessly). */
+    status = pjsip_endpt_send_request_stateless( endpt, tdata, NULL, NULL);
+    if (status != PJ_SUCCESS) {
+	/* Immediate error! */
+	app_perror("    error: send request", status);
+	pjsip_tx_data_dec_ref(tdata);
+	pj_mutex_unlock(rt_test_data[thread_id].mutex);
+	return -620;
+    }
+
+    /* Update counter. */
+    rt_test_data[thread_id].sent_request_count++;
+
+    /* Set timeout timer. */
+    if (rt_test_data[thread_id].timeout_timer.user_data != NULL) {
+	pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer);
+    }
+    timeout_delay.sec = 100; timeout_delay.msec = 0;
+    rt_test_data[thread_id].timeout_timer.user_data = (void*)(pj_ssize_t)1;
+    pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].timeout_timer,
+			       &timeout_delay);
+
+    pj_mutex_unlock(rt_test_data[thread_id].mutex);
+    return PJ_SUCCESS;
+}
+
+static pj_bool_t rt_on_rx_response(pjsip_rx_data *rdata)
+{
+    if (!pj_strncmp(&rdata->msg_info.cid->id, &rt_call_id, rt_call_id.slen)) {
+	char *pos = pj_strchr(&rdata->msg_info.cid->id, '/')+1;
+	int thread_id = (*pos - '0');
+	pj_timestamp recv_time;
+
+	pj_mutex_lock(rt_test_data[thread_id].mutex);
+
+	/* Stop timer. */
+	pjsip_endpt_cancel_timer(endpt, &rt_test_data[thread_id].timeout_timer);
+
+	/* Update counter and end-time. */
+	rt_test_data[thread_id].recv_response_count++;
+	pj_get_timestamp(&recv_time);
+
+	pj_sub_timestamp(&recv_time, &rt_test_data[thread_id].send_time);
+	pj_add_timestamp(&rt_test_data[thread_id].total_rt_time, &recv_time);
+
+	if (!rt_stop) {
+	    pj_time_val tx_delay = { 0, 0 };
+	    pj_assert(rt_test_data[thread_id].tx_timer.user_data == NULL);
+	    rt_test_data[thread_id].tx_timer.user_data = (void*)(pj_ssize_t)1;
+	    pjsip_endpt_schedule_timer(endpt, &rt_test_data[thread_id].tx_timer,
+				       &tx_delay);
+	}
+
+	pj_mutex_unlock(rt_test_data[thread_id].mutex);
+
+	return PJ_TRUE;
+    }
+    return PJ_FALSE;
+}
+
+static void rt_timeout_timer( pj_timer_heap_t *timer_heap,
+			      struct pj_timer_entry *entry )
+{
+    pj_mutex_lock(rt_test_data[entry->id].mutex);
+
+    PJ_UNUSED_ARG(timer_heap);
+    PJ_LOG(3,(THIS_FILE, "    timeout waiting for response"));
+    rt_test_data[entry->id].timeout_timer.user_data = NULL;
+    
+    if (rt_test_data[entry->id].tx_timer.user_data == NULL) {
+	pj_time_val delay = { 0, 0 };
+	rt_test_data[entry->id].tx_timer.user_data = (void*)(pj_ssize_t)1;
+	pjsip_endpt_schedule_timer(endpt, &rt_test_data[entry->id].tx_timer,
+				   &delay);
+    }
+
+    pj_mutex_unlock(rt_test_data[entry->id].mutex);
+}
+
+static void rt_tx_timer( pj_timer_heap_t *timer_heap,
+			 struct pj_timer_entry *entry )
+{
+    pj_mutex_lock(rt_test_data[entry->id].mutex);
+
+    PJ_UNUSED_ARG(timer_heap);
+    pj_assert(rt_test_data[entry->id].tx_timer.user_data != NULL);
+    rt_test_data[entry->id].tx_timer.user_data = NULL;
+    rt_send_request(entry->id);
+
+    pj_mutex_unlock(rt_test_data[entry->id].mutex);
+}
+
+
+static int rt_worker_thread(void *arg)
+{
+    int i;
+    pj_time_val poll_delay = { 0, 10 };
+
+    PJ_UNUSED_ARG(arg);
+
+    /* Sleep to allow main threads to run. */
+    pj_thread_sleep(10);
+
+    while (!rt_stop) {
+	pjsip_endpt_handle_events(endpt, &poll_delay);
+    }
+
+    /* Exhaust responses. */
+    for (i=0; i<100; ++i)
+	pjsip_endpt_handle_events(endpt, &poll_delay);
+
+    return 0;
+}
+
+int transport_rt_test( pjsip_transport_type_e tp_type,
+		       pjsip_transport *ref_tp,
+		       char *target_url,
+		       int *lost)
+{
+    enum { THREADS = 4, INTERVAL = 10 };
+    int i;
+    pj_status_t status;
+    pj_pool_t *pool;
+    pj_bool_t logger_enabled;
+
+    pj_timestamp zero_time, total_time;
+    unsigned usec_rt;
+    unsigned total_sent;
+    unsigned total_recv;
+
+    PJ_UNUSED_ARG(tp_type);
+    PJ_UNUSED_ARG(ref_tp);
+
+    PJ_LOG(3,(THIS_FILE, "  multithreaded round-trip test (%d threads)...",
+		  THREADS));
+    PJ_LOG(3,(THIS_FILE, "    this will take approx %d seconds, please wait..",
+		INTERVAL));
+
+    /* Make sure msg logger is disabled. */
+    logger_enabled = msg_logger_set_enabled(0);
+
+    /* Register module (if not yet registered) */
+    if (rt_module.id == -1) {
+	status = pjsip_endpt_register_module( endpt, &rt_module );
+	if (status != PJ_SUCCESS) {
+	    app_perror("   error: unable to register module", status);
+	    return -600;
+	}
+    }
+
+    /* Create pool for this test. */
+    pool = pjsip_endpt_create_pool(endpt, NULL, 4000, 4000);
+    if (!pool)
+	return -610;
+
+    /* Initialize static test data. */
+    pj_ansi_strcpy(rt_target_uri, target_url);
+    rt_call_id = pj_str("RT-Call-Id/");
+    rt_stop = PJ_FALSE;
+
+    /* Initialize thread data. */
+    for (i=0; i<THREADS; ++i) {
+	char buf[1];
+	pj_str_t str_id;
+	
+	pj_strset(&str_id, buf, 1);
+	pj_bzero(&rt_test_data[i], sizeof(rt_test_data[i]));
+
+	/* Init timer entry */
+	rt_test_data[i].tx_timer.id = i;
+	rt_test_data[i].tx_timer.cb = &rt_tx_timer;
+	rt_test_data[i].timeout_timer.id = i;
+	rt_test_data[i].timeout_timer.cb = &rt_timeout_timer;
+
+	/* Generate Call-ID for each thread. */
+	rt_test_data[i].call_id.ptr = (char*) pj_pool_alloc(pool, rt_call_id.slen+1);
+	pj_strcpy(&rt_test_data[i].call_id, &rt_call_id);
+	buf[0] = '0' + (char)i;
+	pj_strcat(&rt_test_data[i].call_id, &str_id);
+
+	/* Init mutex. */
+	status = pj_mutex_create_recursive(pool, "rt", &rt_test_data[i].mutex);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   error: unable to create mutex", status);
+	    return -615;
+	}
+
+	/* Create thread, suspended. */
+	status = pj_thread_create(pool, "rttest%p", &rt_worker_thread, 
+				  (void*)(pj_ssize_t)i, 0,
+				  PJ_THREAD_SUSPENDED, &rt_test_data[i].thread);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   error: unable to create thread", status);
+	    return -620;
+	}
+    }
+
+    /* Start threads! */
+    for (i=0; i<THREADS; ++i) {
+	pj_time_val delay = {0,0};
+	pj_thread_resume(rt_test_data[i].thread);
+
+	/* Schedule first message transmissions. */
+	rt_test_data[i].tx_timer.user_data = (void*)(pj_ssize_t)1;
+	pjsip_endpt_schedule_timer(endpt, &rt_test_data[i].tx_timer, &delay);
+    }
+
+    /* Sleep for some time. */
+    pj_thread_sleep(INTERVAL * 1000);
+
+    /* Signal thread to stop. */
+    rt_stop = PJ_TRUE;
+
+    /* Wait threads to complete. */
+    for (i=0; i<THREADS; ++i) {
+	pj_thread_join(rt_test_data[i].thread);
+	pj_thread_destroy(rt_test_data[i].thread);
+    }
+
+    /* Destroy rt_test_data */
+    for (i=0; i<THREADS; ++i) {
+	pj_mutex_destroy(rt_test_data[i].mutex);
+	pjsip_endpt_cancel_timer(endpt, &rt_test_data[i].timeout_timer);
+    }
+
+    /* Gather statistics. */
+    pj_bzero(&total_time, sizeof(total_time));
+    pj_bzero(&zero_time, sizeof(zero_time));
+    usec_rt = total_sent = total_recv = 0;
+    for (i=0; i<THREADS; ++i) {
+	total_sent += rt_test_data[i].sent_request_count;
+	total_recv +=  rt_test_data[i].recv_response_count;
+	pj_add_timestamp(&total_time, &rt_test_data[i].total_rt_time);
+    }
+
+    /* Display statistics. */
+    if (total_recv)
+	total_time.u64 = total_time.u64/total_recv;
+    else
+	total_time.u64 = 0;
+    usec_rt = pj_elapsed_usec(&zero_time, &total_time);
+    PJ_LOG(3,(THIS_FILE, "    done."));
+    PJ_LOG(3,(THIS_FILE, "    total %d messages sent", total_sent));
+    PJ_LOG(3,(THIS_FILE, "    average round-trip=%d usec", usec_rt));
+
+    pjsip_endpt_release_pool(endpt, pool);
+
+    *lost = total_sent-total_recv;
+
+    /* Flush events. */
+    flush_events(500);
+
+    /* Restore msg logger. */
+    msg_logger_set_enabled(logger_enabled);
+
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Transport load testing
+ */
+static pj_bool_t load_on_rx_request(pjsip_rx_data *rdata);
+
+static struct mod_load_test
+{
+    pjsip_module    mod;
+    pj_int32_t	    next_seq;
+    pj_bool_t	    err;
+} mod_load = 
+{
+    {
+    NULL, NULL,				/* prev and next	*/
+    { "mod-load-test", 13},		/* Name.		*/
+    -1,					/* Id			*/
+    PJSIP_MOD_PRIORITY_TSX_LAYER-1,	/* Priority		*/
+    NULL,				/* load()		*/
+    NULL,				/* start()		*/
+    NULL,				/* stop()		*/
+    NULL,				/* unload()		*/
+    &load_on_rx_request,		/* on_rx_request()	*/
+    NULL,				/* on_rx_response()	*/
+    NULL,				/* tsx_handler()	*/
+    }
+};
+
+
+static pj_bool_t load_on_rx_request(pjsip_rx_data *rdata)
+{
+    if (rdata->msg_info.cseq->cseq != mod_load.next_seq) {
+	PJ_LOG(1,("THIS_FILE", "    err: expecting cseq %u, got %u", 
+		  mod_load.next_seq, rdata->msg_info.cseq->cseq));
+	mod_load.err = PJ_TRUE;
+	mod_load.next_seq = rdata->msg_info.cseq->cseq + 1;
+    } else 
+	mod_load.next_seq++;
+    return PJ_TRUE;
+}
+
+int transport_load_test(char *target_url)
+{
+    enum { COUNT = 2000 };
+    unsigned i;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* exhaust packets */
+    do {
+	pj_time_val delay = {1, 0};
+	i = 0;
+	pjsip_endpt_handle_events2(endpt, &delay, &i);
+    } while (i != 0);
+
+    PJ_LOG(3,(THIS_FILE, "  transport load test..."));
+
+    if (mod_load.mod.id == -1) {
+	status = pjsip_endpt_register_module( endpt, &mod_load.mod);
+	if (status != PJ_SUCCESS) {
+	    app_perror("error registering module", status);
+	    return -1;
+	}
+    }
+    mod_load.err = PJ_FALSE;
+    mod_load.next_seq = 0;
+
+    for (i=0; i<COUNT && !mod_load.err; ++i) {
+	pj_str_t target, from, call_id;
+	pjsip_tx_data *tdata;
+
+	target = pj_str(target_url);
+	from = pj_str("<sip:user@host>");
+	call_id = pj_str("thecallid");
+	status = pjsip_endpt_create_request(endpt, &pjsip_invite_method, 
+					    &target, &from, 
+					    &target, &from, &call_id, 
+					    i, NULL, &tdata );
+	if (status != PJ_SUCCESS) {
+	    app_perror("error creating request", status);
+	    goto on_return;
+	}
+
+	status = pjsip_endpt_send_request_stateless(endpt, tdata, NULL, NULL);
+	if (status != PJ_SUCCESS) {
+	    app_perror("error sending request", status);
+	    goto on_return;
+	}
+    }
+
+    do {
+	pj_time_val delay = {1, 0};
+	i = 0;
+	pjsip_endpt_handle_events2(endpt, &delay, &i);
+    } while (i != 0);
+
+    if (mod_load.next_seq != COUNT) {
+	PJ_LOG(1,("THIS_FILE", "    err: expecting %u msg, got only %u", 
+		  COUNT, mod_load.next_seq));
+	status = -2;
+	goto on_return;
+    }
+
+on_return:
+    if (mod_load.mod.id != -1) {
+	pjsip_endpt_unregister_module( endpt, &mod_load.mod);
+	mod_load.mod.id = -1;
+    }
+    if (status != PJ_SUCCESS || mod_load.err) {
+	return -2;
+    }
+    PJ_LOG(3,(THIS_FILE, "   success"));
+    return 0;
+}
+
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8eb610a93eaa21cc3e8cddc75cbec137221305e.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8eb610a93eaa21cc3e8cddc75cbec137221305e.svn-base
new file mode 100644
index 0000000..ebf5a2b
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8eb610a93eaa21cc3e8cddc75cbec137221305e.svn-base
@@ -0,0 +1,855 @@
+/* $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 "test.h"
+
+#define THIS_FILE   "stun_sock_test.c"
+
+enum {
+    RESPOND_STUN    = 1,
+    WITH_MAPPED	    = 2,
+    WITH_XOR_MAPPED = 4,
+
+    ECHO	    = 8
+};
+
+/*
+ * Simple STUN server
+ */
+struct stun_srv
+{
+    pj_activesock_t	*asock;
+    unsigned		 flag;
+    pj_sockaddr		 addr;
+    unsigned		 rx_cnt;
+    pj_ioqueue_op_key_t	 send_key;
+    pj_str_t		 ip_to_send;
+    pj_uint16_t		 port_to_send;
+};
+
+static pj_bool_t 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 stun_srv *srv;
+    pj_ssize_t sent;
+
+    srv = (struct stun_srv*) pj_activesock_get_user_data(asock);
+
+    /* Ignore error */
+    if (status != PJ_SUCCESS)
+	return PJ_TRUE;
+
+    ++srv->rx_cnt;
+
+    /* Ignore if we're not responding */
+    if (srv->flag & RESPOND_STUN) {
+	pj_pool_t *pool;
+	pj_stun_msg *req_msg, *res_msg;
+
+	pool = pj_pool_create(mem, "stunsrv", 512, 512, NULL);
+    
+	/* Parse request */
+	status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size, 
+				    PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
+				    &req_msg, NULL, NULL);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   pj_stun_msg_decode()", status);
+	    pj_pool_release(pool);
+	    return PJ_TRUE;
+	}
+
+	/* Create response */
+	status = pj_stun_msg_create(pool, PJ_STUN_BINDING_RESPONSE, PJ_STUN_MAGIC,
+				    req_msg->hdr.tsx_id, &res_msg);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   pj_stun_msg_create()", status);
+	    pj_pool_release(pool);
+	    return PJ_TRUE;
+	}
+
+	/* Add MAPPED-ADDRESS or XOR-MAPPED-ADDRESS (or don't add) */
+	if (srv->flag & WITH_MAPPED) {
+	    pj_sockaddr_in addr;
+
+	    pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
+	    pj_stun_msg_add_sockaddr_attr(pool, res_msg, PJ_STUN_ATTR_MAPPED_ADDR,
+					  PJ_FALSE, &addr, sizeof(addr));
+	} else if (srv->flag & WITH_XOR_MAPPED) {
+	    pj_sockaddr_in addr;
+
+	    pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
+	    pj_stun_msg_add_sockaddr_attr(pool, res_msg, 
+					  PJ_STUN_ATTR_XOR_MAPPED_ADDR,
+					  PJ_TRUE, &addr, sizeof(addr));
+	}
+
+	/* Encode */
+	status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0, 
+				    NULL, &size);
+	if (status != PJ_SUCCESS) {
+	    app_perror("   pj_stun_msg_encode()", status);
+	    pj_pool_release(pool);
+	    return PJ_TRUE;
+	}
+
+	/* Send back */
+	sent = size;
+	pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, 
+			     src_addr, addr_len);
+
+	pj_pool_release(pool);
+
+    } else if (srv->flag & ECHO) {
+	/* Send back */
+	sent = size;
+	pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0, 
+			     src_addr, addr_len);
+
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_status_t create_server(pj_pool_t *pool,
+				 pj_ioqueue_t *ioqueue,
+				 unsigned flag,
+				 struct stun_srv **p_srv)
+{
+    struct stun_srv *srv;
+    pj_activesock_cb activesock_cb;
+    pj_status_t status;
+
+    srv = PJ_POOL_ZALLOC_T(pool, struct stun_srv);
+    srv->flag = flag;
+    srv->ip_to_send = pj_str("1.1.1.1");
+    srv->port_to_send = 1000;
+
+    status = pj_sockaddr_in_init(&srv->addr.ipv4, NULL, 0);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_bzero(&activesock_cb, sizeof(activesock_cb));
+    activesock_cb.on_data_recvfrom = &srv_on_data_recvfrom;
+    status = pj_activesock_create_udp(pool, &srv->addr, NULL, ioqueue,
+				      &activesock_cb, srv, &srv->asock, 
+				      &srv->addr);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
+
+    status = pj_activesock_start_recvfrom(srv->asock, pool, 512, 0);
+    if (status != PJ_SUCCESS) {
+	pj_activesock_close(srv->asock);
+	return status;
+    }
+
+    *p_srv = srv;
+    return PJ_SUCCESS;
+}
+
+static void destroy_server(struct stun_srv *srv)
+{
+    pj_activesock_close(srv->asock);
+}
+
+
+struct stun_client
+{
+    pj_pool_t		*pool;
+    pj_stun_sock	*sock;
+
+    pj_ioqueue_op_key_t	 send_key;
+    pj_bool_t		 destroy_on_err;
+
+    unsigned		 on_status_cnt;
+    pj_stun_sock_op	 last_op;
+    pj_status_t		 last_status;
+
+    unsigned		 on_rx_data_cnt;
+};
+
+static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock, 
+				     pj_stun_sock_op op,
+				     pj_status_t status)
+{
+    struct stun_client *client;
+
+    client = (struct stun_client*) pj_stun_sock_get_user_data(stun_sock);
+    client->on_status_cnt++;
+    client->last_op = op;
+    client->last_status = status;
+
+    if (status != PJ_SUCCESS && client->destroy_on_err) {
+	pj_stun_sock_destroy(client->sock);
+	client->sock = NULL;
+	return PJ_FALSE;
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock,
+				      void *pkt,
+				      unsigned pkt_len,
+				      const pj_sockaddr_t *src_addr,
+				      unsigned addr_len)
+{
+    struct stun_client *client;
+
+    PJ_UNUSED_ARG(pkt);
+    PJ_UNUSED_ARG(pkt_len);
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(addr_len);
+
+    client = (struct stun_client*) pj_stun_sock_get_user_data(stun_sock);
+    client->on_rx_data_cnt++;
+
+    return PJ_TRUE;
+}
+
+static pj_status_t create_client(pj_stun_config *cfg,
+				 struct stun_client **p_client,
+				 pj_bool_t destroy_on_err)
+{
+    pj_pool_t *pool;
+    struct stun_client *client;
+    pj_stun_sock_cfg sock_cfg;
+    pj_stun_sock_cb cb;
+    pj_status_t status;
+
+    pool = pj_pool_create(mem, "test", 512, 512, NULL);
+    client = PJ_POOL_ZALLOC_T(pool, struct stun_client);
+    client->pool = pool;
+
+    pj_stun_sock_cfg_default(&sock_cfg);
+
+    pj_bzero(&cb, sizeof(cb));
+    cb.on_status = &stun_sock_on_status;
+    cb.on_rx_data = &stun_sock_on_rx_data;
+    status = pj_stun_sock_create(cfg, NULL, pj_AF_INET(), &cb,
+				 &sock_cfg, client, &client->sock);
+    if (status != PJ_SUCCESS) {
+	app_perror("   pj_stun_sock_create()", status);
+	pj_pool_release(pool);
+	return status;
+    }
+
+    pj_stun_sock_set_user_data(client->sock, client);
+
+    pj_ioqueue_op_key_init(&client->send_key, sizeof(client->send_key));
+
+    client->destroy_on_err = destroy_on_err;
+
+    *p_client = client;
+
+    return PJ_SUCCESS;
+}
+
+
+static void destroy_client(struct stun_client *client)
+{
+    if (client->sock) {
+	pj_stun_sock_destroy(client->sock);
+	client->sock = NULL;
+    }
+    pj_pool_release(client->pool);
+}
+
+static void handle_events(pj_stun_config *cfg, unsigned msec_delay)
+{
+    pj_time_val delay;
+
+    pj_timer_heap_poll(cfg->timer_heap, NULL);
+
+    delay.sec = 0;
+    delay.msec = msec_delay;
+    pj_time_val_normalize(&delay);
+
+    pj_ioqueue_poll(cfg->ioqueue, &delay);
+}
+
+/*
+ * Timeout test: scenario when no response is received from server
+ */
+static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
+{
+    struct stun_srv *srv;
+    struct stun_client *client;
+    pj_str_t srv_addr;
+    pj_time_val timeout, t;
+    int i, ret = 0;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, "  timeout test [%d]", destroy_on_err));
+
+    status =  create_client(cfg, &client, destroy_on_err);
+    if (status != PJ_SUCCESS)
+	return -10;
+
+    status = create_server(client->pool, cfg->ioqueue, 0, &srv);
+    if (status != PJ_SUCCESS) {
+	destroy_client(client);
+	return -20;
+    }
+
+    srv_addr = pj_str("127.0.0.1");
+    status = pj_stun_sock_start(client->sock, &srv_addr, 
+				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+    if (status != PJ_SUCCESS) {
+	destroy_server(srv);
+	destroy_client(client);
+	return -30;
+    }
+
+    /* Wait until on_status() callback is called with the failure */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 60;
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that callback with correct operation is called */
+    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
+	ret = -40;
+	goto on_return;
+    }
+    /* .. and with the correct status */
+    if (client->last_status != PJNATH_ESTUNTIMEDOUT) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting PJNATH_ESTUNTIMEDOUT"));
+	ret = -50;
+	goto on_return;
+    }
+    /* Check that server received correct retransmissions */
+    if (srv->rx_cnt != PJ_STUN_MAX_TRANSMIT_COUNT) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting %d retransmissions, got %d",
+		   PJ_STUN_MAX_TRANSMIT_COUNT, srv->rx_cnt));
+	ret = -60;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -70;
+	goto on_return;
+    }
+
+on_return:
+    destroy_server(srv);
+    destroy_client(client);
+    for (i=0; i<7; ++i)
+	handle_events(cfg, 50);
+    return ret;
+}
+
+
+/*
+ * Invalid response scenario: when server returns no MAPPED-ADDRESS or
+ * XOR-MAPPED-ADDRESS attribute.
+ */
+static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
+{
+    struct stun_srv *srv;
+    struct stun_client *client;
+    pj_str_t srv_addr;
+    pj_time_val timeout, t;
+    int i, ret = 0;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, "  missing attribute test [%d]", destroy_on_err));
+
+    status =  create_client(cfg, &client, destroy_on_err);
+    if (status != PJ_SUCCESS)
+	return -110;
+
+    status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, &srv);
+    if (status != PJ_SUCCESS) {
+	destroy_client(client);
+	return -120;
+    }
+
+    srv_addr = pj_str("127.0.0.1");
+    status = pj_stun_sock_start(client->sock, &srv_addr, 
+				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+    if (status != PJ_SUCCESS) {
+	destroy_server(srv);
+	destroy_client(client);
+	return -130;
+    }
+
+    /* Wait until on_status() callback is called with the failure */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 60;
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that callback with correct operation is called */
+    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
+	ret = -140;
+	goto on_return;
+    }
+    if (client->last_status != PJNATH_ESTUNNOMAPPEDADDR) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting PJNATH_ESTUNNOMAPPEDADDR"));
+	ret = -150;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -170;
+	goto on_return;
+    }
+
+on_return:
+    destroy_server(srv);
+    destroy_client(client);
+    for (i=0; i<7; ++i)
+	handle_events(cfg, 50);
+    return ret;
+}
+
+/*
+ * Keep-alive test.
+ */
+static int keep_alive_test(pj_stun_config *cfg)
+{
+    struct stun_srv *srv;
+    struct stun_client *client;
+    pj_sockaddr_in mapped_addr;
+    pj_stun_sock_info info;
+    pj_str_t srv_addr;
+    pj_time_val timeout, t;
+    int i, ret = 0;
+    pj_status_t status;
+
+    PJ_LOG(3,(THIS_FILE, "  normal operation"));
+
+    status =  create_client(cfg, &client, PJ_TRUE);
+    if (status != PJ_SUCCESS)
+	return -310;
+
+    status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv);
+    if (status != PJ_SUCCESS) {
+	destroy_client(client);
+	return -320;
+    }
+
+    /*
+     * Part 1: initial Binding resolution.
+     */
+    PJ_LOG(3,(THIS_FILE, "    initial Binding request"));
+    srv_addr = pj_str("127.0.0.1");
+    status = pj_stun_sock_start(client->sock, &srv_addr, 
+				pj_ntohs(srv->addr.ipv4.sin_port), NULL);
+    if (status != PJ_SUCCESS) {
+	destroy_server(srv);
+	destroy_client(client);
+	return -330;
+    }
+
+    /* Wait until on_status() callback is called with success status */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 60;
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that callback with correct operation is called */
+    if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting Binding operation status"));
+	ret = -340;
+	goto on_return;
+    }
+    if (client->last_status != PJ_SUCCESS) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting PJ_SUCCESS status"));
+	ret = -350;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -370;
+	goto on_return;
+    }
+
+    /* Get info */
+    pj_bzero(&info, sizeof(info));
+    pj_stun_sock_get_info(client->sock, &info);
+
+    /* Check that we have server address */
+    if (!pj_sockaddr_has_addr(&info.srv_addr)) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing server address"));
+	ret = -380;
+	goto on_return;
+    }
+    /* .. and bound address port must not be zero */
+    if (pj_sockaddr_get_port(&info.bound_addr)==0) {
+	PJ_LOG(3,(THIS_FILE, "    error: bound address is zero"));
+	ret = -381;
+	goto on_return;
+    }
+    /* .. and mapped address */
+    if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing mapped address"));
+	ret = -382;
+	goto on_return;
+    }
+    /* verify the mapped address */
+    pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
+    if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: mapped address mismatched"));
+	ret = -383;
+	goto on_return;
+    }
+
+    /* .. and at least one alias */
+    if (info.alias_cnt == 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: must have at least one alias"));
+	ret = -384;
+	goto on_return;
+    }
+    if (!pj_sockaddr_has_addr(&info.aliases[0])) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing alias"));
+	ret = -386;
+	goto on_return;
+    }
+
+
+    /*
+     * Part 2: sending and receiving data
+     */
+    PJ_LOG(3,(THIS_FILE, "    sending/receiving data"));
+
+    /* Change server operation mode to echo back data */
+    srv->flag = ECHO;
+
+    /* Reset server */
+    srv->rx_cnt = 0;
+
+    /* Client sending data to echo server */
+    {
+	char txt[100];
+	PJ_LOG(3,(THIS_FILE, "     sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3)));
+    }
+    status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret),
+				 0, &info.srv_addr, 
+				 pj_sockaddr_get_len(&info.srv_addr));
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	app_perror("    error: server sending data", status);
+	ret = -390;
+	goto on_return;
+    }
+
+    /* Wait for a short period until client receives data. We can't wait for
+     * too long otherwise the keep-alive will kick in.
+     */
+    pj_gettimeofday(&timeout);
+    timeout.sec += 1;
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that data is received in server */
+    if (srv->rx_cnt == 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: server didn't receive data"));
+	ret = -395;
+	goto on_return;
+    }
+
+    /* Check that status is still OK */
+    if (client->last_status != PJ_SUCCESS) {
+	app_perror("    error: client has failed", client->last_status);
+	ret = -400;
+	goto on_return;
+    }
+    /* Check that data has been received */
+    if (client->on_rx_data_cnt == 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client doesn't receive data"));
+	ret = -410;
+	goto on_return;
+    }
+
+    /*
+     * Part 3: Successful keep-alive,
+     */
+    PJ_LOG(3,(THIS_FILE, "    successful keep-alive scenario"));
+
+    /* Change server operation mode to normal mode */
+    srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
+
+    /* Reset server */
+    srv->rx_cnt = 0;
+
+    /* Reset client */
+    client->on_status_cnt = 0;
+    client->last_status = PJ_SUCCESS;
+    client->on_rx_data_cnt = 0;
+
+    /* Wait for keep-alive duration to see if client actually sends the
+     * keep-alive.
+     */
+    pj_gettimeofday(&timeout);
+    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that server receives some packets */
+    if (srv->rx_cnt == 0) {
+	PJ_LOG(3, (THIS_FILE, "    error: no keep-alive was received"));
+	ret = -420;
+	goto on_return;
+    }
+    /* Check that client status is still okay and on_status() callback is NOT
+     * called
+     */
+    /* No longer valid due to this ticket:
+     *  http://trac.pjsip.org/repos/ticket/742
+
+    if (client->on_status_cnt != 0) {
+	PJ_LOG(3, (THIS_FILE, "    error: on_status() must not be called on successful"
+			      "keep-alive when mapped-address does not change"));
+	ret = -430;
+	goto on_return;
+    }
+    */
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -440;
+	goto on_return;
+    }
+
+
+    /*
+     * Part 4: Successful keep-alive with IP address change
+     */
+    PJ_LOG(3,(THIS_FILE, "    mapped IP address change"));
+
+    /* Change server operation mode to normal mode */
+    srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
+
+    /* Change mapped address in the response */
+    srv->ip_to_send = pj_str("2.2.2.2");
+    srv->port_to_send++;
+
+    /* Reset server */
+    srv->rx_cnt = 0;
+
+    /* Reset client */
+    client->on_status_cnt = 0;
+    client->last_status = PJ_SUCCESS;
+    client->on_rx_data_cnt = 0;
+
+    /* Wait for keep-alive duration to see if client actually sends the
+     * keep-alive.
+     */
+    pj_gettimeofday(&timeout);
+    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that server receives some packets */
+    if (srv->rx_cnt == 0) {
+	PJ_LOG(3, (THIS_FILE, "    error: no keep-alive was received"));
+	ret = -450;
+	goto on_return;
+    }
+    /* Check that on_status() callback is called (because mapped address
+     * has changed)
+     */
+    if (client->on_status_cnt != 1) {
+	PJ_LOG(3, (THIS_FILE, "    error: on_status() was not called"));
+	ret = -460;
+	goto on_return;
+    }
+    /* Check that callback was called with correct operation */
+    if (client->last_op != PJ_STUN_SOCK_MAPPED_ADDR_CHANGE) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting keep-alive operation status"));
+	ret = -470;
+	goto on_return;
+    }
+    /* Check that last status is still success */
+    if (client->last_status != PJ_SUCCESS) {
+	PJ_LOG(3, (THIS_FILE, "    error: expecting successful status"));
+	ret = -480;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -490;
+	goto on_return;
+    }
+
+    /* Get info */
+    pj_bzero(&info, sizeof(info));
+    pj_stun_sock_get_info(client->sock, &info);
+
+    /* Check that we have server address */
+    if (!pj_sockaddr_has_addr(&info.srv_addr)) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing server address"));
+	ret = -500;
+	goto on_return;
+    }
+    /* .. and mapped address */
+    if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing mapped address"));
+	ret = -510;
+	goto on_return;
+    }
+    /* verify the mapped address */
+    pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
+    if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: mapped address mismatched"));
+	ret = -520;
+	goto on_return;
+    }
+
+    /* .. and at least one alias */
+    if (info.alias_cnt == 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: must have at least one alias"));
+	ret = -530;
+	goto on_return;
+    }
+    if (!pj_sockaddr_has_addr(&info.aliases[0])) {
+	PJ_LOG(3,(THIS_FILE, "    error: missing alias"));
+	ret = -540;
+	goto on_return;
+    }
+
+
+    /*
+     * Part 5: Failed keep-alive
+     */
+    PJ_LOG(3,(THIS_FILE, "    failed keep-alive scenario"));
+    
+    /* Change server operation mode to respond without attribute */
+    srv->flag = RESPOND_STUN;
+
+    /* Reset server */
+    srv->rx_cnt = 0;
+
+    /* Reset client */
+    client->on_status_cnt = 0;
+    client->last_status = PJ_SUCCESS;
+    client->on_rx_data_cnt = 0;
+
+    /* Wait until on_status() is called with failure. */
+    pj_gettimeofday(&timeout);
+    timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5);
+    do {
+	handle_events(cfg, 100);
+	pj_gettimeofday(&t);
+    } while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
+
+    /* Check that callback with correct operation is called */
+    if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting keep-alive operation status"));
+	ret = -600;
+	goto on_return;
+    }
+    if (client->last_status == PJ_SUCCESS) {
+	PJ_LOG(3,(THIS_FILE, "    error: expecting failed keep-alive"));
+	ret = -610;
+	goto on_return;
+    }
+    /* Check that client doesn't receive anything */
+    if (client->on_rx_data_cnt != 0) {
+	PJ_LOG(3,(THIS_FILE, "    error: client shouldn't have received anything"));
+	ret = -620;
+	goto on_return;
+    }
+
+
+on_return:
+    destroy_server(srv);
+    destroy_client(client);
+    for (i=0; i<7; ++i)
+	handle_events(cfg, 50);
+    return ret;
+}
+
+
+#define DO_TEST(expr)	    \
+	    capture_pjlib_state(&stun_cfg, &pjlib_state); \
+	    ret = expr; \
+	    if (ret != 0) goto on_return; \
+	    ret = check_pjlib_state(&stun_cfg, &pjlib_state); \
+	    if (ret != 0) goto on_return;
+
+
+int stun_sock_test(void)
+{
+    struct pjlib_state pjlib_state;
+    pj_stun_config stun_cfg;
+    pj_ioqueue_t *ioqueue = NULL;
+    pj_timer_heap_t *timer_heap = NULL;
+    pj_pool_t *pool = NULL;
+    pj_status_t status;
+    int ret = 0;
+
+    pool = pj_pool_create(mem, NULL, 512, 512, NULL);
+
+    status = pj_ioqueue_create(pool, 12, &ioqueue);
+    if (status != PJ_SUCCESS) {
+	app_perror("   pj_ioqueue_create()", status);
+	ret = -4;
+	goto on_return;
+    }
+
+    status = pj_timer_heap_create(pool, 100, &timer_heap);
+    if (status != PJ_SUCCESS) {
+	app_perror("   pj_timer_heap_create()", status);
+	ret = -8;
+	goto on_return;
+    }
+    
+    pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
+
+    DO_TEST(timeout_test(&stun_cfg, PJ_FALSE));
+    DO_TEST(timeout_test(&stun_cfg, PJ_TRUE));
+
+    DO_TEST(missing_attr_test(&stun_cfg, PJ_FALSE));
+    DO_TEST(missing_attr_test(&stun_cfg, PJ_TRUE));
+
+    DO_TEST(keep_alive_test(&stun_cfg));
+
+on_return:
+    if (timer_heap) pj_timer_heap_destroy(timer_heap);
+    if (ioqueue) pj_ioqueue_destroy(ioqueue);
+    if (pool) pj_pool_release(pool);
+    return ret;
+}
+
+
diff --git a/jni/pjproject-android/.svn/pristine/c8/c8fbdd20b7b30e1d50fd7c24cc110bce0d4baaac.svn-base b/jni/pjproject-android/.svn/pristine/c8/c8fbdd20b7b30e1d50fd7c24cc110bce0d4baaac.svn-base
new file mode 100644
index 0000000..4b5da40
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/c8/c8fbdd20b7b30e1d50fd7c24cc110bce0d4baaac.svn-base
@@ -0,0 +1,116 @@
+/* $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/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 */
+
+
+