Example #1
0
        internal static PayloadAsyncResult CreateFromHandle(IntPtr handle)
        {
            Interop.Cion.ErrorCode ret = Interop.CionPayloadAsyncResult.CionPayloadAsyncResultGetResult(handle, out int code);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Fail to get result code from the AsyncResult");
            }

            ret = Interop.CionPayloadAsyncResult.CionPayloadAsyncResultGetPayloadID(handle, out string payloadId);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Fail to get payload id from the AsyncResult");
            }

            ret = Interop.CionPayloadAsyncResult.CionPayloadAsyncResultGetPeerInfo(handle, out IntPtr peer);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Fail to get peerinfo from the AsyncResult");
            }
            ret = Interop.CionPeerInfo.CionPeerInfoClone(peer, out PeerInfoSafeHandle clone);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Failed to clone peer info.");
            }

            return(new PayloadAsyncResult((PayloadAsyncResultCode)code, new PeerInfo(clone), payloadId));
        }
Example #2
0
        /// <summary>
        /// Starts discovering cion servers.
        /// </summary>
        /// <exception cref="InvalidOperationException">Thrown when the discovery operation is already in progress.</exception>
        /// <since_tizen> 9 </since_tizen>
        public void TryDiscovery()
        {
            Log.Error(LogTag, string.Format("Try discovery start"));

            if (_discoveredCb == null)
            {
                Interop.CionClient.CionClientDiscoveredCb cb = new Interop.CionClient.CionClientDiscoveredCb(
                    (string serviceName, IntPtr peerInfo, IntPtr userData) =>
                {
                    Log.Error(LogTag, string.Format("callback called !!"));

                    Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
                    if (clone_ret != Interop.Cion.ErrorCode.None)
                    {
                        Log.Error(LogTag, "Failed to clone peer info.");
                        return;
                    }
                    OnDiscovered(new PeerInfo(clone));
                });
                _discoveredCb = cb;
            }

            Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientTryDiscovery(_handle, _discoveredCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Failed to try discovery.");
            }
        }
Example #3
0
 /// <summary>
 /// Connects with the cion server.
 /// </summary>
 /// <param name="peer">The peer to connect.</param>
 /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
 /// <since_tizen> 9 </since_tizen>
 public void Connect(PeerInfo peer)
 {
     Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientConnect(_handle, peer?._handle);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to connect.");
     }
 }
Example #4
0
 /// <summary>
 /// Stops the listen operation.
 /// </summary>
 /// <exception cref="InvalidOperationException">Thrown when the server is not listening.</exception>
 /// <since_tizen> 9 </since_tizen>
 public void Stop()
 {
     Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerStop(_handle);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to stop server.");
     }
 }
Example #5
0
 /// <summary>
 /// Publishes payload to current group.
 /// </summary>
 /// <param name="payload">The payload to publish.</param>
 /// <exception cref="ArgumentException">Thrown when the payload is invalid.</exception>
 /// <since_tizen> 9 </since_tizen>
 public void Publish(Payload payload)
 {
     Interop.Cion.ErrorCode ret = Interop.CionGroup.CionGroupPublish(_handle, payload?._handle);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to publish payload.");
     }
 }
Example #6
0
 /// <summary>
 /// Saves file of payload to speicific path.
 /// </summary>
 /// <param name="path">The path of file to save.</param>
 /// <since_tizen> 9 </since_tizen>
 public void SaveAsFile(string path)
 {
     Interop.Cion.ErrorCode ret = Interop.CionPayload.CionPayloadSaveAsFile(_handle, path);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to save as file.");
     }
 }
Example #7
0
 /// <summary>
 /// Sets ondemand launch enabled flag.
 /// </summary>
 /// <param name="enable">Whether ondemand launch is enabled or not.</param>
 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
 /// <privilege>http://tizen.org/privilege/d2d.remotelaunch</privilege>
 /// <since_tizen> 9 </since_tizen>
 public void SetOndemandLaunchEnabled(bool enable)
 {
     Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSetOndemandLaunchEnable(_handle, enable);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to set ondemand launch enable");
     }
 }
Example #8
0
 /// <summary>
 /// Disconnects with the peer.
 /// </summary>
 /// <param name="peerInfo">The peer to disconnect.</param>
 /// <exception cref="ArgumentException">Thrown when the given peer info is invalid.</exception>
 /// <since_tizen> 9 </since_tizen>
 public void Disconnect(PeerInfo peerInfo)
 {
     Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerDisconnect(_handle, peerInfo?._handle);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to stop server.");
     }
 }
Example #9
0
 /// <summary>
 /// Unsubscribes the topic.
 /// </summary>
 /// <since_tizen> 9 </since_tizen>
 public void Unsubscribe()
 {
     Interop.Cion.ErrorCode ret = Interop.CionGroup.CionGroupUnsubscribe(_handle);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to unsubscribe.");
     }
 }
Example #10
0
 /// <summary>
 /// Stops discovering.
 /// </summary>
 /// <exception cref="InvalidOperationException">Thrown when the client is not discovering.</exception>
 /// <since_tizen> 9 </since_tizen>
 public void StopDiscovery()
 {
     Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientStopDiscovery(_handle);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to stop discovery.");
     }
 }
Example #11
0
 /// <summary>
 /// Disconnects from the cion server.
 /// </summary>
 /// <since_tizen> 9 </since_tizen>
 public void Disconnect()
 {
     Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientDisconnect(_handle);
     if (ret != Interop.Cion.ErrorCode.None)
     {
         throw CionErrorFactory.GetException(ret, "Failed to disconnect.");
     }
     _peer = null;
 }
Example #12
0
        /// <summary>
        /// Sends data synchronously to the connected cion server.
        /// </summary>
        /// <param name="data">The data to send.</param>
        /// <param name="timeout">The timeout of sending operation.</param>
        /// <exception cref="ArgumentException">Thrown when the given data is invalid.</exception>
        /// <exception cref="InvalidOperationException">Thrown when there is no connected cion server.</exception>
        /// <since_tizen> 9 </since_tizen>
        public byte[] SendData(byte[] data, int timeout)
        {
            Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientSendData(_handle, data, data?.Length ?? -1, timeout, out IntPtr returnDataPtr, out int returnDataSize);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Failed to send data.");
            }
            byte[] returnData = new byte[returnDataSize];
            Marshal.Copy(returnDataPtr, returnData, 0, returnDataSize);
            Log.Info(LogTag, string.Format("Returned data size: {0}", returnDataSize));

            return(returnData);
        }
Example #13
0
        /// <summary>
        /// Gets connected peers.
        /// </summary>
        /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory to continue the execution of the method.</exception>
        /// <since_tizen> 9 </since_tizen>
        public IEnumerable <PeerInfo> GetConnectedPeerList()
        {
            List <PeerInfo> peerInfoList = new List <PeerInfo>();

            Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerForeachConnectedPeerInfo(_handle, (peer, userData) =>
            {
                Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peer, out PeerInfoSafeHandle clone);
                if (clone_ret != Interop.Cion.ErrorCode.None)
                {
                    throw CionErrorFactory.GetException(clone_ret, "Failed to clone peer info.");
                }
                peerInfoList.Add(new PeerInfo(clone));
            }, IntPtr.Zero);
            return(peerInfoList);
        }
Example #14
0
        /// <summary>
        /// Sends payload asynchronously to the connected cion server.
        /// </summary>
        /// <param name="payload">The payload to send.</param>
        /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
        /// <exception cref="InvalidOperationException">Thrown when there is no connected cion server.</exception>
        /// <since_tizen> 9 </since_tizen>
        public Task <PayloadAsyncResult> SendPayloadAsync(Payload payload)
        {
            if (payload?.Id.Length == 0)
            {
                throw new ArgumentException("Payload is invalid.");
            }

            TaskCompletionSource <PayloadAsyncResult> tcs = new TaskCompletionSource <PayloadAsyncResult>();

            _tcsDictionary[payload.Id] = tcs;

            if (_payloadAsyncResultCb == null)
            {
                Interop.CionClient.CionClientPayloadAsyncResultCb cb = new Interop.CionClient.CionClientPayloadAsyncResultCb(
                    (IntPtr result, IntPtr userData) =>
                {
                    TaskCompletionSource <PayloadAsyncResult> tcsToReturn = _tcsDictionary[payload.Id];
                    PayloadAsyncResult resultPayload = null;
                    try
                    {
                        resultPayload = PayloadAsyncResult.CreateFromHandle(result);
                    }
                    catch (Exception e)
                    {
                        Log.Error(LogTag, string.Format("Failed to create PayloadAsyncResult from result handle: {0}.", e.Message));
                        tcsToReturn.SetException(e);
                        return;
                    }
                    tcsToReturn.SetResult(resultPayload);
                    _tcsDictionary.Remove(resultPayload.PayloadId);
                });
                _payloadAsyncResultCb = cb;
            }

            Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientSendPayloadAsync(_handle, payload?._handle, _payloadAsyncResultCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Failed to send payload.");
            }

            return(tcs.Task);
        }
Example #15
0
        /// <summary>
        /// Starts server and listens for requests from cion clients.
        /// </summary>
        /// <exception cref="InvalidOperationException">Thrown when the listen operation is already in progress.</exception>
        /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
        /// <since_tizen> 9 </since_tizen>
        public void Listen()
        {
            if (_connectionRequestCb == null)
            {
                Interop.CionServer.CionServerConnectionRequestCb cb = new Interop.CionServer.CionServerConnectionRequestCb(
                    (serviceName, peerInfo, userData) =>
                {
                    Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
                    if (clone_ret != Interop.Cion.ErrorCode.None)
                    {
                        Log.Error(LogTag, "Failed to clone peer info");
                        return;
                    }
                    OnConnentionRequest(new PeerInfo(clone));
                });
                _connectionRequestCb = cb;
            }

            Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerListen(_handle, _connectionRequestCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Failed to listen server.");
            }
        }
Example #16
0
        /// <summary>
        /// The constructor of ServerBase class.
        /// </summary>
        /// <param name="serviceName">The name of service.</param>
        /// <param name="displayName">The display name of service.</param>
        /// <param name="security">The security configuration.</param>
        /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory to continue the execution of the method.</exception>
        /// <since_tizen> 9 </since_tizen>
        public ServerBase(string serviceName, string displayName, Cion.SecurityInfo security)
        {
            ServiceName = serviceName;
            DisplayName = displayName;

            Cion.SecuritySafeHandle handle = security?._handle;
            Interop.Cion.ErrorCode  ret    = Interop.CionServer.CionServerCreate(out _handle, serviceName, displayName, handle?.DangerousGetHandle() ?? IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Failed to create server handle.");
            }

            _connectionResultCb = new Interop.CionServer.CionServerConnectionResultCb(
                (string service, IntPtr peerInfo, IntPtr result, IntPtr userData) =>
            {
                Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
                if (clone_ret != Interop.Cion.ErrorCode.None)
                {
                    Log.Error(LogTag, "Failed to clone peer info.");
                    return;
                }
                OnConnectionResult(new PeerInfo(clone), new ConnectionResult(result));
            });
            ret = Interop.CionServer.CionServerAddConnectionResultCb(_handle, _connectionResultCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add connection status changed callback.");
            }

            _dataReceivedCb = new Interop.CionServer.CionServerDataReceivedCb(
                (string service, IntPtr peerInfo, byte[] data, int dataSize, out byte[] returnData, out int returnDataSize, IntPtr userData) =>
            {
                returnData     = OnDataReceived(data, new PeerInfo(new PeerInfoSafeHandle(peerInfo, false)));
                returnDataSize = returnData.Length;
            });
            ret = Interop.CionServer.CionServerSetDataReceivedCb(_handle, _dataReceivedCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to set data received callback.");
            }

            _payloadRecievedCb = new Interop.CionServer.CionServerPayloadRecievedCb(
                (string service, IntPtr peerInfo, IntPtr payload, int status, IntPtr userData) =>
            {
                Payload receivedPayload;
                Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
                switch (type)
                {
                case Interop.CionPayload.PayloadType.Data:
                    receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
                    break;

                case Interop.CionPayload.PayloadType.File:
                    receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
                    break;

                default:
                    Log.Error(LogTag, "Invalid payload type received.");
                    return;
                }
                OnPayloadReceived(receivedPayload, new PeerInfo(new PeerInfoSafeHandle(peerInfo, false)), (PayloadTransferStatus)status);
            });
            ret = Interop.CionServer.CionServerAddPayloadReceivedCb(_handle, _payloadRecievedCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
            }

            _disconnectedCb = new Interop.CionServer.CionServerDisconnectedCb(
                (string service, IntPtr peerInfo, IntPtr userData) =>
            {
                Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
                if (clone_ret != Interop.Cion.ErrorCode.None)
                {
                    Log.Error(LogTag, string.Format("Failed to clone peer info."));
                    return;
                }
                OnDisconnected(new PeerInfo(clone));
            });
            ret = Interop.CionServer.CionServerAddDisconnectedCb(_handle, _disconnectedCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add disconnected callback.");
            }
        }
Example #17
0
        /// <summary>
        /// The constructor of GroupBase class.
        /// </summary>
        /// <param name="topicName">The topic of group.</param>
        /// <param name="security">The security configuration.</param>
        /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory to continue the execution of the method.</exception>
        /// <since_tizen> 9 </since_tizen>
        public GroupBase(string topicName, Cion.SecurityInfo security)
        {
            Topic = topicName;

            Cion.SecuritySafeHandle handle = security?._handle;
            Interop.Cion.ErrorCode  ret    = Interop.CionGroup.CionGroupCreate(out _handle, topicName, handle?.DangerousGetHandle() ?? IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Failed to create group.");
            }

            _payloadReceivedCb = new Interop.CionGroup.CionGroupPayloadReceivedCb(
                (IntPtr group, IntPtr peerInfo, IntPtr payload, IntPtr userData) =>
            {
                Payload receivedPayload;
                Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
                switch (type)
                {
                case Interop.CionPayload.PayloadType.Data:
                    receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
                    break;

                case Interop.CionPayload.PayloadType.File:
                    receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
                    break;

                default:
                    throw new ArgumentException("Invalid payload type received.");
                }
                OnPayloadReceived(receivedPayload, new PeerInfo(new PeerInfoSafeHandle(peerInfo, false)));
            });
            ret = Interop.CionGroup.CionGroupAddPayloadReceivedCb(_handle, _payloadReceivedCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
            }

            _joinedCb = new Interop.CionGroup.CionGroupJoinedCb(
                (string name, IntPtr peerInfo, IntPtr userData) =>
            {
                Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
                if (clone_ret != Interop.Cion.ErrorCode.None)
                {
                    return;
                }
                OnJoined(new PeerInfo(clone));
            });
            ret = Interop.CionGroup.CionGroupAddJoinedCb(_handle, _joinedCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add joined callback.");
            }

            _leftCb = new Interop.CionGroup.CionGroupLeftCb(
                (string name, IntPtr peerInfo, IntPtr userData) =>
            {
                Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
                if (clone_ret != Interop.Cion.ErrorCode.None)
                {
                    return;
                }
                OnLeft(new PeerInfo(clone));
            });
            ret = Interop.CionGroup.CionGroupAddLeftCb(_handle, _leftCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add joined callback.");
            }
        }
Example #18
0
        /// <summary>
        /// The constructor of ClientBase class.
        /// </summary>
        /// <param name="serviceName">The name of service.</param>
        /// <param name="security">The security configuration.</param>
        /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory to continue the execution of the method.</exception>
        /// <since_tizen> 9 </since_tizen>
        public ClientBase(string serviceName, Cion.SecurityInfo security)
        {
            ServiceName = serviceName;

            Cion.SecuritySafeHandle handle = security?._handle;
            Interop.Cion.ErrorCode  ret    = Interop.CionClient.CionClientCreate(out _handle, serviceName, handle?.DangerousGetHandle() ?? IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                throw CionErrorFactory.GetException(ret, "Failed to create client.");
            }

            _connectionResultCb = new Interop.CionClient.CionClientConnectionResultCb(
                (string service, IntPtr peerInfo, IntPtr result, IntPtr userData) =>
            {
                Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
                if (clone_ret != Interop.Cion.ErrorCode.None)
                {
                    Log.Error(LogTag, string.Format("Failed to clone peer info."));
                    return;
                }

                PeerInfo peer = new PeerInfo(clone);
                ConnectionResult connectionResult = new ConnectionResult(result);
                if (connectionResult.Status == ConnectionStatus.OK)
                {
                    _peer = peer;
                }

                OnConnectionResult(peer, connectionResult);
            });
            ret = Interop.CionClient.CionClientAddConnectionResultCb(_handle, _connectionResultCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add connection status changed callback.");
            }

            _payloadRecievedCb = new Interop.CionClient.CionClientPayloadRecievedCb(
                (string service, IntPtr peerInfo, IntPtr payload, int status, IntPtr userData) =>
            {
                Payload receivedPayload;
                Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
                switch (type)
                {
                case Interop.CionPayload.PayloadType.Data:
                    receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
                    break;

                case Interop.CionPayload.PayloadType.File:
                    receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
                    break;

                default:
                    Log.Error(LogTag, "Invalid payload type received.");
                    return;
                }
                OnPayloadReceived(receivedPayload, (PayloadTransferStatus)status);
            });
            ret = Interop.CionClient.CionClientAddPayloadReceivedCb(_handle, _payloadRecievedCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
            }

            _disconnectedCb = new Interop.CionClient.CionClientDisconnectedCb(
                (string service, IntPtr peerInfo, IntPtr userData) =>
            {
                Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
                if (clone_ret != Interop.Cion.ErrorCode.None)
                {
                    Log.Error(LogTag, string.Format("Failed to clone peer info."));
                    return;
                }
                OnDisconnected(new PeerInfo(clone));
            });
            ret = Interop.CionClient.CionClientAddDisconnectedCb(_handle, _disconnectedCb, IntPtr.Zero);
            if (ret != Interop.Cion.ErrorCode.None)
            {
                _handle.Dispose();
                throw CionErrorFactory.GetException(ret, "Failed to add disconnected callback.");
            }
        }