[IoTConnectivity] Fix wrong callback management (#1041)
[platform/core/csapi/tizenfx.git] / src / Tizen.Network.IoTConnectivity / Tizen.Network.IoTConnectivity / RemoteResource.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
18 using System;
19 using System.Collections.Generic;
20 using System.Collections.Concurrent;
21 using System.Net;
22 using System.Runtime.InteropServices;
23 using System.Threading.Tasks;
24
25 namespace Tizen.Network.IoTConnectivity
26 {
27     /// <summary>
28     /// This class represents a remote resource.
29     /// It provides APIs to manage remote resource.
30     /// </summary>
31     /// <since_tizen> 3 </since_tizen>
32     public class RemoteResource : IDisposable
33     {
34         internal const int TimeOutMax = 3600;
35         internal IntPtr _remoteResourceHandle = IntPtr.Zero;
36
37         private bool _disposed = false;
38         private bool _cacheEnabled = false;
39         private ResourceOptions _options;
40
41         private static int _responseCallbackId = 1;
42         private static IDictionary<IntPtr, Interop.IoTConnectivity.Client.RemoteResource.ResponseCallback> _responseCallbacksMap = new ConcurrentDictionary<IntPtr, Interop.IoTConnectivity.Client.RemoteResource.ResponseCallback>();
43
44         private Interop.IoTConnectivity.Client.RemoteResource.CachedRepresentationChangedCallback _cacheUpdatedCallback;
45         private Interop.IoTConnectivity.Client.RemoteResource.StateChangedCallback _stateChangedCallback;
46         private Interop.IoTConnectivity.Client.RemoteResource.ObserveCallback _observeCallback;
47
48         private EventHandler<StateChangedEventArgs> _stateChangedEventHandler;
49
50         /// <summary>
51         /// Creates a remote resource instance.
52         /// </summary>
53         /// <since_tizen> 3 </since_tizen>
54         /// <remarks>
55         /// <para>To use this API, you should provide all the details required to correctly contact and
56         /// observe the object.</para>
57         /// <para>If not, you should discover the resource object manually.</para>
58         /// <para>The <paramref name="policy" /> can contain multiple policies like <c>ResourcePolicy.Discoverable | ResourcePolicy.Observable</c>.</para>
59         /// </remarks>
60         /// <param name="hostAddress">The host address of the resource.</param>
61         /// <param name="uriPath">The URI path of the resource.</param>
62         /// <param name="policy">The policies of the resource.</param>
63         /// <param name="resourceTypes">The resource types of the resource.</param>
64         /// <param name="resourceInterfaces">The resource interfaces of the resource.</param>
65         /// <feature>http://tizen.org/feature/iot.ocf</feature>
66         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported.</exception>
67         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory.</exception>
68         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter.</exception>
69         public RemoteResource(string hostAddress, string uriPath, ResourcePolicy policy, ResourceTypes resourceTypes, ResourceInterfaces resourceInterfaces)
70         {
71             if (hostAddress == null || uriPath == null || resourceTypes == null || resourceInterfaces == null)
72             {
73                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Invalid parameters");
74                 throw new ArgumentException("Invalid parameter");
75             }
76
77             HostAddress = hostAddress;
78             UriPath = uriPath;
79             Policy = policy;
80             Types = new List<string>(resourceTypes);
81             Interfaces = new List<string>(resourceInterfaces);
82             DeviceId = null;
83
84             CreateRemoteResource(resourceTypes._resourceTypeHandle, resourceInterfaces.ResourceInterfacesHandle);
85         }
86
87         internal RemoteResource(IntPtr handleToClone)
88         {
89             int ret = Interop.IoTConnectivity.Client.RemoteResource.Clone(handleToClone, out _remoteResourceHandle);
90             if (ret != (int)IoTConnectivityError.None)
91             {
92                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Faled to clone");
93                 throw IoTConnectivityErrorFactory.GetException(ret);
94             }
95             SetRemoteResource();
96         }
97
98         /// <summary>
99         /// Destructor of the RemoteResource class.
100         /// </summary>
101         ~RemoteResource()
102         {
103             Dispose(false);
104         }
105
106         /// <summary>
107         /// The event is invoked with cached resource attributes.
108         /// </summary>
109         /// <since_tizen> 3 </since_tizen>
110         public event EventHandler<CacheUpdatedEventArgs> CacheUpdated;
111
112         /// <summary>
113         /// Observe an event on the resource sent by the server.
114         /// </summary>
115         /// <since_tizen> 3 </since_tizen>
116         public event EventHandler<ObserverNotifiedEventArgs> ObserverNotified;
117
118         /// <summary>
119         /// The event is called when remote resource's state are changed.
120         /// </summary>
121         /// <since_tizen> 3 </since_tizen>
122         public event EventHandler<StateChangedEventArgs> StateChanged
123         {
124             add
125             {
126                 if (_stateChangedEventHandler == null)
127                 {
128                     RegisterStateChangedEvent();
129                 }
130                 _stateChangedEventHandler += value;
131             }
132             remove
133             {
134                 _stateChangedEventHandler -= value;
135                 if (_stateChangedEventHandler == null)
136                 {
137                     UnregisterStateChangedEvent();
138                 }
139             }
140         }
141
142         /// <summary>
143         /// The host address of the resource.
144         /// </summary>
145         /// <since_tizen> 3 </since_tizen>
146         /// <value>The host address of the resource.</value>
147         public string HostAddress { get; private set; }
148
149         /// <summary>
150         /// The URI path of the resource.
151         /// </summary>
152         /// <since_tizen> 3 </since_tizen>
153         /// <value>The URI path of the resource.</value>
154         public string UriPath { get; private set; }
155
156         /// <summary>
157         /// The resource types of the remote resource.
158         /// </summary>
159         /// <since_tizen> 3 </since_tizen>
160         /// <value>The resource types of the remote resource.</value>
161         public IEnumerable<string> Types { get; private set; }
162
163         /// <summary>
164         /// The interfaces of the resource.
165         /// </summary>
166         /// <since_tizen> 3 </since_tizen>
167         /// <value>The interfaces of the resource.</value>
168         public IEnumerable<string> Interfaces { get; private set; }
169
170         /// <summary>
171         /// The policy of the resource.
172         /// </summary>
173         /// <since_tizen> 3 </since_tizen>
174         /// <value>The policy of the resource.</value>
175         public ResourcePolicy Policy { get; private set; }
176
177         /// <summary>
178         /// The header options of the resource.
179         /// </summary>
180         /// <since_tizen> 3 </since_tizen>
181         /// <value>The header options of the resource.</value>
182         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported.</exception>
183         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter.</exception>
184         public ResourceOptions Options
185         {
186             get
187             {
188                 return _options;
189             }
190             set
191             {
192                 _options = value;
193                 if (value != null)
194                 {
195                     int ret = Interop.IoTConnectivity.Client.RemoteResource.SetOptions(_remoteResourceHandle, value._resourceOptionsHandle);
196                     if (ret != (int)IoTConnectivityError.None)
197                     {
198                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to set options");
199                         throw IoTConnectivityErrorFactory.GetException(ret);
200                     }
201                 }
202             }
203         }
204
205         /// <summary>
206         /// Indicates the CacheEnabled status of the remote resource.
207         /// </summary>
208         /// <since_tizen> 3 </since_tizen>
209         /// <value>
210         /// Client can start caching only when this is set true. Set it to false to stop caching the resource attributes.
211         /// </value>
212         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported.</exception>
213         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter.</exception>
214         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid.</exception>
215         /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have privilege to access.</exception>
216         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory.</exception>
217         public bool CacheEnabled
218         {
219             get
220             {
221                 return _cacheEnabled;
222             }
223             set
224             {
225                 if (_cacheEnabled != value)
226                 {
227                     _cacheEnabled = value;
228                     HandleCachePolicyChanged();
229                 }
230             }
231         }
232
233         /// <summary>
234         /// Time interval of monitoring and caching API.
235         /// </summary>
236         /// <since_tizen> 3 </since_tizen>
237         /// <value>
238         /// Default time interval is 10 seconds.
239         /// Seconds for time interval (must be in range from 1 to 3600).
240         /// </value>
241         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported.</exception>
242         /// <exception cref="ArgumentException">Thrown when there is an invalid parameter.</exception>
243         public int TimeInterval
244         {
245             get
246             {
247                 int interval;
248                 int ret = Interop.IoTConnectivity.Client.RemoteResource.GetTimeInterval(_remoteResourceHandle, out interval);
249                 if (ret != (int)IoTConnectivityError.None)
250                 {
251                     Log.Warn(IoTConnectivityErrorFactory.LogTag, "Failed to get time interval");
252                     return 0;
253                 }
254                 return interval;
255             }
256             set
257             {
258                 int ret = (int)IoTConnectivityError.InvalidParameter;
259                 if (value <= TimeOutMax && value > 0)
260                 {
261                     ret = Interop.IoTConnectivity.Client.RemoteResource.SetTimeInterval(_remoteResourceHandle, value);
262                 }
263                 if (ret != (int)IoTConnectivityError.None)
264                 {
265                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to set time interval");
266                     throw IoTConnectivityErrorFactory.GetException(ret);
267                 }
268             }
269         }
270
271         /// <summary>
272         /// The device ID of the resource.
273         /// </summary>
274         /// <since_tizen> 3 </since_tizen>
275         /// <value>The device ID of the resource.</value>
276         public string DeviceId { get; private set; }
277
278         /// <summary>
279         /// Gets cached representation from the remote resource.
280         /// </summary>
281         /// <since_tizen> 3 </since_tizen>
282         /// <returns>cached representation from the remote resource.</returns>
283         /// <feature>http://tizen.org/feature/iot.ocf</feature>
284         public Representation CachedRepresentation()
285         {
286             IntPtr handle;
287             int ret = Interop.IoTConnectivity.Client.RemoteResource.GetCachedRepresentation(_remoteResourceHandle, out handle);
288             if (ret != (int)IoTConnectivityError.None)
289             {
290                 Log.Warn(IoTConnectivityErrorFactory.LogTag, "Failed to get CachedRepresentation");
291                 return null;
292             }
293
294             Representation representation = new Representation(handle);
295             return representation;
296         }
297
298         /// <summary>
299         /// Starts observing on the resource.
300         /// </summary>
301         /// <since_tizen> 3 </since_tizen>
302         /// <remarks>
303         /// When server sends notification message, <see cref="ObserverNotified"/> will be called.
304         /// </remarks>
305         /// <privilege>http://tizen.org/privilege/internet</privilege>
306         /// <privlevel>public</privlevel>
307         /// <param name="policy">The type to specify how client wants to observe.</param>
308         /// <param name="query">The query to send to server.</param>
309         /// <feature>http://tizen.org/feature/iot.ocf</feature>
310         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported.</exception>
311         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid.</exception>
312         /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have privilege to access.</exception>
313         /// <exception cref="OutOfMemoryException">Thrown when there is not enough memory.</exception>
314         public void StartObserving(ObservePolicy policy, ResourceQuery query = null)
315         {
316             _observeCallback = (IntPtr resource, int err, int sequenceNumber, IntPtr response, IntPtr userData) =>
317             {
318                 int result;
319                 IntPtr representationHandle;
320                 int ret = Interop.IoTConnectivity.Server.Response.GetResult(response, out result);
321                 if (ret != (int)IoTConnectivityError.None)
322                 {
323                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get result");
324                     return;
325                 }
326
327                 ret = Interop.IoTConnectivity.Server.Response.GetRepresentation(response, out representationHandle);
328                 if (ret != (int)IoTConnectivityError.None)
329                 {
330                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get representation");
331                     return;
332                 }
333
334                 Representation repr = null;
335                 try
336                 {
337                     repr = new Representation(representationHandle);
338                 }
339                 catch (Exception exp)
340                 {
341                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to new representation: " + exp.Message);
342                     return;
343                 }
344
345                 ObserverNotifiedEventArgs e = new ObserverNotifiedEventArgs()
346                 {
347                     Representation = repr,
348                     Result = (ResponseCode)result
349                 };
350                 ObserverNotified?.Invoke(this, e);
351             };
352
353             IntPtr queryHandle = IntPtr.Zero;
354             if (query != null)
355             {
356                 queryHandle = query._resourceQueryHandle;
357             }
358
359             int errCode = Interop.IoTConnectivity.Client.RemoteResource.RegisterObserve(_remoteResourceHandle, (int)policy, queryHandle, _observeCallback, IntPtr.Zero);
360             if (errCode != (int)IoTConnectivityError.None)
361             {
362                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to register observe callbacks");
363                 throw IoTConnectivityErrorFactory.GetException(errCode);
364             }
365         }
366
367         /// <summary>
368         /// Stops observing on the resource.
369         /// </summary>
370         /// <since_tizen> 3 </since_tizen>
371         /// <privilege>http://tizen.org/privilege/internet</privilege>
372         /// <privlevel>public</privlevel>
373         /// <feature>http://tizen.org/feature/iot.ocf</feature>
374         /// <exception cref="NotSupportedException">Thrown when the iotcon is not supported.</exception>
375         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid.</exception>
376         /// <exception cref="UnauthorizedAccessException">Thrown when an application does not have privilege to access.</exception>
377         public void StopObserving()
378         {
379             int ret = Interop.IoTConnectivity.Client.RemoteResource.DeregisterObserve(_remoteResourceHandle);
380             if (ret != (int)IoTConnectivityError.None)
381             {
382                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to deregister observe callbacks");
383                 throw IoTConnectivityErrorFactory.GetException(ret);
384             }
385         }
386
387         /// <summary>
388         /// Gets the attributes of a resource asynchronously.
389         /// </summary>
390         /// <since_tizen> 3 </since_tizen>
391         /// <privilege>http://tizen.org/privilege/internet</privilege>
392         /// <privlevel>public</privlevel>
393         /// <param name="query">The ResourceQuery to send to server.</param>
394         /// <returns>Remote response with result and representation.</returns>
395         /// <feature>http://tizen.org/feature/iot.ocf</feature>
396         public async Task<RemoteResponse> GetAsync(ResourceQuery query = null)
397         {
398             TaskCompletionSource<RemoteResponse> tcsRemoteResponse = new TaskCompletionSource<RemoteResponse>();
399
400             IntPtr id = IntPtr.Zero;
401             lock (_responseCallbacksMap)
402             {
403                 id = (IntPtr)_responseCallbackId++;
404             }
405             _responseCallbacksMap[id] = (IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData) =>
406             {
407                 IntPtr responseCallbackId = userData;
408
409                 _responseCallbacksMap.Remove(responseCallbackId);
410
411                 if (responseHandle != IntPtr.Zero)
412                 {
413                     try
414                     {
415                         tcsRemoteResponse.TrySetResult(GetRemoteResponse(responseHandle));
416                     }
417                     catch(Exception exp)
418                     {
419                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
420                         tcsRemoteResponse.TrySetException(exp);
421                     }
422                 }
423                 else
424                 {
425                     tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
426                 }
427             };
428
429             IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
430             int errCode = Interop.IoTConnectivity.Client.RemoteResource.Get(_remoteResourceHandle, queryHandle, _responseCallbacksMap[id], id);
431             if (errCode != (int)IoTConnectivityError.None)
432             {
433                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get resource attributes");
434                 tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException(errCode));
435             }
436             return await tcsRemoteResponse.Task;
437         }
438
439         /// <summary>
440         /// Puts the representation of a resource asynchronously.
441         /// </summary>
442         /// <since_tizen> 3 </since_tizen>
443         /// <privilege>http://tizen.org/privilege/internet</privilege>
444         /// <privlevel>public</privlevel>
445         /// <param name="representation">Resource representation to put.</param>
446         /// <param name="query">The ResourceQuery to send to server.</param>
447         /// <returns>Remote response with result and representation.</returns>
448         /// <feature>http://tizen.org/feature/iot.ocf</feature>
449         public async Task<RemoteResponse> PutAsync(Representation representation, ResourceQuery query = null)
450         {
451             TaskCompletionSource<RemoteResponse> tcsRemoteResponse = new TaskCompletionSource<RemoteResponse>();
452
453             IntPtr id = IntPtr.Zero;
454             lock (_responseCallbacksMap)
455             {
456                 id = (IntPtr)_responseCallbackId++;
457             }
458             _responseCallbacksMap[id] = (IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData) =>
459             {
460                 IntPtr responseCallbackId = userData;
461
462                 _responseCallbacksMap.Remove(responseCallbackId);
463
464                 if (err == (int)(IoTConnectivityError.Iotivity))
465                 {
466                     RemoteResponse response = new RemoteResponse();
467                     response.Result = ResponseCode.Forbidden;
468                     response.Representation = null;
469                     tcsRemoteResponse.TrySetResult(response);
470                 }
471                 else if (responseHandle != IntPtr.Zero)
472                 {
473                     try
474                     {
475                         tcsRemoteResponse.TrySetResult(GetRemoteResponse(responseHandle));
476                     }
477                     catch (Exception exp)
478                     {
479                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
480                         tcsRemoteResponse.TrySetException(exp);
481                     }
482                 }
483                 else
484                 {
485                     tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
486                 }
487             };
488             IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
489             int errCode = Interop.IoTConnectivity.Client.RemoteResource.Put(_remoteResourceHandle, representation._representationHandle, queryHandle, _responseCallbacksMap[id], id);
490             if (errCode != (int)IoTConnectivityError.None)
491             {
492                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to put resource representation");
493                 tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException(errCode));
494             }
495             return await tcsRemoteResponse.Task;
496         }
497
498         /// <summary>
499         /// Posts request on a resource asynchronously.
500         /// </summary>
501         /// <since_tizen> 3 </since_tizen>
502         /// <privilege>http://tizen.org/privilege/internet</privilege>
503         /// <privlevel>public</privlevel>
504         /// <param name="representation">Resource representation of request.</param>
505         /// <param name="query">The ResourceQuery to send to server.</param>
506         /// <returns>Remote response with result and representation.</returns>
507         /// <feature>http://tizen.org/feature/iot.ocf</feature>
508         public async Task<RemoteResponse> PostAsync(Representation representation, ResourceQuery query = null)
509         {
510             TaskCompletionSource<RemoteResponse> tcsRemoteResponse = new TaskCompletionSource<RemoteResponse>();
511
512             IntPtr id = IntPtr.Zero;
513             lock (_responseCallbacksMap)
514             {
515                 id = (IntPtr)_responseCallbackId++;
516             }
517             _responseCallbacksMap[id] = (IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData) =>
518             {
519                 IntPtr responseCallbackId = userData;
520
521                 _responseCallbacksMap.Remove(responseCallbackId);
522
523                 if (responseHandle != IntPtr.Zero)
524                 {
525                     try
526                     {
527                         tcsRemoteResponse.TrySetResult(GetRemoteResponse(responseHandle));
528                     }
529                     catch (Exception exp)
530                     {
531                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
532                         tcsRemoteResponse.TrySetException(exp);
533                     }
534                 }
535                 else
536                 {
537                     tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
538                 }
539             };
540             IntPtr queryHandle = (query == null) ? IntPtr.Zero : query._resourceQueryHandle;
541             int errCode = Interop.IoTConnectivity.Client.RemoteResource.Post(_remoteResourceHandle, representation._representationHandle, queryHandle, _responseCallbacksMap[id], id);
542             if (errCode != (int)IoTConnectivityError.None)
543             {
544                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to post request");
545                 tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException(errCode));
546             }
547             return await tcsRemoteResponse.Task;
548         }
549
550         /// <summary>
551         /// Deletes the resource asynchronously.
552         /// </summary>
553         /// <since_tizen> 3 </since_tizen>
554         /// <privilege>http://tizen.org/privilege/internet</privilege>
555         /// <privlevel>public</privlevel>
556         /// <returns>Remote response with result and representation.</returns>
557         /// <feature>http://tizen.org/feature/iot.ocf</feature>
558         public async Task<RemoteResponse> DeleteAsync()
559         {
560             TaskCompletionSource<RemoteResponse> tcsRemoteResponse = new TaskCompletionSource<RemoteResponse>();
561
562             IntPtr id = IntPtr.Zero;
563             lock (_responseCallbacksMap)
564             {
565                 id = (IntPtr)_responseCallbackId++;
566             }
567             _responseCallbacksMap[id] = (IntPtr resource, int err, int requestType, IntPtr responseHandle, IntPtr userData) =>
568             {
569                 IntPtr responseCallbackId = userData;
570
571                 _responseCallbacksMap.Remove(responseCallbackId);
572
573                 if (err == (int)(IoTConnectivityError.Iotivity))
574                 {
575                     RemoteResponse response = new RemoteResponse();
576                     response.Result = ResponseCode.Forbidden;
577                     response.Representation = null;
578                     tcsRemoteResponse.TrySetResult(response);
579                 }
580                 else if (responseHandle != IntPtr.Zero)
581                 {
582                     try
583                     {
584                         tcsRemoteResponse.TrySetResult(GetRemoteResponse(responseHandle));
585                     }
586                     catch (Exception exp)
587                     {
588                         Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get RemoteResponse: ", exp.Message);
589                         tcsRemoteResponse.TrySetException(exp);
590                     }
591                 }
592                 else
593                 {
594                     tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException((int)IoTConnectivityError.System));
595                 }
596             };
597
598             int errCode = Interop.IoTConnectivity.Client.RemoteResource.Delete(_remoteResourceHandle, _responseCallbacksMap[id], id);
599             if (errCode != (int)IoTConnectivityError.None)
600             {
601                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to delete");
602                 tcsRemoteResponse.TrySetException(IoTConnectivityErrorFactory.GetException(errCode));
603             }
604             return await tcsRemoteResponse.Task;
605         }
606
607         /// <summary>
608         /// Releases any unmanaged resources used by this object.
609         /// </summary>
610         /// <since_tizen> 3 </since_tizen>
611         /// <feature>http://tizen.org/feature/iot.ocf</feature>
612         public void Dispose()
613         {
614             Dispose(true);
615             GC.SuppressFinalize(this);
616         }
617
618         internal static Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType GetConnectivityType(string hostAddress)
619         {
620             Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType type = Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.None;
621
622             Log.Info(IoTConnectivityErrorFactory.LogTag, hostAddress);
623
624             if (hostAddress == IoTConnectivityClientManager.MulticastAddress)
625             {
626                 type = Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.Ipv4;
627             }
628             else
629             {
630                 IPAddress address;
631                 string hostName = hostAddress;
632                 if (hostAddress.Contains(":"))
633                 {
634                     string[] hostParts = hostAddress.Split(':');
635                     if (hostParts.Length == 2)
636                     {
637                         hostName = hostParts[0];
638                     }
639                 }
640                 if (hostAddress.Contains("%"))
641                 {
642                     string[] hostParts = hostAddress.Split('%');
643                     if (hostParts.Length == 2)
644                     {
645                         hostName = hostParts[0];
646                     }
647                 }
648                 if (hostName.Contains("["))
649                 {
650                     string[] hostParts = hostName.Split('[');
651                     if (hostParts.Length == 2)
652                     {
653                         hostName = hostParts[1];
654                     }
655                 }
656                 Log.Info(IoTConnectivityErrorFactory.LogTag, hostName);
657                 if (IPAddress.TryParse(hostName, out address))
658                 {
659                     switch (address.AddressFamily)
660                     {
661                         case System.Net.Sockets.AddressFamily.InterNetwork:
662                             type = Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.Ipv4;
663                             break;
664                         case System.Net.Sockets.AddressFamily.InterNetworkV6:
665                             type = Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.Ipv6;
666                             break;
667                         default:
668                             Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to parse for Ipv4 or Ipv6");
669                             break;
670                     }
671                 }
672                 else
673                 {
674                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to parse hostname " + hostName);
675                 }
676             }
677             return type;
678         }
679
680         /// <summary>
681         /// Releases any unmanaged resources used by this object. Can also dispose any other disposable objects.
682         /// </summary>
683         /// <since_tizen> 3 </since_tizen>
684         /// <param name="disposing">If true, disposes any disposable objects. If false, does not dispose disposable objects.</param>
685         /// <feature>http://tizen.org/feature/iot.ocf</feature>
686         protected virtual void Dispose(bool disposing)
687         {
688             if (_disposed)
689                 return;
690
691             if (disposing)
692             {
693                 // Free managed objects
694             }
695
696             Interop.IoTConnectivity.Client.RemoteResource.Destroy(_remoteResourceHandle);
697             _disposed = true;
698         }
699
700         private void HandleCachePolicyChanged()
701         {
702             if (_cacheEnabled)
703             {
704                 _cacheUpdatedCallback = (IntPtr resource, IntPtr representation, IntPtr userData) =>
705                 {
706                     if (CacheEnabled)
707                     {
708                         Representation repr = null;
709                         try
710                         {
711                             repr = new Representation(representation);
712                         }
713                         catch (Exception exp)
714                         {
715                             Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to new Representation: " + exp.Message);
716                             return;
717                         }
718
719                         CacheUpdatedEventArgs e = new CacheUpdatedEventArgs()
720                         {
721                             Representation = repr
722                         };
723                         CacheUpdated?.Invoke(this, e);
724                     }
725                 };
726
727                 int ret = Interop.IoTConnectivity.Client.RemoteResource.StartCaching(_remoteResourceHandle, _cacheUpdatedCallback, IntPtr.Zero);
728                 if (ret != (int)IoTConnectivityError.None)
729                 {
730                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to add cache updated event handler");
731                     throw IoTConnectivityErrorFactory.GetException(ret);
732                 }
733             }
734             else
735             {
736                 int ret = Interop.IoTConnectivity.Client.RemoteResource.StopCaching(_remoteResourceHandle);
737                 if (ret != (int)IoTConnectivityError.None)
738                 {
739                     Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to remove cache updated event handler");
740                     throw IoTConnectivityErrorFactory.GetException(ret);
741                 }
742             }
743         }
744
745         private void RegisterStateChangedEvent()
746         {
747             _stateChangedCallback = (IntPtr resource, int state, IntPtr userData) =>
748             {
749                 StateChangedEventArgs e = new StateChangedEventArgs()
750                 {
751                     State = (ResourceState)state
752                 };
753                 _stateChangedEventHandler?.Invoke(null, e);
754             };
755
756             int ret = Interop.IoTConnectivity.Client.RemoteResource.StartMonitoring(_remoteResourceHandle, _stateChangedCallback, IntPtr.Zero);
757             if (ret != (int)IoTConnectivityError.None)
758             {
759                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to add state changed event handler");
760                 throw IoTConnectivityErrorFactory.GetException(ret);
761             }
762         }
763
764         private void UnregisterStateChangedEvent()
765         {
766             int ret = Interop.IoTConnectivity.Client.RemoteResource.StopMonitoring(_remoteResourceHandle);
767             if (ret != (int)IoTConnectivityError.None)
768             {
769                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to remove state changed event handler");
770                 throw IoTConnectivityErrorFactory.GetException(ret);
771             }
772         }
773
774         private void CreateRemoteResource(IntPtr resourceTypeHandle, IntPtr resourceInterfaceHandle)
775         {
776             Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType connectivityType = GetConnectivityType(HostAddress);
777             if (connectivityType == Interop.IoTConnectivity.Client.RemoteResource.ConnectivityType.None)
778             {
779                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Unable to parse host address");
780                 throw new ArgumentException("Unable to parse host address");
781             }
782             int ret = Interop.IoTConnectivity.Client.RemoteResource.Create(HostAddress, (int)connectivityType, UriPath, (int)Policy, resourceTypeHandle, resourceInterfaceHandle, out _remoteResourceHandle);
783             if (ret != (int)IoTConnectivityError.None)
784             {
785                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get remote resource");
786                 throw IoTConnectivityErrorFactory.GetException(ret);
787             }
788         }
789
790         private void SetRemoteResource()
791         {
792             IntPtr hostAddressPtr, uriPathPtr;
793             int ret = Interop.IoTConnectivity.Client.RemoteResource.GetHostAddress(_remoteResourceHandle, out hostAddressPtr);
794             if (ret != (int)IoTConnectivityError.None)
795             {
796                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Faled to get host address");
797                 throw IoTConnectivityErrorFactory.GetException(ret);
798             }
799
800             ret = Interop.IoTConnectivity.Client.RemoteResource.GetUriPath(_remoteResourceHandle, out uriPathPtr);
801             if (ret != (int)IoTConnectivityError.None)
802             {
803                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Faled to get uri path");
804                 throw IoTConnectivityErrorFactory.GetException(ret);
805             }
806
807             int policy = (int)ResourcePolicy.NoProperty;
808             ret = Interop.IoTConnectivity.Client.RemoteResource.GetPolicies(_remoteResourceHandle, out policy);
809             if (ret != (int)IoTConnectivityError.None)
810             {
811                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Faled to get uri path");
812                 throw IoTConnectivityErrorFactory.GetException(ret);
813             }
814
815             IntPtr typesHandle, interfacesHandle;
816             ret = Interop.IoTConnectivity.Client.RemoteResource.GetTypes(_remoteResourceHandle, out typesHandle);
817             if (ret != (int)IoTConnectivityError.None)
818             {
819                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get resource types");
820                 throw IoTConnectivityErrorFactory.GetException(ret);
821             }
822
823             ret = Interop.IoTConnectivity.Client.RemoteResource.GetInterfaces(_remoteResourceHandle, out interfacesHandle);
824             if (ret != (int)IoTConnectivityError.None)
825             {
826                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get resource interfaces");
827                 throw IoTConnectivityErrorFactory.GetException(ret);
828             }
829
830             IntPtr deviceIdPtr;
831             ret = Interop.IoTConnectivity.Client.RemoteResource.GetDeviceId(_remoteResourceHandle, out deviceIdPtr);
832             if (ret != (int)IoTConnectivityError.None)
833             {
834                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get device id");
835                 throw IoTConnectivityErrorFactory.GetException(ret);
836             }
837             DeviceId = (deviceIdPtr != IntPtr.Zero) ? Marshal.PtrToStringAnsi(deviceIdPtr) : string.Empty;
838             HostAddress = (hostAddressPtr != IntPtr.Zero) ? Marshal.PtrToStringAnsi(hostAddressPtr) : string.Empty;
839             UriPath = (uriPathPtr != IntPtr.Zero) ? Marshal.PtrToStringAnsi(uriPathPtr) : string.Empty;
840             Types = new ResourceTypes(typesHandle);
841             Interfaces = new ResourceInterfaces(interfacesHandle);
842             Policy = (ResourcePolicy)policy;
843         }
844
845         private RemoteResponse GetRemoteResponse(IntPtr response)
846         {
847             int result;
848             IntPtr representationHandle, optionsHandle;
849             int ret = Interop.IoTConnectivity.Server.Response.GetResult(response, out result);
850             if (ret != (int)IoTConnectivityError.None)
851             {
852                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get result");
853                 throw IoTConnectivityErrorFactory.GetException(ret);
854             }
855
856             ret = Interop.IoTConnectivity.Server.Response.GetRepresentation(response, out representationHandle);
857             if (ret != (int)IoTConnectivityError.None)
858             {
859                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get representation");
860                 throw IoTConnectivityErrorFactory.GetException(ret);
861             }
862
863             ret = Interop.IoTConnectivity.Server.Response.GetOptions(response, out optionsHandle);
864             if (ret != (int)IoTConnectivityError.None)
865             {
866                 Log.Error(IoTConnectivityErrorFactory.LogTag, "Failed to get options");
867                 throw IoTConnectivityErrorFactory.GetException(ret);
868             }
869             return new RemoteResponse()
870             {
871                 Result = (ResponseCode)result,
872                 Representation = new Representation(representationHandle),
873                 Options = (optionsHandle == IntPtr.Zero)? null : new ResourceOptions(optionsHandle)
874             };
875         }
876     }
877 }