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