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 Interop.CionClient.CionClientPayloadAsyncResultCb _payloadAsyncResultCb;
40 private Dictionary<string, TaskCompletionSource<PayloadAsyncResult>> _tcsDictionary = new Dictionary<string, TaskCompletionSource<PayloadAsyncResult>>();
41 private Dictionary<string, Interop.CionClient.CionClientPayloadAsyncResultCb> _payloadAsyncCbDictionary = new Dictionary<string, Interop.CionClient.CionClientPayloadAsyncResultCb>();
44 /// Gets the service name of current cion client.
46 /// <since_tizen> 9 </since_tizen>
47 public string ServiceName { get; }
50 /// Gets peer info of connected cion server.
52 /// <since_tizen> 9 </since_tizen>
53 public PeerInfo PeerInfo
62 /// The constructor of ClientBase class.
64 /// <param name="serviceName">The name of service.</param>
65 /// <remarks>The maximum length of service name is 512.</remarks>
66 /// <exception cref="ArgumentException">Thrown when the given service name is too long.</exception>
67 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
68 /// <since_tizen> 9 </since_tizen>
69 public ClientBase(string serviceName) : this(serviceName, null) { }
72 /// The constructor of ClientBase class.
74 /// <param name="serviceName">The name of service.</param>
75 /// <param name="security">The security configuration.</param>
76 /// <remarks>The maximum length of service name is 512.</remarks>
77 /// <exception cref="ArgumentException">Thrown when the given service name is too long.</exception>
78 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
79 /// <since_tizen> 9 </since_tizen>
80 public ClientBase(string serviceName, Cion.SecurityInfo security)
82 ServiceName = serviceName;
84 SecuritySafeHandle handle = security?._handle;
85 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientCreate(out _handle, serviceName, handle?.DangerousGetHandle() ?? IntPtr.Zero);
86 if (ret != Interop.Cion.ErrorCode.None)
88 throw CionErrorFactory.GetException(ret, "Failed to create client.");
91 _connectionResultCb = new Interop.CionClient.CionClientConnectionResultCb(
92 (string service, IntPtr peerInfo, IntPtr result, IntPtr userData) =>
94 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
95 if (clone_ret != Interop.Cion.ErrorCode.None)
97 Log.Error(LogTag, string.Format("Failed to clone peer info."));
101 PeerInfo peer = new PeerInfo(clone);
102 ConnectionResult connectionResult = new ConnectionResult(result);
103 if (connectionResult.Status == ConnectionStatus.OK)
108 OnConnectionResult(peer, connectionResult);
110 ret = Interop.CionClient.CionClientAddConnectionResultCb(_handle, _connectionResultCb, IntPtr.Zero);
111 if (ret != Interop.Cion.ErrorCode.None)
114 throw CionErrorFactory.GetException(ret, "Failed to add connection status changed callback.");
117 _payloadRecievedCb = new Interop.CionClient.CionClientPayloadReceivedCb(
118 (string service, IntPtr peerInfo, IntPtr payload, int status, IntPtr userData) =>
120 Payload receivedPayload;
121 Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
124 case Interop.CionPayload.PayloadType.Data:
125 receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
127 case Interop.CionPayload.PayloadType.File:
128 receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
131 Log.Error(LogTag, "Invalid payload type received.");
134 OnPayloadReceived(receivedPayload, (PayloadTransferStatus)status);
136 ret = Interop.CionClient.CionClientAddPayloadReceivedCb(_handle, _payloadRecievedCb, IntPtr.Zero);
137 if (ret != Interop.Cion.ErrorCode.None)
140 throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
143 _disconnectedCb = new Interop.CionClient.CionClientDisconnectedCb(
144 (string service, IntPtr peerInfo, IntPtr userData) =>
146 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
147 if (clone_ret != Interop.Cion.ErrorCode.None)
149 Log.Error(LogTag, string.Format("Failed to clone peer info."));
152 OnDisconnected(new PeerInfo(clone));
154 ret = Interop.CionClient.CionClientAddDisconnectedCb(_handle, _disconnectedCb, IntPtr.Zero);
155 if (ret != Interop.Cion.ErrorCode.None)
158 throw CionErrorFactory.GetException(ret, "Failed to add disconnected callback.");
163 /// Starts discovering cion servers.
165 /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
166 /// <privilege>http://tizen.org/privilege/internet</privilege>
167 /// <privlevel>public</privlevel>
168 /// <exception cref="InvalidOperationException">Thrown when the discovery operation is already in progress.</exception>
169 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
170 /// <since_tizen> 9 </since_tizen>
171 public void TryDiscovery()
173 if (_discoveredCb == null)
175 Interop.CionClient.CionClientServerDiscoveredCb cb = new Interop.CionClient.CionClientServerDiscoveredCb(
176 (string serviceName, IntPtr peerInfo, IntPtr userData) =>
178 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
179 if (clone_ret != Interop.Cion.ErrorCode.None)
181 Log.Error(LogTag, "Failed to clone peer info.");
184 OnDiscovered(new PeerInfo(clone));
189 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientTryDiscovery(_handle, _discoveredCb, IntPtr.Zero);
190 if (ret != Interop.Cion.ErrorCode.None)
192 throw CionErrorFactory.GetException(ret, "Failed to try discovery.");
197 /// Stops discovering.
199 /// <exception cref="InvalidOperationException">Thrown when the client is not discovering.</exception>
200 /// <since_tizen> 9 </since_tizen>
201 public void StopDiscovery()
203 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientStopDiscovery(_handle);
204 if (ret != Interop.Cion.ErrorCode.None)
206 throw CionErrorFactory.GetException(ret, "Failed to stop discovery.");
211 /// Connects with the cion server.
213 /// <param name="peer">The peer to connect.</param>
214 /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
215 /// <privilege>http://tizen.org/privilege/internet</privilege>
216 /// <privlevel>public</privlevel>
217 /// <exception cref="InvalidOperationException">Thrown when the client cannot connect to server.</exception>
218 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
219 /// <since_tizen> 9 </since_tizen>
220 public void Connect(PeerInfo peer)
222 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientConnect(_handle, peer?._handle);
223 if (ret != Interop.Cion.ErrorCode.None)
225 throw CionErrorFactory.GetException(ret, "Failed to connect.");
230 /// Disconnects from the cion server.
232 /// <since_tizen> 9 </since_tizen>
233 public void Disconnect()
235 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientDisconnect(_handle);
236 if (ret != Interop.Cion.ErrorCode.None)
238 Log.Error(LogTag, string.Format("Failed to disconnect: {0}", ret));
244 /// Sends data synchronously to the connected cion server.
246 /// <param name="data">The data to send.</param>
247 /// <param name="timeout">The timeout of sending operation.</param>
248 /// <exception cref="ArgumentException">Thrown when the given data is invalid.</exception>
249 /// <exception cref="InvalidOperationException">Thrown when there is no connected cion server or failed to receive reply.</exception>
250 /// <exception cref="TimeoutException">Thrown when a timeout occurred.</exception>
251 /// <since_tizen> 9 </since_tizen>
252 public byte[] SendData(byte[] data, int timeout)
254 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientSendData(_handle, data, data?.Length ?? -1, timeout, out IntPtr returnDataPtr, out int returnDataSize);
255 if (ret != Interop.Cion.ErrorCode.None)
257 throw CionErrorFactory.GetException(ret, "Failed to send data.");
259 byte[] returnData = new byte[returnDataSize];
260 Marshal.Copy(returnDataPtr, returnData, 0, returnDataSize);
261 Log.Info(LogTag, string.Format("Returned data size: {0}", returnDataSize));
267 /// Sends payload asynchronously to the connected cion server.
269 /// <param name="payload">The payload to send.</param>
270 /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
271 /// <exception cref="InvalidOperationException">Thrown when there is no connected cion server or failed to send payload.</exception>
272 /// <since_tizen> 9 </since_tizen>
273 public Task<PayloadAsyncResult> SendPayloadAsync(Payload payload)
275 if (payload == null || payload.Id.Length == 0)
277 throw new ArgumentException("Payload is invalid.");
280 if (_tcsDictionary.ContainsKey(payload.Id))
282 throw new InvalidOperationException("Payload is already sent.");
285 TaskCompletionSource<PayloadAsyncResult> tcs = new TaskCompletionSource<PayloadAsyncResult>();
286 _tcsDictionary[payload.Id] = tcs;
288 Interop.CionClient.CionClientPayloadAsyncResultCb cb = new Interop.CionClient.CionClientPayloadAsyncResultCb(
289 (IntPtr result, IntPtr userData) =>
291 TaskCompletionSource<PayloadAsyncResult> tcsToReturn = _tcsDictionary[payload.Id];
292 PayloadAsyncResult resultPayload = null;
295 resultPayload = PayloadAsyncResult.CreateFromHandle(result);
299 Log.Error(LogTag, string.Format("Failed to create PayloadAsyncResult from result handle: {0}.", e.Message));
300 tcsToReturn.SetException(e);
303 tcsToReturn.SetResult(resultPayload);
304 _payloadAsyncCbDictionary.Remove(resultPayload.PayloadId);
305 _tcsDictionary.Remove(resultPayload.PayloadId);
308 Interop.Cion.ErrorCode ret = Interop.CionClient.CionClientSendPayloadAsync(_handle, payload?._handle, _payloadAsyncResultCb, IntPtr.Zero);
309 if (ret != Interop.Cion.ErrorCode.None)
311 throw CionErrorFactory.GetException(ret, "Failed to send payload.");
314 _payloadAsyncCbDictionary[payload?.Id] = cb;
320 /// The result callback of connection request.
322 /// <param name="peerInfo">The peer info of the cion server.</param>
323 /// <param name="result">The result of the connection.</param>
324 /// <since_tizen> 9 </since_tizen>
325 protected abstract void OnConnectionResult(PeerInfo peerInfo, ConnectionResult result);
328 /// The callback invoked when received payload.
330 /// <param name="payload">The received payload.</param>
331 /// <param name="status">The status of sent payload.</param>
332 /// <since_tizen> 9 </since_tizen>
333 protected abstract void OnPayloadReceived(Payload payload, PayloadTransferStatus status);
336 /// The callback invoked when the cion server discovered.
338 /// <param name="peerInfo">The peer info of discovered cion server.</param>
339 /// <since_tizen> 9 </since_tizen>
340 protected abstract void OnDiscovered(PeerInfo peerInfo);
343 /// The callback invoked when disconnected with cion client.
345 /// <param name="peerInfo">The peer info of the cion server.</param>
346 /// <since_tizen> 9 </since_tizen>
347 protected abstract void OnDisconnected(PeerInfo peerInfo);
349 #region IDisposable Support
350 private bool disposedValue = false;
353 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
355 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
356 /// <since_tizen> 9 </since_tizen>
357 protected virtual void Dispose(bool disposing)
365 disposedValue = true;
370 /// Releases all resources used by the ClientBase class.
372 /// <since_tizen> 9 </since_tizen>
373 public void Dispose()
376 GC.SuppressFinalize(this);