Release 4.0.0-preview1-00051
[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="System.NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
57         /// <exception cref="System.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="System.NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
75         /// <exception cref="System.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="System.NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
86         /// <exception cref="System.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="System.NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
104         /// <exception cref="System.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="System.NotSupportedException">Thrown when the BT/BTLE is not supported.</exception>
122         /// <exception cref="System.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="System.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="System.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="System.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         public void DestroyClient()
213         {
214             _impl.GetHandle().Dispose();
215         }
216
217         /// <summary>
218         /// The address of the remote device.
219         /// </summary>
220         /// <exception cref="System.InvalidOperationException">Thrown when the BT/BTLE is not enabled
221         /// or when the remote device is disconnected.</exception>
222         public string RemoteAddress
223         {
224             get
225             {
226                 if (string.IsNullOrEmpty(_remoteAddress))
227                 {
228                     _remoteAddress = _impl.GetRemoteAddress();
229                 }
230                 return _remoteAddress;
231             }
232         }
233
234         /// <summary>
235         /// Gets the service with the given UUID that belongs to the remote device.
236         /// </summary>
237         /// <param name="uuid">The UUID for the service to get.</param>
238         /// <returns>The service with the given UUID if it exists, null otherwise.</returns>
239         /// <exception cref="System.InvalidOperationException">Thrown when the BT/BTLE is not enabled
240         /// or when the remote device is disconnected, or when the get service fails.</exception>
241         public BluetoothGattService GetService(string uuid)
242         {
243             return _impl.GetService(this, uuid);
244         }
245
246         /// <summary>
247         /// Gets list of services that belongs to the remote device.
248         /// </summary>
249         /// <returns>The list of services that belongs to the remote device.</returns>
250         /// <exception cref="System.InvalidOperationException">Thrown when the BT/BTLE is not enabled
251         /// or when the remote device is disconnected, or when the get service fails.</exception>
252         public IEnumerable<BluetoothGattService> GetServices()
253         {
254             return _impl.GetServices(this);
255         }
256
257         /// <summary>
258         /// Reads the value of a given characteristic from the remote device asynchronously.
259         /// </summary>
260         /// <param name="characteristic">The characteristic to be read.</param>
261         /// <returns>true on success, false otherwise.</returns>
262         /// <exception cref="System.InvalidOperationException">Thrown when the BT/BTLE is not enabled
263         /// or when the remote device is disconnected, or when the read attribute value fails.</exception>
264         public async Task<bool> ReadValueAsync(BluetoothGattCharacteristic characteristic)
265         {
266             return await _impl.ReadValueAsyncTask(characteristic.GetHandle());
267         }
268
269         /// <summary>
270         /// Reads the value of the given descriptor from the remote device asynchronously.
271         /// </summary>
272         /// <param name="descriptor">The descriptor to be read.</param>
273         /// <returns>true on success, false otherwise.</returns>
274         /// <exception cref="System.InvalidOperationException">Thrown when the BT/BTLE is not enabled
275         /// or when the remote device is disconnected, or when the read attribute value fails.</exception>
276         public async Task<bool> ReadValueAsync(BluetoothGattDescriptor descriptor)
277         {
278             return await _impl.ReadValueAsyncTask(descriptor.GetHandle());
279         }
280
281         /// <summary>
282         /// Writes the value of a given characteristic to the remote device asynchronously.
283         /// </summary>
284         /// <param name="characteristic">The characteristic to be written.</param>
285         /// <returns>true on success, false otherwise.</returns>
286         /// <exception cref="System.InvalidOperationException">Thrown when the BT/BTLE is not enabled
287         /// or when the remote device is disconnected or when the write attribute value fails.</exception>
288         public async Task<bool> WriteValueAsync(BluetoothGattCharacteristic characteristic)
289         {
290             return await _impl.WriteValueAsyncTask(characteristic.GetHandle());
291         }
292
293         /// <summary>
294         /// Writes the value of the given descriptor to the remote device asynchronously.
295         /// </summary>
296         /// <param name="descriptor">The descriptor to be written.</param>
297         /// <returns>true on success, false otherwise.</returns>
298         /// <exception cref="System.InvalidOperationException">Thrown when the BT/BTLE is not enabled
299         /// or when the remote device is disconnected, or when the write attribute value fails.</exception>
300         public async Task<bool> WriteValueAsync(BluetoothGattDescriptor descriptor)
301         {
302             return await _impl.WriteValueAsyncTask(descriptor.GetHandle());
303         }
304
305         internal bool Isvalid()
306         {
307             return _impl.GetHandle().IsInvalid == false;
308         }
309     }
310
311     /// <summary>
312     /// The Bluetooth GATT service.
313     /// </summary>
314     public class BluetoothGattService
315     {
316         private BluetoothGattServiceImpl _impl;
317         private BluetoothGattClient _parentClient = null;
318         private BluetoothGattServer _parentServer = null;
319         private BluetoothGattService _parentService = null;
320
321         /// <summary>
322         /// The constructor.
323         /// </summary>
324         /// <param name="uuid">The UUID of the service.</param>
325         /// <param name="type">The type of service.</param>
326         /// <exception cref="System.InvalidOperationException">Thrown when the create GATT service procedure fails.</exception>
327         public BluetoothGattService(string uuid, BluetoothGattServiceType type)
328         {
329             Uuid = uuid;
330             _impl = new BluetoothGattServiceImpl(uuid, type);
331         }
332
333         internal BluetoothGattService(BluetoothGattServiceImpl impl, string uuid)
334         {
335             Uuid = uuid;
336             _impl = impl;
337         }
338
339         /// <summary>
340         /// Specification name from the UUID.
341         /// </summary>
342         public string Uuid { get; }
343
344         /// <summary>
345         /// Adds a characteristic to this service.
346         /// </summary>
347         /// <param name="characteristic">The characteristic to be added.</param>
348         /// <returns>true on success, false otherwise.</returns>
349         /// <exception cref="System.InvalidOperationException">Thrown when the add GATT characteristic procedure fails.</exception>
350         public void AddCharacteristic(BluetoothGattCharacteristic characteristic)
351         {
352             if (GetGattClient() != null)
353             {
354                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NotSupported);
355             }
356
357             if (characteristic.GetService() != null)
358             {
359                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.InvalidParameter);
360             }
361
362             _impl.AddCharacteristic(characteristic);
363             characteristic.SetParent(this);
364         }
365
366         /// <summary>
367         /// Gets the characteristic with the given UUID that belongs to this service.
368         /// </summary>
369         /// <param name="uuid">The UUID for the characteristic to get.</param>
370         /// <returns>The characteristic with a given UUID if it exists, null otherwise.</returns>
371         public BluetoothGattCharacteristic GetCharacteristic(string uuid)
372         {
373             return _impl.GetCharacteristic(this, uuid);
374         }
375
376         /// <summary>
377         /// Gets list of the characteristic that belongs to this service.
378         /// </summary>
379         /// <returns>The list of the characteristic that belongs to this service.</returns>
380         public IEnumerable<BluetoothGattCharacteristic> GetCharacteristics()
381         {
382             return _impl.GetCharacteristics(this);
383         }
384
385         /// <summary>
386         /// Includes a service to this service.
387         /// </summary>
388         /// <param name="service">The service to be included.</param>
389         /// <returns>true on success, false otherwise</returns>
390         /// <exception cref="System.InvalidOperationException">Thrown when the add GATT service procedure fails.</exception>/// 
391         public void AddService(BluetoothGattService service)
392         {
393             if (GetGattClient() != null)
394             {
395                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NotSupported);
396             }
397
398             if (service.IsRegistered())
399             {
400                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.InvalidParameter);
401             }
402
403             _impl.AddIncludeService(service);
404             service.SetParent(this);
405         }
406
407         /// <summary>
408         /// Gets the included service.
409         /// </summary>
410         /// <param name="uuid">The UUID for the service to get.</param>
411         /// <returns>The service with a given UUID if it exists, null otherwise.</returns>
412         public BluetoothGattService GetIncludeService(string uuid)
413         {
414             return _impl.GetIncludeService(this, uuid);
415         }
416
417         /// <summary>
418         /// Gets the included service list of this service.
419         /// </summary>
420         /// <returns>The included service list of this service.</returns>
421         public IEnumerable<BluetoothGattService> GetIncludeServices()
422         {
423             return _impl.GetIncludeServices(this);
424         }
425
426         /// <summary>
427         /// Gets the server instance which the specified service belongs to.
428         /// </summary>
429         /// <returns>The server instance which the specified service belongs to.</returns>
430         public BluetoothGattServer GetGattServer()
431         {
432             return _parentServer;
433         }
434
435         /// <summary>
436         /// Gets the client instance which the specified service belongs to.
437         /// </summary>
438         /// <returns>The client instance which the specified service belongs to.</returns>
439         public BluetoothGattClient GetGattClient()
440         {
441             return _parentClient;
442         }
443
444         internal BluetoothGattAttributeHandle GetHandle()
445         {
446             return _impl.GetHandle();
447         }
448
449         internal void SetParent(BluetoothGattService parent)
450         {
451             if (!IsRegistered())
452             {
453                 _parentService = parent;
454                 _impl.ReleaseHandleOwnership();
455             }
456         }
457
458         internal void SetParent(BluetoothGattClient parent)
459         {
460             if (!IsRegistered())
461             {
462                 _parentClient = parent;
463                 _impl.ReleaseHandleOwnership();
464             }
465         }
466
467         internal void SetParent(BluetoothGattServer parent)
468         {
469             if (!IsRegistered())
470             {
471                 _parentServer = parent;
472                 _impl.ReleaseHandleOwnership();
473             }
474         }
475
476         internal void UnregisterService()
477         {
478             _parentServer = null;
479             _parentClient = null;
480             _parentService = null;
481         }
482
483         internal bool IsRegistered()
484         {
485             return _parentClient != null || _parentServer != null || _parentService != null;
486         }
487     }
488
489     /// <summary>
490     /// The Bluetooth GATT characteristic.
491     /// </summary>
492     public class BluetoothGattCharacteristic : BluetoothGattAttribute
493     {
494         private BluetoothGattCharacteristicImpl _impl;
495         private BluetoothGattService _parent = null;
496
497         private Interop.Bluetooth.BtClientCharacteristicValueChangedCallback _characteristicValueChangedCallback;
498         private Interop.Bluetooth.BtGattServerNotificationStateChangeCallback _notificationStateChangedCallback;
499
500         private EventHandler<ValueChangedEventArgs> _characteristicValueChanged;
501         internal EventHandler<NotificationStateChangedEventArg> _notificationStateChanged;
502
503         /// <summary>
504         /// The constructor.
505         /// </summary>
506         /// <param name="uuid">The UUID of the characterstic.param>
507         /// <param name="permissions">Permissions for the characterstic.</param>
508         /// <param name="properties">Properties set for the characterstic.</param>
509         /// <param name="value">The value associated with the characterstic.</param>
510         /// <remarks>throws in case of internal error.</remarks>
511         /// <exception cref="System.InvalidOperationException">Thrown when the create GATT characteristics procedure fails.</exception>
512         public BluetoothGattCharacteristic(string uuid, BluetoothGattPermission permissions, BluetoothGattProperty properties, byte[] value) : base(uuid, permissions)
513         {
514             _impl = new BluetoothGattCharacteristicImpl(uuid, permissions, properties, value);
515         }
516
517         internal BluetoothGattCharacteristic(BluetoothGattCharacteristicImpl impl, string uuid, BluetoothGattPermission permission) : base(uuid, permission)
518         {
519             _impl = impl;
520         }
521
522         /// <summary>
523         /// The CharacteristicValueChanged event is raised when the server notifies for change in this characteristic value.
524         /// </summary>
525         /// <remarks>
526         /// Adding the event handle on characteristic on the server side will not have any effect.
527         /// </remarks>
528         public event EventHandler<ValueChangedEventArgs> ValueChanged
529         {
530             add
531             {
532                 if (Client != null)
533                 {
534                     if (_characteristicValueChanged == null)
535                     {
536                         _characteristicValueChangedCallback = (gattHandle, characteristicValue, len, userData) =>
537                         {
538                             _characteristicValueChanged?.Invoke(this, new ValueChangedEventArgs(characteristicValue));
539                         };
540
541                         _impl.SetCharacteristicValueChangedEvent(_characteristicValueChangedCallback);
542                     }
543                     _characteristicValueChanged = value;
544                 }
545             }
546             remove
547             {
548                 if (Client != null)
549                 {
550                     _characteristicValueChanged = null;
551                     if (_characteristicValueChanged == null)
552                     {
553                         _impl.UnsetCharacteristicValueChangedEvent();
554                     }
555
556                 }
557             }
558         }
559
560         /// <summary>
561         /// The NotificationStateChanged event is called when the client enables or disables the Notification/Indication for particular characteristics.
562         /// </summary>
563         /// <remarks>
564         /// Adding event handle on the characteristic on the client side will not have any effect.
565         /// </remarks>
566         public event EventHandler<NotificationStateChangedEventArg> NotificationStateChanged
567         {
568             add
569             {
570                 if (Server != null)
571                 {
572                     if (_notificationStateChangedCallback == null)
573                     {
574                         _notificationStateChangedCallback = (notify, serverHandle, characteristicHandle, userData) =>
575                         {
576                             _notificationStateChanged?.Invoke(this, new NotificationStateChangedEventArg(Server, notify));
577                         };
578
579                         _impl.SetNotificationStateChangedEvent(_notificationStateChangedCallback);
580                     }
581
582                     _notificationStateChanged = value;
583                 }
584             }
585             remove
586             {
587                 if (Server != null)
588                 {
589                     _notificationStateChanged = null;
590                     // CAPI does not allow unsetting ReadValueRequestedEventCallback.
591                 }
592             }
593         }
594
595         /// <summary>
596         /// The property for this characteristic.
597         /// </summary>
598         public BluetoothGattProperty Properties
599         {
600             get
601             {
602                 return _impl.GetProperties();
603             }
604             set
605             {
606                 if (Server != null)
607                 {
608                     _impl.SetProperties(value);
609                 }
610             }
611         }
612
613         /// <summary>
614         /// The write type to be used for write operations.
615         /// </summary>
616         public BluetoothGattWriteType WriteType
617         {
618             get
619             {
620                 return _impl.GetWriteType();
621             }
622             set
623             {
624                 _impl.SetWriteType(value);
625             }
626         }
627
628         internal override BluetoothGattClient Client
629         {
630             get
631             {
632                 return _parent?.GetGattClient();
633             }
634         }
635
636         internal override BluetoothGattServer Server
637         {
638             get
639             {
640                 return _parent?.GetGattServer();
641             }
642         }
643
644         internal override BluetoothGattAttributeImpl Impl
645         {
646             get
647             {
648                 return _impl;
649             }
650         }
651
652         /// <summary>
653         /// Adds a descriptor to this characteristic.
654         /// </summary>
655         /// <param name="descriptor">The descriptor to be added.</param>
656         /// <returns>true on success, false otherwise.</returns>
657         /// <exception cref="System.InvalidOperationException">Thrown when the add GATT descriptor procedure fails.</exception>
658         public void AddDescriptor(BluetoothGattDescriptor descriptor)
659         {
660             if (Client != null)
661             {
662                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.NotSupported);
663             }
664
665             if (descriptor.GetCharacteristic() != null)
666             {
667                 BluetoothErrorFactory.ThrowBluetoothException((int)BluetoothError.InvalidParameter);
668             }
669
670             _impl.AddDescriptor(descriptor);
671             descriptor.SetParent(this);
672         }
673
674         /// <summary>
675         /// Gets the descriptor with the given UUID that belongs to this characteristic.
676         /// </summary>
677         /// <param name="uuid">The UUID for the descriptor to get.</param>
678         /// <returns>The descriptor with a given UUID if it exists, null otherwise.</returns>
679         public BluetoothGattDescriptor GetDescriptor(string uuid)
680         {
681             return _impl.GetDescriptor(this, uuid);
682         }
683
684         /// <summary>
685         /// Gets the list of descriptors that belongs to this characteristic.
686         /// </summary>
687         /// <returns>The list of descriptors that belongs to this characteristic.</returns>
688         public IEnumerable<BluetoothGattDescriptor> GetDescriptors()
689         {
690             return _impl.GetDescriptors(this);
691         }
692
693         /// <summary>
694         /// Gets the service instance, which the specified characterstic belongs to.
695         /// </summary>
696         /// <returns>The characteristic instance, the specified characterstic belongs to.</returns>
697         public BluetoothGattService GetService()
698         {
699             return _parent;
700         }
701
702         internal void SetParent(BluetoothGattService parent)
703         {
704             if (_parent == null)
705             {
706                 _parent = parent;
707                 ReleaseHandleOwnership();
708             }
709          }
710     }
711
712     /// <summary>
713     /// The Bluetooth GATT descriptor.
714     /// </summary>
715     public class BluetoothGattDescriptor : BluetoothGattAttribute
716     {
717         private BluetoothGattCharacteristic _parent = null;
718         private BluetoothGattDescriptorImpl _impl;
719
720         /// <summary>
721         /// The constructor.
722         /// </summary>
723         /// <param name="uuid">The UUID of the descriptor.</param>
724         /// <param name="permisions">Permissions for the descriptor.</param>
725         /// <param name="value">The value associated with the descriptor.</param>
726         /// <remarks>throws in case of internal error.</remarks>
727         /// <exception cref="System.InvalidOperationException">Thrown when the create GATT descriptor procedure fails.</exception>
728         public BluetoothGattDescriptor(string uuid, BluetoothGattPermission permisions, byte[] value) : base (uuid, permisions)
729         {
730             _impl = new BluetoothGattDescriptorImpl(uuid, permisions, value);
731         }
732
733         internal BluetoothGattDescriptor(BluetoothGattDescriptorImpl impl, string uuid, BluetoothGattPermission permission) : base(uuid, permission)
734         {
735             _impl = impl;
736         }
737
738         internal override BluetoothGattClient Client
739         {
740             get
741             {
742                 return _parent?.Client;
743             }
744         }
745
746         internal override BluetoothGattServer Server
747         {
748             get
749             {
750                 return _parent?.Server;
751             }
752         }
753
754         internal override BluetoothGattAttributeImpl Impl
755         {
756             get
757             {
758                 return _impl;
759             }
760         }
761
762         /// <summary>
763         /// Gets the characteristic instance, which the specified descriptor belongs to.
764         /// </summary>
765         /// <returns>The characteristic instance, the specified descriptor belongs to.</returns>
766         public BluetoothGattCharacteristic GetCharacteristic()
767         {
768             return _parent;
769         }
770
771         internal void SetParent(BluetoothGattCharacteristic parent)
772         {
773             if (_parent == null)
774             {
775                 _parent = parent;
776                 ReleaseHandleOwnership();
777             }
778         }
779     }
780
781     /// <summary>
782     /// The Bluetooth GATT attribute.
783     /// </summary>
784     public abstract class BluetoothGattAttribute
785     {
786         private Interop.Bluetooth.BtGattServerReadValueRequestedCallback _readValueRequestedCallback;
787         private Interop.Bluetooth.BtGattServerWriteValueRequestedCallback _writeValueRequestedCallback;
788
789         private EventHandler<ReadRequestedEventArgs> _readValueRequested;
790         private EventHandler<WriteRequestedEventArgs> _writeValueRequested;
791
792         public BluetoothGattAttribute(string uuid, BluetoothGattPermission permission)
793         {
794             Uuid = uuid;
795             Permissions = permission;
796         }
797
798         // Events
799
800         /// <summary>
801         /// This event is called when the client request to read the value of a characteristic or a descriptor.
802         /// </summary>
803         /// <exception cref="System.InvalidOperationException">Thrown when the set read value requested callback procedure fails.</exception>
804         public event EventHandler<ReadRequestedEventArgs> ReadRequested
805         {
806             add
807             {
808                 if (Server == null) return;
809                 if (_readValueRequestedCallback == null)
810                 {
811                     _readValueRequestedCallback = (clientAddress, requestId, serverHandle, gattHandle, offset, userData) =>
812                     {
813                         _readValueRequested?.Invoke(this, new ReadRequestedEventArgs(Server, clientAddress, requestId, offset));
814                     };
815                     Impl.SetReadValueRequestedEventCallback(_readValueRequestedCallback);
816                 }
817                 _readValueRequested = value;
818             }
819             remove
820             {
821                 if (Server == null) return;
822                 _readValueRequested = null;
823                 // CAPI does not allow unsetting ReadValueRequestedEventCallback.
824             }
825         }
826
827         /// <summary>
828         /// This event is called when a value of a characteristic or a descriptor has been changed by a client.
829         /// </summary>
830         /// <exception cref="System.InvalidOperationException">Thrown when the set write value requested callback procedure fails.</exception>
831         public event EventHandler<WriteRequestedEventArgs> WriteRequested
832         {
833             add
834             {
835                 if (Server == null) return;
836                 if (_writeValueRequested == null)
837                 {
838                     _writeValueRequestedCallback = (clientAddress, requestId, serverHandle, gattHandle, offset, response_needed, valueToWrite, len, userData) =>
839                     {
840                         _writeValueRequested?.Invoke(this, new WriteRequestedEventArgs(Server, clientAddress, requestId, valueToWrite, offset, response_needed));
841                     };
842                     Impl.SetWriteValueRequestedEventCallback(_writeValueRequestedCallback);
843                 }
844                 _writeValueRequested = value;
845             }
846             remove
847             {
848                 if (Server == null) return;
849                 _writeValueRequested = null;
850                 // CAPI does not allow unsetting ReadValueRequestedEventCallback.
851             }
852         }
853
854         /// <summary>
855         /// The attribute's UUID.
856         /// </summary>
857         public string Uuid { get; }
858
859         /// <summary>
860         /// Permissions for this attribute.
861         /// </summary>
862         public BluetoothGattPermission Permissions { get; }
863
864         /// <summary>
865         /// The value of this descriptor.
866         /// </summary>
867         public byte[] Value
868         {
869             get
870             {
871                 return Impl.GetValue();
872             }
873             set
874             {
875                 Impl.SetValue(value);
876             }
877         }
878
879         internal abstract BluetoothGattClient Client { get; }
880         internal abstract BluetoothGattServer Server { get; }
881         internal abstract BluetoothGattAttributeImpl Impl { get; }
882
883         /// <summary>
884         /// Returns a string value at the specified offset.
885         /// </summary>
886         /// <param name="offset"></param>
887         /// <returns>The string value at specified offset.</returns>
888         public string GetValue(int offset)
889         {
890             return Impl.GetValue(offset);
891         }
892
893         /// <summary>
894         /// Sets the string value as a specified offset.
895         /// </summary>
896         /// <param name="value">value to set</param>
897         /// <exception cref="InvalidOperationException">Throws exception if the value is null.</exception>
898         public void SetValue(string value)
899         {
900             if (string.IsNullOrEmpty(value))
901                 GattUtil.ThrowForError((int)BluetoothError.InvalidParameter, "value should not be null");
902
903             byte[] val = Encoding.UTF8.GetBytes(value);
904             Impl.SetValue(val);
905         }
906
907         /// <summary>
908         /// Returns a value at specified offset as the int value of the specified type.
909         /// </summary>
910         /// <param name="type">The type of the int value.</param>
911         /// <param name="An offset">An offset in the attribute value buffer.</param>
912         /// <returns>The int value at given offset.</returns>
913         /// <exception cref="InvalidOperationException">Throws exception if (offset + size of int value) is greater than the length of the value buffer.</exception>
914         public int GetValue(IntDataType type, int offset)
915         {
916             return Impl.GetValue(type, offset);
917         }
918
919         /// <summary>
920         /// Updates a value at the specified offset by the int value of the specified type.
921         /// </summary>
922         /// <param name="type">The type of the int value.</param>
923         /// <param name="value">The value to set.</param>
924         /// <param name="offset">An offset in the attribute value buffer.</param>
925         /// <exception cref="InvalidOperationException">Throws exception if (offset + size of int value) is greater than the length of the value buffer.</exception>
926         public void SetValue(IntDataType type, int value, int offset)
927         {
928             Impl.SetValue(type, value, offset);
929         }
930
931         /// <summary>
932         /// Returns a value at the specified offset as the float value of the specified type.
933         /// </summary>
934         /// <param name="type">The type of the float value.</param>
935         /// <param name="offset">An offset in the attribute value buffer.</param>
936         /// <returns>The float value at given offset.</returns>
937         /// <exception cref="InvalidOperationException">Throws exception if (offset + size of float value) is greater than the length of the value buffer.</exception>
938         public float GetValue(FloatDataType type, int offset)
939         {
940             return Impl.GetValue(type, offset);
941         }
942
943         /// <summary>
944         /// Updates the value at the specified offset by the float value of the specified type.
945         /// </summary>
946         /// <param name="type">The type of the float value.</param>
947         /// <param name="mantissa">The mantissa of the float value.</param>
948         /// <param name="exponent">An exponent of the float value.</param>
949         /// <param name="offset">An offset in the attribute value buffer.</param>
950         /// <exception cref="InvalidOperationException">Throws exception if (offset + size of float value) is greater than the length of the value buffer.</exception>
951         public void SetValue(FloatDataType type, int mantissa, int exponent, int offset)
952         {
953             Impl.SetValue(type, mantissa, exponent, offset);
954         }
955
956         internal void ReleaseHandleOwnership()
957         {
958             Impl.ReleaseHandleOwnership();
959         }
960
961         internal BluetoothGattAttributeHandle GetHandle()
962         {
963             return Impl.GetHandle();
964         }
965     }
966 }