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 server.
27 /// <since_tizen> 9 </since_tizen>
28 public abstract class ServerBase : IDisposable
30 private readonly string LogTag = "Tizen.Applications.Cion";
32 private string _displayName;
33 private readonly ServerSafeHandle _handle;
34 private Interop.CionServer.CionServerConnectionRequestCb _connectionRequestCb;
35 private Interop.CionServer.CionServerConnectionResultCb _connectionResultCb;
36 private Interop.CionServer.CionServerDataReceivedCb _dataReceivedCb;
37 private Interop.CionServer.CionServerPayloadReceivedCb _payloadRecievedCb;
38 private Interop.CionServer.CionServerDisconnectedCb _disconnectedCb;
39 private Interop.CionServer.CionServerPayloadAsyncResultCb _payloadAsyncResultCb;
40 private Dictionary<Tuple<string, string>, TaskCompletionSource<PayloadAsyncResult>> _tcsDictionary = new Dictionary<Tuple<string, string>, TaskCompletionSource<PayloadAsyncResult>>();
43 /// Gets the service name of current cion server.
45 /// <since_tizen> 9 </since_tizen>
46 public string ServiceName { get; }
49 /// Gets or sets the display name of current cion server.
51 /// <since_tizen> 9 </since_tizen>
52 public string DisplayName
61 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSetDisplayName(_handle, value);
62 if (ret != Interop.Cion.ErrorCode.None)
64 Log.Error(LogTag, string.Format("Failed to set display name: {0}", ret));
74 /// The constructor of ServerBase class.
76 /// <param name="serviceName">The name of service.</param>
77 /// <param name="displayName">The display name of service.</param>
78 /// <remarks>The maximum length of service name is 512.</remarks>
79 /// <exception cref="ArgumentException">Thrown when the given service name is too long.</exception>
80 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
81 /// <since_tizen> 9 </since_tizen>
82 public ServerBase(string serviceName, string displayName) : this(serviceName, displayName, null) { }
85 /// The constructor of ServerBase class.
87 /// <param name="serviceName">The name of service.</param>
88 /// <param name="displayName">The display name of service.</param>
89 /// <param name="security">The security configuration.</param>
90 /// <remarks>The maximum length of service name is 512.</remarks>
91 /// <exception cref="ArgumentException">Thrown when the given service name is too long.</exception>
92 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
93 /// <since_tizen> 9 </since_tizen>
94 public ServerBase(string serviceName, string displayName, Cion.SecurityInfo security)
96 ServiceName = serviceName;
97 _displayName = displayName;
99 Cion.SecuritySafeHandle handle = security?._handle;
100 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerCreate(out _handle, serviceName, displayName, handle?.DangerousGetHandle() ?? IntPtr.Zero);
101 if (ret != Interop.Cion.ErrorCode.None)
103 throw CionErrorFactory.GetException(ret, "Failed to create server handle.");
106 _connectionResultCb = new Interop.CionServer.CionServerConnectionResultCb(
107 (string service, IntPtr peerInfo, IntPtr result, IntPtr userData) =>
109 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
110 if (clone_ret != Interop.Cion.ErrorCode.None)
112 Log.Error(LogTag, "Failed to clone peer info.");
115 OnConnectionResult(new PeerInfo(clone), new ConnectionResult(result));
117 ret = Interop.CionServer.CionServerAddConnectionResultCb(_handle, _connectionResultCb, IntPtr.Zero);
118 if (ret != Interop.Cion.ErrorCode.None)
121 throw CionErrorFactory.GetException(ret, "Failed to add connection status changed callback.");
124 _dataReceivedCb = new Interop.CionServer.CionServerDataReceivedCb(
125 (string service, IntPtr peerInfo, byte[] data, int dataSize, out IntPtr returnData, out int returnDataSize, IntPtr userData) =>
127 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
128 if (clone_ret != Interop.Cion.ErrorCode.None)
130 Log.Error(LogTag, "Failed to clone peer info.");
131 returnData = IntPtr.Zero;
134 byte[] returnDataRaw = OnDataReceived(data, new PeerInfo(clone));
135 returnDataSize = returnDataRaw.Length;
136 returnData = Interop.Cion.Malloc(returnDataSize);
137 Marshal.Copy(returnDataRaw, 0, returnData, returnDataSize);
139 ret = Interop.CionServer.CionServerSetDataReceivedCb(_handle, _dataReceivedCb, IntPtr.Zero);
140 if (ret != Interop.Cion.ErrorCode.None)
143 throw CionErrorFactory.GetException(ret, "Failed to set data received callback.");
146 _payloadRecievedCb = new Interop.CionServer.CionServerPayloadReceivedCb(
147 (string service, IntPtr peerInfo, IntPtr payload, int status, IntPtr userData) =>
149 Payload receivedPayload;
150 Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
153 case Interop.CionPayload.PayloadType.Data:
154 receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
156 case Interop.CionPayload.PayloadType.File:
157 receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
160 Log.Error(LogTag, "Invalid payload type received.");
163 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
164 if (clone_ret != Interop.Cion.ErrorCode.None)
166 Log.Error(LogTag, "Failed to clone peer info.");
169 OnPayloadReceived(receivedPayload, new PeerInfo(clone), (PayloadTransferStatus)status);
171 ret = Interop.CionServer.CionServerAddPayloadReceivedCb(_handle, _payloadRecievedCb, IntPtr.Zero);
172 if (ret != Interop.Cion.ErrorCode.None)
175 throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
178 _disconnectedCb = new Interop.CionServer.CionServerDisconnectedCb(
179 (string service, IntPtr peerInfo, IntPtr userData) =>
181 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
182 if (clone_ret != Interop.Cion.ErrorCode.None)
184 Log.Error(LogTag, string.Format("Failed to clone peer info."));
187 OnDisconnected(new PeerInfo(clone));
189 ret = Interop.CionServer.CionServerAddDisconnectedCb(_handle, _disconnectedCb, IntPtr.Zero);
190 if (ret != Interop.Cion.ErrorCode.None)
193 throw CionErrorFactory.GetException(ret, "Failed to add disconnected callback.");
198 /// Starts server and listens for requests from cion clients.
200 /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
201 /// <privilege>http://tizen.org/privilege/internet</privilege>
202 /// <privlevel>public</privlevel>
203 /// <exception cref="InvalidOperationException">Thrown when the listen operation is already in progress.</exception>
204 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
205 /// <since_tizen> 9 </since_tizen>
208 if (_connectionRequestCb == null)
210 Interop.CionServer.CionServerConnectionRequestCb cb = new Interop.CionServer.CionServerConnectionRequestCb(
211 (serviceName, peerInfo, userData) =>
213 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
214 if (clone_ret != Interop.Cion.ErrorCode.None)
216 Log.Error(LogTag, "Failed to clone peer info");
219 OnConnectionRequest(new PeerInfo(clone));
221 _connectionRequestCb = cb;
224 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerListen(_handle, _connectionRequestCb, IntPtr.Zero);
225 if (ret != Interop.Cion.ErrorCode.None)
227 throw CionErrorFactory.GetException(ret, "Failed to listen server.");
232 /// Stops the listen operation.
234 /// <exception cref="InvalidOperationException">Thrown when the server is not listening.</exception>
235 /// <since_tizen> 9 </since_tizen>
238 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerStop(_handle);
239 if (ret != Interop.Cion.ErrorCode.None)
241 throw CionErrorFactory.GetException(ret, "Failed to stop server.");
246 /// Disconnects with the peer.
248 /// <param name="peerInfo">The peer to disconnect.</param>
249 /// <exception cref="ArgumentException">Thrown when the given peer info is invalid.</exception>
250 /// <since_tizen> 9 </since_tizen>
251 public void Disconnect(PeerInfo peerInfo)
253 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerDisconnect(_handle, peerInfo?._handle);
254 if (ret != Interop.Cion.ErrorCode.None)
256 throw CionErrorFactory.GetException(ret, "Failed to stop server.");
261 /// Sends the payload to a peer asynchronously.
263 /// <param name="payload">The payload to send.</param>
264 /// <param name="peerInfo">The peer to send payload.</param>
265 /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
266 /// <exception cref="InvalidOperationException">Thrown when there is no such connected cion client or failed to send payload.</exception>
267 /// <since_tizen> 9 </since_tizen>
268 public Task<PayloadAsyncResult> SendPayloadAsync(Payload payload, PeerInfo peerInfo)
270 if (payload == null || payload.Id.Length == 0 || peerInfo == null || peerInfo.UUID.Length == 0)
272 throw new ArgumentException("Payload or peerinfo is invalid.");
275 TaskCompletionSource<PayloadAsyncResult> tcs = new TaskCompletionSource<PayloadAsyncResult>();
276 _tcsDictionary[Tuple.Create(payload.Id, peerInfo.UUID)] = tcs;
278 if (_payloadAsyncResultCb == null)
280 Interop.CionServer.CionServerPayloadAsyncResultCb cb = new Interop.CionServer.CionServerPayloadAsyncResultCb(
281 (IntPtr result, IntPtr userData) =>
283 PayloadAsyncResult resultPayload = null;
286 resultPayload = PayloadAsyncResult.CreateFromHandle(result);
290 Log.Error(LogTag, string.Format("Failed to create PayloadAsyncResult from result handle: {0}.", e.Message));
293 TaskCompletionSource<PayloadAsyncResult> tcsToReturn = _tcsDictionary[Tuple.Create(resultPayload.PayloadId, resultPayload.PeerInfo.UUID)];
294 tcsToReturn.SetResult(resultPayload);
295 _tcsDictionary.Remove(Tuple.Create(resultPayload.PayloadId, resultPayload.PeerInfo.UUID));
297 _payloadAsyncResultCb = cb;
300 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSendPayloadAsync(_handle, peerInfo?._handle, payload?._handle, _payloadAsyncResultCb, IntPtr.Zero);
301 if (ret != Interop.Cion.ErrorCode.None)
303 throw CionErrorFactory.GetException(ret, "Failed to send payload.");
310 /// Sends the payload to all of connected peer asynchronously.
312 /// <param name="payload">The payload to send.</param>
313 /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
314 /// <exception cref="InvalidOperationException">Thrown when failed to send payload.</exception>
315 /// <since_tizen> 9 </since_tizen>
316 public void SendPayloadAsync(Payload payload)
318 var peerList = GetConnectedPeerList();
319 foreach (var peer in peerList)
321 SendPayloadAsync(payload, peer);
326 /// Accepts the connection request from the peer.
328 /// <param name="peerInfo">The peer to accept the connection request.</param>
329 /// <since_tizen> 9 </since_tizen>
330 public void Accept(PeerInfo peerInfo)
332 Interop.CionServer.CionServerAccept(_handle, peerInfo?._handle);
336 /// Rejects the connection request from the peer.
338 /// <param name="peerInfo">The peer to reject the connection request.</param>
339 /// <param name="reason">The reason why reject the connection request.</param>
340 /// <since_tizen> 9 </since_tizen>
341 public void Reject(PeerInfo peerInfo, string reason)
343 Interop.CionServer.CionServerReject(_handle, peerInfo?._handle, reason);
347 /// Gets connected peers.
349 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
350 /// <since_tizen> 9 </since_tizen>
351 public IEnumerable<PeerInfo> GetConnectedPeerList()
353 List<PeerInfo> peerInfoList = new List<PeerInfo>();
354 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerForeachConnectedPeerInfo(_handle, (peer, userData) =>
356 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peer, out PeerInfoSafeHandle clone);
357 if (clone_ret != Interop.Cion.ErrorCode.None)
359 Log.Error(LogTag, "Failed to clone peer info.");
362 peerInfoList.Add(new PeerInfo(clone));
369 /// Sets ondemand launch enabled flag.
371 /// <param name="enable">Whether ondemand launch is enabled or not.</param>
372 /// <privilege>http://tizen.org/privilege/d2d.remotelaunch</privilege>
373 /// <privlevel>public</privlevel>
374 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
375 /// <since_tizen> 9 </since_tizen>
376 public void SetOndemandLaunchEnabled(bool enable)
378 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSetOnDemandLaunchEnabled(_handle, enable);
379 if (ret != Interop.Cion.ErrorCode.None)
381 throw CionErrorFactory.GetException(ret, "Failed to set ondemand launch enable");
386 /// The result callback of connection request.
388 /// <param name="peerInfo">The peer info of the cion client.</param>
389 /// <param name="result">The result of the connection.</param>
390 /// <since_tizen> 9 </since_tizen>
391 protected abstract void OnConnectionResult(PeerInfo peerInfo, ConnectionResult result);
394 /// The callback invoked when received data.
396 /// <param name="data">The received data.</param>
397 /// <param name="peerInfo">The peer info of the cion client.</param>
398 /// <since_tizen> 9 </since_tizen>
399 protected abstract byte[] OnDataReceived(byte[] data, PeerInfo peerInfo);
402 /// The callback invoked when received payload.
404 /// <param name="data">The received data.</param>
405 /// <param name="peerInfo">The peer info of the cion client.</param>
406 /// <param name="status">The status of payload transfer.</param>
407 /// <since_tizen> 9 </since_tizen>
408 protected abstract void OnPayloadReceived(Payload data, PeerInfo peerInfo, PayloadTransferStatus status);
411 /// The callback invoked when connection requested from the cion client.
413 /// <param name="peerInfo">The peer info of the cion client.</param>
414 /// <since_tizen> 9 </since_tizen>
415 protected abstract void OnConnectionRequest(PeerInfo peerInfo);
418 /// The callback invoked when disconnected with cion client.
420 /// <param name="peerInfo">The peer info of the cion client.</param>
421 /// <since_tizen> 9 </since_tizen>
422 protected abstract void OnDisconnected(PeerInfo peerInfo);
424 #region IDisposable Support
425 private bool disposedValue = false;
428 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
430 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
431 /// <since_tizen> 9 </since_tizen>
432 protected virtual void Dispose(bool disposing)
440 disposedValue = true;
445 /// Releases all resources used by the ServerBase class.
447 /// <since_tizen> 9 </since_tizen>
448 public void Dispose()
451 GC.SuppressFinalize(this);