Index: /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.c.template
===================================================================
--- /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.c.template	(revision 4565)
+++ /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.c.template	(revision 4566)
@@ -3,4 +3,40 @@
 #include "callbacks.h"
 
+/*
+ * Wrapper of pjsua_logging_config callback
+ */
+static PjsuaLoggingConfigCallback *pjsua_logging_config_cb = NULL;
+
+void pjsua_logging_config_callback_proxy(int level, const char *data, int len)
+{
+  PJ_UNUSED_ARG(len);
+  pjsua_logging_config_cb->on_log(level, data);
+}
+
+void setPjsuaLoggingConfigCallback(PjsuaLoggingConfigCallback *callback)
+{
+  pjsua_logging_config_cb = callback;
+}
+
+
+/*
+ * Wrapper of pj_timer_heap_callback
+ */
+void pj_timer_heap_callback_proxy(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
+{
+  PjTimerHeapCallback *cb = (PjTimerHeapCallback*)entry->user_data;
+  pj_assert(cb);
+  cb->on_timer(timer_heap, entry);
+}
+
+void setPjTimerHeapCallback(pj_timer_entry *entry, PjTimerHeapCallback *callback)
+{
+  entry->user_data = callback;
+}
+
+
+/*
+ * Wrapper of pjsua_callback
+ */
 static PjsuaCallback* cb = NULL;
 
@@ -15,3 +51,3 @@
 };
 
-pjsua_callback* PJSUA_CALLBACK_PROXY = &my_cb_proxy;
+pjsua_callback* pjsua_callback_proxy = &my_cb_proxy;
Index: /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.h.template
===================================================================
--- /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.h.template	(revision 4565)
+++ /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.h.template	(revision 4566)
@@ -3,10 +3,37 @@
 #include <pjsua-lib/pjsua.h>
 
+/*
+ * Wrapper of pjsua_logging_config callback
+ */
+class PjsuaLoggingConfigCallback {
+public:
+  virtual void on_log(int level, const char *data) {}
+  virtual ~PjsuaLoggingConfigCallback() {}
+};
+
 #ifndef SWIG /* SWIG should ignore these */
-class PjsuaCallback;
-extern void setPjsuaCallback(PjsuaCallback* callback);
-extern pjsua_callback* PJSUA_CALLBACK_PROXY;
+extern void pjsua_logging_config_callback_proxy(int level, const char *data, int len);
+extern void setPjsuaLoggingConfigCallback(PjsuaLoggingConfigCallback *callback);
 #endif
 
+
+/*
+ * Wrapper of pj_timer_heap_callback
+ */
+class PjTimerHeapCallback {
+public:
+  virtual void on_timer(pj_timer_heap_t *timer_heap, pj_timer_entry *entry) {}
+  virtual ~PjTimerHeapCallback() {}
+};
+
+#ifndef SWIG /* SWIG should ignore these */
+extern void pj_timer_heap_callback_proxy(pj_timer_heap_t *timer_heap, pj_timer_entry *entry);
+extern void setPjTimerHeapCallback(pj_timer_entry *entry, PjTimerHeapCallback *callback);
+#endif
+
+
+/*
+ * Wrapper of pjsua_callback
+ */
 class PjsuaCallback {
 public:
@@ -14,2 +41,7 @@
   virtual ~PjsuaCallback() {}
 };
+
+#ifndef SWIG /* SWIG should ignore these */
+extern void setPjsuaCallback(PjsuaCallback* callback);
+extern pjsua_callback* pjsua_callback_proxy;
+#endif
Index: /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.i
===================================================================
--- /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.i	(revision 4565)
+++ /pjproject/branches/projects/jni/pjsip-apps/src/jni/callbacks.i	(revision 4566)
@@ -5,13 +5,54 @@
 %}
 
+/*
+ * Wrap pjsua_logging_config callback
+ */
+%feature("director") PjsuaLoggingConfigCallback;
+%ignore pjsua_logging_config::cb;
+%extend pjsua_logging_config {
+    void setCb(PjsuaLoggingConfigCallback* callback) {
+	setPjsuaLoggingConfigCallback(callback);
+	$self->cb = callback? &pjsua_logging_config_callback_proxy : NULL;
+    }
+}
+
+
+/*
+ * Wrap pj_timer_heap_callback in pj_timer_entry
+ */
+%feature("director") PjTimerHeapCallback;
+%ignore pj_timer_entry::cb;
+/* Suppress user_data, app can put user data in PjTimerHeapCallback inherited class */
+%ignore pj_timer_entry::user_data;
+%extend pj_timer_entry {
+    void setCb(PjTimerHeapCallback* callback) {
+	setPjTimerHeapCallback($self, callback);
+	$self->cb = callback? &pj_timer_heap_callback_proxy : NULL;
+    }
+}
+
+
+/*
+ * Wrap pjsua_callback
+ */
 %feature("director") PjsuaCallback;
 %ignore pjsua_callback;
 %ignore pjsua_config::cb;
 %extend pjsua_config {
-    void setCb(PjsuaCallback *pjsuaCb) {
-	$self->cb = *PJSUA_CALLBACK_PROXY;
-	setPjsuaCallback(pjsuaCb);
+    void setCb(PjsuaCallback *callback) {
+	setPjsuaCallback(callback);
+	if (callback)
+	    $self->cb = *pjsua_callback_proxy;
+	else
+	    pj_bzero(&$self->cb, sizeof($self->cb));
     }
 }
 
+
 %include <callbacks.h>
+
+/* Ignore these callbacks */
+%ignore pjsip_msg_body::print_body;
+%ignore pjsip_msg_body::clone_data;
+%ignore pjsip_tx_data::cb;
+%ignore pjsip_transaction::state_handler;
Index: /pjproject/branches/projects/jni/pjsip-apps/src/jni/header.i
===================================================================
--- /pjproject/branches/projects/jni/pjsip-apps/src/jni/header.i	(revision 4565)
+++ /pjproject/branches/projects/jni/pjsip-apps/src/jni/header.i	(revision 4566)
@@ -1,3 +1,10 @@
 /* $Id$ */
+
+/* TODO:
+ * - fix memory leaks in pj_str_t - java String typemaps
+ * - typemap for pj_str_t as output param for callback/director,
+ *   e.g: "st_text" param in pjsua_callback::on_call_replace_request()
+ * - workaround for nested struct/union, i.e: moving out inner classes to global scope
+ */
 
 %module (directors="1") pjsua
@@ -15,4 +22,12 @@
 %rename("%(strip:[pjsua_])s", %$isfunction) "";
 
+/* Suppress pjsua_schedule_timer2(), app can still use pjsua_schedule_timer() */
+%ignore pjsua_schedule_timer2;
+
+/* Suppress aux function pjsua_resolve_stun_servers(), usually app won't need this
+ * as app can just simply configure STUN server to use STUN.
+ */
+%ignore pjsua_resolve_stun_servers;
+
 /* Map 'void *' simply as long, app can use this "long" as index of its real user data */
 %apply long long { void * };
@@ -26,4 +41,7 @@
 %apply bool { pj_bool_t };
 
+/* Map pjsua_call_dump() output buffer */
+%apply (char *STRING, size_t LENGTH) { (char *buffer, unsigned maxlen) };
+
 /* Map "int*" & "unsigned*" as input & output */
 %apply unsigned	*INOUT  { unsigned * };
@@ -33,6 +51,8 @@
 %apply int      *INOUT	{ pj_stun_nat_type * };
 %apply int      *INOUT	{ pjsip_status_code * };
+%apply pj_str_t *INOUT  { pj_str_t *p_contact };
+
+/* Apply array of integer on array of enum */
 %apply int[ANY]         { pjmedia_format_id dec_fmt_id[ANY] };
-%apply pj_str_t *INOUT  { pj_str_t *p_contact };
 
 /* Handle members typed array of pj_str_t */
Index: /pjproject/branches/projects/jni/pjsip-apps/src/jni/hello.java
===================================================================
--- /pjproject/branches/projects/jni/pjsip-apps/src/jni/hello.java	(revision 4565)
+++ /pjproject/branches/projects/jni/pjsip-apps/src/jni/hello.java	(revision 4566)
@@ -5,16 +5,9 @@
 import java.io.IOException;
 
-import org.pjsip.pjsua.pj_pool_t;
-import org.pjsip.pjsua.pjsip_rx_data;
-import org.pjsip.pjsua.pjsip_transport_type_e;
-
-import org.pjsip.pjsua.pjsua;
-import org.pjsip.pjsua.pjsua_acc_config;
-import org.pjsip.pjsua.pjsua_call_info;
-import org.pjsip.pjsua.pjsua_call_media_status;
-import org.pjsip.pjsua.pjsua_config;
-import org.pjsip.pjsua.pjsua_logging_config;
-import org.pjsip.pjsua.pjsua_transport_config;
-import org.pjsip.pjsua.PjsuaCallback;
+import org.pjsip.pjsua.*;
+
+class app_config {
+	public static int cur_call_id = -1;
+}
 
 class MyPjsuaCallback extends PjsuaCallback {
@@ -30,4 +23,6 @@
 		}
 	}
+	
+	/* Testing pj_str_t-String map with director */
 	@Override
 	public void on_pager(int call_id, String from, String to, String contact, String mime_type, String body)
@@ -44,6 +39,29 @@
 		/* Auto answer */
 		pjsua.call_answer(call_id, 200, null, null);
-	}
-}
+		app_config.cur_call_id = call_id;
+	}
+}
+
+class MyLogger extends PjsuaLoggingConfigCallback {
+	@Override
+	public void on_log(int level, String data)
+	{
+		System.out.print("LOG: " + data);
+	}
+}
+
+class MyTimerCallback extends PjTimerHeapCallback {
+	private int _user_data;
+	
+	/* Use this class itself to store app user data */
+	MyTimerCallback(int user_data) { _user_data = user_data; }
+	
+	@Override
+	public void on_timer(pj_timer_heap_t timer_heap, pj_timer_entry entry)
+	{
+		System.out.println("======== Timer fired (user data = " + _user_data + ")");
+	}
+}
+
 
 public class hello {
@@ -80,7 +98,12 @@
 			pjsua_config cfg = new pjsua_config();
 			pjsua.config_default(cfg);
-			/* Setup callback */
 			cfg.setCb(new MyPjsuaCallback());
-			status = pjsua.init(cfg, null, null);
+
+			pjsua_logging_config log_cfg = new pjsua_logging_config();
+			pjsua.logging_config_default(log_cfg);
+			log_cfg.setLevel(4);
+			log_cfg.setCb(new MyLogger());
+
+			status = pjsua.init(cfg, log_cfg, null);
 			if (status != pjsua.PJ_SUCCESS) {
 				pj_error_exit("Error inintializing pjsua", status);
@@ -117,8 +140,21 @@
 		/* Make call to the URL. */
 		if (false) {
-			status = pjsua.call_make_call(acc_id[0], "sip:localhost", null, 0, null, call_id);
+			status = pjsua.call_make_call(acc_id[0], "sip:localhost:6000", null, 0, null, call_id);
 			if (status != pjsua.PJ_SUCCESS) {
 				pj_error_exit("Error making call", status);
 			}
+			app_config.cur_call_id = call_id[0];
+		}
+		
+		/* Test timer */
+		{
+			pj_timer_entry timer = new pj_timer_entry();
+			timer.setCb(new MyTimerCallback(1234567890));
+
+			pj_time_val tv = new pj_time_val();
+			tv.setSec(0);
+			tv.setMsec(1000);
+
+			pjsua.schedule_timer(timer, tv);
 		}
 		
@@ -141,4 +177,5 @@
 			} else if (userInput.equals("h")) {
 				pjsua.call_hangup_all();
+				app_config.cur_call_id = -1;
 			} else if (userInput.equals("c")) {
 				/* Test string as output param, it is wrapped as string array */
@@ -154,4 +191,20 @@
 			} else if (userInput.equals("dd")) {
 				pjsua.dump(true);
+			} else if (userInput.equals("dq")) {
+				if (app_config.cur_call_id == -1) {
+					System.out.println("No active call");
+					continue;
+				}
+				
+				byte[] buf = new byte[1024*2];
+				pjsua.call_dump(app_config.cur_call_id, true, buf, "");
+
+				/* Build string from byte array, find null terminator first */
+				int len = 0;
+				while (len < buf.length && buf[len] != 0) ++len;
+				String call_dump = new String(buf, 0, len);
+
+				System.out.println("Statistics of call " + app_config.cur_call_id);
+				System.out.println(call_dump);
 			}
 		}
Index: /pjproject/branches/projects/jni/pjsip-apps/src/jni/my_typemaps.i
===================================================================
--- /pjproject/branches/projects/jni/pjsip-apps/src/jni/my_typemaps.i	(revision 4565)
+++ /pjproject/branches/projects/jni/pjsip-apps/src/jni/my_typemaps.i	(revision 4566)
@@ -214,4 +214,5 @@
       $1->slen = str_len;
     }
+    jenv->ReleaseStringUTFChars($input, str_ptr);
   }
 }
Index: /pjproject/branches/projects/jni/pjsip-apps/src/jni/swig_gen.py
===================================================================
--- /pjproject/branches/projects/jni/pjsip-apps/src/jni/swig_gen.py	(revision 4565)
+++ /pjproject/branches/projects/jni/pjsip-apps/src/jni/swig_gen.py	(revision 4566)
@@ -13,4 +13,6 @@
 #SOURCE_PATH  = PJ_ROOT_PATH + "pjlib-util/include/pjlib-util/scanner.h"
 #SOURCE_PATH  = PJ_ROOT_PATH + "pjlib/include/pj/types.h"
+OUTDIR = "output" if len(sys.argv)<=1 else sys.argv[1]
+
 
 # CPP is needed by pycparser.
@@ -62,4 +64,6 @@
 	'pjsip_rx_data_op_key',
 	'pjsip_module',
+	'pjsip_uri',
+	'pjsip_uri_vptr',
 	'pjmedia_port',
 	'pjmedia_transport',
@@ -72,5 +76,5 @@
 
 class MyGen(c_generator.CGenerator):
-	
+
 	def __init__(self, ast):
 		super(MyGen, self).__init__()
@@ -86,5 +90,5 @@
 			if name and not name.startswith(BASE_PREFIX):
 				continue
-		
+
 			if name in self.nodes:
 				self.nodes[name].append(ext)
@@ -94,5 +98,5 @@
 			else:
 				self.nodes[name] = [ext]
-				
+
 		# Generate dependencies, they are all APIs to be exported
 		for name in self.nodes.keys():
@@ -102,5 +106,5 @@
 				self._add_dep(name)
 		self.deps_frozen = True
-	
+
 	# Override visit_IdentifierType() to collect "node name" and generate dependencies
 	# from this node's children
@@ -114,5 +118,5 @@
 			return False
 		return True
-	
+
 	def _get_node_name(self, node):
 		s = getattr(node, 'name', None)
@@ -135,9 +139,9 @@
 			name in self.deps or name in self.deps_pending:
 			return
-			
+
 		if name in FORCE_STRUCT_AS_OPAQUE:
 			self.deps.append(name)
 			return
-			
+
 		self.deps_pending.append(name)
 		for node in self.nodes[name]:
@@ -147,5 +151,5 @@
 			sys.exit(1)
 		self.deps.append(name)
-		
+
 	# Opaque struct is identified by empty struct member declaration.
 	def _is_struct_opaque(self, node):
@@ -177,6 +181,6 @@
 			code = code.replace('typedef enum '+name+' '+name+';\n', '', 1)
 		return code
-	
-	def _gen_pjsua_callback(self):
+
+	def _gen_pjsua_callback(self, code):
 		# init
 		cbclass  = ''
@@ -184,6 +188,5 @@
 		cbdef = []
 
-		n = self.nodes['pjsua_callback'][0]
-		raw_lines = self._print_node(n).splitlines()
+		raw_lines = code.splitlines()
 		for idx, line in enumerate(raw_lines):
 			if idx in [0, 1, len(raw_lines)-1]: continue
@@ -208,5 +211,5 @@
 				fstrs[0] = m.group(1).strip()
 				fstrs[2] = m.group(3)
-		
+
 			cbclass += '  virtual ' + ' '.join(fstrs)
 			if fstrs[0] == 'void':
@@ -227,9 +230,36 @@
 
 			cbdef.append('  &' + fstrs[1])
-				
-		# trail	
-		
+
 		return [cbclass, cbproxy, ',\n'.join(cbdef)+'\n']
-	
+
+	def _process_pjsua_callback(self, name, code):
+		if name != 'pjsua_callback':
+			return code
+
+		cb = self._gen_pjsua_callback(code)
+
+		fout = open(OUTDIR+'/callbacks.h', 'w+')
+		fin  = open('callbacks.h.template', 'r')
+		for line in fin:
+			if line.find(r'$PJSUA_CALLBACK_CLASS$') >= 0:
+				fout.write(cb[0])
+			else:
+				fout.write(line)
+		fin.close()
+		fout.close()
+
+		fout = open(OUTDIR+'/callbacks.c', 'w+')
+		fin  = open('callbacks.c.template', 'r')
+		for line in fin:
+			if line.find(r'$PJSUA_CALLBACK_PROXY$') >= 0:
+				fout.write(cb[1])
+			elif line.find(r'$PJSUA_CALLBACK_DEF$') >= 0:
+				fout.write(cb[2])
+			else:
+				fout.write(line)
+		fin.close()
+		fout.close()
+		return ''
+
 	# Generate code from the specified node.
 	def _print_node(self, node):
@@ -248,31 +278,10 @@
 			for node in self.nodes[name]:
 				ss += self._print_node(node)
-			s += self._process_opaque_struct(name, ss)
+
+			ss = self._process_opaque_struct(name, ss)
+			ss = self._process_pjsua_callback(name, ss)
+			s += ss
 		return s
-	
-	def write_pjsua_callback(self, outdir):
-		cb = self._gen_pjsua_callback()
-		
-		fout = open(outdir+'/callbacks.h', 'w+')
-		fin  = open('callbacks.h.template', 'r')
-		for line in fin:
-			if line.find(r'$PJSUA_CALLBACK_CLASS$') >= 0:
-				fout.write(cb[0])
-			else:
-				fout.write(line)
-		fin.close()
-		fout.close()
-
-		fout = open(outdir+'/callbacks.c', 'w+')
-		fin  = open('callbacks.c.template', 'r')
-		for line in fin:
-			if line.find(r'$PJSUA_CALLBACK_PROXY$') >= 0:
-				fout.write(cb[1])
-			elif line.find(r'$PJSUA_CALLBACK_DEF$') >= 0:
-				fout.write(cb[2])
-			else:
-				fout.write(line)
-		fin.close()
-		fout.close()
+
 
 # MAIN			
@@ -291,8 +300,4 @@
 #   print d
 
-sys.argv.pop(0)
-outdir = sys.argv.pop() if len(sys.argv) else 'output'
-mygen.write_pjsua_callback(outdir)
-
 s = mygen.go()
 print s
