Index: pjproject/trunk/pjsip-apps/src/python/pjsua.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/python/pjsua.py	(revision 2158)
+++ pjproject/trunk/pjsip-apps/src/python/pjsua.py	(revision 2163)
@@ -151,4 +151,6 @@
 import _pjsua
 import thread
+import threading
+import weakref
 
 class Error:
@@ -624,8 +626,12 @@
 
     def __init__(self, lib, id):
-        self._lib = lib
+        self._lib = weakref.proxy(lib)
         self._id = id
-        self._obj_name = "Transport " + self.info().description
-
+        self._obj_name = "{Transport " + self.info().description + "}"
+        _Trace((self, 'created'))
+
+    def __del__(self):
+        _Trace((self, 'destroyed'))
+        
     def __str__(self):
         return self._obj_name
@@ -634,4 +640,5 @@
         """Get TransportInfo.
         """
+        lck = self._lib.auto_lock()
         ti = _pjsua.transport_get_info(self._id)
         if not ti:
@@ -641,4 +648,5 @@
     def enable(self):
         """Enable this transport."""
+        lck = self._lib.auto_lock()
         err = _pjsua.transport_set_enable(self._id, True)
         self._lib._err_check("enable()", self, err)
@@ -646,4 +654,5 @@
     def disable(self):
         """Disable this transport."""
+        lck = self._lib.auto_lock()
         err = _pjsua.transport_set_enable(self._id, 0)
         self._lib._err_check("disable()", self, err)
@@ -655,4 +664,5 @@
         force   -- force deletion of this transport (not recommended).
         """
+        lck = self._lib.auto_lock()
         err = _pjsua.transport_close(self._id, force)
         self._lib._err_check("close()", self, err)
@@ -976,6 +986,15 @@
     account = None
 
-    def __init__(self, account):
-        self.account = account
+    def __init__(self, account=None):
+        self._set_account(account)
+
+    def __del__(self):
+        pass
+
+    def _set_account(self, account):
+        if account:
+            self.account = weakref.proxy(account)
+        else:
+            self.account = None
 
     def on_reg_state(self):
@@ -995,5 +1014,5 @@
         call.hangup()
 
-    def on_incoming_subscribe(self, buddy, from_uri, pres_obj):
+    def on_incoming_subscribe(self, buddy, from_uri, contact_uri, pres_obj):
         """Notification when incoming SUBSCRIBE request is received. 
         
@@ -1099,5 +1118,5 @@
     _obj_name = ""
 
-    def __init__(self, lib, id):
+    def __init__(self, lib, id, cb=None):
         """Construct this class. This is normally called by Lib class and
         not by application.
@@ -1106,13 +1125,19 @@
         lib -- the Lib instance.
         id  -- the pjsua account ID.
-        """
-        self._cb = AccountCallback(self)
+        cb  -- AccountCallback instance to receive events from this Account.
+               If callback is not specified here, it must be set later
+               using set_callback().
+        """
         self._id = id
-        self._lib = lib
-        self._lib._associate_account(self._id, self)
-        self._obj_name = "Account " + self.info().uri
+        self._lib = weakref.ref(lib)
+        self._obj_name = "{Account " + self.info().uri + "}"
+        self.set_callback(cb)
+        _pjsua.acc_set_user_data(self._id, self)
+        _Trace((self, 'created'))
 
     def __del__(self):
-        self._lib._disassociate_account(self._id, self)
+        if self._id != -1:
+            _pjsua.acc_set_user_data(self._id, 0)
+        _Trace((self, 'destroyed'))
 
     def __str__(self):
@@ -1122,7 +1147,8 @@
         """Retrieve AccountInfo for this account.
         """
+        lck = self._lib().auto_lock()
         ai = _pjsua.acc_get_info(self._id)
         if ai==None:
-            self._lib._err_check("info()", self, -1, "Invalid account")
+            self._lib()._err_check("info()", self, -1, "Invalid account")
         return AccountInfo(ai)
 
@@ -1132,4 +1158,5 @@
 
         """
+        lck = self._lib().auto_lock()
         return _pjsua.acc_is_valid(self._id)
 
@@ -1145,4 +1172,5 @@
         else:
             self._cb = AccountCallback(self)
+        self._cb._set_account(self)
 
     def set_default(self):
@@ -1151,6 +1179,7 @@
         matching criteria fails.
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.acc_set_default(self._id)
-        self._lib._err_check("set_default()", self, err)
+        self._lib()._err_check("set_default()", self, err)
 
     def is_default(self):
@@ -1158,4 +1187,5 @@
 
         """
+        lck = self._lib().auto_lock()
         def_id = _pjsua.acc_get_default()
         return self.is_valid() and def_id==self._id
@@ -1165,6 +1195,10 @@
         
         """
+        lck = self._lib().auto_lock()
+        err = _pjsua.acc_set_user_data(self._id, 0)
+        self._lib()._err_check("delete()", self, err)
         err = _pjsua.acc_del(self._id)
-        self._lib._err_check("delete()", self, err)
+        self._lib()._err_check("delete()", self, err)
+        self._id = -1
 
     def set_basic_status(self, is_online):
@@ -1175,6 +1209,7 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.acc_set_online_status(self._id, is_online)
-        self._lib._err_check("set_basic_status()", self, err)
+        self._lib()._err_check("set_basic_status()", self, err)
 
     def set_presence_status(self, is_online, 
@@ -1191,7 +1226,8 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.acc_set_online_status2(self._id, is_online, activity,
                                             pres_text, rpid_id)
-        self._lib._err_check("set_presence_status()", self, err)
+        self._lib()._err_check("set_presence_status()", self, err)
 
     def set_registration(self, renew):
@@ -1203,16 +1239,7 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.acc_set_registration(self._id, renew)
-        self._lib._err_check("set_registration()", self, err)
-
-    def has_registration(self):
-        """Returns True if registration is active for this account.
-
-        """
-        acc_info = _pjsua.acc_get_info(self._id)
-        if not acc_info:
-            self._lib._err_check("has_registration()", self, -1, 
-                                 "invalid account")
-        return acc_info.has_registration
+        self._lib()._err_check("set_registration()", self, err)
 
     def set_transport(self, transport):
@@ -1224,36 +1251,52 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.acc_set_transport(self._id, transport._id)
-        self._lib._err_check("set_transport()", self, err)
-
-    def make_call(self, dst_uri, hdr_list=None):
+        self._lib()._err_check("set_transport()", self, err)
+
+    def make_call(self, dst_uri, cb=None, hdr_list=None):
         """Make outgoing call to the specified URI.
 
         Keyword arguments:
         dst_uri  -- Destination SIP URI.
+        cb       -- CallCallback instance to be installed to the newly
+                    created Call object. If this CallCallback is not
+                    specified (i.e. None is given), it must be installed
+                    later using call.set_callback().
         hdr_list -- Optional list of headers to be sent with outgoing
                     INVITE
 
-        """
+        Return:
+            Call instance.
+        """
+        lck = self._lib().auto_lock()
+        call = Call(self._lib(), -1, cb)
         err, cid = _pjsua.call_make_call(self._id, dst_uri, 0, 
-                                           0, Lib._create_msg_data(hdr_list))
-        self._lib._err_check("make_call()", self, err)
-        return Call(self._lib, cid)
-
-    def add_buddy(self, uri):
+                                         call, Lib._create_msg_data(hdr_list))
+        self._lib()._err_check("make_call()", self, err)
+        call.attach_to_id(cid)
+        return call
+
+    def add_buddy(self, uri, cb=None):
         """Add new buddy.
 
         Keyword argument:
-        uri         -- SIP URI of the buddy
+        uri     -- SIP URI of the buddy
+        cb      -- BuddyCallback instance to be installed to the newly
+                   created Buddy object. If this callback is not specified
+                   (i.e. None is given), it must be installed later using
+                   buddy.set_callback().
 
         Return:
             Buddy object
         """
+        lck = self._lib().auto_lock()
         buddy_cfg = _pjsua.buddy_config_default()
         buddy_cfg.uri = uri
         buddy_cfg.subscribe = False
         err, buddy_id = _pjsua.buddy_add(buddy_cfg)
-        self._lib._err_check("add_buddy()", self, err)
-        return Buddy(self._lib, buddy_id, self)
+        self._lib()._err_check("add_buddy()", self, err)
+        buddy = Buddy(self._lib(), buddy_id, self, cb)
+        return buddy
 
     def pres_notify(self, pres_obj, state, reason="", hdr_list=None):
@@ -1268,4 +1311,5 @@
         hdr_list    -- Optional header list.
         """
+        lck = self._lib().auto_lock()
         _pjsua.acc_pres_notify(self._id, pres_obj, state, reason, 
                                Lib._create_msg_data(hdr_list))
@@ -1284,6 +1328,15 @@
     call = None
 
-    def __init__(self, call):
-        self.call = call
+    def __init__(self, call=None):
+        self._set_call(call)
+
+    def __del__(self):
+        pass
+
+    def _set_call(self, call):
+        if call:
+            self.call = weakref.proxy(call)
+        else:
+            self.call = None
 
     def on_state(self):
@@ -1469,6 +1522,6 @@
         self.media_dir = ci.media_dir
         self.conf_slot = ci.conf_slot
-        self.call_time = ci.connect_duration.sec
-        self.total_time = ci.total_duration.sec
+        self.call_time = ci.connect_duration / 1000
+        self.total_time = ci.total_duration / 1000
 
 
@@ -1484,16 +1537,28 @@
     _obj_name = ""
 
-    def __init__(self, lib, call_id):
-        self._cb = CallCallback(self) 
-        self._id = call_id
-        self._lib = lib
-        self._lib._associate_call(call_id, self)
-        self._obj_name = "Call " + self.info().remote_uri
+    def __init__(self, lib, call_id, cb=None):
+        self._lib = weakref.ref(lib)
+        self.set_callback(cb)
+        self.attach_to_id(call_id)
+        _Trace((self, 'created'))
 
     def __del__(self):
-        self._lib._disassociate_call(self._id, self)
+        if self._id != -1:
+            _pjsua.call_set_user_data(self._id, 0)
+        _Trace((self, 'destroyed'))
 
     def __str__(self):
         return self._obj_name
+
+    def attach_to_id(self, call_id):
+        lck = self._lib().auto_lock()
+        if self._id != -1:
+            _pjsua.call_set_user_data(self._id, 0)
+        self._id = call_id
+        if self._id != -1:
+            _pjsua.call_set_user_data(self._id, self)
+            self._obj_name = "{Call " + self.info().remote_uri + "}"
+        else:
+            self._obj_name = "{Call object}"
 
     def set_callback(self, cb):
@@ -1508,4 +1573,5 @@
         else:
             self._cb = CallCallback(self)
+        self._cb._set_call(self)
 
     def info(self):
@@ -1513,8 +1579,10 @@
         Get the CallInfo.
         """
+        lck = self._lib().auto_lock()
         ci = _pjsua.call_get_info(self._id)
         if not ci:
-            self._lib._err_check("info", self, -1, "Invalid call")
-        return CallInfo(self._lib, ci)
+            self._lib()._err_check("info", self, -1, "Invalid call")
+        call_info = CallInfo(self._lib(), ci)
+        return call_info
 
     def is_valid(self):
@@ -1522,4 +1590,5 @@
         Check if this call is still valid.
         """
+        lck = self._lib().auto_lock()
         return _pjsua.call_is_active(self._id)
 
@@ -1528,4 +1597,5 @@
         Dump the call status.
         """
+        lck = self._lib().auto_lock()
         return _pjsua.call_dump(self._id, with_media, max_len, indent)
 
@@ -1542,7 +1612,8 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_answer(self._id, code, reason, 
                                    Lib._create_msg_data(hdr_list))
-        self._lib._err_check("answer()", self, err)
+        self._lib()._err_check("answer()", self, err)
 
     def hangup(self, code=603, reason="", hdr_list=None):
@@ -1558,7 +1629,8 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_hangup(self._id, code, reason, 
                                    Lib._create_msg_data(hdr_list))
-        self._lib._err_check("hangup()", self, err)
+        self._lib()._err_check("hangup()", self, err)
 
     def hold(self, hdr_list=None):
@@ -1570,6 +1642,7 @@
                     message.
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_set_hold(self._id, Lib._create_msg_data(hdr_list))
-        self._lib._err_check("hold()", self, err)
+        self._lib()._err_check("hold()", self, err)
 
     def unhold(self, hdr_list=None):
@@ -1582,7 +1655,8 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_reinvite(self._id, True, 
                                      Lib._create_msg_data(hdr_list))
-        self._lib._err_check("unhold()", self, err)
+        self._lib()._err_check("unhold()", self, err)
 
     def reinvite(self, hdr_list=None):
@@ -1595,7 +1669,8 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_reinvite(self._id, True, 
                                      Lib._create_msg_data(hdr_list))
-        self._lib._err_check("reinvite()", self, err)
+        self._lib()._err_check("reinvite()", self, err)
 
     def update(self, hdr_list=None, options=0):
@@ -1609,7 +1684,8 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_update(self._id, options, 
                                    Lib._create_msg_data(hdr_list))
-        self._lib._err_check("update()", self, err)
+        self._lib()._err_check("update()", self, err)
 
     def transfer(self, dest_uri, hdr_list=None):
@@ -1623,7 +1699,8 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_xfer(self._id, dest_uri, 
                                  Lib._create_msg_data(hdr_list))
-        self._lib._err_check("transfer()", self, err)
+        self._lib()._err_check("transfer()", self, err)
 
     def transfer_to_call(self, call, hdr_list=None, options=0):
@@ -1638,7 +1715,8 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_xfer_replaces(self._id, call._id, options,
                                           Lib._create_msg_data(hdr_list))
-        self._lib._err_check("transfer_to_call()", self, err)
+        self._lib()._err_check("transfer_to_call()", self, err)
 
     def dial_dtmf(self, digits):
@@ -1650,6 +1728,7 @@
 
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.call_dial_dtmf(self._id, digits)
-        self._lib._err_check("dial_dtmf()", self, err)
+        self._lib()._err_check("dial_dtmf()", self, err)
 
     def send_request(self, method, hdr_list=None, content_type=None,
@@ -1670,4 +1749,5 @@
 
         """
+        lck = self._lib().auto_lock()
         if hdr_list and body:
             msg_data = _pjsua.Msg_Data()
@@ -1682,5 +1762,5 @@
                 
         err = _pjsua.call_send_request(self._id, method, msg_data)
-        self._lib._err_check("send_request()", self, err)
+        self._lib()._err_check("send_request()", self, err)
 
 
@@ -1737,6 +1817,12 @@
     buddy = None
 
-    def __init__(self, buddy):
-        self.buddy = buddy
+    def __init__(self, buddy=None):
+        self._set_buddy(buddy)
+
+    def _set_buddy(self, buddy):
+        if buddy:
+            self.buddy = weakref.proxy(buddy)
+        else:
+            self.buddy = None
 
     def on_state(self):
@@ -1794,14 +1880,17 @@
     _acc = None
 
-    def __init__(self, lib, id, account):
-        self._cb = BuddyCallback(self)
-        self._lib = lib
+    def __init__(self, lib, id, account, cb):
         self._id = id
-        self._acc = account
-        lib._associate_buddy(self._id, self)
-        self._obj_name = "Buddy " + self.info().uri
+        self._lib = weakref.ref(lib)
+        self._acc = weakref.ref(account)
+        self._obj_name = "{Buddy " + self.info().uri + "}"
+        self.set_callback(cb)
+        _pjsua.buddy_set_user_data(self._id, self)
+        _Trace((self, 'created'))
 
     def __del__(self):
-        self._lib._disassociate_buddy(self)
+        if self._id != -1:
+            _pjsua.buddy_set_user_data(self._id, 0)
+        _Trace((self, 'destroyed'))
 
     def __str__(self):
@@ -1812,4 +1901,5 @@
         Get buddy info as BuddyInfo.
         """
+        lck = self._lib().auto_lock()
         return BuddyInfo(_pjsua.buddy_get_info(self._id))
 
@@ -1824,4 +1914,5 @@
         else:
             self._cb = BuddyCallback(self)
+        self._cb._set_buddy(self)
 
     def subscribe(self):
@@ -1829,6 +1920,7 @@
         Subscribe to buddy's presence status notification.
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.buddy_subscribe_pres(self._id, True)
-        self._lib._err_check("subscribe()", self, err)
+        self._lib()._err_check("subscribe()", self, err)
 
     def unsubscribe(self):
@@ -1836,6 +1928,7 @@
         Unsubscribe from buddy's presence status notification.
         """
+        lck = self._lib().auto_lock()
         err = _pjsua.buddy_subscribe_pres(self._id, False)
-        self._lib._err_check("unsubscribe()", self, err)
+        self._lib()._err_check("unsubscribe()", self, err)
 
     def delete(self):
@@ -1843,6 +1936,9 @@
         Remove this buddy from the buddy list.
         """
+        lck = self._lib().auto_lock()
+        if self._id != -1:
+            _pjsua.buddy_set_user_data(self._id, 0)
         err = _pjsua.buddy_del(self._id)
-        self._lib._err_check("delete()", self, err)
+        self._lib()._err_check("delete()", self, err)
 
     def send_pager(self, text, im_id=0, content_type="text/plain", \
@@ -1860,9 +1956,10 @@
 
         """
-        err = _pjsua.im_send(self._acc._id, self.info().uri, \
+        lck = self._lib().auto_lock()
+        err = _pjsua.im_send(self._acc()._id, self.info().uri, \
                              content_type, text, \
                              Lib._create_msg_data(hdr_list), \
                              im_id)
-        self._lib._err_check("send_pager()", self, err)
+        self._lib()._err_check("send_pager()", self, err)
 
     def send_typing_ind(self, is_typing=True, hdr_list=None):
@@ -1875,7 +1972,8 @@
 
         """
-        err = _pjsua.im_typing(self._acc._id, self.info().uri, \
+        lck = self._lib().auto_lock()
+        err = _pjsua.im_typing(self._acc()._id, self.info().uri, \
                                is_typing, Lib._create_msg_data(hdr_list))
-        self._lib._err_check("send_typing_ind()", self, err)
+        self._lib()._err_check("send_typing_ind()", self, err)
 
 
@@ -1982,4 +2080,16 @@
 
 
+# Library mutex
+class _LibMutex:
+    def __init__(self, lck):
+        self._lck = lck
+        self._lck.acquire()
+        #print 'lck acquire'
+
+    def __del__(self):
+        self._lck.release()
+        #print 'lck release'
+
+
 # PJSUA Library
 _lib = None
@@ -1988,11 +2098,7 @@
     
     """
-    call = {}
-    account = {}
-    buddy = {}
-    buddy_by_uri = {}
-    buddy_by_contact = {}
     _quit = False
     _has_thread = False
+    _lock = None
 
     def __init__(self):
@@ -2001,5 +2107,6 @@
             raise Error("__init()__", None, -1, 
                         "Library instance already exist")
-            
+
+        self._lock = threading.RLock()
         err = _pjsua.create()
         self._err_check("_pjsua.create()", None, err)
@@ -2008,4 +2115,6 @@
     def __del__(self):
         _pjsua.destroy()
+        del self._lock
+        print 'Lib destroyed'
 
     def __str__(self):
@@ -2049,5 +2158,5 @@
 
         err = _pjsua.init(py_ua_cfg, log_cfg._cvt_to_pjsua(), 
-                            media_cfg._cvt_to_pjsua())
+                          media_cfg._cvt_to_pjsua())
         self._err_check("init()", self, err)
 
@@ -2059,9 +2168,9 @@
             loop = 0
             while self._quit != 2 and loop < 400:
-                _pjsua.handle_events(50)
+                self.handle_events(50)
                 loop = loop + 1
         _pjsua.destroy()
         _lib = None
-        
+
     def start(self, with_thread=True):
         """Start the library. 
@@ -2088,4 +2197,5 @@
 
         """
+        lck = self.auto_lock()
         return _pjsua.handle_events(timeout)
 
@@ -2101,4 +2211,5 @@
 
         """
+        lck = self.auto_lock()
         return _pjsua.verify_sip_url(sip_url)
 
@@ -2114,4 +2225,5 @@
 
         """
+        lck = self.auto_lock()
         if not cfg: cfg=TransportConfig(type)
         err, tp_id = _pjsua.transport_create(type, cfg._cvt_to_pjsua())
@@ -2119,5 +2231,5 @@
         return Transport(self, tp_id)
 
-    def create_account(self, acc_config, set_default=True):
+    def create_account(self, acc_config, set_default=True, cb=None):
         """
         Create a new local pjsua account using the specified configuration.
@@ -2127,4 +2239,5 @@
         set_default -- boolean to specify whether to use this as the
                        default account.
+        cb          -- AccountCallback instance.
 
         Return:
@@ -2132,9 +2245,11 @@
 
         """
+        lck = self.auto_lock()
         err, acc_id = _pjsua.acc_add(acc_config._cvt_to_pjsua(), set_default)
         self._err_check("create_account()", self, err)
-        return Account(self, acc_id)
-
-    def create_account_for_transport(self, transport, set_default=True):
+        return Account(self, acc_id, cb)
+
+    def create_account_for_transport(self, transport, set_default=True,
+                                     cb=None):
         """Create a new local pjsua transport for the specified transport.
 
@@ -2143,4 +2258,5 @@
         set_default -- boolean to specify whether to use this as the
                        default account.
+        cb          -- AccountCallback instance.
 
         Return:
@@ -2148,7 +2264,8 @@
 
         """
+        lck = self.auto_lock()
         err, acc_id = _pjsua.acc_add_local(transport._id, set_default)
         self._err_check("create_account_for_transport()", self, err)
-        return Account(self, acc_id)
+        return Account(self, acc_id, cb)
 
     def hangup_all(self):
@@ -2156,4 +2273,5 @@
 
         """
+        lck = self.auto_lock()
         _pjsua.call_hangup_all()
 
@@ -2167,4 +2285,5 @@
             the device ID for the device.
         """
+        lck = self.auto_lock()
         sdi_list = _pjsua.enum_snd_devs()
         info = []
@@ -2179,4 +2298,5 @@
             (capture_dev_id, playback_dev_id) tuple
         """
+        lck = self.auto_lock()
         return _pjsua.get_snd_dev()
 
@@ -2189,4 +2309,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.set_snd_dev(capture_dev, playback_dev)
         self._err_check("set_current_sound_devices()", self, err)
@@ -2197,4 +2318,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.set_null_snd_dev()
         self._err_check("set_null_snd_dev()", self, err)
@@ -2210,4 +2332,5 @@
 
         """
+        lck = self.auto_lock()
         return _pjsua.conf_get_max_ports()
 
@@ -2231,4 +2354,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.conf_connect(src_slot, dst_slot)
         self._err_check("conf_connect()", self, err)
@@ -2244,4 +2368,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.conf_disconnect(src_slot, dst_slot)
         self._err_check("conf_disconnect()", self, err)
@@ -2256,4 +2381,5 @@
                        adjustment, while value 0 means to mute the port.
         """
+        lck = self.auto_lock()
         err = _pjsua.conf_set_tx_level(slot, level)
         self._err_check("conf_set_tx_level()", self, err)
@@ -2268,4 +2394,5 @@
                        adjustment, while value 0 means to mute the port.
         """
+        lck = self.auto_lock()
         err = _pjsua.conf_set_rx_level(slot, level)
         self._err_check("conf_set_rx_level()", self, err)
@@ -2283,4 +2410,5 @@
             (tx_level, rx_level) tuple.
         """
+        lck = self.auto_lock()
         err, tx_level, rx_level = _pjsua.conf_get_signal_level(slot)
         self._err_check("conf_get_signal_level()", self, err)
@@ -2298,4 +2426,5 @@
 
         """
+        lck = self.auto_lock()
         ci_list = _pjsua.enum_codecs()
         codec_info = []
@@ -2314,4 +2443,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.codec_set_priority(name, priority)
         self._err_check("set_codec_priority()", self, err)
@@ -2324,4 +2454,5 @@
 
         """
+        lck = self.auto_lock()
         cp = _pjsua.codec_get_param(name)
         if not cp:
@@ -2338,4 +2469,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.codec_set_param(name, param._cvt_to_pjsua())
         self._err_check("set_codec_parameter()", self, err)
@@ -2354,4 +2486,5 @@
 
         """
+        lck = self.auto_lock()
         opt = 0
         if not loop:
@@ -2371,4 +2504,5 @@
 
         """
+        lck = self.auto_lock()
         slot = _pjsua.player_get_conf_port(player_id)
         if slot < 0:
@@ -2385,4 +2519,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.player_set_pos(player_id, pos)
         self._err_check("player_set_pos()", self, err)
@@ -2395,4 +2530,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.player_destroy(player_id)
         self._err_check("player_destroy()", self, err)
@@ -2411,4 +2547,5 @@
             playlist_id
         """
+        lck = self.auto_lock()
         opt = 0
         if not loop:
@@ -2428,4 +2565,5 @@
 
         """
+        lck = self.auto_lock()
         slot = _pjsua.player_get_conf_port(playlist_id)
         if slot < 0:
@@ -2441,4 +2579,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.player_destroy(playlist_id)
         self._err_check("playlist_destroy()", self, err)
@@ -2454,4 +2593,5 @@
 
         """
+        lck = self.auto_lock()
         err, rec_id = _pjsua.recorder_create(filename, 0, None, -1, 0)
         self._err_check("create_recorder()", self, err)
@@ -2468,4 +2608,5 @@
 
         """
+        lck = self.auto_lock()
         slot = _pjsua.recorder_get_conf_port(rec_id)
         if slot < 1:
@@ -2481,4 +2622,5 @@
 
         """
+        lck = self.auto_lock()
         err = _pjsua.recorder_destroy(rec_id)
         self._err_check("recorder_destroy()", self, err)
@@ -2503,45 +2645,28 @@
         return msg_data
     
+    def auto_lock(self):
+        return _LibMutex(self._lock)
+
     # Internal dictionary manipulation for calls, accounts, and buddies
 
-    def _associate_call(self, call_id, call):
-        self.call[call_id] = call
-
     def _lookup_call(self, call_id):
-        return self.call.has_key(call_id) and self.call[call_id] or None
-
-    def _disassociate_call(self, call):
-        if self._lookup_call(call._id)==call:
-            del self.call[call._id]
-
-    def _associate_account(self, acc_id, account):
-        self.account[acc_id] = account
+        return _pjsua.call_get_user_data(call_id)
 
     def _lookup_account(self, acc_id):
-        return self.account.has_key(acc_id) and self.account[acc_id] or None
-
-    def _disassociate_account(self, account):
-        if self._lookup_account(account._id)==account:
-            del self.account[account._id]
-
-    def _associate_buddy(self, buddy_id, buddy):
-        self.buddy[buddy_id] = buddy
-        uri = SIPUri(buddy.info().uri)
-        self.buddy_by_uri[(uri.user, uri.host)] = buddy
+        return _pjsua.acc_get_user_data(acc_id)
 
     def _lookup_buddy(self, buddy_id, uri=None):
-        buddy = self.buddy.has_key(buddy_id) and self.buddy[buddy_id] or None
-        if uri and not buddy:
-            sip_uri = SIPUri(uri)
-            buddy = self.buddy_by_uri.has_key( (sip_uri.user, sip_uri.host) ) \
-                    and self.buddy_by_uri[(sip_uri.user, sip_uri.host)] or \
-                    None
+        if buddy_id != -1:
+            buddy = _pjsua.buddy_get_user_data(buddy_id)
+        elif uri:
+            buddy_id = _pjsua.buddy_find(uri)
+            if buddy_id != -1:
+                buddy = _pjsua.buddy_get_user_data(buddy_id)
+            else:
+                buddy = None
+        else:
+            buddy = None
+            
         return buddy 
-
-    def _disassociate_buddy(self, buddy):
-        if self._lookup_buddy(buddy._id)==buddy:
-            del self.buddy[buddy._id]
-        if self.buddy_by_uri.has_key(buddy.info().uri):
-            del self.buddy_by_uri[buddy.info().uri]
 
     # Account allbacks
@@ -2552,9 +2677,11 @@
             acc._cb.on_reg_state()
 
-    def _cb_on_incoming_subscribe(self, acc_id, buddy_id, from_uri, pres_obj):
+    def _cb_on_incoming_subscribe(self, acc_id, buddy_id, from_uri, 
+                                  contact_uri, pres_obj):
         acc = self._lookup_account(acc_id)
         if acc:
             buddy = self._lookup_buddy(buddy_id)
-            return acc._cb.on_incoming_subscribe(buddy, from_uri, pres_obj)
+            return acc._cb.on_incoming_subscribe(buddy, from_uri, contact_uri,
+                                                 pres_obj)
         else:
             return (404, None)
@@ -2572,5 +2699,12 @@
         call = self._lookup_call(call_id)
         if call:
+            if call._id == -1:
+                call.attach_to_id(call_id)
+            done = (call.info().state == CallState.DISCONNECTED)
             call._cb.on_state()
+            if done:
+                _pjsua.call_set_user_data(call_id, 0)
+        else:
+            pass
 
     def _cb_on_call_media_state(self, call_id):
@@ -2695,6 +2829,7 @@
     _lib._cb_on_reg_state(acc_id)
 
-def _cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, pres):
-    return _lib._cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, pres)
+def _cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, contact_uri, pres):
+    return _lib._cb_on_incoming_subscribe(acc_id, buddy_id, from_uri, 
+                                          contact_uri, pres)
 
 def _cb_on_buddy_state(buddy_id):
@@ -2718,7 +2853,13 @@
     err = _pjsua.thread_register("python worker", thread_desc)
     _lib._err_check("thread_register()", _lib, err)
-    while _lib._quit == 0:
-        _pjsua.handle_events(50)
-    _lib._quit = 2
-
-
+    while _lib and _lib._quit == 0:
+        _lib.handle_events(50)
+    if _lib:
+        _lib._quit = 2
+
+def _Trace(args):
+    if True:
+        print "** ",
+        for arg in args:
+            print arg,
+        print " **"
