[Applications.Cion] Fix namespace, ServerBase.DisplayName property, docfx (#3553)
[platform/core/csapi/tizenfx.git] / src / Tizen.Applications.Cion / Tizen.Applications.Cion / ServerBase.cs
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 using System;
18 using System.Collections.Generic;
19 using System.Threading.Tasks;
20
21 namespace Tizen.Applications.Cion
22 {
23     /// <summary>
24     /// An abstract class to represent cion server.
25     /// </summary>
26     /// <since_tizen> 9 </since_tizen>
27     public abstract class ServerBase : IDisposable
28     {
29         private readonly string LogTag = "Tizen.Applications.Cion";
30
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>>();
40
41         /// <summary>
42         /// Gets the service name of current cion server.
43         /// </summary>
44         /// <since_tizen> 9 </since_tizen>
45         public string ServiceName { get; }
46
47         /// <summary>
48         /// Gets or sets the display name of current cion server.
49         /// </summary>
50         /// <since_tizen> 9 </since_tizen>
51         public string DisplayName
52         {
53             get
54             {
55                 return _displayName;
56             }
57
58             set
59             {
60                 Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSetDisplayName(_handle, value);
61                 if (ret != Interop.Cion.ErrorCode.None)
62                 {
63                     Log.Error(LogTag, string.Format("Failed to set display name: {0}", ret));
64                 }
65                 else
66                 {
67                     _displayName = value;
68                 }
69             }
70         }
71
72         /// <summary>
73         /// The constructor of ServerBase class.
74         /// </summary>
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) { }
82
83         /// <summary>
84         /// The constructor of ServerBase class.
85         /// </summary>
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)
94         {
95             ServiceName = serviceName;
96             _displayName = displayName;
97
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)
101             {
102                 throw CionErrorFactory.GetException(ret, "Failed to create server handle.");
103             }
104
105             _connectionResultCb = new Interop.CionServer.CionServerConnectionResultCb(
106                 (string service, IntPtr peerInfo, IntPtr result, IntPtr userData) =>
107                 {
108                     Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
109                     if (clone_ret != Interop.Cion.ErrorCode.None)
110                     {
111                         Log.Error(LogTag, "Failed to clone peer info.");
112                         return;
113                     }
114                     OnConnectionResult(new PeerInfo(clone), new ConnectionResult(result));
115                 });
116             ret = Interop.CionServer.CionServerAddConnectionResultCb(_handle, _connectionResultCb, IntPtr.Zero);
117             if (ret != Interop.Cion.ErrorCode.None)
118             {
119                 _handle.Dispose();
120                 throw CionErrorFactory.GetException(ret, "Failed to add connection status changed callback.");
121             }
122
123             _dataReceivedCb = new Interop.CionServer.CionServerDataReceivedCb(
124                 (string service, IntPtr peerInfo, byte[] data, int dataSize, out byte[] returnData, out int returnDataSize, IntPtr userData) =>
125                 {
126                     Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
127                     if (clone_ret != Interop.Cion.ErrorCode.None)
128                     {
129                         Log.Error(LogTag, "Failed to clone peer info.");
130                         returnData = null;
131                         returnDataSize = -1;
132                     }
133                     returnData = OnDataReceived(data, new PeerInfo(clone));
134                     returnDataSize = returnData.Length;
135                 });
136             ret = Interop.CionServer.CionServerSetDataReceivedCb(_handle, _dataReceivedCb, IntPtr.Zero);
137             if (ret != Interop.Cion.ErrorCode.None)
138             {
139                 _handle.Dispose();
140                 throw CionErrorFactory.GetException(ret, "Failed to set data received callback.");
141             }     
142
143             _payloadRecievedCb = new Interop.CionServer.CionServerPayloadReceivedCb(
144                 (string service, IntPtr peerInfo, IntPtr payload, int status, IntPtr userData) =>
145                 {
146                     Payload receivedPayload;
147                     Interop.CionPayload.CionPayloadGetType(payload, out Interop.CionPayload.PayloadType type);
148                     switch (type)
149                     {
150                         case Interop.CionPayload.PayloadType.Data:
151                             receivedPayload = new DataPayload(new PayloadSafeHandle(payload, false));
152                             break;
153                         case Interop.CionPayload.PayloadType.File:
154                             receivedPayload = new FilePayload(new PayloadSafeHandle(payload, false));
155                             break;
156                         default:
157                             Log.Error(LogTag, "Invalid payload type received.");
158                             return;
159                     }
160                     Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
161                     if (clone_ret != Interop.Cion.ErrorCode.None)
162                     {
163                         Log.Error(LogTag, "Failed to clone peer info.");
164                         return;
165                     }
166                     OnPayloadReceived(receivedPayload, new PeerInfo(clone), (PayloadTransferStatus)status);
167                 });
168             ret = Interop.CionServer.CionServerAddPayloadReceivedCb(_handle, _payloadRecievedCb, IntPtr.Zero);
169             if (ret != Interop.Cion.ErrorCode.None)
170             {
171                 _handle.Dispose();
172                 throw CionErrorFactory.GetException(ret, "Failed to add payload received callback.");
173             }
174
175             _disconnectedCb = new Interop.CionServer.CionServerDisconnectedCb(
176                 (string service, IntPtr peerInfo, IntPtr userData) =>
177                 {
178                     Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
179                     if (clone_ret != Interop.Cion.ErrorCode.None)
180                     {
181                         Log.Error(LogTag, string.Format("Failed to clone peer info."));
182                         return;
183                     }
184                     OnDisconnected(new PeerInfo(clone));
185                 });
186             ret = Interop.CionServer.CionServerAddDisconnectedCb(_handle, _disconnectedCb, IntPtr.Zero);
187             if (ret != Interop.Cion.ErrorCode.None)
188             {
189                 _handle.Dispose();
190                 throw CionErrorFactory.GetException(ret, "Failed to add disconnected callback.");
191             }
192         }
193
194         /// <summary>
195         /// Starts server and listens for requests from cion clients.
196         /// </summary>
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>
203         public void Listen()
204         {
205             if (_connectionRequestCb == null)
206             {
207                 Interop.CionServer.CionServerConnectionRequestCb cb = new Interop.CionServer.CionServerConnectionRequestCb(
208                     (serviceName, peerInfo, userData) =>
209                     {
210                         Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peerInfo, out PeerInfoSafeHandle clone);
211                         if (clone_ret != Interop.Cion.ErrorCode.None)
212                         {
213                             Log.Error(LogTag, "Failed to clone peer info");
214                             return;
215                         }
216                         OnConnectionRequest(new PeerInfo(clone));
217                     });
218                 _connectionRequestCb = cb;
219             }
220
221             Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerListen(_handle, _connectionRequestCb, IntPtr.Zero);
222             if (ret != Interop.Cion.ErrorCode.None)
223             {
224                 throw CionErrorFactory.GetException(ret, "Failed to listen server.");
225             }
226         }
227
228         /// <summary>
229         /// Stops the listen operation.
230         /// </summary>
231         /// <exception cref="InvalidOperationException">Thrown when the server is not listening.</exception>
232         /// <since_tizen> 9 </since_tizen>
233         public void Stop()
234         {
235             Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerStop(_handle);
236             if (ret != Interop.Cion.ErrorCode.None)
237             {
238                 throw CionErrorFactory.GetException(ret, "Failed to stop server.");
239             }
240         }
241
242         /// <summary>
243         /// Disconnects with the peer.
244         /// </summary>
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)
249         {
250             Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerDisconnect(_handle, peerInfo?._handle);
251             if (ret != Interop.Cion.ErrorCode.None)
252             {
253                 throw CionErrorFactory.GetException(ret, "Failed to stop server.");
254             }
255         }
256
257         /// <summary>
258         /// Sends the payload to a peer asynchronously.
259         /// </summary>
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)
266         {
267             if (payload == null || payload.Id.Length == 0 || peerInfo == null || peerInfo.UUID.Length == 0)
268             {
269                 throw new ArgumentException("Payload or peerinfo is invalid.");
270             }
271
272             TaskCompletionSource<PayloadAsyncResult> tcs = new TaskCompletionSource<PayloadAsyncResult>();
273             _tcsDictionary[Tuple.Create(payload.Id, peerInfo.UUID)] = tcs;
274
275             if (_payloadAsyncResultCb == null)
276             {
277                 Interop.CionServer.CionServerPayloadAsyncResultCb cb = new Interop.CionServer.CionServerPayloadAsyncResultCb(
278                     (IntPtr result, IntPtr userData) =>
279                     {
280                         PayloadAsyncResult resultPayload = null;
281                         try
282                         {
283                             resultPayload = PayloadAsyncResult.CreateFromHandle(result);
284                         }
285                         catch (Exception e)
286                         {
287                             Log.Error(LogTag, string.Format("Failed to create PayloadAsyncResult from result handle: {0}.", e.Message));
288                             return;
289                         }
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));
293                     });
294                 _payloadAsyncResultCb = cb;
295             }
296
297             Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSendPayloadAsync(_handle, peerInfo?._handle, payload?._handle, _payloadAsyncResultCb, IntPtr.Zero);
298             if (ret != Interop.Cion.ErrorCode.None)
299             {
300                 throw CionErrorFactory.GetException(ret, "Failed to send payload.");
301             }
302
303             return tcs.Task;
304         }
305
306         /// <summary>
307         /// Sends the payload to all of connected peer asynchronously.
308         /// </summary>
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)
314         {
315             var peerList = GetConnectedPeerList();
316             foreach (var peer in peerList)
317             {
318                 SendPayloadAsync(payload, peer);
319             }
320         }
321
322         /// <summary>
323         /// Accepts the connection request from the peer.
324         /// </summary>
325         /// <param name="peerInfo">The peer to accept the connection request.</param>
326         /// <since_tizen> 9 </since_tizen>
327         public void Accept(PeerInfo peerInfo)
328         {
329             Interop.CionServer.CionServerAccept(_handle, peerInfo?._handle);
330         }
331
332         /// <summary>
333         /// Rejects the connection request from the peer.
334         /// </summary>
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)
339         {
340             Interop.CionServer.CionServerReject(_handle, peerInfo?._handle, reason);
341         }
342
343         /// <summary>
344         /// Gets connected peers.
345         /// </summary>
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()
349         {
350             List<PeerInfo> peerInfoList = new List<PeerInfo>();
351             Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerForeachConnectedPeerInfo(_handle, (peer, userData) =>
352             {
353                 Interop.Cion.ErrorCode clone_ret = Interop.CionPeerInfo.CionPeerInfoClone(peer, out PeerInfoSafeHandle clone);
354                 if (clone_ret != Interop.Cion.ErrorCode.None)
355                 {
356                     Log.Error(LogTag, "Failed to clone peer info.");
357                     return false;
358                 }
359                 peerInfoList.Add(new PeerInfo(clone));
360                 return true;
361             }, IntPtr.Zero);
362             return peerInfoList;
363         }
364
365         /// <summary>
366         /// Sets ondemand launch enabled flag.
367         /// </summary>
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)
374         {
375             Interop.Cion.ErrorCode ret = Interop.CionServer.CionServerSetOnDemandLaunchEnabled(_handle, enable);
376             if (ret != Interop.Cion.ErrorCode.None)
377             {
378                 throw CionErrorFactory.GetException(ret, "Failed to set ondemand launch enable");
379             }
380         }
381
382         /// <summary>
383         /// The result callback of connection request.
384         /// </summary>
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);
389
390         /// <summary>
391         /// The callback invoked when received data.
392         /// </summary>
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);
397
398         /// <summary>
399         /// The callback invoked when received payload.
400         /// </summary>
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);
406
407         /// <summary>
408         /// The callback invoked when connection requested from the cion client.
409         /// </summary>
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);
413
414         /// <summary>
415         /// The callback invoked when disconnected with cion client.
416         /// </summary>
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);
420
421         #region IDisposable Support
422         private bool disposedValue = false;
423
424         /// <summary>
425         /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
426         /// </summary>
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)
430         {
431             if (!disposedValue)
432             {
433                 if (disposing)
434                 {
435                     _handle.Dispose();
436                 }
437                 disposedValue = true;
438             }
439         }
440
441         /// <summary>
442         /// Releases all resources used by the ServerBase class.
443         /// </summary>
444         /// <since_tizen> 9 </since_tizen>
445         public void Dispose()
446         {
447             Dispose(true);
448             GC.SuppressFinalize(this);
449         }
450         #endregion
451     }
452 }