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