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.Threading.Tasks;
21 namespace Tizen.Applications.Cion
24 /// An abstract class to represent cion server.
26 /// <since_tizen> 9 </since_tizen>
27 public abstract class ServerBase : IDisposable
29 private readonly string LogTag = "Tizen.Applications.Cion";
31 private string _displayName;
32 private readonly ServerSafeHandle _handle;
33 private Interop.CionServer.CionServerConnectionRequestCb _connectionRequestCb;
34 private Interop.CionServer.CionServerConnectionResultCb _connectionResultCb;
35 private Interop.CionServer.CionServerDataReceivedCb _dataReceivedCb;
36 private Interop.CionServer.CionServerPayloadReceivedCb _payloadRecievedCb;
37 private Interop.CionServer.CionServerDisconnectedCb _disconnectedCb;
38 private Interop.CionServer.CionServerPayloadAsyncResultCb _payloadAsyncResultCb;
39 private Dictionary<Tuple<string, string>, TaskCompletionSource<PayloadAsyncResult>> _tcsDictionary = new Dictionary<Tuple<string, string>, TaskCompletionSource<PayloadAsyncResult>>();
42 /// Gets the service name of current cion server.
44 /// <since_tizen> 9 </since_tizen>
45 public string ServiceName { get; }
48 /// Gets or sets the display name of current cion server.
50 /// <since_tizen> 9 </since_tizen>
51 public string DisplayName
60 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSetDisplayName(_handle, value);
61 if (ret != Interop.Cion.ErrorCode.None)
63 Log.Error(LogTag, string.Format("Failed to set display name: {0}", ret));
73 /// The constructor of ServerBase class.
75 /// <param name="serviceName">The name of service.</param>
76 /// <param name="displayName">The display name of service.</param>
77 /// <remarks>The maximum length of service name is 512.</remarks>
78 /// <exception cref="ArgumentException">Thrown when the given service name is too long.</exception>
79 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
80 /// <since_tizen> 9 </since_tizen>
81 public ServerBase(string serviceName, string displayName) : this(serviceName, displayName, null) { }
84 /// The constructor of ServerBase class.
86 /// <param name="serviceName">The name of service.</param>
87 /// <param name="displayName">The display name of service.</param>
88 /// <param name="security">The security configuration.</param>
89 /// <remarks>The maximum length of service name is 512.</remarks>
90 /// <exception cref="ArgumentException">Thrown when the given service name is too long.</exception>
91 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
92 /// <since_tizen> 9 </since_tizen>
93 public ServerBase(string serviceName, string displayName, Cion.SecurityInfo security)
95 ServiceName = serviceName;
96 _displayName = displayName;
98 Cion.SecuritySafeHandle handle = security?._handle;
99 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerCreate(out _handle, serviceName, displayName, handle?.DangerousGetHandle() ?? IntPtr.Zero);
100 if (ret != Interop.Cion.ErrorCode.None)
102 throw CionErrorFactory.GetException(ret, "Failed to create server handle.");
105 _connectionResultCb = new Interop.CionServer.CionServerConnectionResultCb(
106 (string service, IntPtr peerInfo, IntPtr result, IntPtr userData) =>
108 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
109 if (clone_ret != Interop.Cion.ErrorCode.None)
111 Log.Error(LogTag, "Failed to clone peer info.");
114 OnConnectionResult(new PeerInfo(clone), new ConnectionResult(result));
116 ret = Interop.CionServer.CionServerAddConnectionResultCb(_handle, _connectionResultCb, IntPtr.Zero);
117 if (ret != Interop.Cion.ErrorCode.None)
120 throw CionErrorFactory.GetException(ret, "Failed to add connection status changed callback.");
123 _dataReceivedCb = new Interop.CionServer.CionServerDataReceivedCb(
124 (string service, IntPtr peerInfo, byte[] data, int dataSize, out byte[] returnData, out int returnDataSize, IntPtr userData) =>
126 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
127 if (clone_ret != Interop.Cion.ErrorCode.None)
129 Log.Error(LogTag, "Failed to clone peer info.");
133 returnData = OnDataReceived(data, new PeerInfo(clone));
134 returnDataSize = returnData.Length;
136 ret = Interop.CionServer.CionServerSetDataReceivedCb(_handle, _dataReceivedCb, IntPtr.Zero);
137 if (ret != Interop.Cion.ErrorCode.None)
140 throw CionErrorFactory.GetException(ret, "Failed to set data received callback.");
143 _payloadRecievedCb = new Interop.CionServer.CionServerPayloadReceivedCb(
144 (string service, IntPtr peerInfo, IntPtr payload, int status, IntPtr userData) =>
146 Payload receivedPayload;
147 Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
150 case Interop.CionPayload.PayloadType.Data:
151 receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
153 case Interop.CionPayload.PayloadType.File:
154 receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
157 Log.Error(LogTag, "Invalid payload type received.");
160 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
161 if (clone_ret != Interop.Cion.ErrorCode.None)
163 Log.Error(LogTag, "Failed to clone peer info.");
166 OnPayloadReceived(receivedPayload, new PeerInfo(clone), (PayloadTransferStatus)status);
168 ret = Interop.CionServer.CionServerAddPayloadReceivedCb(_handle, _payloadRecievedCb, IntPtr.Zero);
169 if (ret != Interop.Cion.ErrorCode.None)
172 throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
175 _disconnectedCb = new Interop.CionServer.CionServerDisconnectedCb(
176 (string service, 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, string.Format("Failed to clone peer info."));
184 OnDisconnected(new PeerInfo(clone));
186 ret = Interop.CionServer.CionServerAddDisconnectedCb(_handle, _disconnectedCb, IntPtr.Zero);
187 if (ret != Interop.Cion.ErrorCode.None)
190 throw CionErrorFactory.GetException(ret, "Failed to add disconnected callback.");
195 /// Starts server and listens for requests from cion clients.
197 /// <privilege>http://tizen.org/privilege/d2d.datasharing</privilege>
198 /// <privilege>http://tizen.org/privilege/internet</privilege>
199 /// <privlevel>public</privlevel>
200 /// <exception cref="InvalidOperationException">Thrown when the listen operation is already in progress.</exception>
201 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
202 /// <since_tizen> 9 </since_tizen>
205 if (_connectionRequestCb == null)
207 Interop.CionServer.CionServerConnectionRequestCb cb = new Interop.CionServer.CionServerConnectionRequestCb(
208 (serviceName, peerInfo, userData) =>
210 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
211 if (clone_ret != Interop.Cion.ErrorCode.None)
213 Log.Error(LogTag, "Failed to clone peer info");
216 OnConnectionRequest(new PeerInfo(clone));
218 _connectionRequestCb = cb;
221 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerListen(_handle, _connectionRequestCb, IntPtr.Zero);
222 if (ret != Interop.Cion.ErrorCode.None)
224 throw CionErrorFactory.GetException(ret, "Failed to listen server.");
229 /// Stops the listen operation.
231 /// <exception cref="InvalidOperationException">Thrown when the server is not listening.</exception>
232 /// <since_tizen> 9 </since_tizen>
235 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerStop(_handle);
236 if (ret != Interop.Cion.ErrorCode.None)
238 throw CionErrorFactory.GetException(ret, "Failed to stop server.");
243 /// Disconnects with the peer.
245 /// <param name="peerInfo">The peer to disconnect.</param>
246 /// <exception cref="ArgumentException">Thrown when the given peer info is invalid.</exception>
247 /// <since_tizen> 9 </since_tizen>
248 public void Disconnect(PeerInfo peerInfo)
250 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerDisconnect(_handle, peerInfo?._handle);
251 if (ret != Interop.Cion.ErrorCode.None)
253 throw CionErrorFactory.GetException(ret, "Failed to stop server.");
258 /// Sends the payload to a peer asynchronously.
260 /// <param name="payload">The payload to send.</param>
261 /// <param name="peerInfo">The peer to send payload.</param>
262 /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
263 /// <exception cref="InvalidOperationException">Thrown when there is no such connected cion client or failed to send payload.</exception>
264 /// <since_tizen> 9 </since_tizen>
265 public Task<PayloadAsyncResult> SendPayloadAsync(Payload payload, PeerInfo peerInfo)
267 if (payload == null || payload.Id.Length == 0 || peerInfo == null || peerInfo.UUID.Length == 0)
269 throw new ArgumentException("Payload or peerinfo is invalid.");
272 TaskCompletionSource<PayloadAsyncResult> tcs = new TaskCompletionSource<PayloadAsyncResult>();
273 _tcsDictionary[Tuple.Create(payload.Id, peerInfo.UUID)] = tcs;
275 if (_payloadAsyncResultCb == null)
277 Interop.CionServer.CionServerPayloadAsyncResultCb cb = new Interop.CionServer.CionServerPayloadAsyncResultCb(
278 (IntPtr result, IntPtr userData) =>
280 PayloadAsyncResult resultPayload = null;
283 resultPayload = PayloadAsyncResult.CreateFromHandle(result);
287 Log.Error(LogTag, string.Format("Failed to create PayloadAsyncResult from result handle: {0}.", e.Message));
290 TaskCompletionSource<PayloadAsyncResult> tcsToReturn = _tcsDictionary[Tuple.Create(resultPayload.PayloadId, resultPayload.PeerInfo.UUID)];
291 tcsToReturn.SetResult(resultPayload);
292 _tcsDictionary.Remove(Tuple.Create(resultPayload.PayloadId, resultPayload.PeerInfo.UUID));
294 _payloadAsyncResultCb = cb;
297 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSendPayloadAsync(_handle, peerInfo?._handle, payload?._handle, _payloadAsyncResultCb, IntPtr.Zero);
298 if (ret != Interop.Cion.ErrorCode.None)
300 throw CionErrorFactory.GetException(ret, "Failed to send payload.");
307 /// Sends the payload to all of connected peer asynchronously.
309 /// <param name="payload">The payload to send.</param>
310 /// <exception cref="ArgumentException">Thrown when the payload is not valid.</exception>
311 /// <exception cref="InvalidOperationException">Thrown when failed to send payload.</exception>
312 /// <since_tizen> 9 </since_tizen>
313 public void SendPayloadAsync(Payload payload)
315 var peerList = GetConnectedPeerList();
316 foreach (var peer in peerList)
318 SendPayloadAsync(payload, peer);
323 /// Accepts the connection request from the peer.
325 /// <param name="peerInfo">The peer to accept the connection request.</param>
326 /// <since_tizen> 9 </since_tizen>
327 public void Accept(PeerInfo peerInfo)
329 Interop.CionServer.CionServerAccept(_handle, peerInfo?._handle);
333 /// Rejects the connection request from the peer.
335 /// <param name="peerInfo">The peer to reject the connection request.</param>
336 /// <param name="reason">The reason why reject the connection request.</param>
337 /// <since_tizen> 9 </since_tizen>
338 public void Reject(PeerInfo peerInfo, string reason)
340 Interop.CionServer.CionServerReject(_handle, peerInfo?._handle, reason);
344 /// Gets connected peers.
346 /// <exception cref="InvalidOperationException">Thrown when there is not enough memory to continue the execution of the method.</exception>
347 /// <since_tizen> 9 </since_tizen>
348 public IEnumerable<PeerInfo> GetConnectedPeerList()
350 List<PeerInfo> peerInfoList = new List<PeerInfo>();
351 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerForeachConnectedPeerInfo(_handle, (peer, userData) =>
353 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peer, out PeerInfoSafeHandle clone);
354 if (clone_ret != Interop.Cion.ErrorCode.None)
356 Log.Error(LogTag, "Failed to clone peer info.");
359 peerInfoList.Add(new PeerInfo(clone));
366 /// Sets ondemand launch enabled flag.
368 /// <param name="enable">Whether ondemand launch is enabled or not.</param>
369 /// <privilege>http://tizen.org/privilege/d2d.remotelaunch</privilege>
370 /// <privlevel>public</privlevel>
371 /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have the privilege to access this method.</exception>
372 /// <since_tizen> 9 </since_tizen>
373 public void SetOndemandLaunchEnabled(bool enable)
375 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSetOnDemandLaunchEnabled(_handle, enable);
376 if (ret != Interop.Cion.ErrorCode.None)
378 throw CionErrorFactory.GetException(ret, "Failed to set ondemand launch enable");
383 /// The result callback of connection request.
385 /// <param name="peerInfo">The peer info of the cion client.</param>
386 /// <param name="result">The result of the connection.</param>
387 /// <since_tizen> 9 </since_tizen>
388 protected abstract void OnConnectionResult(PeerInfo peerInfo, ConnectionResult result);
391 /// The callback invoked when received data.
393 /// <param name="data">The received data.</param>
394 /// <param name="peerInfo">The peer info of the cion client.</param>
395 /// <since_tizen> 9 </since_tizen>
396 protected abstract byte[] OnDataReceived(byte[] data, PeerInfo peerInfo);
399 /// The callback invoked when received payload.
401 /// <param name="data">The received data.</param>
402 /// <param name="peerInfo">The peer info of the cion client.</param>
403 /// <param name="status">The status of payload transfer.</param>
404 /// <since_tizen> 9 </since_tizen>
405 protected abstract void OnPayloadReceived(Payload data, PeerInfo peerInfo, PayloadTransferStatus status);
408 /// The callback invoked when connection requested from the cion client.
410 /// <param name="peerInfo">The peer info of the cion client.</param>
411 /// <since_tizen> 9 </since_tizen>
412 protected abstract void OnConnectionRequest(PeerInfo peerInfo);
415 /// The callback invoked when disconnected with cion client.
417 /// <param name="peerInfo">The peer info of the cion client.</param>
418 /// <since_tizen> 9 </since_tizen>
419 protected abstract void OnDisconnected(PeerInfo peerInfo);
421 #region IDisposable Support
422 private bool disposedValue = false;
425 /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
427 /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
428 /// <since_tizen> 9 </since_tizen>
429 protected virtual void Dispose(bool disposing)
437 disposedValue = true;
442 /// Releases all resources used by the ServerBase class.
444 /// <since_tizen> 9 </since_tizen>
445 public void Dispose()
448 GC.SuppressFinalize(this);