Release 4.0.0-preview1-00285
[platform/core/csapi/tizenfx.git] / src / Tizen.Network.Bluetooth / Tizen.Network.Bluetooth / BluetoothGatt.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 using System.Text;
21 using System.Threading.Tasks;
22
23 namespace Tizen.Network.Bluetooth
24 {
25     /// <summary>
26     /// The Bluetooth GATT server.
27     /// </summary>
28     public class BluetoothGattServer
29     {
30         private static BluetoothGattServer _instance;
31         private BluetoothGattServerImpl _impl;
32         private BluetoothGattServer()
33         {
34             _impl = new BluetoothGattServerImpl();
35         }
36
37         /// <summary>
38         /// (event) This event is called when the indication acknowledgement is received for each notified client.
39         /// </summary>
40         public event EventHandler<NotificationSentEventArg> NotificationSent
41         {
42             add
43             {
44                 _impl._notificationSent += value;
45             }
46             remove
47             {
48                 _impl._notificationSent -= value;
49             }
50         }
51
52         /// <summary>
53         /// Creates the Bluetooth GATT server.
54         /// </summary>
55         /// <returns></returns>
56         /// <exception cref="NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
57         /// <exception cref="InvalidOperationException">Thrown when the create GATT server fails.</exception>
58         public static BluetoothGattServer CreateServer()
59         {
60             if (_instance == null)
61             {
62                 BluetoothGattServer server = new BluetoothGattServer();
63                 if (server.IsValid())
64                 {
65                     _instance = server;
66                 }
67             }
68             return _instance;
69         }
70
71         /// <summary>
72         /// Registers the server along with the GATT services of the application it is hosting.
73         /// </summary>
74         /// <exception cref="NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
75         /// <exception cref="InvalidOperationException">Thrown when the register server application fails.</exception>
76         public void Start()
77         {
78             _impl.Start();
79         }
80
81         /// <summary>
82         /// Registers a specified service to this server.
83         /// </summary>
84         /// <param name="service">The service, which needs to be registered with this server.</param>
85         /// <exception cref="NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
86         /// <exception cref="InvalidOperationException">Thrown when the register service fails.</exception>
87         public void RegisterGattService(BluetoothGattService service)
88         {
89             if (service.IsRegistered())
90             {
91                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.InvalidParameter);
92             }
93             _impl.RegisterGattService(this, service);
94         }
95
96         /// <summary>
97         /// Unregisters a specified service from this server.
98         /// </summary>
99         /// <param name="service">The service, which needs to be unregistered from this server.</param>
100         /// <remarks>
101         /// Once unregistered, the service object will become invalid and should not be used to access sevices or any children attribute's methods/members.
102         /// </remarks>
103         /// <exception cref="NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
104         /// <exception cref="InvalidOperationException">Thrown when the unregister service fails.</exception>
105         public void UnregisterGattService(BluetoothGattService service)
106         {
107             if (service.GetGattServer() != this)
108             {
109                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.InvalidParameter);
110             }
111
112             _impl.UnregisterGattService(service);
113         }
114
115         /// <summary>
116         /// Unregisters all services from this server.
117         /// </summary>
118         /// <remarks>
119         /// Once unregistered, servicees will become invalid and should not be used to access sevices or any children attribute's methods/members.
120         /// </remarks>
121         /// <exception cref="NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
122         /// <exception cref="InvalidOperationException">Thrown when the unregister all services fail.</exception>
123         public void UnregisterGattServices()
124         {
125             _impl.UnregisterAllGattServices(this);
126         }
127
128         /// <summary>
129         /// Gets service with given UUID that belongs to this server.
130         /// </summary>
131         /// <param name="uuid">The UUID for the service to get.</param>
132         /// <returns>The Service with the given UUID if it exists, null otherwise.</returns>
133         public BluetoothGattService GetService(string uuid)
134         {
135             return _impl.GetService(this, uuid);
136         }
137
138         /// <summary>
139         /// Gets the list of services that belongs to this server.
140         /// </summary>
141         /// <returns>The list of services that belongs to this server.</returns>
142         public IEnumerable<BluetoothGattService> GetServices()
143         {
144             return _impl.GetServices(this);
145         }
146
147         /// <summary>
148         /// Sends indication for the value change of the characteristic to the remote devices.
149         /// </summary>
150         /// <param name="characteristic">The characteristic whose the value is changed.</param>
151         /// <param name="clientAddress">The remote device address to send, notify, or indicate and if set to NULL, then notify/indicate all is enabled.</param>
152         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
153         /// or when the remote device is disconnected, or when service is not registered, or when the CCCD is not enabled.</exception>
154         public async Task<bool> SendIndicationAsync(BluetoothGattCharacteristic characteristic, string clientAddress)
155         {
156             return await _impl.SendIndicationAsync(this, characteristic, clientAddress);
157         }
158
159         /// <summary>
160         /// Sends the notification for the value change of the characteristic to the remote devices.
161         /// </summary>
162         /// <param name="characteristic">The characteristic, which has a changed value.</param>
163         /// <param name="clientAddress">The remote device address to send, notify, or indicate and if set to NULL, then notify/indicate all is enabled.</param>
164         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
165         /// or when the remote device is disconnected, or when service is not registered, or when the CCCD is not enabled.</exception>
166         public void SendNotification(BluetoothGattCharacteristic characteristic, string clientAddress)
167         {
168             _impl.SendNotification(characteristic, clientAddress);
169         }
170
171         /// <summary>
172         /// Sends a response to the remote device as a result of a read/write request.
173         /// </summary>
174         /// <param name="requestId">The identification of a read/write request.</param>
175         /// <param name="type">The request type for read/write.</param>
176         /// <param name="status">The error value in case of failure, 0 for success.</param>
177         /// <param name="value">The value to be sent.</param>
178         /// <param name="offset">The offset from where the value is read.</param>
179         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
180         /// or when the remote device is disconnected, or the send response procedure fails.</exception>
181         public void SendResponse(int requestId, BluetoothGattRequestType type, int status, byte[] value, int offset)
182         {
183             _impl.SendResponse(requestId, (int)type, status, value, offset);
184         }
185
186         internal bool IsValid()
187         {
188             return _impl.GetHandle().IsInvalid == false;
189         }
190     }
191
192     /// <summary>
193     /// The Bluetooth GATT client.
194     /// </summary>
195     public class BluetoothGattClient
196     {
197         private BluetoothGattClientImpl _impl;
198         private string _remoteAddress = string.Empty;
199
200         internal BluetoothGattClient(string remoteAddress)
201         {
202             _impl = new BluetoothGattClientImpl(remoteAddress);
203             _remoteAddress = remoteAddress;
204         }
205
206         internal static BluetoothGattClient CreateClient(string remoteAddress)
207         {
208             BluetoothGattClient client = new BluetoothGattClient(remoteAddress);
209             return client.Isvalid() ? client : null;
210         }
211
212         /// <summary>
213         /// Destroy Bluetooth GATT client
214         /// </summary>
215         public void DestroyClient()
216         {
217             _impl.GetHandle().Dispose();
218         }
219
220         /// <summary>
221         /// The address of the remote device.
222         /// </summary>
223         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
224         /// or when the remote device is disconnected.</exception>
225         public string RemoteAddress
226         {
227             get
228             {
229                 if (string.IsNullOrEmpty(_remoteAddress))
230                 {
231                     _remoteAddress = _impl.GetRemoteAddress();
232                 }
233                 return _remoteAddress;
234             }
235         }
236
237         /// <summary>
238         /// Gets the service with the given UUID that belongs to the remote device.
239         /// </summary>
240         /// <param name="uuid">The UUID for the service to get.</param>
241         /// <returns>The service with the given UUID if it exists, null otherwise.</returns>
242         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
243         /// or when the remote device is disconnected, or when the get service fails.</exception>
244         public BluetoothGattService GetService(string uuid)
245         {
246             return _impl.GetService(this, uuid);
247         }
248
249         /// <summary>
250         /// Gets list of services that belongs to the remote device.
251         /// </summary>
252         /// <returns>The list of services that belongs to the remote device.</returns>
253         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
254         /// or when the remote device is disconnected, or when the get service fails.</exception>
255         public IEnumerable<BluetoothGattService> GetServices()
256         {
257             return _impl.GetServices(this);
258         }
259
260         /// <summary>
261         /// Reads the value of a given characteristic from the remote device asynchronously.
262         /// </summary>
263         /// <param name="characteristic">The characteristic to be read.</param>
264         /// <returns>true on success, false otherwise.</returns>
265         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
266         /// or when the remote device is disconnected, or when the read attribute value fails.</exception>
267         public async Task<bool> ReadValueAsync(BluetoothGattCharacteristic characteristic)
268         {
269             return await _impl.ReadValueAsyncTask(characteristic.GetHandle());
270         }
271
272         /// <summary>
273         /// Reads the value of the given descriptor from the remote device asynchronously.
274         /// </summary>
275         /// <param name="descriptor">The descriptor to be read.</param>
276         /// <returns>true on success, false otherwise.</returns>
277         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
278         /// or when the remote device is disconnected, or when the read attribute value fails.</exception>
279         public async Task<bool> ReadValueAsync(BluetoothGattDescriptor descriptor)
280         {
281             return await _impl.ReadValueAsyncTask(descriptor.GetHandle());
282         }
283
284         /// <summary>
285         /// Writes the value of a given characteristic to the remote device asynchronously.
286         /// </summary>
287         /// <param name="characteristic">The characteristic to be written.</param>
288         /// <returns>true on success, false otherwise.</returns>
289         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
290         /// or when the remote device is disconnected or when the write attribute value fails.</exception>
291         public async Task<bool> WriteValueAsync(BluetoothGattCharacteristic characteristic)
292         {
293             return await _impl.WriteValueAsyncTask(characteristic.GetHandle());
294         }
295
296         /// <summary>
297         /// Writes the value of the given descriptor to the remote device asynchronously.
298         /// </summary>
299         /// <param name="descriptor">The descriptor to be written.</param>
300         /// <returns>true on success, false otherwise.</returns>
301         /// <exception cref="InvalidOperationException">Thrown when the BT/BTLE is not enabled
302         /// or when the remote device is disconnected, or when the write attribute value fails.</exception>
303         public async Task<bool> WriteValueAsync(BluetoothGattDescriptor descriptor)
304         {
305             return await _impl.WriteValueAsyncTask(descriptor.GetHandle());
306         }
307
308         internal bool Isvalid()
309         {
310             return _impl.GetHandle().IsInvalid == false;
311         }
312     }
313
314     /// <summary>
315     /// The Bluetooth GATT service.
316     /// </summary>
317     public class BluetoothGattService
318     {
319         private BluetoothGattServiceImpl _impl;
320         private BluetoothGattClient _parentClient = null;
321         private BluetoothGattServer _parentServer = null;
322         private BluetoothGattService _parentService = null;
323
324         /// <summary>
325         /// The constructor.
326         /// </summary>
327         /// <param name="uuid">The UUID of the service.</param>
328         /// <param name="type">The type of service.</param>
329         /// <exception cref="InvalidOperationException">Thrown when the create GATT service procedure fails.</exception>
330         public BluetoothGattService(string uuid, BluetoothGattServiceType type)
331         {
332             Uuid = uuid;
333             _impl = new BluetoothGattServiceImpl(uuid, type);
334         }
335
336         internal BluetoothGattService(BluetoothGattServiceImpl impl, string uuid)
337         {
338             Uuid = uuid;
339             _impl = impl;
340         }
341
342         /// <summary>
343         /// Specification name from the UUID.
344         /// </summary>
345         public string Uuid { get; }
346
347         /// <summary>
348         /// Adds a characteristic to this service.
349         /// </summary>
350         /// <param name="characteristic">The characteristic to be added.</param>
351         /// <returns>true on success, false otherwise.</returns>
352         /// <exception cref="InvalidOperationException">Thrown when the add GATT characteristic procedure fails.</exception>
353         public void AddCharacteristic(BluetoothGattCharacteristic characteristic)
354         {
355             if (GetGattClient() != null)
356             {
357                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NotSupported);
358             }
359
360             if (characteristic.GetService() != null)
361             {
362                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.InvalidParameter);
363             }
364
365             _impl.AddCharacteristic(characteristic);
366             characteristic.SetParent(this);
367         }
368
369         /// <summary>
370         /// Gets the characteristic with the given UUID that belongs to this service.
371         /// </summary>
372         /// <param name="uuid">The UUID for the characteristic to get.</param>
373         /// <returns>The characteristic with a given UUID if it exists, null otherwise.</returns>
374         public BluetoothGattCharacteristic GetCharacteristic(string uuid)
375         {
376             return _impl.GetCharacteristic(this, uuid);
377         }
378
379         /// <summary>
380         /// Gets list of the characteristic that belongs to this service.
381         /// </summary>
382         /// <returns>The list of the characteristic that belongs to this service.</returns>
383         public IEnumerable<BluetoothGattCharacteristic> GetCharacteristics()
384         {
385             return _impl.GetCharacteristics(this);
386         }
387
388         /// <summary>
389         /// Includes a service to this service.
390         /// </summary>
391         /// <param name="service">The service to be included.</param>
392         /// <returns>true on success, false otherwise</returns>
393         /// <exception cref="InvalidOperationException">Thrown when the add GATT service procedure fails.</exception>///
394         public void AddService(BluetoothGattService service)
395         {
396             if (GetGattClient() != null)
397             {
398                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NotSupported);
399             }
400
401             if (service.IsRegistered())
402             {
403                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.InvalidParameter);
404             }
405
406             _impl.AddIncludeService(service);
407             service.SetParent(this);
408         }
409
410         /// <summary>
411         /// Gets the included service.
412         /// </summary>
413         /// <param name="uuid">The UUID for the service to get.</param>
414         /// <returns>The service with a given UUID if it exists, null otherwise.</returns>
415         public BluetoothGattService GetIncludeService(string uuid)
416         {
417             return _impl.GetIncludeService(this, uuid);
418         }
419
420         /// <summary>
421         /// Gets the included service list of this service.
422         /// </summary>
423         /// <returns>The included service list of this service.</returns>
424         public IEnumerable<BluetoothGattService> GetIncludeServices()
425         {
426             return _impl.GetIncludeServices(this);
427         }
428
429         /// <summary>
430         /// Gets the server instance which the specified service belongs to.
431         /// </summary>
432         /// <returns>The server instance which the specified service belongs to.</returns>
433         public BluetoothGattServer GetGattServer()
434         {
435             return _parentServer;
436         }
437
438         /// <summary>
439         /// Gets the client instance which the specified service belongs to.
440         /// </summary>
441         /// <returns>The client instance which the specified service belongs to.</returns>
442         public BluetoothGattClient GetGattClient()
443         {
444             return _parentClient;
445         }
446
447         internal BluetoothGattAttributeHandle GetHandle()
448         {
449             return _impl.GetHandle();
450         }
451
452         internal void SetParent(BluetoothGattService parent)
453         {
454             if (!IsRegistered())
455             {
456                 _parentService = parent;
457                 _impl.ReleaseHandleOwnership();
458             }
459         }
460
461         internal void SetParent(BluetoothGattClient parent)
462         {
463             if (!IsRegistered())
464             {
465                 _parentClient = parent;
466                 _impl.ReleaseHandleOwnership();
467             }
468         }
469
470         internal void SetParent(BluetoothGattServer parent)
471         {
472             if (!IsRegistered())
473             {
474                 _parentServer = parent;
475                 _impl.ReleaseHandleOwnership();
476             }
477         }
478
479         internal void UnregisterService()
480         {
481             _parentServer = null;
482             _parentClient = null;
483             _parentService = null;
484         }
485
486         internal bool IsRegistered()
487         {
488             return _parentClient != null || _parentServer != null || _parentService != null;
489         }
490     }
491
492     /// <summary>
493     /// The Bluetooth GATT characteristic.
494     /// </summary>
495     public class BluetoothGattCharacteristic : BluetoothGattAttribute
496     {
497         private BluetoothGattCharacteristicImpl _impl;
498         private BluetoothGattService _parent = null;
499
500         private Interop.Bluetooth.BtClientCharacteristicValueChangedCallback _characteristicValueChangedCallback;
501         private Interop.Bluetooth.BtGattServerNotificationStateChangeCallback _notificationStateChangedCallback;
502
503         private EventHandler<ValueChangedEventArgs> _characteristicValueChanged;
504         internal EventHandler<NotificationStateChangedEventArg> _notificationStateChanged;
505
506         /// <summary>
507         /// The constructor.
508         /// </summary>
509         /// <param name="uuid">The UUID of the characterstic.</param>
510         /// <param name="permissions">Permissions for the characterstic.</param>
511         /// <param name="properties">Properties set for the characterstic.</param>
512         /// <param name="value">The value associated with the characterstic.</param>
513         /// <remarks>throws in case of internal error.</remarks>
514         /// <exception cref="InvalidOperationException">Thrown when the create GATT characteristics procedure fails.</exception>
515         public BluetoothGattCharacteristic(string uuid, BluetoothGattPermission permissions, BluetoothGattProperty properties, byte[] value) : base(uuid, permissions)
516         {
517             _impl = new BluetoothGattCharacteristicImpl(uuid, permissions, properties, value);
518         }
519
520         internal BluetoothGattCharacteristic(BluetoothGattCharacteristicImpl impl, string uuid, BluetoothGattPermission permission) : base(uuid, permission)
521         {
522             _impl = impl;
523         }
524
525         /// <summary>
526         /// The CharacteristicValueChanged event is raised when the server notifies for change in this characteristic value.
527         /// </summary>
528         /// <remarks>
529         /// Adding the event handle on characteristic on the server side will not have any effect.
530         /// </remarks>
531         public event EventHandler<ValueChangedEventArgs> ValueChanged
532         {
533             add
534             {
535                 if (Client != null)
536                 {
537                     if (_characteristicValueChanged == null)
538                     {
539                         _characteristicValueChangedCallback = (gattHandle, characteristicValue, len, userData) =>
540                         {
541                             _characteristicValueChanged?.Invoke(this, new ValueChangedEventArgs(characteristicValue));
542                         };
543
544                         _impl.SetCharacteristicValueChangedEvent(_characteristicValueChangedCallback);
545                     }
546                     _characteristicValueChanged = value;
547                 }
548             }
549             remove
550             {
551                 if (Client != null)
552                 {
553                     _characteristicValueChanged = null;
554                     if (_characteristicValueChanged == null)
555                     {
556                         _impl.UnsetCharacteristicValueChangedEvent();
557                     }
558
559                 }
560             }
561         }
562
563         /// <summary>
564         /// The NotificationStateChanged event is called when the client enables or disables the Notification/Indication for particular characteristics.
565         /// </summary>
566         /// <remarks>
567         /// Adding event handle on the characteristic on the client side will not have any effect.
568         /// </remarks>
569         public event EventHandler<NotificationStateChangedEventArg> NotificationStateChanged
570         {
571             add
572             {
573                 if (Server != null)
574                 {
575                     if (_notificationStateChangedCallback == null)
576                     {
577                         _notificationStateChangedCallback = (notify, serverHandle, characteristicHandle, userData) =>
578                         {
579                             _notificationStateChanged?.Invoke(this, new NotificationStateChangedEventArg(Server, notify));
580                         };
581
582                         _impl.SetNotificationStateChangedEvent(_notificationStateChangedCallback);
583                     }
584
585                     _notificationStateChanged = value;
586                 }
587             }
588             remove
589             {
590                 if (Server != null)
591                 {
592                     _notificationStateChanged = null;
593                     // CAPI does not allow unsetting ReadValueRequestedEventCallback.
594                 }
595             }
596         }
597
598         /// <summary>
599         /// The property for this characteristic.
600         /// </summary>
601         public BluetoothGattProperty Properties
602         {
603             get
604             {
605                 return _impl.GetProperties();
606             }
607             set
608             {
609                 if (Server != null)
610                 {
611                     _impl.SetProperties(value);
612                 }
613             }
614         }
615
616         /// <summary>
617         /// The write type to be used for write operations.
618         /// </summary>
619         public BluetoothGattWriteType WriteType
620         {
621             get
622             {
623                 return _impl.GetWriteType();
624             }
625             set
626             {
627                 _impl.SetWriteType(value);
628             }
629         }
630
631         internal override BluetoothGattClient Client
632         {
633             get
634             {
635                 return _parent?.GetGattClient();
636             }
637         }
638
639         internal override BluetoothGattServer Server
640         {
641             get
642             {
643                 return _parent?.GetGattServer();
644             }
645         }
646
647         internal override BluetoothGattAttributeImpl Impl
648         {
649             get
650             {
651                 return _impl;
652             }
653         }
654
655         /// <summary>
656         /// Adds a descriptor to this characteristic.
657         /// </summary>
658         /// <param name="descriptor">The descriptor to be added.</param>
659         /// <returns>true on success, false otherwise.</returns>
660         /// <exception cref="InvalidOperationException">Thrown when the add GATT descriptor procedure fails.</exception>
661         public void AddDescriptor(BluetoothGattDescriptor descriptor)
662         {
663             if (Client != null)
664             {
665                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NotSupported);
666             }
667
668             if (descriptor.GetCharacteristic() != null)
669             {
670                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.InvalidParameter);
671             }
672
673             _impl.AddDescriptor(descriptor);
674             descriptor.SetParent(this);
675         }
676
677         /// <summary>
678         /// Gets the descriptor with the given UUID that belongs to this characteristic.
679         /// </summary>
680         /// <param name="uuid">The UUID for the descriptor to get.</param>
681         /// <returns>The descriptor with a given UUID if it exists, null otherwise.</returns>
682         public BluetoothGattDescriptor GetDescriptor(string uuid)
683         {
684             return _impl.GetDescriptor(this, uuid);
685         }
686
687         /// <summary>
688         /// Gets the list of descriptors that belongs to this characteristic.
689         /// </summary>
690         /// <returns>The list of descriptors that belongs to this characteristic.</returns>
691         public IEnumerable<BluetoothGattDescriptor> GetDescriptors()
692         {
693             return _impl.GetDescriptors(this);
694         }
695
696         /// <summary>
697         /// Gets the service instance, which the specified characterstic belongs to.
698         /// </summary>
699         /// <returns>The characteristic instance, the specified characterstic belongs to.</returns>
700         public BluetoothGattService GetService()
701         {
702             return _parent;
703         }
704
705         internal void SetParent(BluetoothGattService parent)
706         {
707             if (_parent == null)
708             {
709                 _parent = parent;
710                 ReleaseHandleOwnership();
711             }
712          }
713     }
714
715     /// <summary>
716     /// The Bluetooth GATT descriptor.
717     /// </summary>
718     public class BluetoothGattDescriptor : BluetoothGattAttribute
719     {
720         private BluetoothGattCharacteristic _parent = null;
721         private BluetoothGattDescriptorImpl _impl;
722
723         /// <summary>
724         /// The constructor.
725         /// </summary>
726         /// <param name="uuid">The UUID of the descriptor.</param>
727         /// <param name="permisions">Permissions for the descriptor.</param>
728         /// <param name="value">The value associated with the descriptor.</param>
729         /// <remarks>throws in case of internal error.</remarks>
730         /// <exception cref="InvalidOperationException">Thrown when the create GATT descriptor procedure fails.</exception>
731         public BluetoothGattDescriptor(string uuid, BluetoothGattPermission permisions, byte[] value) : base (uuid, permisions)
732         {
733             _impl = new BluetoothGattDescriptorImpl(uuid, permisions, value);
734         }
735
736         internal BluetoothGattDescriptor(BluetoothGattDescriptorImpl impl, string uuid, BluetoothGattPermission permission) : base(uuid, permission)
737         {
738             _impl = impl;
739         }
740
741         internal override BluetoothGattClient Client
742         {
743             get
744             {
745                 return _parent?.Client;
746             }
747         }
748
749         internal override BluetoothGattServer Server
750         {
751             get
752             {
753                 return _parent?.Server;
754             }
755         }
756
757         internal override BluetoothGattAttributeImpl Impl
758         {
759             get
760             {
761                 return _impl;
762             }
763         }
764
765         /// <summary>
766         /// Gets the characteristic instance, which the specified descriptor belongs to.
767         /// </summary>
768         /// <returns>The characteristic instance, the specified descriptor belongs to.</returns>
769         public BluetoothGattCharacteristic GetCharacteristic()
770         {
771             return _parent;
772         }
773
774         internal void SetParent(BluetoothGattCharacteristic parent)
775         {
776             if (_parent == null)
777             {
778                 _parent = parent;
779                 ReleaseHandleOwnership();
780             }
781         }
782     }
783
784     /// <summary>
785     /// The Bluetooth GATT attribute.
786     /// </summary>
787     public abstract class BluetoothGattAttribute
788     {
789         private Interop.Bluetooth.BtGattServerReadValueRequestedCallback _readValueRequestedCallback;
790         private Interop.Bluetooth.BtGattServerWriteValueRequestedCallback _writeValueRequestedCallback;
791
792         private EventHandler<ReadRequestedEventArgs> _readValueRequested;
793         private EventHandler<WriteRequestedEventArgs> _writeValueRequested;
794
795         /// <summary>
796         /// The constructor.
797         /// </summary>
798         /// <param name="uuid">The UUID of the GATT attribute.</param>
799         /// <param name="permission">Permission for the GATT attribute.</param>
800         public BluetoothGattAttribute(string uuid, BluetoothGattPermission permission)
801         {
802             Uuid = uuid;
803             Permissions = permission;
804         }
805
806         // Events
807
808         /// <summary>
809         /// This event is called when the client request to read the value of a characteristic or a descriptor.
810         /// </summary>
811         /// <exception cref="InvalidOperationException">Thrown when the set read value requested callback procedure fails.</exception>
812         public event EventHandler<ReadRequestedEventArgs> ReadRequested
813         {
814             add
815             {
816                 if (Server == null) return;
817                 if (_readValueRequestedCallback == null)
818                 {
819                     _readValueRequestedCallback = (clientAddress, requestId, serverHandle, gattHandle, offset, userData) =>
820                     {
821                         _readValueRequested?.Invoke(this, new ReadRequestedEventArgs(Server, clientAddress, requestId, offset));
822                     };
823                     Impl.SetReadValueRequestedEventCallback(_readValueRequestedCallback);
824                 }
825                 _readValueRequested = value;
826             }
827             remove
828             {
829                 if (Server == null) return;
830                 _readValueRequested = null;
831                 // CAPI does not allow unsetting ReadValueRequestedEventCallback.
832             }
833         }
834
835         /// <summary>
836         /// This event is called when a value of a characteristic or a descriptor has been changed by a client.
837         /// </summary>
838         /// <exception cref="InvalidOperationException">Thrown when the set write value requested callback procedure fails.</exception>
839         public event EventHandler<WriteRequestedEventArgs> WriteRequested
840         {
841             add
842             {
843                 if (Server == null) return;
844                 if (_writeValueRequested == null)
845                 {
846                     _writeValueRequestedCallback = (clientAddress, requestId, serverHandle, gattHandle, offset, response_needed, valueToWrite, len, userData) =>
847                     {
848                         _writeValueRequested?.Invoke(this, new WriteRequestedEventArgs(Server, clientAddress, requestId, valueToWrite, offset, response_needed));
849                     };
850                     Impl.SetWriteValueRequestedEventCallback(_writeValueRequestedCallback);
851                 }
852                 _writeValueRequested = value;
853             }
854             remove
855             {
856                 if (Server == null) return;
857                 _writeValueRequested = null;
858                 // CAPI does not allow unsetting ReadValueRequestedEventCallback.
859             }
860         }
861
862         /// <summary>
863         /// The attribute's UUID.
864         /// </summary>
865         public string Uuid { get; }
866
867         /// <summary>
868         /// Permissions for this attribute.
869         /// </summary>
870         public BluetoothGattPermission Permissions { get; }
871
872         /// <summary>
873         /// The value of this descriptor.
874         /// </summary>
875         public byte[] Value
876         {
877             get
878             {
879                 return Impl.GetValue();
880             }
881             set
882             {
883                 Impl.SetValue(value);
884             }
885         }
886
887         internal abstract BluetoothGattClient Client { get; }
888         internal abstract BluetoothGattServer Server { get; }
889         internal abstract BluetoothGattAttributeImpl Impl { get; }
890
891         /// <summary>
892         /// Returns a string value at the specified offset.
893         /// </summary>
894         /// <param name="offset"></param>
895         /// <returns>The string value at specified offset.</returns>
896         public string GetValue(int offset)
897         {
898             return Impl.GetValue(offset);
899         }
900
901         /// <summary>
902         /// Sets the string value as a specified offset.
903         /// </summary>
904         /// <param name="value">value to set</param>
905         /// <exception cref="InvalidOperationException">Throws exception if the value is null.</exception>
906         public void SetValue(string value)
907         {
908             if (string.IsNullOrEmpty(value))
909                 GattUtil.ThrowForError((int)BluetoothError.InvalidParameter, "value should not be null");
910
911             byte[] val = Encoding.UTF8.GetBytes(value);
912             Impl.SetValue(val);
913         }
914
915         /// <summary>
916         /// Returns a value at specified offset as the int value of the specified type.
917         /// </summary>
918         /// <param name="type">The type of the int value.</param>
919         /// <param name="offset">An offset in the attribute value buffer.</param>
920         /// <returns>The int value at given offset.</returns>
921         /// <exception cref="InvalidOperationException">Throws exception if (offset + size of int value) is greater than the length of the value buffer.</exception>
922         public int GetValue(IntDataType type, int offset)
923         {
924             return Impl.GetValue(type, offset);
925         }
926
927         /// <summary>
928         /// Updates a value at the specified offset by the int value of the specified type.
929         /// </summary>
930         /// <param name="type">The type of the int value.</param>
931         /// <param name="value">The value to set.</param>
932         /// <param name="offset">An offset in the attribute value buffer.</param>
933         /// <exception cref="InvalidOperationException">Throws exception if (offset + size of int value) is greater than the length of the value buffer.</exception>
934         public void SetValue(IntDataType type, int value, int offset)
935         {
936             Impl.SetValue(type, value, offset);
937         }
938
939         /// <summary>
940         /// Returns a value at the specified offset as the float value of the specified type.
941         /// </summary>
942         /// <param name="type">The type of the float value.</param>
943         /// <param name="offset">An offset in the attribute value buffer.</param>
944         /// <returns>The float value at given offset.</returns>
945         /// <exception cref="InvalidOperationException">Throws exception if (offset + size of float value) is greater than the length of the value buffer.</exception>
946         public float GetValue(FloatDataType type, int offset)
947         {
948             return Impl.GetValue(type, offset);
949         }
950
951         /// <summary>
952         /// Updates the value at the specified offset by the float value of the specified type.
953         /// </summary>
954         /// <param name="type">The type of the float value.</param>
955         /// <param name="mantissa">The mantissa of the float value.</param>
956         /// <param name="exponent">An exponent of the float value.</param>
957         /// <param name="offset">An offset in the attribute value buffer.</param>
958         /// <exception cref="InvalidOperationException">Throws exception if (offset + size of float value) is greater than the length of the value buffer.</exception>
959         public void SetValue(FloatDataType type, int mantissa, int exponent, int offset)
960         {
961             Impl.SetValue(type, mantissa, exponent, offset);
962         }
963
964         internal void ReleaseHandleOwnership()
965         {
966             Impl.ReleaseHandleOwnership();
967         }
968
969         internal BluetoothGattAttributeHandle GetHandle()
970         {
971             return Impl.GetHandle();
972         }
973     }
974 }