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, IntPtr 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[] receivedData = new byte[dataSize];
135 Marshal.Copy(data, receivedData, 0, dataSize);
136 byte[] returnDataRaw = OnDataReceived(receivedData, new PeerInfo(clone));
137 returnDataSize = returnDataRaw.Length;
138 returnData = Interop.Cion.Malloc(returnDataSize);
139 Marshal.Copy(returnDataRaw, 0, returnData, returnDataSize);
141 ret = Interop.CionServer.CionServerSetDataReceivedCb(_handle, _dataReceivedCb, IntPtr.Zero);
142 if (ret != Interop.Cion.ErrorCode.None)
145 throw CionErrorFactory.GetException(ret, "Failed to set data received callback.");
148 _payloadRecievedCb = new Interop.CionServer.CionServerPayloadReceivedCb(
149 (string service, IntPtr peerInfo, IntPtr payload, int status, IntPtr userData) =>
151 Payload receivedPayload;
152 Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
155 case Interop.CionPayload.PayloadType.Data:
156 receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
158 case Interop.CionPayload.PayloadType.File:
159 receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
162 Log.Error(LogTag, "Invalid payload type received.");
165 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
166 if (clone_ret != Interop.Cion.ErrorCode.None)
168 Log.Error(LogTag, "Failed to clone peer info.");
171 OnPayloadReceived(receivedPayload, new PeerInfo(clone), (PayloadTransferStatus)status);
173 ret = Interop.CionServer.CionServerAddPayloadReceivedCb(_handle, _payloadRecievedCb, IntPtr.Zero);
174 if (ret != Interop.Cion.ErrorCode.None)
177 throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
180 _disconnectedCb = new Interop.CionServer.CionServerDisconnectedCb(
181 (string service, IntPtr peerInfo, IntPtr userData) =>
183 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
184 if (clone_ret != Interop.Cion.ErrorCode.None)
186 Log.Error(LogTag, string.Format("Failed to clone peer info."));
189 OnDisconnected(new PeerInfo(clone));
191 ret = Interop.CionServer.CionServerAddDisconnectedCb(_handle, _disconnectedCb, IntPtr.Zero);
192 if (ret != Interop.Cion.ErrorCode.None)
195 throw CionErrorFactory.GetException(ret, "Failed to add disconnected callback.");
200 /// Starts server and listens for requests from cion clients.
202 /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
203 /// <privilege>http://tizen.org/privilege/internet</privilege>
204 /// <privlevel>public</privlevel>
205 /// <exception cref="InvalidOperationException">Thrown when the listen operation is already in progress.</exception>
206 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
207 /// <since_tizen> 9 </since_tizen>
210 if (_connectionRequestCb == null)
212 Interop.CionServer.CionServerConnectionRequestCb cb = new Interop.CionServer.CionServerConnectionRequestCb(
213 (serviceName, peerInfo, userData) =>
215 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
216 if (clone_ret != Interop.Cion.ErrorCode.None)
218 Log.Error(LogTag, "Failed to clone peer info");
221 OnConnectionRequest(new PeerInfo(clone));
223 _connectionRequestCb = cb;
226 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerListen(_handle, _connectionRequestCb, IntPtr.Zero);
227 if (ret != Interop.Cion.ErrorCode.None)
229 throw CionErrorFactory.GetException(ret, "Failed to listen server.");
234 /// Stops the listen operation.
236 /// <exception cref="InvalidOperationException">Thrown when the server is not listening.</exception>
237 /// <since_tizen> 9 </since_tizen>
240 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerStop(_handle);
241 if (ret != Interop.Cion.ErrorCode.None)
243 throw CionErrorFactory.GetException(ret, "Failed to stop server.");
248 /// Disconnects with the peer.
250 /// <param name="peerInfo">The peer to disconnect.</param>
251 /// <exception cref="ArgumentException">Thrown when the given peer info is invalid.</exception>
252 /// <since_tizen> 9 </since_tizen>
253 public void Disconnect(PeerInfo peerInfo)
255 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerDisconnect(_handle, peerInfo?._handle);
256 if (ret != Interop.Cion.ErrorCode.None)
258 throw CionErrorFactory.GetException(ret, "Failed to stop server.");
263 /// Sends the payload to a peer asynchronously.
265 /// <param name="payload">The payload to send.</param>
266 /// <param name="peerInfo">The peer to send payload.</param>
267 /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
268 /// <exception cref="InvalidOperationException">Thrown when there is no such connected cion client or failed to send payload.</exception>
269 /// <since_tizen> 9 </since_tizen>
270 public Task<PayloadAsyncResult> SendPayloadAsync(Payload payload, PeerInfo peerInfo)
272 if (payload == null || payload.Id.Length == 0 || peerInfo == null || peerInfo.UUID.Length == 0)
274 throw new ArgumentException("Payload or peerinfo is invalid.");
277 TaskCompletionSource<PayloadAsyncResult> tcs = new TaskCompletionSource<PayloadAsyncResult>();
278 _tcsDictionary[Tuple.Create(payload.Id, peerInfo.UUID)] = tcs;
280 if (_payloadAsyncResultCb == null)
282 Interop.CionServer.CionServerPayloadAsyncResultCb cb = new Interop.CionServer.CionServerPayloadAsyncResultCb(
283 (IntPtr result, IntPtr userData) =>
285 PayloadAsyncResult resultPayload = null;
288 resultPayload = PayloadAsyncResult.CreateFromHandle(result);
292 Log.Error(LogTag, string.Format("Failed to create PayloadAsyncResult from result handle: {0}.", e.Message));
295 TaskCompletionSource<PayloadAsyncResult> tcsToReturn = _tcsDictionary[Tuple.Create(resultPayload.PayloadId, resultPayload.PeerInfo.UUID)];
296 tcsToReturn.SetResult(resultPayload);
297 _tcsDictionary.Remove(Tuple.Create(resultPayload.PayloadId, resultPayload.PeerInfo.UUID));
299 _payloadAsyncResultCb = cb;
302 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSendPayloadAsync(_handle, peerInfo?._handle, payload?._handle, _payloadAsyncResultCb, IntPtr.Zero);
303 if (ret != Interop.Cion.ErrorCode.None)
305 throw CionErrorFactory.GetException(ret, "Failed to send payload.");
312 /// Sends the payload to all of connected peer asynchronously.
314 /// <param name="payload">The payload to send.</param>
315 /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
316 /// <exception cref="InvalidOperationException">Thrown when failed to send payload.</exception>
317 /// <since_tizen> 9 </since_tizen>
318 public void SendPayloadAsync(Payload payload)
320 var peerList = GetConnectedPeerList();
321 foreach (var peer in peerList)
323 SendPayloadAsync(payload, peer);
328 /// Accepts the connection request from the peer.
330 /// <param name="peerInfo">The peer to accept the connection request.</param>
331 /// <since_tizen> 9 </since_tizen>
332 public void Accept(PeerInfo peerInfo)
334 Interop.CionServer.CionServerAccept(_handle, peerInfo?._handle);
338 /// Rejects the connection request from the peer.
340 /// <param name="peerInfo">The peer to reject the connection request.</param>
341 /// <param name="reason">The reason why reject the connection request.</param>
342 /// <since_tizen> 9 </since_tizen>
343 public void Reject(PeerInfo peerInfo, string reason)
345 Interop.CionServer.CionServerReject(_handle, peerInfo?._handle, reason);
349 /// Gets connected peers.
351 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
352 /// <since_tizen> 9 </since_tizen>
353 public IEnumerable<PeerInfo> GetConnectedPeerList()
355 List<PeerInfo> peerInfoList = new List<PeerInfo>();
356 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerForeachConnectedPeerInfo(_handle, (peer, userData) =>
358 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peer, out PeerInfoSafeHandle clone);
359 if (clone_ret != Interop.Cion.ErrorCode.None)
361 Log.Error(LogTag, "Failed to clone peer info.");
364 peerInfoList.Add(new PeerInfo(clone));
371 /// Sets ondemand launch enabled flag.
373 /// <param name="enable">Whether ondemand launch is enabled or not.</param>
374 /// <privilege>http://tizen.org/privilege/d2d.remotelaunch</privilege>
375 /// <privlevel>public</privlevel>
376 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
377 /// <since_tizen> 9 </since_tizen>
378 public void SetOndemandLaunchEnabled(bool enable)
380 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSetOnDemandLaunchEnabled(_handle, enable);
381 if (ret != Interop.Cion.ErrorCode.None)
383 throw CionErrorFactory.GetException(ret, "Failed to set ondemand launch enable");
388 /// The result callback of connection request.
390 /// <param name="peerInfo">The peer info of the cion client.</param>
391 /// <param name="result">The result of the connection.</param>
392 /// <since_tizen> 9 </since_tizen>
393 protected abstract void OnConnectionResult(PeerInfo peerInfo, ConnectionResult result);
396 /// The callback invoked when received data.
398 /// <param name="data">The received data.</param>
399 /// <param name="peerInfo">The peer info of the cion client.</param>
400 /// <since_tizen> 9 </since_tizen>
401 protected abstract byte[] OnDataReceived(byte[] data, PeerInfo peerInfo);
404 /// The callback invoked when received payload.
406 /// <param name="data">The received data.</param>
407 /// <param name="peerInfo">The peer info of the cion client.</param>
408 /// <param name="status">The status of payload transfer.</param>
409 /// <since_tizen> 9 </since_tizen>
410 protected abstract void OnPayloadReceived(Payload data, PeerInfo peerInfo, PayloadTransferStatus status);
413 /// The callback invoked when connection requested from the cion client.
415 /// <param name="peerInfo">The peer info of the cion client.</param>
416 /// <since_tizen> 9 </since_tizen>
417 protected abstract void OnConnectionRequest(PeerInfo peerInfo);
420 /// The callback invoked when disconnected with cion client.
422 /// <param name="peerInfo">The peer info of the cion client.</param>
423 /// <since_tizen> 9 </since_tizen>
424 protected abstract void OnDisconnected(PeerInfo peerInfo);
426 #region IDisposable Support
427 private bool disposedValue = false;
430 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
432 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
433 /// <since_tizen> 9 </since_tizen>
434 protected virtual void Dispose(bool disposing)
442 disposedValue = true;
447 /// Releases all resources used by the ServerBase class.
449 /// <since_tizen> 9 </since_tizen>
450 public void Dispose()
453 GC.SuppressFinalize(this);