Index: pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java	(revision 5016)
+++ pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/CallActivity.java	(revision 5017)
@@ -32,165 +32,178 @@
 
 public class CallActivity extends Activity
-	implements Handler.Callback, SurfaceHolder.Callback
+			  implements Handler.Callback, SurfaceHolder.Callback
 {
-	
-	public static Handler handler_;
-
-	private final Handler handler = new Handler(this);
-	private static CallInfo lastCallInfo;
-
-	@Override
-	protected void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		setContentView(R.layout.activity_call);
-		
-		SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surfaceIncomingVideo);
-		if (MainActivity.currentCall == null ||
-			MainActivity.currentCall.vidWin == null)
-		{
-			surfaceView.setVisibility(View.GONE);
-		}
-		surfaceView.getHolder().addCallback(this);
-		
-		handler_ = handler;
-		if (MainActivity.currentCall != null) {
-			try {
-				lastCallInfo = MainActivity.currentCall.getInfo();
-				updateCallState(lastCallInfo);
-			} catch (Exception e) {
-				System.out.println(e);
-			}
-		} else {
-			updateCallState(lastCallInfo);
-		}
-	}
+
+    public static Handler handler_;
+
+    private final Handler handler = new Handler(this);
+    private static CallInfo lastCallInfo;
 
     @Override
-    protected void onDestroy() {
-    	super.onDestroy();
-    	handler_ = null;
-    }
-
-    private void updateVideoWindow(SurfaceHolder holder) {
-		if (MainActivity.currentCall != null &&
-			MainActivity.currentCall.vidWin != null)
-		{
-			VideoWindowHandle vidWH = new VideoWindowHandle();
-			if (holder == null)
-				vidWH.getHandle().setWindow(null);
-			else
-			    	vidWH.getHandle().setWindow(holder.getSurface());
-			try {
-				MainActivity.currentCall.vidWin.setWindow(vidWH);
-			} catch (Exception e) {}
-		}
-    }
-    
-	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-		updateVideoWindow(holder);
-    }
-
-    public void surfaceCreated(SurfaceHolder holder) {
-    }
-
-    public void surfaceDestroyed(SurfaceHolder holder) {
-    	updateVideoWindow(null);
-    }
-    
-	public void acceptCall(View view) {
-		CallOpParam prm = new CallOpParam();
-		prm.setStatusCode(pjsip_status_code.PJSIP_SC_OK);
-		try {
-			MainActivity.currentCall.answer(prm);
-		} catch (Exception e) {
-			System.out.println(e);
-		}
-		
-		view.setVisibility(View.GONE);
-	}
-
-	public void hangupCall(View view) {
-		handler_ = null;
-		finish();
-		
-		if (MainActivity.currentCall != null) {
-			CallOpParam prm = new CallOpParam();
-			prm.setStatusCode(pjsip_status_code.PJSIP_SC_DECLINE);
-			try {
-				MainActivity.currentCall.hangup(prm);
-			} catch (Exception e) {
-				System.out.println(e);
-			}
-		}
-	}
-	
-	private void setupVideoSurface() {
-
-		SurfaceView surfaceView = (SurfaceView)findViewById(R.id.surfaceIncomingVideo);
-		surfaceView.setVisibility(View.VISIBLE);
-		updateVideoWindow(surfaceView.getHolder());
-		
-	}
-	
-	@Override
-	public boolean handleMessage(Message m) {
-		
-		if (m.what == MainActivity.MSG_TYPE.CALL_STATE) {
-			
-			lastCallInfo = (CallInfo) m.obj;
-			updateCallState(lastCallInfo);
-			
-		} else if (m.what == MainActivity.MSG_TYPE.CALL_MEDIA_STATE) {
-			
-			if (MainActivity.currentCall.vidWin != null) {
-				/* If there's incoming video, display it. */
-				setupVideoSurface();
-			}
-
-		} else {
-			
-			/* Message not handled */
-			return false;
-			
-		}
-			
-		return true;
-	}
-	
-	private void updateCallState(CallInfo ci) {
-		TextView tvPeer  = (TextView) findViewById(R.id.textViewPeer);
-		TextView tvState = (TextView) findViewById(R.id.textViewCallState);
-		Button buttonHangup = (Button) findViewById(R.id.buttonHangup);
-		Button buttonAccept = (Button) findViewById(R.id.buttonAccept);
-		String call_state = "";
-		
-		if (ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAC) {
-			buttonAccept.setVisibility(View.GONE);
-		}
-				
-		if (ci.getState().swigValue() < pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED.swigValue())
-		{
-			if (ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAS) {
-				call_state = "Incoming call..";
-				/* Default button texts are already 'Accept' & 'Reject' */
-			} else {
-				buttonHangup.setText("Cancel");
-				call_state = ci.getStateText();
-			}
-		}
-		else if (ci.getState().swigValue() >= pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED.swigValue())
-		{
-			buttonAccept.setVisibility(View.GONE);
-			call_state = ci.getStateText();
-			if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) {
-				buttonHangup.setText("Hangup");
-			} else if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) {
-				buttonHangup.setText("OK");
-				call_state = "Call disconnected: " + ci.getLastReason();
-			}
-		}
-		
-		tvPeer.setText(ci.getRemoteUri());
-		tvState.setText(call_state);
-	}
+    protected void onCreate(Bundle savedInstanceState)
+    {
+	super.onCreate(savedInstanceState);
+	setContentView(R.layout.activity_call);
+
+	SurfaceView surfaceView = (SurfaceView)
+				  findViewById(R.id.surfaceIncomingVideo);
+	if (MainActivity.currentCall == null ||
+	    MainActivity.currentCall.vidWin == null)
+	{
+	    surfaceView.setVisibility(View.GONE);
+	}
+	surfaceView.getHolder().addCallback(this);
+
+	handler_ = handler;
+	if (MainActivity.currentCall != null) {
+	    try {
+		lastCallInfo = MainActivity.currentCall.getInfo();
+		updateCallState(lastCallInfo);
+	    } catch (Exception e) {
+		System.out.println(e);
+	    }
+	} else {
+	    updateCallState(lastCallInfo);
+	}
+    }
+
+    @Override
+    protected void onDestroy()
+    {
+	super.onDestroy();
+	handler_ = null;
+    }
+
+    private void updateVideoWindow(SurfaceHolder holder)
+    {
+	if (MainActivity.currentCall != null &&
+	    MainActivity.currentCall.vidWin != null)
+	{
+	    VideoWindowHandle vidWH = new VideoWindowHandle();
+	    if (holder == null)
+		vidWH.getHandle().setWindow(null);
+	    else
+		vidWH.getHandle().setWindow(holder.getSurface());
+	    try {
+		MainActivity.currentCall.vidWin.setWindow(vidWH);
+	    } catch (Exception e) {}
+	}
+    }
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
+    {
+	updateVideoWindow(holder);
+    }
+
+    public void surfaceCreated(SurfaceHolder holder)
+    {
+    }
+
+    public void surfaceDestroyed(SurfaceHolder holder)
+    {
+	updateVideoWindow(null);
+    }
+
+    public void acceptCall(View view)
+    {
+	CallOpParam prm = new CallOpParam();
+	prm.setStatusCode(pjsip_status_code.PJSIP_SC_OK);
+	try {
+	    MainActivity.currentCall.answer(prm);
+	} catch (Exception e) {
+	    System.out.println(e);
+	}
+
+	view.setVisibility(View.GONE);
+    }
+
+    public void hangupCall(View view)
+    {
+	handler_ = null;
+	finish();
+
+	if (MainActivity.currentCall != null) {
+	    CallOpParam prm = new CallOpParam();
+	    prm.setStatusCode(pjsip_status_code.PJSIP_SC_DECLINE);
+	    try {
+		MainActivity.currentCall.hangup(prm);
+	    } catch (Exception e) {
+		System.out.println(e);
+	    }
+	}
+    }
+
+    private void setupVideoSurface()
+    {
+	SurfaceView surfaceView = (SurfaceView)
+				  findViewById(R.id.surfaceIncomingVideo);
+	surfaceView.setVisibility(View.VISIBLE);
+	updateVideoWindow(surfaceView.getHolder());
+    }
+
+    @Override
+    public boolean handleMessage(Message m)
+    {
+	if (m.what == MainActivity.MSG_TYPE.CALL_STATE) {
+
+	    lastCallInfo = (CallInfo) m.obj;
+	    updateCallState(lastCallInfo);
+
+	} else if (m.what == MainActivity.MSG_TYPE.CALL_MEDIA_STATE) {
+
+	    if (MainActivity.currentCall.vidWin != null) {
+		/* If there's incoming video, display it. */
+		setupVideoSurface();
+	    }
+
+	} else {
+
+	    /* Message not handled */
+	    return false;
+
+	}
+
+	return true;
+    }
+
+    private void updateCallState(CallInfo ci) {
+	TextView tvPeer  = (TextView) findViewById(R.id.textViewPeer);
+	TextView tvState = (TextView) findViewById(R.id.textViewCallState);
+	Button buttonHangup = (Button) findViewById(R.id.buttonHangup);
+	Button buttonAccept = (Button) findViewById(R.id.buttonAccept);
+	String call_state = "";
+
+	if (ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAC) {
+	    buttonAccept.setVisibility(View.GONE);
+	}
+
+	if (ci.getState().swigValue() <
+	    pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED.swigValue())
+	{
+	    if (ci.getRole() == pjsip_role_e.PJSIP_ROLE_UAS) {
+		call_state = "Incoming call..";
+		/* Default button texts are already 'Accept' & 'Reject' */
+	    } else {
+		buttonHangup.setText("Cancel");
+		call_state = ci.getStateText();
+	    }
+	}
+	else if (ci.getState().swigValue() >=
+		 pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED.swigValue())
+	{
+	    buttonAccept.setVisibility(View.GONE);
+	    call_state = ci.getStateText();
+	    if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_CONFIRMED) {
+		buttonHangup.setText("Hangup");
+	    } else if (ci.getState() ==
+		       pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
+	    {
+		buttonHangup.setText("OK");
+		call_state = "Call disconnected: " + ci.getLastReason();
+	    }
+	}
+
+	tvPeer.setText(ci.getRemoteUri());
+	tvState.setText(call_state);
+    }
 }
Index: pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java	(revision 5016)
+++ pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MainActivity.java	(revision 5017)
@@ -44,480 +44,536 @@
 import org.pjsip.pjsua2.*;
 
-public class MainActivity extends Activity implements Handler.Callback, MyAppObserver {
-	public static MyApp app = null;
-	public static MyCall currentCall = null;
-	public static MyAccount account = null;
-	public static AccountConfig accCfg = null;
-	
-	private ListView buddyListView;
-	private SimpleAdapter buddyListAdapter;
-	private int buddyListSelectedIdx = -1;
+public class MainActivity extends Activity
+			  implements Handler.Callback, MyAppObserver
+{
+    public static MyApp app = null;
+    public static MyCall currentCall = null;
+    public static MyAccount account = null;
+    public static AccountConfig accCfg = null;
+
+    private ListView buddyListView;
+    private SimpleAdapter buddyListAdapter;
+    private int buddyListSelectedIdx = -1;
     ArrayList<Map<String, String>> buddyList;
     private String lastRegStatus = "";
 
-	private final Handler handler = new Handler(this);
-	public class MSG_TYPE {
-		public final static int INCOMING_CALL = 1;
-		public final static int CALL_STATE = 2;
-		public final static int REG_STATE = 3;
-		public final static int BUDDY_STATE = 4;
-		public final static int CALL_MEDIA_STATE = 5;
-	}
-
-	private HashMap<String, String> putData(String uri, String status) {
-		HashMap<String, String> item = new HashMap<String, String>();
-		item.put("uri", uri);
-		item.put("status", status);
-		return item;
-	}
-	
-	private void showCallActivity() {
-        Intent intent = new Intent(this, CallActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        startActivity(intent);
-	}
-
-	@Override
-	protected void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-		setContentView(R.layout.activity_main);
-		
-		if (app == null) {
-			app = new MyApp();
-			/* Wait for GDB to init */
-			if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-				try {
-					Thread.sleep(5000);
-				} catch (InterruptedException e) {}
+    private final Handler handler = new Handler(this);
+    public class MSG_TYPE
+    {
+	public final static int INCOMING_CALL = 1;
+	public final static int CALL_STATE = 2;
+	public final static int REG_STATE = 3;
+	public final static int BUDDY_STATE = 4;
+	public final static int CALL_MEDIA_STATE = 5;
+    }
+
+    private HashMap<String, String> putData(String uri, String status)
+    {
+	HashMap<String, String> item = new HashMap<String, String>();
+	item.put("uri", uri);
+	item.put("status", status);
+	return item;
+    }
+
+    private void showCallActivity()
+    {
+	Intent intent = new Intent(this, CallActivity.class);
+	intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+	startActivity(intent);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState)
+    {
+	super.onCreate(savedInstanceState);
+	setContentView(R.layout.activity_main);
+
+	if (app == null) {
+	    app = new MyApp();
+	    // Wait for GDB to init, for native debugging only
+	    if (false &&
+		(getApplicationInfo().flags & 
+		ApplicationInfo.FLAG_DEBUGGABLE) != 0)
+	    {
+		try {
+		    Thread.sleep(5000);
+		} catch (InterruptedException e) {}
+	    }
+
+	    app.init(this, getFilesDir().getAbsolutePath());
+	}
+
+	if (app.accList.size() == 0) {
+	    accCfg = new AccountConfig();
+	    accCfg.setIdUri("sip:localhost");
+	    accCfg.getNatConfig().setIceEnabled(true);
+	    account = app.addAcc(accCfg);
+	} else {
+	    account = app.accList.get(0);
+	    accCfg = account.cfg;
+	}
+
+	buddyList = new ArrayList<Map<String, String>>();
+	for (int i = 0; i < account.buddyList.size(); i++) {
+	    buddyList.add(putData(account.buddyList.get(i).cfg.getUri(),
+				  account.buddyList.get(i).getStatusText()));
+	}
+
+	String[] from = { "uri", "status" };
+	int[] to = { android.R.id.text1, android.R.id.text2 };
+	buddyListAdapter = new SimpleAdapter(
+					this, buddyList,
+					android.R.layout.simple_list_item_2,
+					from, to);
+
+	buddyListView = (ListView) findViewById(R.id.listViewBuddy);;
+	buddyListView.setAdapter(buddyListAdapter);
+	buddyListView.setOnItemClickListener(
+	    new AdapterView.OnItemClickListener()
+	    {
+		@Override
+		public void onItemClick(AdapterView<?> parent,
+					final View view,
+					int position, long id) 
+		{
+		    view.setSelected(true);
+		    buddyListSelectedIdx = position;
+		}
+	    }
+	);
+
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu)
+    {
+	// Inflate the menu; this adds items to the action bar
+	// if it is present.
+	getMenuInflater().inflate(R.menu.main, menu);
+	return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item)
+    {
+	switch (item.getItemId()) {
+	    case R.id.action_acc_config:
+		dlgAccountSetting();
+		break;
+
+	    case R.id.action_quit:
+		Message m = Message.obtain(handler, 0);
+		m.sendToTarget();
+		break;
+
+	    default:
+		break;
+	}
+
+	return true;
+    } 	
+
+    @Override
+    public boolean handleMessage(Message m)
+    {
+	if (m.what == 0) {
+
+	    app.deinit();
+	    finish();
+	    Runtime.getRuntime().gc();
+	    android.os.Process.killProcess(android.os.Process.myPid());
+
+	} else if (m.what == MSG_TYPE.CALL_STATE) {
+
+	    CallInfo ci = (CallInfo) m.obj;
+
+	    /* Forward the message to CallActivity */
+	    if (CallActivity.handler_ != null) {
+		Message m2 = Message.obtain(CallActivity.handler_,
+		    MSG_TYPE.CALL_STATE, ci);
+		m2.sendToTarget();
+	    }
+
+	} else if (m.what == MSG_TYPE.CALL_MEDIA_STATE) {
+
+	    /* Forward the message to CallActivity */
+	    if (CallActivity.handler_ != null) {
+		Message m2 = Message.obtain(CallActivity.handler_,
+		    MSG_TYPE.CALL_MEDIA_STATE,
+		    null);
+		m2.sendToTarget();
+	    }
+
+	} else if (m.what == MSG_TYPE.BUDDY_STATE) {
+
+	    MyBuddy buddy = (MyBuddy) m.obj;
+	    int idx = account.buddyList.indexOf(buddy);
+
+	    /* Update buddy status text, if buddy is valid and
+	    * the buddy lists in account and UI are sync-ed.
+	    */
+	    if (idx >= 0 && account.buddyList.size() == buddyList.size())
+	    {
+		buddyList.get(idx).put("status", buddy.getStatusText());
+		buddyListAdapter.notifyDataSetChanged();
+		// TODO: selection color/mark is gone after this,
+		//       dont know how to return it back.
+		//buddyListView.setSelection(buddyListSelectedIdx);
+		//buddyListView.performItemClick(buddyListView,
+		//				     buddyListSelectedIdx,
+		//				     buddyListView.
+		//		    getItemIdAtPosition(buddyListSelectedIdx));
+
+		/* Return back Call activity */
+		notifyCallState(currentCall);
+	    }
+
+	} else if (m.what == MSG_TYPE.REG_STATE) {
+
+	    String msg_str = (String) m.obj;
+	    lastRegStatus = msg_str;
+
+	} else if (m.what == MSG_TYPE.INCOMING_CALL) {
+
+	    /* Incoming call */
+	    final MyCall call = (MyCall) m.obj;
+	    CallOpParam prm = new CallOpParam();
+
+	    /* Only one call at anytime */
+	    if (currentCall != null) {
+		/*
+		prm.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE);
+		try {
+		call.hangup(prm);
+		} catch (Exception e) {}
+		*/
+		// TODO: set status code
+		call.delete();
+		return true;
+	    }
+
+	    /* Answer with ringing */
+	    prm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING);
+	    try {
+		call.answer(prm);
+	    } catch (Exception e) {}
+
+	    currentCall = call;
+	    showCallActivity();
+
+	} else {
+
+	    /* Message not handled */
+	    return false;
+
+	}
+
+	return true;
+    }
+
+
+    private void dlgAccountSetting()
+    {
+	LayoutInflater li = LayoutInflater.from(this);
+	View view = li.inflate(R.layout.dlg_account_config, null);
+
+	if (lastRegStatus.length()!=0) {
+	    TextView tvInfo = (TextView)view.findViewById(R.id.textViewInfo);
+	    tvInfo.setText("Last status: " + lastRegStatus);
+	}
+
+	AlertDialog.Builder adb = new AlertDialog.Builder(this);
+	adb.setView(view);
+	adb.setTitle("Account Settings");
+
+	final EditText etId    = (EditText)view.findViewById(R.id.editTextId);
+	final EditText etReg   = (EditText)view.findViewById(R.id.editTextRegistrar);
+	final EditText etProxy = (EditText)view.findViewById(R.id.editTextProxy);
+	final EditText etUser  = (EditText)view.findViewById(R.id.editTextUsername);
+	final EditText etPass  = (EditText)view.findViewById(R.id.editTextPassword);
+
+	etId.   setText(accCfg.getIdUri());
+	etReg.  setText(accCfg.getRegConfig().getRegistrarUri());
+	StringVector proxies = accCfg.getSipConfig().getProxies();
+	if (proxies.size() > 0)
+	    etProxy.setText(proxies.get(0));
+	else
+	    etProxy.setText("");
+	AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds();
+	if (creds.size() > 0) {
+	    etUser. setText(creds.get(0).getUsername());
+	    etPass. setText(creds.get(0).getData());
+	} else {
+	    etUser. setText("");
+	    etPass. setText("");
+	}
+
+	adb.setCancelable(false);
+	adb.setPositiveButton("OK",
+	    new DialogInterface.OnClickListener()
+	    {
+		public void onClick(DialogInterface dialog,int id)
+		{
+		    String acc_id 	 = etId.getText().toString();
+		    String registrar = etReg.getText().toString();
+		    String proxy 	 = etProxy.getText().toString();
+		    String username  = etUser.getText().toString();
+		    String password  = etPass.getText().toString();
+
+		    accCfg.setIdUri(acc_id);
+		    accCfg.getRegConfig().setRegistrarUri(registrar);
+		    AuthCredInfoVector creds = accCfg.getSipConfig().
+							    getAuthCreds();
+		    creds.clear();
+		    if (username.length() != 0) {
+			creds.add(new AuthCredInfo("Digest", "*", username, 0,
+						   password));
+		    }
+		    StringVector proxies = accCfg.getSipConfig().getProxies();
+		    proxies.clear();
+		    if (proxy.length() != 0) {
+			proxies.add(proxy);
+		    }
+
+		    /* Enable ICE */
+		    accCfg.getNatConfig().setIceEnabled(true);
+
+		    /* Finally */
+		    lastRegStatus = "";
+		    try {
+			account.modify(accCfg);
+		    } catch (Exception e) {}
+		}
+	    }
+	);
+	adb.setNegativeButton("Cancel",
+	    new DialogInterface.OnClickListener()
+	    {
+		public void onClick(DialogInterface dialog,int id)
+		{
+		    dialog.cancel();
+		}
+	    }
+	);
+
+	AlertDialog ad = adb.create();
+	ad.show();
+    }
+
+
+    public void makeCall(View view)
+    {
+	if (buddyListSelectedIdx == -1)
+	    return;
+
+	/* Only one call at anytime */
+	if (currentCall != null) {
+	    return;
+	}
+
+	HashMap<String, String> item = (HashMap<String, String>) buddyListView.
+				       getItemAtPosition(buddyListSelectedIdx);
+	String buddy_uri = item.get("uri");
+
+	MyCall call = new MyCall(account, -1);
+	CallOpParam prm = new CallOpParam(true);
+
+	try {
+	    call.makeCall(buddy_uri, prm);
+	} catch (Exception e) {
+	    call.delete();
+	    return;
+	}
+
+	currentCall = call;
+	showCallActivity();
+    }
+
+    private void dlgAddEditBuddy(BuddyConfig initial)
+    {
+	final BuddyConfig cfg = new BuddyConfig();
+	final BuddyConfig old_cfg = initial;
+	final boolean is_add = initial == null;
+
+	LayoutInflater li = LayoutInflater.from(this);
+	View view = li.inflate(R.layout.dlg_add_buddy, null);
+
+	AlertDialog.Builder adb = new AlertDialog.Builder(this);
+	adb.setView(view);
+
+	final EditText etUri  = (EditText)view.findViewById(R.id.editTextUri);
+	final CheckBox cbSubs = (CheckBox)view.findViewById(R.id.checkBoxSubscribe);
+
+	if (is_add) {
+	    adb.setTitle("Add Buddy");
+	} else {
+	    adb.setTitle("Edit Buddy");
+	    etUri. setText(initial.getUri());
+	    cbSubs.setChecked(initial.getSubscribe());
+	}
+
+	adb.setCancelable(false);
+	adb.setPositiveButton("OK",
+	    new DialogInterface.OnClickListener()
+	    {
+		public void onClick(DialogInterface dialog,int id)
+		{
+		    cfg.setUri(etUri.getText().toString());
+		    cfg.setSubscribe(cbSubs.isChecked());
+
+		    if (is_add) {
+			account.addBuddy(cfg);
+			buddyList.add(putData(cfg.getUri(), ""));
+			buddyListAdapter.notifyDataSetChanged();
+			buddyListSelectedIdx = -1;
+		    } else {
+			if (!old_cfg.getUri().equals(cfg.getUri())) {
+			    account.delBuddy(buddyListSelectedIdx);
+			    account.addBuddy(cfg);
+			    buddyList.remove(buddyListSelectedIdx);
+			    buddyList.add(putData(cfg.getUri(), ""));
+			    buddyListAdapter.notifyDataSetChanged();
+			    buddyListSelectedIdx = -1;
+			} else if (old_cfg.getSubscribe() != 
+				   cfg.getSubscribe())
+			{
+			    MyBuddy bud = account.buddyList.get(
+							buddyListSelectedIdx);
+			    try {
+				bud.subscribePresence(cfg.getSubscribe());
+			    } catch (Exception e) {}
 			}
-			
-		    app.init(this, getFilesDir().getAbsolutePath());
+		    }
 		}
-		
-	    if (app.accList.size() == 0) {
-	    	accCfg = new AccountConfig();
-	    	accCfg.setIdUri("sip:localhost");
-	    	accCfg.getNatConfig().setIceEnabled(true);
-	    	account = app.addAcc(accCfg);
-	    } else {
-	    	account = app.accList.get(0);
-	    	accCfg = account.cfg;
-	    }
-	    
-		buddyList = new ArrayList<Map<String, String>>();
-		for (int i = 0; i < account.buddyList.size(); i++) {
-			buddyList.add(putData(account.buddyList.get(i).cfg.getUri(),
-								  account.buddyList.get(i).getStatusText()));
+	    }
+	);
+	adb.setNegativeButton("Cancel",
+	    new DialogInterface.OnClickListener()
+	    {
+		public void onClick(DialogInterface dialog,int id) {
+		    dialog.cancel();
 		}
-
-		String[] from = { "uri", "status" };
-	    int[] to = { android.R.id.text1, android.R.id.text2 };
-	    buddyListAdapter = new SimpleAdapter(this, buddyList, android.R.layout.simple_list_item_2, from, to);
-	    
-		buddyListView = (ListView) findViewById(R.id.listViewBuddy);;
-	    buddyListView.setAdapter(buddyListAdapter);
-	    buddyListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-	    	@Override
-	    	public void onItemClick(AdapterView<?> parent, final View view,
-	    			int position, long id) 
-	    	{
-	    		view.setSelected(true);
-	    		buddyListSelectedIdx = position;
-	    	}
-	    });
-	    
-	}
-
-	@Override
-	public boolean onCreateOptionsMenu(Menu menu) {
-		// Inflate the menu; this adds items to the action bar if it is present.
-		getMenuInflater().inflate(R.menu.main, menu);
-		return true;
-	}
-
-	@Override
-	public boolean onOptionsItemSelected(MenuItem item) {
-		switch (item.getItemId()) {
-		case R.id.action_acc_config:
-			dlgAccountSetting();
+	    }
+	);
+
+	AlertDialog ad = adb.create();
+	ad.show();
+    }
+
+    public void addBuddy(View view)
+    {
+	dlgAddEditBuddy(null);
+    }
+
+    public void editBuddy(View view)
+    {
+	if (buddyListSelectedIdx == -1)
+	    return;
+
+	BuddyConfig old_cfg = account.buddyList.get(buddyListSelectedIdx).cfg;
+	dlgAddEditBuddy(old_cfg);
+    }
+
+    public void delBuddy(View view) {
+	if (buddyListSelectedIdx == -1)
+	    return;
+
+	final HashMap<String, String> item = (HashMap<String, String>)
+			buddyListView.getItemAtPosition(buddyListSelectedIdx);
+	String buddy_uri = item.get("uri");
+
+	DialogInterface.OnClickListener ocl =
+					new DialogInterface.OnClickListener()
+	{
+	    @Override
+	    public void onClick(DialogInterface dialog, int which)
+	    {
+		switch (which) {
+		    case DialogInterface.BUTTON_POSITIVE:
+			account.delBuddy(buddyListSelectedIdx);
+			buddyList.remove(item);
+			buddyListAdapter.notifyDataSetChanged();
+			buddyListSelectedIdx = -1;
 			break;
-
-		case R.id.action_quit:
-			Message m = Message.obtain(handler, 0);
-			m.sendToTarget();
-			break;
-			
-		default:
+		    case DialogInterface.BUTTON_NEGATIVE:
 			break;
 		}
-
-		return true;
-	} 	
-
-	@Override
-	public boolean handleMessage(Message m) {
-		
-		if (m.what == 0) {
-			
-			app.deinit();
-			finish();
-			Runtime.getRuntime().gc();
-			android.os.Process.killProcess(android.os.Process.myPid());
-			
-		} else if (m.what == MSG_TYPE.CALL_STATE) {
-			
-			CallInfo ci = (CallInfo) m.obj;
-			
-			/* Forward the message to CallActivity */
-			if (CallActivity.handler_ != null) {
-				Message m2 = Message.obtain(CallActivity.handler_, MSG_TYPE.CALL_STATE, ci);
-				m2.sendToTarget();
-			}
-			
-		} else if (m.what == MSG_TYPE.CALL_MEDIA_STATE) {
-			
-			/* Forward the message to CallActivity */
-			if (CallActivity.handler_ != null) {
-				Message m2 = Message.obtain(CallActivity.handler_, MSG_TYPE.CALL_MEDIA_STATE, null);
-				m2.sendToTarget();
-			}
-			
-		} else if (m.what == MSG_TYPE.BUDDY_STATE) {
-			
-			MyBuddy buddy = (MyBuddy) m.obj;
-			int idx = account.buddyList.indexOf(buddy);
-
-			/* Update buddy status text, if buddy is valid and
-			 * the buddy lists in account and UI are sync-ed.
-			 */
-			if (idx >= 0 && account.buddyList.size() == buddyList.size()) {
-				buddyList.get(idx).put("status", buddy.getStatusText());
-				buddyListAdapter.notifyDataSetChanged();
-				// TODO: selection color/mark is gone after this,
-				//       dont know how to return it back.
-				//buddyListView.setSelection(buddyListSelectedIdx);
-				//buddyListView.performItemClick(buddyListView, buddyListSelectedIdx,
-				//							   buddyListView.getItemIdAtPosition(buddyListSelectedIdx));
-				
-				/* Return back Call activity */
-				notifyCallState(currentCall);
-			}
-			
-		} else if (m.what == MSG_TYPE.REG_STATE) {
-			
-			String msg_str = (String) m.obj;
-			lastRegStatus = msg_str;
-			
-		} else if (m.what == MSG_TYPE.INCOMING_CALL) {
-			
-			/* Incoming call */
-			final MyCall call = (MyCall) m.obj;
-			CallOpParam prm = new CallOpParam();
-			
-			/* Only one call at anytime */
-			if (currentCall != null) {
-				/*
-				prm.setStatusCode(pjsip_status_code.PJSIP_SC_BUSY_HERE);
-				try {
-					call.hangup(prm);
-				} catch (Exception e) {}
-				*/
-				// TODO: set status code
-				call.delete();
-				return true;
-			}
-
-			/* Answer with ringing */
-			prm.setStatusCode(pjsip_status_code.PJSIP_SC_RINGING);
-			try {
-				call.answer(prm);
-			} catch (Exception e) {}
-			
-			currentCall = call;
-			showCallActivity();
-
-		} else {
-			
-			/* Message not handled */
-			return false;
-			
-		}
-			
-		return true;
-	}
-	
-
-	private void dlgAccountSetting() {
-		
-		LayoutInflater li = LayoutInflater.from(this);
-		View view = li.inflate(R.layout.dlg_account_config, null);
-		
-		if (lastRegStatus.length()!=0) {
-			TextView tvInfo = (TextView)view.findViewById(R.id.textViewInfo);
-			tvInfo.setText("Last status: " + lastRegStatus);
-		}
-
-		AlertDialog.Builder adb = new AlertDialog.Builder(this);
-		adb.setView(view);
-		adb.setTitle("Account Settings");
-
-		final EditText etId    = (EditText)view.findViewById(R.id.editTextId);
-		final EditText etReg   = (EditText)view.findViewById(R.id.editTextRegistrar);
-		final EditText etProxy = (EditText)view.findViewById(R.id.editTextProxy);
-		final EditText etUser  = (EditText)view.findViewById(R.id.editTextUsername);
-		final EditText etPass  = (EditText)view.findViewById(R.id.editTextPassword);
-		
-		etId.   setText(accCfg.getIdUri());
-		etReg.  setText(accCfg.getRegConfig().getRegistrarUri());
-		StringVector proxies = accCfg.getSipConfig().getProxies();
-		if (proxies.size() > 0)
-			etProxy.setText(proxies.get(0));
-		else
-			etProxy.setText("");
-		AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds();
-		if (creds.size() > 0) {
-			etUser. setText(creds.get(0).getUsername());
-			etPass. setText(creds.get(0).getData());
-		} else {
-			etUser. setText("");
-			etPass. setText("");
-		}
-		
-		adb.setCancelable(false);
-		adb.setPositiveButton("OK",
-			  new DialogInterface.OnClickListener() {
-			    public void onClick(DialogInterface dialog,int id) {
-			    	String acc_id 	 = etId.getText().toString();
-			    	String registrar = etReg.getText().toString();
-			    	String proxy 	 = etProxy.getText().toString();
-			    	String username  = etUser.getText().toString();
-			    	String password  = etPass.getText().toString();
-			    	
-			    	accCfg.setIdUri(acc_id);
-			    	accCfg.getRegConfig().setRegistrarUri(registrar);
-					AuthCredInfoVector creds = accCfg.getSipConfig().getAuthCreds();
-					creds.clear();
-					if (username.length() != 0) {
-						creds.add(new AuthCredInfo("Digest", "*", username, 0, password));
-					}
-					StringVector proxies = accCfg.getSipConfig().getProxies();
-					proxies.clear();
-					if (proxy.length() != 0) {
-						proxies.add(proxy);
-					}
-					
-			    	/* Enable ICE */
-			    	accCfg.getNatConfig().setIceEnabled(true);
-			    	
-					/* Finally */
-					lastRegStatus = "";
-					try {
-						account.modify(accCfg);
-					} catch (Exception e) {}
-			    }
-			  });
-		adb.setNegativeButton("Cancel",
-			  new DialogInterface.OnClickListener() {
-			    public void onClick(DialogInterface dialog,int id) {
-				dialog.cancel();
-			    }
-			  });
-
-		AlertDialog ad = adb.create();
-		ad.show();
-	}
-	
-	
-	public void makeCall(View view) {
-		if (buddyListSelectedIdx == -1)
-			return;
-		
-		/* Only one call at anytime */
-		if (currentCall != null) {
-			return;
-		}
-		
-		HashMap<String, String> item = (HashMap<String, String>) buddyListView.getItemAtPosition(buddyListSelectedIdx);
-		String buddy_uri = item.get("uri");
-		
-		MyCall call = new MyCall(account, -1);
-		CallOpParam prm = new CallOpParam(true);
-
-		try {
-			call.makeCall(buddy_uri, prm);
-		} catch (Exception e) {
-			call.delete();
-			return;
-		}
-		
-		currentCall = call;
-		showCallActivity();
-	}
-
-	private void dlgAddEditBuddy(BuddyConfig initial) {
-		final BuddyConfig cfg = new BuddyConfig();
-		final BuddyConfig old_cfg = initial;
-		final boolean is_add = initial == null;
-		
-		LayoutInflater li = LayoutInflater.from(this);
-		View view = li.inflate(R.layout.dlg_add_buddy, null);
-
-		AlertDialog.Builder adb = new AlertDialog.Builder(this);
-		adb.setView(view);
-
-		final EditText etUri    = (EditText)view.findViewById(R.id.editTextUri);
-		final CheckBox cbSubs  = (CheckBox)view.findViewById(R.id.checkBoxSubscribe);
-
-		if (is_add) {
-			adb.setTitle("Add Buddy");
-		} else {
-			adb.setTitle("Edit Buddy");
-			etUri. setText(initial.getUri());
-			cbSubs.setChecked(initial.getSubscribe());
-		}
-
-		adb.setCancelable(false);
-		adb.setPositiveButton("OK",
-			  new DialogInterface.OnClickListener() {
-			    public void onClick(DialogInterface dialog,int id) {
-			    	cfg.setUri(etUri.getText().toString());
-			    	cfg.setSubscribe(cbSubs.isChecked());
-			    	
-			    	if (is_add) {
-			    		account.addBuddy(cfg);
-						buddyList.add(putData(cfg.getUri(), ""));
-						buddyListAdapter.notifyDataSetChanged();
-						buddyListSelectedIdx = -1;
-			    	} else {
-			    		if (!old_cfg.getUri().equals(cfg.getUri())) {
-			    			account.delBuddy(buddyListSelectedIdx);
-			    			account.addBuddy(cfg);
-							buddyList.remove(buddyListSelectedIdx);
-							buddyList.add(putData(cfg.getUri(), ""));
-			    			buddyListAdapter.notifyDataSetChanged();
-			    			buddyListSelectedIdx = -1;
-			    		} else if (old_cfg.getSubscribe() != cfg.getSubscribe()) {
-			    			MyBuddy bud = account.buddyList.get(buddyListSelectedIdx);
-							try {
-				    			bud.subscribePresence(cfg.getSubscribe());
-							} catch (Exception e) {}
-			    		}
-			    	}
-			    }
-			  });
-		adb.setNegativeButton("Cancel",
-			  new DialogInterface.OnClickListener() {
-			    public void onClick(DialogInterface dialog,int id) {
-				dialog.cancel();
-			    }
-			  });
-
-		AlertDialog ad = adb.create();
-		ad.show();
-	}
-	
-	public void addBuddy(View view) {
-		dlgAddEditBuddy(null);
-	}
-
-	public void editBuddy(View view) {
-		if (buddyListSelectedIdx == -1)
-			return;
-		
-		BuddyConfig old_cfg = account.buddyList.get(buddyListSelectedIdx).cfg;
-		dlgAddEditBuddy(old_cfg);
-	}
-	
-	public void delBuddy(View view) {
-		if (buddyListSelectedIdx == -1)
-			return;
-		
-		final HashMap<String, String> item = (HashMap<String, String>) buddyListView.getItemAtPosition(buddyListSelectedIdx);
-		String buddy_uri = item.get("uri");
-		
-		DialogInterface.OnClickListener ocl = new DialogInterface.OnClickListener() {
-		    @Override
-		    public void onClick(DialogInterface dialog, int which) {
-		        switch (which) {
-		        case DialogInterface.BUTTON_POSITIVE:
-		        	account.delBuddy(buddyListSelectedIdx);
-		    		buddyList.remove(item);
-		    		buddyListAdapter.notifyDataSetChanged();
-		    		buddyListSelectedIdx = -1;
-		            break;
-		        case DialogInterface.BUTTON_NEGATIVE:
-		            break;
-		        }
-		    }
-		};
-
-		AlertDialog.Builder adb = new AlertDialog.Builder(this);
-		adb.setTitle(buddy_uri);
-		adb.setMessage("\nDelete this buddy?\n");
-		adb.setPositiveButton("Yes", ocl);
-		adb.setNegativeButton("No", ocl);
-		adb.show();
-	}
-	
-	
-	/*
-	 * === MyAppObserver ===
-	 * 
-	 * As we cannot do UI from worker thread, the callbacks mostly just send
-	 * a message to UI/main thread.
-	 */
-	
-	public void notifyIncomingCall(MyCall call) {
-		Message m = Message.obtain(handler, MSG_TYPE.INCOMING_CALL, call);
-		m.sendToTarget();
-	}
-
-	public void notifyRegState(pjsip_status_code code, String reason, int expiration) {
-		String msg_str = "";
-		if (expiration == 0)
-			msg_str += "Unregistration";
-		else
-			msg_str += "Registration";
-		
-		if (code.swigValue()/100 == 2)
-			msg_str += " successful";
-		else
-			msg_str += " failed: " + reason;
-		
-		Message m = Message.obtain(handler, MSG_TYPE.REG_STATE, msg_str);
-		m.sendToTarget();
-	}
-	
-	public void notifyCallState(MyCall call) {
-		if (currentCall == null || call.getId() != currentCall.getId())
-			return;
-		
-		CallInfo ci;
-		try {
-			ci = call.getInfo();
-		} catch (Exception e) {
-			ci = null;
-		}
-		Message m = Message.obtain(handler, MSG_TYPE.CALL_STATE, ci);
-		m.sendToTarget();
-		
-		if (ci != null &&
-			ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
-		{
-			currentCall = null;
-		}
-	}
-	
-	public void notifyCallMediaState(MyCall call) {
-		Message m = Message.obtain(handler, MSG_TYPE.CALL_MEDIA_STATE, null);
-		m.sendToTarget();
-	}
-	
-	public void notifyBuddyState(MyBuddy buddy) {
-		Message m = Message.obtain(handler, MSG_TYPE.BUDDY_STATE, buddy);
-		m.sendToTarget();
-	}
-
-	/* === end of MyAppObserver ==== */
+	    }
+	};
+
+	AlertDialog.Builder adb = new AlertDialog.Builder(this);
+	adb.setTitle(buddy_uri);
+	adb.setMessage("\nDelete this buddy?\n");
+	adb.setPositiveButton("Yes", ocl);
+	adb.setNegativeButton("No", ocl);
+	adb.show();
+    }
+
+
+    /*
+    * === MyAppObserver ===
+    * 
+    * As we cannot do UI from worker thread, the callbacks mostly just send
+    * a message to UI/main thread.
+    */
+
+    public void notifyIncomingCall(MyCall call)
+    {
+	Message m = Message.obtain(handler, MSG_TYPE.INCOMING_CALL, call);
+	m.sendToTarget();
+    }
+
+    public void notifyRegState(pjsip_status_code code, String reason,
+			       int expiration)
+    {
+	String msg_str = "";
+	if (expiration == 0)
+	    msg_str += "Unregistration";
+	else
+	    msg_str += "Registration";
+
+	if (code.swigValue()/100 == 2)
+	    msg_str += " successful";
+	else
+	    msg_str += " failed: " + reason;
+
+	Message m = Message.obtain(handler, MSG_TYPE.REG_STATE, msg_str);
+	m.sendToTarget();
+    }
+
+    public void notifyCallState(MyCall call)
+    {
+	if (currentCall == null || call.getId() != currentCall.getId())
+	    return;
+
+	CallInfo ci;
+	try {
+	    ci = call.getInfo();
+	} catch (Exception e) {
+	    ci = null;
+	}
+	Message m = Message.obtain(handler, MSG_TYPE.CALL_STATE, ci);
+	m.sendToTarget();
+
+	if (ci != null &&
+	    ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
+	{
+	    currentCall = null;
+	}
+    }
+
+    public void notifyCallMediaState(MyCall call)
+    {
+	Message m = Message.obtain(handler, MSG_TYPE.CALL_MEDIA_STATE, null);
+	m.sendToTarget();
+    }
+
+    public void notifyBuddyState(MyBuddy buddy)
+    {
+	Message m = Message.obtain(handler, MSG_TYPE.BUDDY_STATE, buddy);
+	m.sendToTarget();
+    }
+
+    /* === end of MyAppObserver ==== */
 
 }
Index: pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java
===================================================================
--- pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java	(revision 5016)
+++ pjproject/trunk/pjsip-apps/src/swig/java/android/src/org/pjsip/pjsua2/app/MyApp.java	(revision 5017)
@@ -26,459 +26,506 @@
 
 /* Interface to separate UI & engine a bit better */
-interface MyAppObserver {
-	abstract void notifyRegState(pjsip_status_code code, String reason, int expiration);
-	abstract void notifyIncomingCall(MyCall call);
-	abstract void notifyCallState(MyCall call);
-	abstract void notifyCallMediaState(MyCall call);
-	abstract void notifyBuddyState(MyBuddy buddy);
-}
-
-
-class MyLogWriter extends LogWriter {
-	@Override
-	public void write(LogEntry entry) {
-		System.out.println(entry.getMsg());
-	}
-}
-
-
-class MyCall extends Call {
-	public VideoWindow vidWin;
-	
-	MyCall(MyAccount acc, int call_id) {
-		super(acc, call_id);
-		vidWin = null;
-	}
-
-	@Override
-	public void onCallState(OnCallStateParam prm) {
-		MyApp.observer.notifyCallState(this);
+interface MyAppObserver
+{
+    abstract void notifyRegState(pjsip_status_code code, String reason,
+				 int expiration);
+    abstract void notifyIncomingCall(MyCall call);
+    abstract void notifyCallState(MyCall call);
+    abstract void notifyCallMediaState(MyCall call);
+    abstract void notifyBuddyState(MyBuddy buddy);
+}
+
+
+class MyLogWriter extends LogWriter
+{
+    @Override
+    public void write(LogEntry entry)
+    {
+	System.out.println(entry.getMsg());
+    }
+}
+
+
+class MyCall extends Call
+{
+    public VideoWindow vidWin;
+
+    MyCall(MyAccount acc, int call_id)
+    {
+	super(acc, call_id);
+	vidWin = null;
+    }
+
+    @Override
+    public void onCallState(OnCallStateParam prm)
+    {
+	    MyApp.observer.notifyCallState(this);
+	    try {
+		CallInfo ci = getInfo();
+		if (ci.getState() == 
+		    pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED)
+		{
+		    this.delete();
+		}
+	    } catch (Exception e) {
+		return;
+	    }
+    }
+
+    @Override
+    public void onCallMediaState(OnCallMediaStateParam prm)
+    {
+	CallInfo ci;
+	try {
+	    ci = getInfo();
+	} catch (Exception e) {
+	    return;
+	}
+
+	CallMediaInfoVector cmiv = ci.getMedia();
+
+	for (int i = 0; i < cmiv.size(); i++) {
+	    CallMediaInfo cmi = cmiv.get(i);
+	    if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_AUDIO &&
+		(cmi.getStatus() == 
+		 	pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE ||
+		 cmi.getStatus() == 
+			pjsua_call_media_status.PJSUA_CALL_MEDIA_REMOTE_HOLD))
+	    {
+		// unfortunately, on Java too, the returned Media cannot be
+		// downcasted to AudioMedia 
+		Media m = getMedia(i);
+		AudioMedia am = AudioMedia.typecastFromMedia(m);
+
+		// connect ports
 		try {
-			CallInfo ci = getInfo();
-			if (ci.getState() == pjsip_inv_state.PJSIP_INV_STATE_DISCONNECTED) {
-				this.delete();
-			}
+		    MyApp.ep.audDevManager().getCaptureDevMedia().
+							    startTransmit(am);
+		    am.startTransmit(MyApp.ep.audDevManager().
+				     getPlaybackDevMedia());
 		} catch (Exception e) {
-			return;
+		    continue;
 		}
-	}
-	
-	@Override
-	public void onCallMediaState(OnCallMediaStateParam prm) {
-		CallInfo ci;
+	    } else if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_VIDEO &&
+		       cmi.getStatus() == 
+			    pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE &&
+		       cmi.getVideoIncomingWindowId() != pjsua2.INVALID_ID)
+	    {
+		vidWin = new VideoWindow(cmi.getVideoIncomingWindowId());
+	    }
+	}
+
+	MyApp.observer.notifyCallMediaState(this);
+    }
+}
+
+
+class MyAccount extends Account
+{
+    public ArrayList<MyBuddy> buddyList = new ArrayList<MyBuddy>();
+    public AccountConfig cfg;
+
+    MyAccount(AccountConfig config)
+    {
+	super();
+	cfg = config;
+    }
+
+    public MyBuddy addBuddy(BuddyConfig bud_cfg)
+    {
+	/* Create Buddy */
+	MyBuddy bud = new MyBuddy(bud_cfg);
+	try {
+	    bud.create(this, bud_cfg);
+	} catch (Exception e) {
+	    bud.delete();
+	    bud = null;
+	}
+
+	if (bud != null) {
+	    buddyList.add(bud);
+	    if (bud_cfg.getSubscribe())
 		try {
-			ci = getInfo();
-		} catch (Exception e) {
-			return;
+		    bud.subscribePresence(true);
+	    } catch (Exception e) {}
+	}
+
+	return bud;
+    }
+
+    public void delBuddy(MyBuddy buddy)
+    {
+	buddyList.remove(buddy);
+	buddy.delete();
+    }
+
+    public void delBuddy(int index)
+    {
+	MyBuddy bud = buddyList.get(index);
+	buddyList.remove(index);
+	bud.delete();
+    }
+
+    @Override
+    public void onRegState(OnRegStateParam prm)
+    {
+        MyApp.observer.notifyRegState(prm.getCode(), prm.getReason(),
+				      prm.getExpiration());
+    }
+
+    @Override
+    public void onIncomingCall(OnIncomingCallParam prm)
+    {
+	System.out.println("======== Incoming call ======== ");
+	MyCall call = new MyCall(this, prm.getCallId());
+	MyApp.observer.notifyIncomingCall(call);
+    }
+
+    @Override
+    public void onInstantMessage(OnInstantMessageParam prm)
+    {
+	System.out.println("======== Incoming pager ======== ");
+	System.out.println("From     : " + prm.getFromUri());
+	System.out.println("To       : " + prm.getToUri());
+	System.out.println("Contact  : " + prm.getContactUri());
+	System.out.println("Mimetype : " + prm.getContentType());
+	System.out.println("Body     : " + prm.getMsgBody());
+    }
+}
+
+
+class MyBuddy extends Buddy
+{
+    public BuddyConfig cfg;
+
+    MyBuddy(BuddyConfig config)
+    {
+	super();
+	cfg = config;
+    }
+
+    String getStatusText()
+    {
+	BuddyInfo bi;
+
+	try {
+	    bi = getInfo();
+	} catch (Exception e) {
+	    return "?";
+	}
+
+	String status = "";
+	if (bi.getSubState() == pjsip_evsub_state.PJSIP_EVSUB_STATE_ACTIVE) {
+	    if (bi.getPresStatus().getStatus() ==
+		pjsua_buddy_status.PJSUA_BUDDY_STATUS_ONLINE)
+	    {
+		status = bi.getPresStatus().getStatusText();
+		if (status == null || status.length()==0) {
+		    status = "Online";
 		}
-		
-		CallMediaInfoVector cmiv = ci.getMedia();
-		
-		for (int i = 0; i < cmiv.size(); i++) {
-			CallMediaInfo cmi = cmiv.get(i);
-			if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_AUDIO &&
-			    (cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE ||
-			     cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_REMOTE_HOLD))
-			{
-				// unfortunately, on Java too, the returned Media cannot be downcasted to AudioMedia 
-				Media m = getMedia(i);
-				AudioMedia am = AudioMedia.typecastFromMedia(m);
-				
-				// connect ports
-				try {
-					MyApp.ep.audDevManager().getCaptureDevMedia().startTransmit(am);
-					am.startTransmit(MyApp.ep.audDevManager().getPlaybackDevMedia());
-				} catch (Exception e) {
-					continue;
-				}
-			} else if (cmi.getType() == pjmedia_type.PJMEDIA_TYPE_VIDEO &&
-				       cmi.getStatus() == pjsua_call_media_status.PJSUA_CALL_MEDIA_ACTIVE &&
-				       cmi.getVideoIncomingWindowId() != pjsua2.INVALID_ID)
-			{
-				vidWin = new VideoWindow(cmi.getVideoIncomingWindowId());
-			}
-		}
-		
-		MyApp.observer.notifyCallMediaState(this);
-	}
-}
-
-
-class MyAccount extends Account {
-	public ArrayList<MyBuddy> buddyList = new ArrayList<MyBuddy>();
-	public AccountConfig cfg;
-	
-	MyAccount(AccountConfig config) {
-		super();
-		cfg = config;
-	}
-	
-	public MyBuddy addBuddy(BuddyConfig bud_cfg)
-	{
-		/* Create Buddy */
-		MyBuddy bud = new MyBuddy(bud_cfg);
-		try {
-			bud.create(this, bud_cfg);
-		} catch (Exception e) {
-			bud.delete();
-			bud = null;
-		}
-		
-		if (bud != null) {
-			buddyList.add(bud);
-			if (bud_cfg.getSubscribe())
-				try {
-					bud.subscribePresence(true);
-				} catch (Exception e) {}
-		}
-		
-		return bud;
-	}
-	
-	public void delBuddy(MyBuddy buddy) {
-		buddyList.remove(buddy);
-		buddy.delete();
-	}
-	
-	public void delBuddy(int index) {
-		MyBuddy bud = buddyList.get(index);
-		buddyList.remove(index);
-		bud.delete();
-	}
-	
-	@Override
-	public void onRegState(OnRegStateParam prm) {
-		MyApp.observer.notifyRegState(prm.getCode(), prm.getReason(), prm.getExpiration());
-	}
-
-	@Override
-	public void onIncomingCall(OnIncomingCallParam prm) {
-		System.out.println("======== Incoming call ======== ");
-		MyCall call = new MyCall(this, prm.getCallId());
-		MyApp.observer.notifyIncomingCall(call);
-	}
-	
-	@Override
-	public void onInstantMessage(OnInstantMessageParam prm) {
-		System.out.println("======== Incoming pager ======== ");
-		System.out.println("From 		: " + prm.getFromUri());
-		System.out.println("To			: " + prm.getToUri());
-		System.out.println("Contact		: " + prm.getContactUri());
-		System.out.println("Mimetype	: " + prm.getContentType());
-		System.out.println("Body		: " + prm.getMsgBody());
-	}
-}
-
-
-class MyBuddy extends Buddy {
-	public BuddyConfig cfg;
-	
-	MyBuddy(BuddyConfig config) {
-		super();
-		cfg = config;
-	}
-	
-	String getStatusText() {
-		BuddyInfo bi;
-		
-		try {
-			bi = getInfo();
-		} catch (Exception e) {
-			return "?";
-		}
-		
-		String status = "";
-		if (bi.getSubState() == pjsip_evsub_state.PJSIP_EVSUB_STATE_ACTIVE) {
-			if (bi.getPresStatus().getStatus() == pjsua_buddy_status.PJSUA_BUDDY_STATUS_ONLINE) {
-				status = bi.getPresStatus().getStatusText();
-				if (status == null || status.length()==0) {
-					status = "Online";
-				}
-			} else if (bi.getPresStatus().getStatus() == pjsua_buddy_status.PJSUA_BUDDY_STATUS_OFFLINE) {
-				status = "Offline";
-			} else {
-				status = "Unknown";
-			}
-		}
-		return status;
-	}
-
-	@Override
-	public void onBuddyState() {
-		MyApp.observer.notifyBuddyState(this);
-	}
-	
-}
-
-
-class MyAccountConfig {
-	public AccountConfig accCfg = new AccountConfig();
-	public ArrayList<BuddyConfig> buddyCfgs = new ArrayList<BuddyConfig>();
-	
-	public void readObject(ContainerNode node) {
-		try {
-			ContainerNode acc_node = node.readContainer("Account");
-			accCfg.readObject(acc_node);
-			ContainerNode buddies_node = acc_node.readArray("buddies");
-			buddyCfgs.clear();
-			while (buddies_node.hasUnread()) {
-				BuddyConfig bud_cfg = new BuddyConfig(); 
-				bud_cfg.readObject(buddies_node);
-				buddyCfgs.add(bud_cfg);
-			}
-		} catch (Exception e) {}
-	}
-	
-	public void writeObject(ContainerNode node) {
-		try {
-			ContainerNode acc_node = node.writeNewContainer("Account");
-			accCfg.writeObject(acc_node);
-			ContainerNode buddies_node = acc_node.writeNewArray("buddies");
-			for (int j = 0; j < buddyCfgs.size(); j++) {
-				buddyCfgs.get(j).writeObject(buddies_node);
-			}
-		} catch (Exception e) {}
-	}
+	    } else if (bi.getPresStatus().getStatus() == 
+		       pjsua_buddy_status.PJSUA_BUDDY_STATUS_OFFLINE)
+	    {
+		status = "Offline";
+	    } else {
+		status = "Unknown";
+	    }
+	}
+	return status;
+    }
+
+    @Override
+    public void onBuddyState()
+    {
+	MyApp.observer.notifyBuddyState(this);
+    }
+
+}
+
+
+class MyAccountConfig
+{
+    public AccountConfig accCfg = new AccountConfig();
+    public ArrayList<BuddyConfig> buddyCfgs = new ArrayList<BuddyConfig>();
+
+    public void readObject(ContainerNode node)
+    {
+	try {
+	    ContainerNode acc_node = node.readContainer("Account");
+	    accCfg.readObject(acc_node);
+	    ContainerNode buddies_node = acc_node.readArray("buddies");
+	    buddyCfgs.clear();
+	    while (buddies_node.hasUnread()) {
+		BuddyConfig bud_cfg = new BuddyConfig(); 
+		bud_cfg.readObject(buddies_node);
+		buddyCfgs.add(bud_cfg);
+	    }
+	} catch (Exception e) {}
+    }
+
+    public void writeObject(ContainerNode node)
+    {
+	try {
+	    ContainerNode acc_node = node.writeNewContainer("Account");
+	    accCfg.writeObject(acc_node);
+	    ContainerNode buddies_node = acc_node.writeNewArray("buddies");
+	    for (int j = 0; j < buddyCfgs.size(); j++) {
+		buddyCfgs.get(j).writeObject(buddies_node);
+	    }
+	} catch (Exception e) {}
+    }
 }
 
 
 class MyApp {
-	static {
-	    	try{
-		    System.loadLibrary("openh264");
-		    System.loadLibrary("yuv");
-	    	} catch (UnsatisfiedLinkError e) {
-	    	    System.out.println("UnsatisfiedLinkError: " + e.getMessage());
-	    	    System.out.println("This could be safely ignored if you " +
-		    		       "don't need video.");
-	    	}
-		System.loadLibrary("pjsua2");
-		System.out.println("Library loaded");
-	}
-	
-	public static Endpoint ep = new Endpoint();
-	public static MyAppObserver observer;
-	public ArrayList<MyAccount> accList = new ArrayList<MyAccount>();
-
-	private ArrayList<MyAccountConfig> accCfgs = new ArrayList<MyAccountConfig>();
-	private EpConfig epConfig = new EpConfig();
-	private TransportConfig sipTpConfig = new TransportConfig();
-	private String appDir;
-	
-	/* Maintain reference to log writer to avoid premature cleanup by GC */
-	private MyLogWriter logWriter;
-
-	private final String configName = "pjsua2.json";
-	private final int SIP_PORT  = 6000;
-	private final int LOG_LEVEL = 4;
-	
-	public void init(MyAppObserver obs, String app_dir) {
-		init(obs, app_dir, false);
-	}
-	
-	public void init(MyAppObserver obs, String app_dir, boolean own_worker_thread) {
-		observer = obs;
-		appDir = app_dir;
-		
-		/* Create endpoint */
-		try {
-			ep.libCreate();
-		} catch (Exception e) {
-			return;
-		}
-			
-		
-		/* Load config */
-		String configPath = appDir + "/" + configName;
-		File f = new File(configPath);
-		if (f.exists()) {
-			loadConfig(configPath);
-		} else {
-			/* Set 'default' values */
-			sipTpConfig.setPort(SIP_PORT);
-		}
-		
-		/* Override log level setting */
-		epConfig.getLogConfig().setLevel(LOG_LEVEL);
-		epConfig.getLogConfig().setConsoleLevel(LOG_LEVEL);
-		
-		/* Set log config. */
-		LogConfig log_cfg = epConfig.getLogConfig();
-		logWriter = new MyLogWriter();
-		log_cfg.setWriter(logWriter);
-		log_cfg.setDecor(log_cfg.getDecor() & 
-						 ~(pj_log_decoration.PJ_LOG_HAS_CR.swigValue() | 
-						   pj_log_decoration.PJ_LOG_HAS_NEWLINE.swigValue()));
-		
-		/* Set ua config. */
-		UaConfig ua_cfg = epConfig.getUaConfig();
-		ua_cfg.setUserAgent("Pjsua2 Android " + ep.libVersion().getFull());
-		StringVector stun_servers = new StringVector();
-		stun_servers.add("stun.pjsip.org");
-		ua_cfg.setStunServer(stun_servers);
-		if (own_worker_thread) {
-			ua_cfg.setThreadCnt(0);
-			ua_cfg.setMainThreadOnly(true);
-		}
-		
-		/* Init endpoint */
-		try {
-			ep.libInit(epConfig);
-		} catch (Exception e) {
-			return;
-		}
-		
-		/* Create transports. */
-		try {
-			ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP, sipTpConfig);
-		} catch (Exception e) {
-			System.out.println(e);
-		}
-
-		try {
-			ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TCP, sipTpConfig);
-		} catch (Exception e) {
-			System.out.println(e);
-		}
-		
-		/* Create accounts. */
-		for (int i = 0; i < accCfgs.size(); i++) {
-			MyAccountConfig my_cfg = accCfgs.get(i);
-			MyAccount acc = addAcc(my_cfg.accCfg);
-			if (acc == null)
-				continue;
-			
-			/* Add Buddies */
-			for (int j = 0; j < my_cfg.buddyCfgs.size(); j++) {
-				BuddyConfig bud_cfg = my_cfg.buddyCfgs.get(j);
-				acc.addBuddy(bud_cfg);
-			}
-		}
-
-		/* Start. */
-		try {
-			ep.libStart();
-		} catch (Exception e) {
-			return;
-		}
-	}
-	
-	public MyAccount addAcc(AccountConfig cfg) {
-		MyAccount acc = new MyAccount(cfg);
-		try {
-			acc.create(cfg);
-		} catch (Exception e) {
-			acc = null;
-			return null;
-		}
-		
-		accList.add(acc);
-		return acc;
-	}
-	
-	public void delAcc(MyAccount acc) {
-		accList.remove(acc);
-	}
-	
-	private void loadConfig(String filename) {
-		JsonDocument json = new JsonDocument();
-		
-		try {
-			/* Load file */
-			json.loadFile(filename);
-			ContainerNode root = json.getRootContainer();
-			
-			/* Read endpoint config */
-			epConfig.readObject(root);
-			
-			/* Read transport config */
-			ContainerNode tp_node = root.readContainer("SipTransport");
-			sipTpConfig.readObject(tp_node);
-			
-			/* Read account configs */
-			accCfgs.clear();
-			ContainerNode accs_node = root.readArray("accounts");
-			while (accs_node.hasUnread()) {
-				MyAccountConfig acc_cfg = new MyAccountConfig();
-				acc_cfg.readObject(accs_node);
-				accCfgs.add(acc_cfg);
-			}
-		} catch (Exception e) {
-			System.out.println(e);
-		}
-		
-		/* Force delete json now, as I found that Java somehow destroys it
-		 * after lib has been destroyed and from non-registered thread.
-		 */
-		json.delete();
-	}
-
-	private void buildAccConfigs() {
-		/* Sync accCfgs from accList */
-		accCfgs.clear();
-		for (int i = 0; i < accList.size(); i++) {
-			MyAccount acc = accList.get(i);
-			MyAccountConfig my_acc_cfg = new MyAccountConfig();
-			my_acc_cfg.accCfg = acc.cfg;
-			
-			my_acc_cfg.buddyCfgs.clear();
-			for (int j = 0; j < acc.buddyList.size(); j++) {
-				MyBuddy bud = acc.buddyList.get(j);
-				my_acc_cfg.buddyCfgs.add(bud.cfg);
-			}
-			
-			accCfgs.add(my_acc_cfg);
-		}
-	}
-	
-	private void saveConfig(String filename) {
-		JsonDocument json = new JsonDocument();
-		
-		try {
-			/* Write endpoint config */
-			json.writeObject(epConfig);
-			
-			/* Write transport config */
-			ContainerNode tp_node = json.writeNewContainer("SipTransport");
-			sipTpConfig.writeObject(tp_node);
-			
-			/* Write account configs */
-			buildAccConfigs();
-			ContainerNode accs_node = json.writeNewArray("accounts");
-			for (int i = 0; i < accCfgs.size(); i++) {
-				accCfgs.get(i).writeObject(accs_node);
-			}
-			
-			/* Save file */
-			json.saveFile(filename);
-		} catch (Exception e) {}
-
-		/* Force delete json now, as I found that Java somehow destroys it
-		 * after lib has been destroyed and from non-registered thread.
-		 */
-		json.delete();
-	}
-	
-	public void deinit() {
-		String configPath = appDir + "/" + configName;
-		saveConfig(configPath);
-		
-		/* Try force GC to avoid late destroy of PJ objects as they should be
-		 * deleted before lib is destroyed.
-		 */
-		Runtime.getRuntime().gc();
-		
-		/* Shutdown pjsua. Note that Endpoint destructor will also invoke
-		 * libDestroy(), so this will be a test of double libDestroy().
-		 */
-		try {
-			ep.libDestroy();
-		} catch (Exception e) {}
-		
-		/* Force delete Endpoint here, to avoid deletion from a non-
-		 * registered thread (by GC?). 
-		 */
-		ep.delete();
-		ep = null;
-	} 
-}
+    static {
+	try{
+	    System.loadLibrary("openh264");
+	    System.loadLibrary("yuv");
+	} catch (UnsatisfiedLinkError e) {
+	    System.out.println("UnsatisfiedLinkError: " + e.getMessage());
+	    System.out.println("This could be safely ignored if you " +
+			       "don't need video.");
+	}
+	System.loadLibrary("pjsua2");
+	System.out.println("Library loaded");
+    }
+
+    public static Endpoint ep = new Endpoint();
+    public static MyAppObserver observer;
+    public ArrayList<MyAccount> accList = new ArrayList<MyAccount>();
+
+    private ArrayList<MyAccountConfig> accCfgs =
+					  new ArrayList<MyAccountConfig>();
+    private EpConfig epConfig = new EpConfig();
+    private TransportConfig sipTpConfig = new TransportConfig();
+    private String appDir;
+
+    /* Maintain reference to log writer to avoid premature cleanup by GC */
+    private MyLogWriter logWriter;
+
+    private final String configName = "pjsua2.json";
+    private final int SIP_PORT  = 6000;
+    private final int LOG_LEVEL = 4;
+
+    public void init(MyAppObserver obs, String app_dir)
+    {
+	init(obs, app_dir, false);
+    }
+
+    public void init(MyAppObserver obs, String app_dir,
+		     boolean own_worker_thread)
+    {
+	observer = obs;
+	appDir = app_dir;
+
+	/* Create endpoint */
+	try {
+	    ep.libCreate();
+	} catch (Exception e) {
+	    return;
+	}
+
+
+	/* Load config */
+	String configPath = appDir + "/" + configName;
+	File f = new File(configPath);
+	if (f.exists()) {
+	    loadConfig(configPath);
+	} else {
+	    /* Set 'default' values */
+	    sipTpConfig.setPort(SIP_PORT);
+	}
+
+	/* Override log level setting */
+	epConfig.getLogConfig().setLevel(LOG_LEVEL);
+	epConfig.getLogConfig().setConsoleLevel(LOG_LEVEL);
+
+	/* Set log config. */
+	LogConfig log_cfg = epConfig.getLogConfig();
+	logWriter = new MyLogWriter();
+	log_cfg.setWriter(logWriter);
+	log_cfg.setDecor(log_cfg.getDecor() & 
+			 ~(pj_log_decoration.PJ_LOG_HAS_CR.swigValue() | 
+			 pj_log_decoration.PJ_LOG_HAS_NEWLINE.swigValue()));
+
+	/* Set ua config. */
+	UaConfig ua_cfg = epConfig.getUaConfig();
+	ua_cfg.setUserAgent("Pjsua2 Android " + ep.libVersion().getFull());
+	StringVector stun_servers = new StringVector();
+	stun_servers.add("stun.pjsip.org");
+	ua_cfg.setStunServer(stun_servers);
+	if (own_worker_thread) {
+	    ua_cfg.setThreadCnt(0);
+	    ua_cfg.setMainThreadOnly(true);
+	}
+
+	/* Init endpoint */
+	try {
+	    ep.libInit(epConfig);
+	} catch (Exception e) {
+	    return;
+	}
+
+	/* Create transports. */
+	try {
+	    ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP,
+			       sipTpConfig);
+	} catch (Exception e) {
+	    System.out.println(e);
+	}
+
+	try {
+	    ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_TCP,
+			       sipTpConfig);
+	} catch (Exception e) {
+	    System.out.println(e);
+	}
+
+	/* Create accounts. */
+	for (int i = 0; i < accCfgs.size(); i++) {
+	    MyAccountConfig my_cfg = accCfgs.get(i);
+	    MyAccount acc = addAcc(my_cfg.accCfg);
+	    if (acc == null)
+		continue;
+
+	    /* Add Buddies */
+	    for (int j = 0; j < my_cfg.buddyCfgs.size(); j++) {
+		BuddyConfig bud_cfg = my_cfg.buddyCfgs.get(j);
+		acc.addBuddy(bud_cfg);
+	    }
+	}
+
+	/* Start. */
+	try {
+	    ep.libStart();
+	} catch (Exception e) {
+	    return;
+	}
+    }
+
+    public MyAccount addAcc(AccountConfig cfg)
+    {
+	MyAccount acc = new MyAccount(cfg);
+	try {
+	    acc.create(cfg);
+	} catch (Exception e) {
+	    acc = null;
+	    return null;
+	}
+
+	accList.add(acc);
+	return acc;
+    }
+
+    public void delAcc(MyAccount acc)
+    {
+	accList.remove(acc);
+    }
+
+    private void loadConfig(String filename)
+    {
+	JsonDocument json = new JsonDocument();
+
+	try {
+	    /* Load file */
+	    json.loadFile(filename);
+	    ContainerNode root = json.getRootContainer();
+
+	    /* Read endpoint config */
+	    epConfig.readObject(root);
+
+	    /* Read transport config */
+	    ContainerNode tp_node = root.readContainer("SipTransport");
+	    sipTpConfig.readObject(tp_node);
+
+	    /* Read account configs */
+	    accCfgs.clear();
+	    ContainerNode accs_node = root.readArray("accounts");
+	    while (accs_node.hasUnread()) {
+		MyAccountConfig acc_cfg = new MyAccountConfig();
+		acc_cfg.readObject(accs_node);
+		accCfgs.add(acc_cfg);
+	    }
+	} catch (Exception e) {
+	    System.out.println(e);
+	}
+
+	/* Force delete json now, as I found that Java somehow destroys it
+	* after lib has been destroyed and from non-registered thread.
+	*/
+	json.delete();
+    }
+
+    private void buildAccConfigs()
+    {
+	/* Sync accCfgs from accList */
+	accCfgs.clear();
+	for (int i = 0; i < accList.size(); i++) {
+	    MyAccount acc = accList.get(i);
+	    MyAccountConfig my_acc_cfg = new MyAccountConfig();
+	    my_acc_cfg.accCfg = acc.cfg;
+
+	    my_acc_cfg.buddyCfgs.clear();
+	    for (int j = 0; j < acc.buddyList.size(); j++) {
+		MyBuddy bud = acc.buddyList.get(j);
+		my_acc_cfg.buddyCfgs.add(bud.cfg);
+	    }
+
+	    accCfgs.add(my_acc_cfg);
+	}
+    }
+
+    private void saveConfig(String filename)
+    {
+	JsonDocument json = new JsonDocument();
+
+	try {
+	    /* Write endpoint config */
+	    json.writeObject(epConfig);
+
+	    /* Write transport config */
+	    ContainerNode tp_node = json.writeNewContainer("SipTransport");
+	    sipTpConfig.writeObject(tp_node);
+
+	    /* Write account configs */
+	    buildAccConfigs();
+	    ContainerNode accs_node = json.writeNewArray("accounts");
+	    for (int i = 0; i < accCfgs.size(); i++) {
+		accCfgs.get(i).writeObject(accs_node);
+	    }
+
+	    /* Save file */
+	    json.saveFile(filename);
+	} catch (Exception e) {}
+
+	/* Force delete json now, as I found that Java somehow destroys it
+	* after lib has been destroyed and from non-registered thread.
+	*/
+	json.delete();
+    }
+
+    public void deinit()
+    {
+	String configPath = appDir + "/" + configName;
+	saveConfig(configPath);
+
+	/* Try force GC to avoid late destroy of PJ objects as they should be
+	* deleted before lib is destroyed.
+	*/
+	Runtime.getRuntime().gc();
+
+	/* Shutdown pjsua. Note that Endpoint destructor will also invoke
+	* libDestroy(), so this will be a test of double libDestroy().
+	*/
+	try {
+	    ep.libDestroy();
+	} catch (Exception e) {}
+
+	/* Force delete Endpoint here, to avoid deletion from a non-
+	* registered thread (by GC?). 
+	*/
+	ep.delete();
+	ep = null;
+    } 
+}
