2 * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 using System.Collections.Generic;
19 using System.Runtime.InteropServices;
20 using System.Threading.Tasks;
22 namespace Tizen.Applications.Cion
25 /// An abstract class to represent cion client.
27 /// <since_tizen> 9 </since_tizen>
28 public abstract class ClientBase : IDisposable
30 private readonly string LogTag = "Tizen.Applications.Cion";
31 private readonly ClientSafeHandle _handle;
33 private PeerInfo _peer;
35 private Interop.CionClient.CionClientServerDiscoveredCb _discoveredCb;
36 private Interop.CionClient.CionClientConnectionResultCb _connectionResultCb;
37 private Interop.CionClient.CionClientPayloadReceivedCb _payloadRecievedCb;
38 private Interop.CionClient.CionClientDisconnectedCb _disconnectedCb;
39 private Dictionary<string, TaskCompletionSource<PayloadAsyncResult>> _tcsDictionary = new Dictionary<string, TaskCompletionSource<PayloadAsyncResult>>();
40 private Dictionary<string, Interop.CionClient.CionClientPayloadAsyncResultCb> _payloadAsyncCbDictionary = new Dictionary<string, Interop.CionClient.CionClientPayloadAsyncResultCb>();
43 /// Gets the service name of current cion client.
45 /// <since_tizen> 9 </since_tizen>
46 public string ServiceName { get; }
49 /// Gets peer info of connected cion server.
51 /// <since_tizen> 9 </since_tizen>
52 public PeerInfo PeerInfo
61 /// The constructor of ClientBase class.
63 /// <param name="serviceName">The name of service.</param>
64 /// <remarks>The maximum length of service name is 512.</remarks>
65 /// <exception cref="ArgumentException">Thrown when the given service name is too long.</exception>
66 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
67 /// <since_tizen> 9 </since_tizen>
68 public ClientBase(string serviceName) : this(serviceName, null) { }
71 /// The constructor of ClientBase class.
73 /// <param name="serviceName">The name of service.</param>
74 /// <param name="security">The security configuration.</param>
75 /// <remarks>The maximum length of service name is 512.</remarks>
76 /// <exception cref="ArgumentException">Thrown when the given service name is too long.</exception>
77 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
78 /// <since_tizen> 9 </since_tizen>
79 public ClientBase(string serviceName, Cion.SecurityInfo security)
81 ServiceName = serviceName;
83 SecuritySafeHandle handle = security?._handle;
84 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientCreate(out _handle, serviceName, handle?.DangerousGetHandle() ?? IntPtr.Zero);
85 if (ret != Interop.Cion.ErrorCode.None)
87 throw CionErrorFactory.GetException(ret, "Failed to create client.");
90 _connectionResultCb = new Interop.CionClient.CionClientConnectionResultCb(
91 (string service, IntPtr peerInfo, IntPtr result, IntPtr userData) =>
93 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
94 if (clone_ret != Interop.Cion.ErrorCode.None)
96 Log.Error(LogTag, string.Format("Failed to clone peer info."));
100 PeerInfo peer = new PeerInfo(clone);
101 ConnectionResult connectionResult = new ConnectionResult(result);
102 if (connectionResult.Status == ConnectionStatus.OK)
107 OnConnectionResult(peer, connectionResult);
109 ret = Interop.CionClient.CionClientAddConnectionResultCb(_handle, _connectionResultCb, IntPtr.Zero);
110 if (ret != Interop.Cion.ErrorCode.None)
113 throw CionErrorFactory.GetException(ret, "Failed to add connection status changed callback.");
116 _payloadRecievedCb = new Interop.CionClient.CionClientPayloadReceivedCb(
117 (string service, IntPtr peerInfo, IntPtr payload, int status, IntPtr userData) =>
119 Payload receivedPayload;
120 Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
123 case Interop.CionPayload.PayloadType.Data:
124 receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
126 case Interop.CionPayload.PayloadType.File:
127 receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
130 Log.Error(LogTag, "Invalid payload type received.");
133 OnPayloadReceived(receivedPayload, (PayloadTransferStatus)status);
135 ret = Interop.CionClient.CionClientAddPayloadReceivedCb(_handle, _payloadRecievedCb, IntPtr.Zero);
136 if (ret != Interop.Cion.ErrorCode.None)
139 throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
142 _disconnectedCb = new Interop.CionClient.CionClientDisconnectedCb(
143 (string service, IntPtr peerInfo, IntPtr userData) =>
145 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
146 if (clone_ret != Interop.Cion.ErrorCode.None)
148 Log.Error(LogTag, string.Format("Failed to clone peer info."));
151 OnDisconnected(new PeerInfo(clone));
153 ret = Interop.CionClient.CionClientAddDisconnectedCb(_handle, _disconnectedCb, IntPtr.Zero);
154 if (ret != Interop.Cion.ErrorCode.None)
157 throw CionErrorFactory.GetException(ret, "Failed to add disconnected callback.");
162 /// Starts discovering cion servers.
164 /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
165 /// <privilege>http://tizen.org/privilege/internet</privilege>
166 /// <privlevel>public</privlevel>
167 /// <exception cref="InvalidOperationException">Thrown when the discovery operation is already in progress.</exception>
168 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
169 /// <since_tizen> 9 </since_tizen>
170 public void TryDiscovery()
172 if (_discoveredCb == null)
174 Interop.CionClient.CionClientServerDiscoveredCb cb = new Interop.CionClient.CionClientServerDiscoveredCb(
175 (string serviceName, IntPtr peerInfo, IntPtr userData) =>
177 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
178 if (clone_ret != Interop.Cion.ErrorCode.None)
180 Log.Error(LogTag, "Failed to clone peer info.");
183 OnDiscovered(new PeerInfo(clone));
188 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientTryDiscovery(_handle, _discoveredCb, IntPtr.Zero);
189 if (ret != Interop.Cion.ErrorCode.None)
191 throw CionErrorFactory.GetException(ret, "Failed to try discovery.");
196 /// Stops discovering.
198 /// <exception cref="InvalidOperationException">Thrown when the client is not discovering.</exception>
199 /// <since_tizen> 9 </since_tizen>
200 public void StopDiscovery()
202 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientStopDiscovery(_handle);
203 if (ret != Interop.Cion.ErrorCode.None)
205 throw CionErrorFactory.GetException(ret, "Failed to stop discovery.");
210 /// Connects with the cion server.
212 /// <param name="peer">The peer to connect.</param>
213 /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
214 /// <privilege>http://tizen.org/privilege/internet</privilege>
215 /// <privlevel>public</privlevel>
216 /// <exception cref="InvalidOperationException">Thrown when the client cannot connect to server.</exception>
217 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
218 /// <since_tizen> 9 </since_tizen>
219 public void Connect(PeerInfo peer)
221 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientConnect(_handle, peer?._handle);
222 if (ret != Interop.Cion.ErrorCode.None)
224 throw CionErrorFactory.GetException(ret, "Failed to connect.");
229 /// Disconnects from the cion server.
231 /// <since_tizen> 9 </since_tizen>
232 public void Disconnect()
234 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientDisconnect(_handle);
235 if (ret != Interop.Cion.ErrorCode.None)
237 Log.Error(LogTag, string.Format("Failed to disconnect: {0}", ret));
243 /// Sends data synchronously to the connected cion server.
245 /// <param name="data">The data to send.</param>
246 /// <param name="timeout">The timeout of sending operation.</param>
247 /// <exception cref="ArgumentException">Thrown when the given data is invalid.</exception>
248 /// <exception cref="InvalidOperationException">Thrown when there is no connected cion server or failed to receive reply.</exception>
249 /// <exception cref="TimeoutException">Thrown when a timeout occurred.</exception>
250 /// <since_tizen> 9 </since_tizen>
251 public byte[] SendData(byte[] data, int timeout)
253 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientSendData(_handle, data, data?.Length ?? -1, timeout, out IntPtr returnDataPtr, out int returnDataSize);
254 if (ret != Interop.Cion.ErrorCode.None)
256 throw CionErrorFactory.GetException(ret, "Failed to send data.");
258 byte[] returnData = new byte[returnDataSize];
259 Marshal.Copy(returnDataPtr, returnData, 0, returnDataSize);
260 Log.Info(LogTag, string.Format("Returned data size: {0}", returnDataSize));
266 /// Sends payload asynchronously to the connected cion server.
268 /// <param name="payload">The payload to send.</param>
269 /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
270 /// <exception cref="InvalidOperationException">Thrown when there is no connected cion server or failed to send payload.</exception>
271 /// <since_tizen> 9 </since_tizen>
272 public Task<PayloadAsyncResult> SendPayloadAsync(Payload payload)
274 if (payload == null || payload.Id.Length == 0)
276 throw new ArgumentException("Payload is invalid.");
279 if (_tcsDictionary.ContainsKey(payload.Id))
281 throw new InvalidOperationException("Payload is already sent.");
284 TaskCompletionSource<PayloadAsyncResult> tcs = new TaskCompletionSource<PayloadAsyncResult>();
285 _tcsDictionary[payload.Id] = tcs;
287 Interop.CionClient.CionClientPayloadAsyncResultCb cb = new Interop.CionClient.CionClientPayloadAsyncResultCb(
288 (IntPtr result, IntPtr userData) =>
290 TaskCompletionSource<PayloadAsyncResult> tcsToReturn = _tcsDictionary[payload.Id];
291 PayloadAsyncResult resultPayload = null;
294 resultPayload = PayloadAsyncResult.CreateFromHandle(result);
298 Log.Error(LogTag, string.Format("Failed to create PayloadAsyncResult from result handle: {0}.", e.Message));
299 tcsToReturn.SetException(e);
302 tcsToReturn.SetResult(resultPayload);
303 _payloadAsyncCbDictionary.Remove(resultPayload.PayloadId);
304 _tcsDictionary.Remove(resultPayload.PayloadId);
307 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientSendPayloadAsync(_handle, payload?._handle, cb, IntPtr.Zero);
308 if (ret != Interop.Cion.ErrorCode.None)
310 throw CionErrorFactory.GetException(ret, "Failed to send payload.");
313 _payloadAsyncCbDictionary[payload?.Id] = cb;
319 /// The result callback of connection request.
321 /// <param name="peerInfo">The peer info of the cion server.</param>
322 /// <param name="result">The result of the connection.</param>
323 /// <since_tizen> 9 </since_tizen>
324 protected abstract void OnConnectionResult(PeerInfo peerInfo, ConnectionResult result);
327 /// The callback invoked when received payload.
329 /// <param name="payload">The received payload.</param>
330 /// <param name="status">The status of sent payload.</param>
331 /// <since_tizen> 9 </since_tizen>
332 protected abstract void OnPayloadReceived(Payload payload, PayloadTransferStatus status);
335 /// The callback invoked when the cion server discovered.
337 /// <param name="peerInfo">The peer info of discovered cion server.</param>
338 /// <since_tizen> 9 </since_tizen>
339 protected abstract void OnDiscovered(PeerInfo peerInfo);
342 /// The callback invoked when disconnected with cion client.
344 /// <param name="peerInfo">The peer info of the cion server.</param>
345 /// <since_tizen> 9 </since_tizen>
346 protected abstract void OnDisconnected(PeerInfo peerInfo);
348 #region IDisposable Support
349 private bool disposedValue = false;
352 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
354 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
355 /// <since_tizen> 9 </since_tizen>
356 protected virtual void Dispose(bool disposing)
364 disposedValue = true;
369 /// Releases all resources used by the ClientBase class.
371 /// <since_tizen> 9 </since_tizen>
372 public void Dispose()
375 GC.SuppressFinalize(this);