Update according to design review
[platform/core/csapi/tizenfx.git] / src / Tizen.Network.IoTConnectivity / Tizen.Network.IoTConnectivity / IoTConnectivityClientManager.cs
1  /*
2  * Copyright (c) 2016 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.Runtime.InteropServices;
20
21 namespace Tizen.Network.IoTConnectivity
22 {
23     /// <summary>
24     /// IoT connectivity client manager consists of client side APIs.
25     /// </summary>
26     public static class IoTConnectivityClientManager
27     {
28         /// <summary>
29         /// The IP Address for multicast
30         /// </summary>
31         public const string MulticastAddress = null;
32
33         private static int s_presenceListenerId = 1;
34         private static Dictionary<IntPtr, Interop.IoTConnectivity.Client.Presence.PresenceCallback> s_presenceCallbacksMap = new Dictionary<IntPtr, Interop.IoTConnectivity.Client.Presence.PresenceCallback>();
35         private static Dictionary<IntPtr, IntPtr> s_presenceHandlesMap = new Dictionary<IntPtr, IntPtr>();
36
37         private static int s_requestId = 1;
38         private static Dictionary<IntPtr, Interop.IoTConnectivity.Client.ResourceFinder.FoundResourceCallback> s_resourceFoundCallbacksMap = new Dictionary<IntPtr, Interop.IoTConnectivity.Client.ResourceFinder.FoundResourceCallback>();
39         private static Dictionary<IntPtr, Interop.IoTConnectivity.Client.DeviceInformation.DeviceInformationCallback> s_deviceInformationCallbacksMap = new Dictionary<IntPtr, Interop.IoTConnectivity.Client.DeviceInformation.DeviceInformationCallback>();
40         private static Dictionary<IntPtr, Interop.IoTConnectivity.Client.PlatformInformation.PlatformInformationCallback> s_platformInformationCallbacksMap = new Dictionary<IntPtr, Interop.IoTConnectivity.Client.PlatformInformation.PlatformInformationCallback>();
41
42         /// <summary>
43         /// PresenceReceived event. This event is occurred when server starts sending presence of a resource.
44         /// </summary>
45         public static event EventHandler<PresenceReceivedEventArgs> PresenceReceived;
46
47         /// <summary>
48         /// ResourceFound event. This event is occurred when a resource is found from the remote server
49         /// after sending request using API StartFindingResource().
50         /// </summary>
51         public static event EventHandler<ResourceFoundEventArgs> ResourceFound;
52
53         /// <summary>
54         /// PlatformInformationFound event. This event is occurred when platform information is found
55         /// after sending request using API StartFindingPlatformInformation().
56         /// </summary>
57         public static event EventHandler<PlatformInformationFoundEventArgs> PlatformInformationFound;
58
59         /// <summary>
60         /// DeviceInformationFound event. This event is occurred when device information is found
61         /// after sending request using API StartFindingDeviceInformation().
62         /// </summary>
63         public static event EventHandler<DeviceInformationFoundEventArgs> DeviceInformationFound;
64
65         /// <summary>
66         /// FindingError event. This event is occurred when an error is found.
67         /// </summary>
68         public static event EventHandler<FindingErrorOccurredEventArgs> FindingErrorOccurred;
69
70         /// <summary>
71         /// Timeout in seconds
72         /// </summary>
73         /// <remarks>
74         /// Value to be set must be in range from 1 to 3600. Default timeout interval value is 30.\n
75         /// Sets/gets the timeout of StartFindingResource(), StartFindingDeviceInformation(), StartFindingPlatformInformation(),
76         /// RemoteResource.GetAsync(), RemoteResource.PutAsync(), RemoteResource.PostAsync() and RemoteResource.DeleteAsync() APIs.\n
77         /// Setter can throw exception.
78         /// </remarks>
79         /// <pre>
80         /// Initialize() should be called to initialize
81         /// </pre>
82         /// <code>
83         /// IoTConnectivityClientManager.Initialize();
84         /// IoTConnectivityClientManager.TimeOut = 120;
85         /// </code>
86         public static int TimeOut
87         {
88             get
89             {
90                 int timeout;
91                 int ret = Interop.IoTConnectivity.Client.IoTCon.GetTimeout(out timeout);
92                 if (ret != (int)IoTConnectivityError.None)
93                 {
94                     Log.Warn(IoTConnectivityErrorFactory.LogTag, "Failed to get timeout");
95                     return 0;
96                 }
97                 return timeout;
98             }
99             set
100             {
101                 int ret = Interop.IoTConnectivity.Client.IoTCon.SetTimeout(value);
102                 if (ret != (int)IoTConnectivityError.None)
103                 {
104                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to set timeout");
105                     throw IoTConnectivityErrorFactory.GetException(ret);
106                 }
107             }
108         }
109
110         /// <summary>
111         /// Polling interval of IoTConnectivity
112         /// </summary>
113         /// <remarks>
114         /// Sets/Gets the polling inerval(milliseconds) of IoTCon. Default value is 100 milliseconds.
115         /// Value to be set must be in range from 1 to 999. The closer to 0, the faster it operates.
116         /// Setter is invoked immediately for changing the interval.
117         /// If you want the faster operation, we recommend you set 10 milliseconds for polling interval.
118         /// Setter can throw exception.
119         /// </remarks>
120         /// <pre>
121         /// Initialize() should be called to initialize
122         /// </pre>
123         /// <code>
124         /// IoTConnectivityClientManager.Initialize();
125         /// IoTConnectivityClientManager.PollingInterval = 100;
126         /// </code>
127         public static int PollingInterval
128         {
129             get
130             {
131                 int interval;
132                 int ret = Interop.IoTConnectivity.Client.IoTCon.GetPollingInterval(out interval);
133                 if (ret != (int)IoTConnectivityError.None)
134                 {
135                     Log.Warn(IoTConnectivityErrorFactory.LogTag, "Failed to get polling interval");
136                     return 0;
137                 }
138                 return interval;
139             }
140             set
141             {
142                 int ret = Interop.IoTConnectivity.Client.IoTCon.SetPollingInterval(value);
143                 if (ret != (int)IoTConnectivityError.None)
144                 {
145                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to set polling interval");
146                     throw IoTConnectivityErrorFactory.GetException(ret);
147                 }
148             }
149         }
150
151         /// <summary>
152         /// Initializes IoTCon.
153         /// Call this function to start IoTCon.
154         /// </summary>
155         /// <remarks>
156         /// @a filePath point to a file for handling secure virtual resources.
157         /// The file that is CBOR(Concise Binary Object Representation)-format must already exist
158         /// in @a filePath. We recommend to use application-local file for @a filePath.
159         /// </remarks>
160         /// <privilege>
161         /// http://tizen.org/privilege/network.get \n
162         /// http://tizen.org/privilege/internet
163         /// </privilege>
164         /// <param name="filePath">The file path to point to storage for handling secure virtual resources.</param>
165         /// <post>
166         /// You must call Deinitialize() if IoTCon API is no longer needed.
167         /// </post>
168         /// <seealso cref="Deinitialize()"/>
169         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
170         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
171         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access</exception>
172         /// <code>
173         /// string filePath = "../../res/iotcon-test-svr-db-client.dat";
174         /// IoTConnectivityClientManager.Initialize(filePath);
175         /// </code>
176         public static void Initialize(string filePath)
177         {
178             int ret = Interop.IoTConnectivity.Client.IoTCon.Initialize(filePath);
179             if (ret != (int)IoTConnectivityError.None)
180             {
181                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to initialize");
182                 throw IoTConnectivityErrorFactory.GetException(ret);
183             }
184         }
185
186         /// <summary>
187         /// Deinitializes IoTCon.
188         /// </summary>
189         /// <remarks>
190         /// This API must be called if IoTCon API is no longer needed.
191         /// </remarks>
192         /// <pre>
193         /// Initialize() should be called to initialize.
194         /// </pre>
195         /// <seealso cref="Initialize()"/>
196         /// <seealso cref="SecureInitialize()"/>
197         /// <code>
198         /// IoTConnectivityClientManager.Deinitialize();
199         /// </code>
200         public static void Deinitialize()
201         {
202             s_presenceListenerId = 1;
203             s_presenceCallbacksMap.Clear();
204             s_presenceHandlesMap.Clear();
205
206             s_requestId = 1;
207             s_resourceFoundCallbacksMap.Clear();
208             s_deviceInformationCallbacksMap.Clear();
209             s_platformInformationCallbacksMap.Clear();
210
211             PresenceReceived = delegate{};
212             ResourceFound = delegate{};
213             PlatformInformationFound = delegate{};
214             DeviceInformationFound = delegate{};
215             FindingErrorOccurred = delegate{};
216
217             Interop.IoTConnectivity.Client.IoTCon.Deinitialize();
218         }
219
220         /// <summary>
221         /// Invokes a next message from a queue for receiving messages from others, immediately.
222         /// </summary>
223         /// <remarks>
224         /// This API invokes a next message from a queue for receiving messages from others, immediately.
225         /// After calling the API, it continues the polling with existing interval.
226         /// </remarks>
227         /// <pre>
228         /// Initialize() should be called to initialize.
229         /// </pre>
230         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
231         /// <code>
232         /// IoTConnectivityClientManager.InvokePolling();
233         /// </code>
234         public static void InvokePolling()
235         {
236             int ret = Interop.IoTConnectivity.Client.IoTCon.InvokePolling();
237             if (ret != (int)IoTConnectivityError.None)
238             {
239                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to invoke polling");
240                 throw IoTConnectivityErrorFactory.GetException(ret);
241             }
242         }
243
244         /// <summary>
245         /// Starts receiving presence events
246         /// </summary>
247         /// <remarks>
248         /// Sends request to receive presence to an interested server's resource with resourceType.
249         /// If succeeded, <see cref="PresenceReceived"/> event handler will be triggered when the server sends presence.
250         /// A server sends presence events when adds / removes / alters a resource or start / stop presence.\n
251         /// @a hostAddress could be <see cref="MulticastAddress"/> for IPv4 multicast.
252         /// The length of @ resourceType should be less than or equal to 61. The @ resourceType must start with a lowercase alphabetic character, followed by a sequence
253         /// of lowercase alphabetic, numeric, ".", or "-" characters, and contains no white space.
254         /// </remarks>
255         /// <privilege>
256         /// http://tizen.org/privilege/internet
257         /// </privilege>
258         /// <param name="hostAddress">The address or addressable name of the server</param>
259         /// <param name="resourceType">A resource type that a client is interested in</param>
260         /// <returns>PresenceId - An identifier for this request</returns>
261         /// <pre>Initialize() should be called to initialize.</pre>
262         /// <post>
263         /// When the resource receive presence, <see cref="PresenceReceived"/> event handler will be invoked.\n
264         /// You must destroy presence by calling StopReceivingPresence() if presence event is no longer needed.
265         /// </post>
266         /// <seealso cref="IoTConnectivityServerManager.StartSendingPresence()"/>
267         /// <seealso cref="IoTConnectivityServerManager.StopSendingPresence()"/>
268         /// <seealso cref="StopReceivingPresence()"/>
269         /// <seealso cref="PresenceReceived"/>
270         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
271         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
272         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
273         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access</exception>
274         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory</exception>
275         /// <code>
276         /// EventHandler<PresenceReceivedEventArgs> handler = (sender, e) => {
277         ///     Console.Log("PresenceReceived, presence id :" + e.PresenceId);
278         /// }
279         /// EventHandler<FindingErrorOccurredEventArgs> errorHandler = (sender, e) => {
280         ///     Console.Log("Found error :" + e.Error.Message);
281         /// }
282         /// IoTConnectivityClientManager.PresenceReceived += handler;
283         /// IoTConnectivityClientManager.FindingErrorOccurred += errorHandler;
284         /// // Do not forget to remove these event handlers when they are not required any more.
285         /// int id = IoTConnectivityClientManager.StartReceivingPresence(IoTConnectivityClientManager.MulticastAddress, "oic.iot.door");
286         /// </code>
287         public static int StartReceivingPresence(string hostAddress, string resourceType)
288         {
289             Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType connectivityType = Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.Ip;
290
291             if (resourceType != null && !ResourceTypes.IsValid(resourceType))
292             {
293                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Invalid type");
294                 throw new ArgumentException("Invalid type");
295             }
296
297             IntPtr id = IntPtr.Zero;
298             lock (s_presenceCallbacksMap)
299             {
300                 id = (IntPtr)s_presenceListenerId++;
301             }
302             s_presenceCallbacksMap[id] = (IntPtr presence, int result, IntPtr presenceResponseHandle, IntPtr userData) =>
303             {
304                 int presenceId = (int)userData;
305                 if (result == (int)IoTConnectivityError.None)
306                 {
307                     if (presenceResponseHandle != IntPtr.Zero)
308                     {
309                         PresenceReceivedEventArgs e = GetPresenceReceivedEventArgs(presenceId, presenceResponseHandle);
310                         if (e == null)
311                         {
312                             Log.Error(IoTConnectivityErrorFactory.LogTag, "Can't get PresenceReceivedEventArgs");
313                             return;
314                         }
315                         PresenceReceived?.Invoke(null, e);
316                     }
317                     else
318                     {
319                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Handle is null");
320                         return;
321                     }
322                 }
323                 else
324                 {
325                     FindingErrorOccurredEventArgs e = GetFindingErrorOccurredEventArgs(presenceId, result);
326                     FindingErrorOccurred?.Invoke(null, e);
327                 }
328             };
329
330             IntPtr presenceHandle;
331             int errorCode = Interop.IoTConnectivity.Client.Presence.AddPresenceCb(hostAddress, (int)connectivityType, resourceType, s_presenceCallbacksMap[id], id, out presenceHandle);
332             if (errorCode != (int)IoTConnectivityError.None)
333             {
334                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to register presence event handler");
335                 lock (s_presenceCallbacksMap)
336                 {
337                     s_presenceCallbacksMap.Remove(id);
338                 }
339                 throw IoTConnectivityErrorFactory.GetException(errorCode);
340             }
341
342             lock (s_presenceHandlesMap)
343             {
344                 s_presenceHandlesMap[id] = presenceHandle;
345             }
346             return (int)id;
347         }
348
349         /// <summary>
350         /// Stops receiving presence events
351         /// </summary>
352         /// <remarks>
353         /// Sends request to not to receive server's presence any more.
354         /// </remarks>
355         /// <privilege>
356         /// http://tizen.org/privilege/internet
357         /// </privilege>
358         /// <param name="presenceId">The start presence request identifier</param>
359         /// <pre>
360         /// Initialize() should be called to initialize.
361         /// </pre>
362         /// <seealso cref="IoTConnectivityServerManager.StartSendingPresence()"/>
363         /// <seealso cref="IoTConnectivityServerManager.StopSendingPresence()"/>
364         /// <seealso cref="StartReceivingPresence()"/>
365         /// <seealso cref="PresenceReceived"/>
366         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
367         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
368         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
369         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access</exception>
370         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory</exception>
371         /// <code>
372         /// EventHandler<PresenceReceivedEventArgs> handler = (sender, e) => {
373         ///     Console.Log("PresenceReceived, presence id :" + e.PresenceId);
374         /// }
375         /// EventHandler<FindingErrorOccurredEventArgs> errorHandler = (sender, e) => {
376         ///     Console.Log("Found error :" + e.Error.Message);
377         /// }
378         /// IoTConnectivityClientManager.PresenceReceived += handler;
379         /// IoTConnectivityClientManager.FindingErrorOccurred += errorHandler;
380         /// int id = IoTConnectivityClientManager.StartReceivingPresence(IoTConnectivityClientManager.MulticastAddress, "oic.iot.door");
381         /// await Task.Delay(5000); // Do other things here
382         /// // Call StopReceivingPresence() when receiving presence is not required any more
383         /// IoTConnectivityClientManager.PresenceReceived -= handler;
384         /// IoTConnectivityClientManager.FindingErrorOccurred -= errorHandler;
385         /// IoTConnectivityClientManager.StopReceivingPresence(id);
386         /// </code>
387         public static void StopReceivingPresence(int presenceId)
388         {
389             if (s_presenceHandlesMap.ContainsKey((IntPtr)presenceId))
390             {
391                 IntPtr presenceHandle = s_presenceHandlesMap[(IntPtr)presenceId];
392                 int ret = Interop.IoTConnectivity.Client.Presence.RemovePresenceCb(presenceHandle);
393                 if (ret != (int)IoTConnectivityError.None)
394                 {
395                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to deregister presence event handler");
396                     throw IoTConnectivityErrorFactory.GetException(ret);
397                 }
398
399                 lock (s_presenceHandlesMap)
400                 {
401                     s_presenceHandlesMap.Remove((IntPtr)presenceId);
402                 }
403             }
404
405             if (s_presenceCallbacksMap.ContainsKey((IntPtr)presenceId))
406             {
407                 lock (s_presenceCallbacksMap)
408                 {
409                     s_presenceCallbacksMap.Remove((IntPtr)presenceId);
410                 }
411             }
412         }
413
414         /// <summary>
415         /// Starts finding resources.
416         /// </summary>
417         /// <remarks>
418         /// Sends request to find a resource of @a hostAddress server with @a resourceType.
419         /// If succeeded, <see cref="ResourceFound"/> event handler will be triggered with information of the resource.\n
420         /// @a hostAddress could be <see cref="MulticastAddress"/> for IPv4 multicast.
421         /// The length of @a resourceType should be less than or equal to 61. The @ resourceType must start with a lowercase alphabetic character, followed by a sequence
422         /// of lowercase alphabetic, numeric, ".", or "-" characters, and contains no white space.
423         /// </remarks>
424         /// <privilege>
425         /// http://tizen.org/privilege/internet
426         /// </privilege>
427         /// <param name="hostAddress">The address or addressable name of the server. The address includes a protocol like coaps://</param>
428         /// <param name="query">The query specified as a filter for founding resources</param>
429         /// <returns>RequestId - An identifier for this request</returns>
430         /// <pre>Initialize() should be called to initialize.</pre>
431         /// <post>
432         /// When the resource is found, <see cref="ResourceFound"/> event handler will be invoked.
433         /// </post>
434         /// <seealso cref="ResourceFound"/>
435         /// <seealso cref="ResourceFoundEventArgs"/>
436         /// <seealso cref="TimeOut"/>
437         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
438         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
439         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
440         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access</exception>
441         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory</exception>
442         /// <code>
443         /// EventHandler<ResourceFoundEventArgs> handler = (sender, e) => {
444         ///     Console.Log("Found resource at host address :" + e.Resource.HostAddress + ", uri :" + e.Resource.UriPath);
445         /// }
446         /// EventHandler<FindingErrorOccurredEventArgs> errorHandler = (sender, e) => {
447         ///     Console.Log("Found error :" + e.Error.Message);
448         /// }
449         /// IoTConnectivityClientManager.ResourceFound += handler;
450         /// IoTConnectivityClientManager.FindingErrorOccurred += errorHandler;
451         /// ResourceQuery query = new ResourceQuery();
452         /// query.Type = "oic.iot.door";
453         /// // Do not forget to remove these event handlers when they are not required any more.
454         /// int id = IoTConnectivityClientManager.StartFindingResource(null, query);
455         /// </code>
456         public static int StartFindingResource(string hostAddress, ResourceQuery query = null)
457         {
458             Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType connectivityType = Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.Ip;
459
460             IntPtr id = IntPtr.Zero;
461             lock (s_resourceFoundCallbacksMap)
462             {
463                 id = (IntPtr)s_requestId++;
464             }
465             s_resourceFoundCallbacksMap[id] = (IntPtr remoteResourceHandle, int result, IntPtr userData) =>
466             {
467                 if (ResourceFound == null)
468                     return false;
469
470                 int requestId = (int)userData;
471                 if (result == (int)IoTConnectivityError.None)
472                 {
473                     if (remoteResourceHandle != IntPtr.Zero)
474                     {
475                         RemoteResource resource = null;
476                         try
477                         {
478                             resource = new RemoteResource(remoteResourceHandle);
479                         }
480                         catch (Exception exp)
481                         {
482                             Log.Error(IoTConnectivityErrorFactory.LogTag, "Can't clone RemoteResource's handle: " + exp.Message);
483                             return true;
484                         }
485                         ResourceFoundEventArgs e = new ResourceFoundEventArgs()
486                         {
487                             RequestId = requestId,
488                             Resource = resource
489                         };
490                         ResourceFound?.Invoke(null, e);
491                     }
492                     else
493                     {
494                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Handle is null");
495                     }
496                 }
497                 else
498                 {
499                     FindingErrorOccurredEventArgs e = GetFindingErrorOccurredEventArgs(requestId, result);
500                     FindingErrorOccurred?.Invoke(null, e);
501
502                     lock (s_resourceFoundCallbacksMap)
503                     {
504                         s_resourceFoundCallbacksMap.Remove(id);
505                     }
506                 }
507                 return true;
508             };
509             IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
510             int errorCode = Interop.IoTConnectivity.Client.ResourceFinder.AddResourceFoundCb(hostAddress, (int)connectivityType, queryHandle, s_resourceFoundCallbacksMap[id], id);
511             if (errorCode != (int)IoTConnectivityError.None)
512             {
513                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to register resource found event handler");
514                 lock (s_resourceFoundCallbacksMap)
515                 {
516                     s_resourceFoundCallbacksMap.Remove(id);
517                 }
518                 throw IoTConnectivityErrorFactory.GetException(errorCode);
519             }
520             return (int)id;
521         }
522
523         /// <summary>
524         /// Starts finding the device information of remote server.
525         /// </summary>
526         /// <remarks>
527         /// Requests server for device information.
528         /// If succeeded, <see cref="DeviceInformationFound"/> event handler will be triggered with information of the device.\n
529         /// @a hostAddress could be <see cref="MulticastAddress"/> for IPv4 multicast.
530         /// </remarks>
531         /// <privilege>
532         /// http://tizen.org/privilege/internet
533         /// </privilege>
534         /// <param name="hostAddress">The host address of remote server</param>
535         /// <param name="query">The query specified as a filter for founding resources</param>
536         /// <returns>RequestId - An identifier for this request</returns>
537         /// <pre>Initialize() should be called to initialize.</pre>
538         /// <post>
539         /// <see cref="DeviceInformationFound" /> event handler will be invoked.
540         /// </post>
541         /// <seealso cref="IoTConnectivityServerManager.SetDeviceName()"/>
542         /// <seealso cref="DeviceInformationFound"/>
543         /// <seealso cref="DeviceInformationFoundEventArgs"/>
544         /// <seealso cref="TimeOut"/>
545         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
546         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
547         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
548         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access</exception>
549         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory</exception>
550         /// <code>
551         /// EventHandler<DeviceInformationFoundEventArgs> handler = (sender, e) => {
552         ///     Console.Log("Device information found, id : " + e.RequestId + ", name : " + e.Name);
553         /// }
554         /// EventHandler<FindingErrorOccurredEventArgs> errorHandler = (sender, e) => {
555         ///     Console.Log("Found error :" + e.Error.Message);
556         /// }
557         /// IoTConnectivityClientManager.DeviceInformationFound += handler;
558         /// IoTConnectivityClientManager.FindingErrorOccurred += errorHandler;
559         /// // Do not forget to remove these event handlers when they are not required any more.
560         /// int id = IoTConnectivityClientManager.StartFindingDeviceInformation(IoTConnectivityClientManager.MulticastAddress);
561         /// </code>
562         public static int StartFindingDeviceInformation(string hostAddress, ResourceQuery query = null)
563         {
564             Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType connectivityType = Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.Ip;
565
566             IntPtr id = IntPtr.Zero;
567             lock (s_deviceInformationCallbacksMap)
568             {
569                 id = (IntPtr)s_requestId++;
570             }
571             s_deviceInformationCallbacksMap[id] = (IntPtr deviceInfoHandle, int result, IntPtr userData) =>
572             {
573                 if (DeviceInformationFound == null)
574                     return false;
575
576                 int requestId = (int)userData;
577                 if (result == (int)IoTConnectivityError.None)
578                 {
579                     if (deviceInfoHandle != IntPtr.Zero)
580                     {
581                         DeviceInformationFoundEventArgs e = GetDeviceInformationFoundEventArgs(requestId, deviceInfoHandle);
582                         if (e == null)
583                         {
584                             Log.Error(IoTConnectivityErrorFactory.LogTag, "Can't get DeviceInformationFoundEventArgs");
585                             return true;
586                         }
587                         DeviceInformationFound?.Invoke(null, e);
588                     }
589                     else
590                     {
591                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Handle is null");
592                     }
593                 }
594                 else
595                 {
596                     FindingErrorOccurredEventArgs e = GetFindingErrorOccurredEventArgs(requestId, result);
597                     FindingErrorOccurred?.Invoke(null, e);
598
599                     lock (s_deviceInformationCallbacksMap)
600                     {
601                         s_deviceInformationCallbacksMap.Remove(id);
602                     }
603                 }
604                 return true;
605             };
606
607             IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
608             int errorCode = Interop.IoTConnectivity.Client.DeviceInformation.Find(hostAddress, (int)connectivityType, queryHandle, s_deviceInformationCallbacksMap[id], id);
609             if (errorCode != (int)IoTConnectivityError.None)
610             {
611                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get device information");
612                 lock (s_deviceInformationCallbacksMap)
613                 {
614                     s_deviceInformationCallbacksMap.Remove(id);
615                 }
616                 throw IoTConnectivityErrorFactory.GetException(errorCode);
617             }
618
619             return (int)id;
620         }
621
622         /// <summary>
623         /// Starts finding the platform information of remote server.
624         /// </summary>
625         /// <remarks>
626         /// Requests server for platform information.
627         /// If succeeded, <see cref="PlatformInformationFound" /> event handler will be triggered with information of the platform.\n
628         /// @a hostAddress could be <see cref="MulticastAddress"/> for IPv4 multicast.
629         /// </remarks>
630         /// <privilege>
631         /// http://tizen.org/privilege/internet
632         /// </privilege>
633         /// <param name="hostAddress">The host address of remote server</param>
634         /// <param name="query">The query specified as a filter for founding resources</param>
635         /// <returns>RequestId - An identifier for this request</returns>
636         /// <pre>Initialize() should be called to initialize.</pre>
637         /// <post>
638         /// <see cref="PlatformInformationFound" /> event handler will be invoked.
639         /// </post>
640         /// <seealso cref="PlatformInformationFound"/>
641         /// <seealso cref="PlatformInformationFoundEventArgs"/>
642         /// <seealso cref="TimeOut"/>
643         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported</exception>
644         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter</exception>
645         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid</exception>
646         /// <exception cref="UnauthorizedAccessException">Thrown when app does not have privilege to access</exception>
647         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory</exception>
648         /// <code>
649         /// EventHandler<PlatformInformationFoundEventArgs> handler = (sender, e) => {
650         ///     Console.Log("PlatformInformationFound :" + e.RequestId);
651         /// }
652         /// EventHandler<FindingErrorOccurredEventArgs> errorHandler = (sender, e) => {
653         ///     Console.Log("Found error :" + e.Error.Message);
654         /// }
655         /// IoTConnectivityClientManager.PlatformInformationFound += handler;
656         /// IoTConnectivityClientManager.FindingErrorOccurred += errorHandler;
657         /// // Do not forget to remove these event handlers when they are not required any more.
658         /// int id = IoTConnectivityClientManager.StartFindingPlatformInformation(IoTConnectivityClientManager.MulticastAddress);
659         /// </code>
660         public static int StartFindingPlatformInformation(string hostAddress, ResourceQuery query = null)
661         {
662             Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType connectivityType = Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.Ip;
663
664             IntPtr id = IntPtr.Zero;
665             lock (s_platformInformationCallbacksMap)
666             {
667                 id = (IntPtr)s_requestId++;
668             }
669             s_platformInformationCallbacksMap[id] = (IntPtr platformInfoHandle, int result, IntPtr userData) =>
670             {
671                 if (PlatformInformationFound == null)
672                     return false;
673
674                 int requestId = (int)userData;
675                 if (result == (int)IoTConnectivityError.None)
676                 {
677                     if (platformInfoHandle != IntPtr.Zero)
678                     {
679                         PlatformInformationFoundEventArgs e = GetPlatformInformationFoundEventArgs(requestId, platformInfoHandle);
680                         if (e == null)
681                         {
682                             Log.Error(IoTConnectivityErrorFactory.LogTag, "Can't get PlatformInformationFoundEventArgs");
683                             return true; ;
684                         }
685                         PlatformInformationFound?.Invoke(null, e);
686                     }
687                     else
688                     {
689                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Handle is null");
690                     }
691                 }
692                 else
693                 {
694                     FindingErrorOccurredEventArgs e = GetFindingErrorOccurredEventArgs(requestId, result);
695                     FindingErrorOccurred?.Invoke(null, e);
696
697                     lock (s_platformInformationCallbacksMap)
698                     {
699                         s_platformInformationCallbacksMap.Remove(id);
700                     }
701                 }
702                 return true;
703             };
704
705             IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
706             int errorCode = Interop.IoTConnectivity.Client.PlatformInformation.Find(hostAddress, (int)connectivityType, queryHandle, s_platformInformationCallbacksMap[id], id);
707             if (errorCode != (int)IoTConnectivityError.None)
708             {
709                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get platform information");
710                 lock (s_platformInformationCallbacksMap)
711                 {
712                     s_platformInformationCallbacksMap.Remove(id);
713                 }
714                 throw IoTConnectivityErrorFactory.GetException(errorCode);
715             }
716
717             return (int)id;
718         }
719
720         // Private methods
721         private static PresenceReceivedEventArgs GetPresenceReceivedEventArgs(int presenceId, IntPtr presenceResponseHandle)
722         {
723             int trigger;
724             IntPtr host, type;
725
726             int ret = Interop.IoTConnectivity.Client.PresenceResponse.GetHostAddress(presenceResponseHandle, out host);
727             if (ret != (int)IoTConnectivityError.None)
728             {
729                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get host address");
730                 return null;
731             }
732
733             ret = Interop.IoTConnectivity.Client.PresenceResponse.GetResourceType(presenceResponseHandle, out type);
734             if (ret != (int)IoTConnectivityError.None)
735             {
736                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get resource type");
737                 return null;
738             }
739
740             ret = Interop.IoTConnectivity.Client.PresenceResponse.GetTrigger(presenceResponseHandle, out trigger);
741             if (ret != (int)IoTConnectivityError.None)
742             {
743                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get event type");
744                 return null;
745             }
746
747             PresenceReceivedEventArgs e = new PresenceReceivedEventArgs()
748             {
749                 PresenceId = presenceId,
750                 HostAddress = Marshal.PtrToStringAnsi(host),
751                 Type = Marshal.PtrToStringAnsi(type),
752                 EventType = (PresenceEventType)trigger
753             };
754
755             return e;
756         }
757
758         private static DeviceInformationFoundEventArgs GetDeviceInformationFoundEventArgs(int requestId, IntPtr deviceInfoHandle)
759         {
760             IntPtr name, specVersion, deviceId, dataModelVersion;
761
762             int ret = Interop.IoTConnectivity.Client.DeviceInformation.GetProperty(deviceInfoHandle, (int)Interop.IoTConnectivity.Client.DeviceInformation.Property.Name, out name);
763             if (ret != (int)IoTConnectivityError.None)
764             {
765                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get name");
766                 return null;
767             }
768
769             ret = Interop.IoTConnectivity.Client.DeviceInformation.GetProperty(deviceInfoHandle, (int)Interop.IoTConnectivity.Client.DeviceInformation.Property.SpecVersion, out specVersion);
770             if (ret != (int)IoTConnectivityError.None)
771             {
772                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get spec version");
773                 return null;
774             }
775
776             ret = Interop.IoTConnectivity.Client.DeviceInformation.GetProperty(deviceInfoHandle, (int)Interop.IoTConnectivity.Client.DeviceInformation.Property.Id, out deviceId);
777             if (ret != (int)IoTConnectivityError.None)
778             {
779                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get device id");
780                 return null;
781             }
782
783             ret = Interop.IoTConnectivity.Client.DeviceInformation.GetProperty(deviceInfoHandle, (int)Interop.IoTConnectivity.Client.DeviceInformation.Property.DataModelVersion, out dataModelVersion);
784             if (ret != (int)IoTConnectivityError.None)
785             {
786                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get data model version");
787                 return null;
788             }
789
790             DeviceInformationFoundEventArgs e = new DeviceInformationFoundEventArgs()
791             {
792                 RequestId = requestId,
793                 Name = Marshal.PtrToStringAnsi(name),
794                 SpecVersion = Marshal.PtrToStringAnsi(specVersion),
795                 DeviceId = Marshal.PtrToStringAnsi(deviceId),
796                 DataModelVersion = Marshal.PtrToStringAnsi(dataModelVersion)
797             };
798
799             return e;
800         }
801
802         private static PlatformInformationFoundEventArgs GetPlatformInformationFoundEventArgs(int requestId, IntPtr platformInfoHandle)
803         {
804             IntPtr platformId, manufacturerName, manufacturerUrl, modelNumber, dateOfManufacture, platformVersion, osVersion, hardwareVersion, firmwareVersion, supportUrl, systemTime;
805
806             int ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.Id, out platformId);
807             if (ret != (int)IoTConnectivityError.None)
808             {
809                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get platform id");
810                 return null;
811             }
812
813             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.MfgName, out manufacturerName);
814             if (ret != (int)IoTConnectivityError.None)
815             {
816                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get manufacturer name");
817                 return null;
818             }
819
820             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.MfgUrl, out manufacturerUrl);
821             if (ret != (int)IoTConnectivityError.None)
822             {
823                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get manufacturer url");
824                 return null;
825             }
826
827             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.ModelNumber, out modelNumber);
828             if (ret != (int)IoTConnectivityError.None)
829             {
830                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get model number");
831                 return null;
832             }
833
834             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.DateOfMfg, out dateOfManufacture);
835             if (ret != (int)IoTConnectivityError.None)
836             {
837                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get date of manufacture");
838                 return null;
839             }
840
841             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.PlatformVer, out platformVersion);
842             if (ret != (int)IoTConnectivityError.None)
843             {
844                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get platform version");
845                 return null;
846             }
847
848             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.OsVer, out osVersion);
849             if (ret != (int)IoTConnectivityError.None)
850             {
851                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to os version");
852                 return null;
853             }
854
855             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.HardwareVer, out hardwareVersion);
856             if (ret != (int)IoTConnectivityError.None)
857             {
858                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to hardware version");
859                 return null;
860             }
861
862             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.FirmwareVer, out firmwareVersion);
863             if (ret != (int)IoTConnectivityError.None)
864             {
865                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get firmware version");
866                 return null;
867             }
868
869             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.SupportUrl, out supportUrl);
870             if (ret != (int)IoTConnectivityError.None)
871             {
872                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get support url");
873                 return null;
874             }
875
876             ret = Interop.IoTConnectivity.Client.PlatformInformation.GetProperty(platformInfoHandle, (int)Interop.IoTConnectivity.Client.PlatformInformation.Propery.SystemTime, out systemTime);
877             if (ret != (int)IoTConnectivityError.None)
878             {
879                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get system time");
880                 return null;
881             }
882
883             PlatformInformationFoundEventArgs e = new PlatformInformationFoundEventArgs()
884             {
885                 RequestId = requestId,
886                 PlatformId = (platformId != IntPtr.Zero) ? Marshal.PtrToStringAnsi(platformId) : string.Empty,
887                 ManufacturerName = (manufacturerName != IntPtr.Zero) ? Marshal.PtrToStringAnsi(manufacturerName) : string.Empty,
888                 ManufacturerURL = (manufacturerUrl != IntPtr.Zero) ? Marshal.PtrToStringAnsi(manufacturerUrl) : string.Empty,
889                 DateOfManufacture = (dateOfManufacture != IntPtr.Zero) ? Marshal.PtrToStringAnsi(dateOfManufacture) : string.Empty,
890                 ModelNumber = (modelNumber != IntPtr.Zero) ? Marshal.PtrToStringAnsi(modelNumber) : string.Empty,
891                 PlatformVersion = (platformVersion != IntPtr.Zero) ? Marshal.PtrToStringAnsi(platformVersion) : string.Empty,
892                 OsVersion = (osVersion != IntPtr.Zero) ? Marshal.PtrToStringAnsi(osVersion) : string.Empty,
893                 HardwareVersion = (hardwareVersion != IntPtr.Zero) ? Marshal.PtrToStringAnsi(hardwareVersion) : string.Empty,
894                 FirmwareVersion = (firmwareVersion != IntPtr.Zero) ? Marshal.PtrToStringAnsi(firmwareVersion) : string.Empty,
895                 SupportUrl = (supportUrl != IntPtr.Zero) ? Marshal.PtrToStringAnsi(supportUrl) : string.Empty,
896                 SystemTime = (systemTime != IntPtr.Zero) ? Marshal.PtrToStringAnsi(systemTime) : string.Empty
897             };
898
899             return e;
900         }
901
902         private static FindingErrorOccurredEventArgs GetFindingErrorOccurredEventArgs(int requestId, int err)
903         {
904             FindingErrorOccurredEventArgs e = new FindingErrorOccurredEventArgs()
905             {
906                 RequestId = requestId,
907                 Error = IoTConnectivityErrorFactory.GetException(err)
908             };
909             return e;
910         }
911     }
912 }