1# -*- coding: utf-8 -*-
2# Copyright 2019 Oticon A/S
3# SPDX-License-Identifier: Apache-2.0
4
5import string;
6from enum import IntEnum;
7from components.utils import *;
8
9ATT_CID = 4;
10
11class ATTProperty(IntEnum):
12    ATT_PROP_BROADCAST                   =   1
13    ATT_PROP_READ                        =   2
14    ATT_PROP_WRITE_WITHOUT_RESPONSE      =   4
15    ATT_PROP_WRITE                       =   8
16    ATT_PROP_NOTIFY                      =  16
17    ATT_PROP_INDICATE                    =  32
18    ATT_PROP_AUTHENTICATED_SIGNED_WRITES =  64
19    ATT_PROP_EXTENDED_PROPERTIES         = 128
20
21class ATTPermission(IntEnum):
22    ATT_PERM_NONE                        =   0
23    ATT_PERM_READ                        =   1
24    ATT_PERM_WRITE                       =   2
25    ATT_PERM_READ_ENCRYPT                =   4
26    ATT_PERM_WRITE_ENCRYPT               =   8
27    ATT_PERM_READ_AUTHEN                 =  16
28    ATT_PERM_WRITE_AUTHEN                =  32
29    ATT_PERM_WRITE_PREPARED              =  64
30    ATT_PERM_READ_AUTHOR                 = 128
31    ATT_PERM_WRITE_AUTHOR                = 256
32    ATT_PERM_ANY_READ                    = ATT_PERM_READ | ATT_PERM_READ_ENCRYPT | ATT_PERM_READ_AUTHEN | ATT_PERM_READ_AUTHOR
33    ATT_PERM_ANY_WRITE                   = ATT_PERM_WRITE | ATT_PERM_WRITE_ENCRYPT | ATT_PERM_WRITE_AUTHEN | ATT_PERM_WRITE_PREPARED | ATT_PERM_WRITE_AUTHOR
34
35#
36# The Opcode field has the following bits:
37#    Bits:
38#     0-5: Method
39#       6: Command Flag (64)
40#       7: Authentication Signature Flag (128)
41#
42class ATTOpcode(IntEnum):
43    ATT_ERROR_RESPONSE                   =   1 # Request Opcode in Error, Attribute Handle In Error, Error Code
44    ATT_EXCH_MTU_REQUEST                 =   2 # Client Rx MTU
45    ATT_EXCH_MTU_RESPONSE                =   3 # Server Rx MTU
46    ATT_FIND_INFORMATION_REQUEST         =   4 # Starting Handle, Ending Handle
47    ATT_FIND_INFORMATION_RESPONSE        =   5 # Format, List of Handle, UUID
48    ATT_FIND_BY_TYPE_VALUE_REQUEST       =   6 # Starting Handle, Ending Handle, Attribute Type, Attribute Value
49    ATT_FIND_BY_TYPE_VALUE_RESPONSE      =   7 # Handles Information List
50    ATT_READ_BY_TYPE_REQUEST             =   8 # Starting Handle, Ending Handle, UUID
51    ATT_READ_BY_TYPE_RESPONSE            =   9 # Length, Attribute Data List
52    ATT_READ_REQUEST                     =  10 # Attribute Handle
53    ATT_READ_RESPONSE                    =  11 # Attribute Value
54    ATT_READ_BLOB_REQUEST                =  12 # Attribute Handle, Value Offset
55    ATT_READ_BLOB_RESPONSE               =  13 # Part Attribute Value
56    ATT_READ_MULTIPLE_REQUEST            =  14 # Handle Set
57    ATT_READ_MULTIPLE_RESPONSE           =  15 # Value Set
58    ATT_READ_BY_GROUP_TYPE_REQUEST       =  16 # Start Handle, Ending Handle, UUID
59    ATT_READ_BY_GROUP_TYPE_RESPONSE      =  17 # Length, Attribute Data List
60    ATT_WRITE_REQUEST                    =  18 # Attribute Handle, Attribute Value
61    ATT_WRITE_RESPONSE                   =  19 # -
62    ATT_PREPARE_WRITE_REQUEST            =  22 # Attribute Handle, Value Offset, Part Attribute Value
63    ATT_PREPARE_WRITE_RESPONSE           =  23 # Attribute Handle, Value Offset, Part Attribute Value
64    ATT_EXECUTE_WRITE_REQUEST            =  24 # Flags
65    ATT_EXECUTE_WRITE_RESPONSE           =  25 # -
66    ATT_HANDLE_VALUE_NOTIFICATION        =  27 # Attribute Handle, Attribute Value
67    ATT_HANDLE_VALUE_INDICATION          =  29 # Attribute Handle, Attribute Value
68    ATT_HANDLE_VALUE_CONFIRMATION        =  30 # -
69    ATT_INVALID_REQUEST                  =  31 # -
70    ATT_WRITE_COMMAND                    =  82 # Attribute Handle, Attribute Value (64+18)
71    ATT_INVALID_COMMAND                  =  95 # -
72    ATT_SIGNED_WRITE_COMMAND             = 210 # Attribute Handle, Attribute Value, Authentication Signature (128+64+18)
73
74class ATTError(IntEnum):
75    ATT_ERROR_INVALID_HANDLE                       = 0x01 # The attribute handle given was not valid on this server.
76    ATT_ERROR_READ_NOT_PERMITTED                   = 0x02 # The attribute cannot be read.
77    ATT_ERROR_WRITE_NOT_PERMITTED                  = 0x03 # The attribute cannot be written.
78    ATT_ERROR_INVALID_PDU                          = 0x04 # The attribute PDU was invalid.
79    ATT_ERROR_INSUFFICIENT_AUTHENTICATION          = 0x05 # The attribute requires authentication before it can be read or written.
80    ATT_ERROR_REQUEST_NOT_SUPPORTED                = 0x06 # Attribute server does not support the request received from the client.
81    ATT_ERROR_INVALID_OFFSET                       = 0x07 # Offset specified was past the end of the attribute.
82    ATT_ERROR_INSUFFICIENT_AUTHORIZATION           = 0x08 # The attribute requires authorization before it can be read or written.
83    ATT_ERROR_PREPARE_QUEUE_FULL                   = 0x09 # Too many prepare writes have been queued.
84    ATT_ERROR_ATTRIBUTE_NOT_FOUND                  = 0x0A # No attribute found within the given attribute handle range.
85    ATT_ERROR_ATTRIBUTE_NOT_LONG                   = 0x0B # The attribute cannot be read using the Read Blob Request.
86    ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE     = 0x0C # The Encryption Key Size used for encrypting this link is insufficient.
87    ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH       = 0x0D # The attribute value length is invalid for the operation.
88    ATT_ERROR_UNLIKELY_ERROR                       = 0x0E # The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested.
89    ATT_ERROR_INSUFFICIENT_ENCRYPTION              = 0x0F # The attribute requires encryption before it can be read or written.
90    ATT_ERROR_UNSUPPORTED_GROUP_TYPE               = 0x10 # The attribute type is not a supported grouping attribute as defined by a higher layer specification.
91    ATT_ERROR_INSUFFICIENT_RESOURCES               = 0x11 # Insufficient Resources to complete the request.
92    ATT_ERROR_APPLICATION_ERROR                    = 0x80 # Application error code defined by a higher layer specification.
93    ATT_ERROR_WRITE_REQUEST_REJECTED               = 0xFC # Write Request Rejected.
94    ATT_ERROR_CCC_DESCRIPTOR_IMPROPERLY_CONFIGURED = 0xFD # Client Characteristic Configuration Descriptor Improperly Configured.
95    ATT_ERROR_PROCEDURE_ALREADY_IN_PROGRESS        = 0xFE # Procedure Already in Progress.
96    ATT_ERROR_OUT_OF_RANGE                         = 0xFF # Attribute value is out of range as defined by a profile or service specification.
97
98class ATTData:
99
100    def __init__(self):
101        self.data = [];
102
103    def encode(self, opcode, *args):
104      #
105      # encode ATT_EXCH_MTU_REQUEST, <mtu>
106      #       where <mtu> 2 octets
107      #
108        if   ( opcode == ATTOpcode.ATT_EXCH_MTU_REQUEST ):
109            self.data = [ opcode ] + toArray( args[0], 2 );
110      #
111      # encode ATT_FIND_INFORMATION_REQUEST, <start_handle>, <end_handle>
112      #       where <start_handle> 2 octets; <end_handle> 2 octets
113      #
114        elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_REQUEST ):
115            self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 );
116      #
117      # encode ATT_FIND_BY_TYPE_VALUE_REQUEST, <start_handle>, <end_handle>, <attribute_type>, <attribute_values>...
118      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_type> 2 octets UUID; <attribute_values> 1 octet each
119      #
120        elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_REQUEST ):
121            self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ) + toArray( args[2], 2 );
122            for arg in args[3:]:
123                self.data += arg;
124      #
125      # encode ATT_READ_BY_TYPE_REQUEST, <start_handle>, <end_handle>, <attribute_group_type>
126      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_group_type> 2 or 16 octets UUID
127      #
128        elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_REQUEST ):
129            self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ) + toArray( args[2], 2 if args[2] <= 0xFFFF else 16);
130      #
131      # encode ATT_READ_REQUEST, <attribute_handle>
132      #       where <attribute_handle> 2 octets
133      #
134        elif ( opcode == ATTOpcode.ATT_READ_REQUEST ):
135            self.data = [ opcode ] + toArray( args[0], 2 );
136      #
137      # encode ATT_READ_BLOB_REQUEST, <attribute_handle>, <value_offset>
138      #       where <attribute_handle> 2 octets; <value_offset> 2 octets
139      #
140        elif ( opcode == ATTOpcode.ATT_READ_BLOB_REQUEST ):
141            self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 );
142      #
143      # encode ATT_READ_MULTIPLE_REQUEST, <handles>...
144      #       where <handles> 2 octets each
145      #
146        elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_REQUEST ):
147            self.data = [ opcode ];
148            for arg in args[0]:
149                self.data += toArray( arg, 2 );
150      #
151      # encode ATT_READ_BY_GROUP_TYPE_REQUEST, <start_handle>, <end_handle>, <attribute_group_type>
152      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_group_type> 2 or 16 octets UUID
153      #
154        elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_REQUEST ):
155            self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 ) + toArray( args[2], 2 if args[2] <= 0xFFFF else 16);
156      #
157      # encode ATT_WRITE_REQUEST, <attribute_handle>, <values>...
158      #       where <attribute_handle> 2 octets; <values> 1 octet each
159      #
160        elif ( opcode == ATTOpcode.ATT_WRITE_REQUEST ):
161            self.data = [ opcode ] + toArray( args[0], 2 );
162            for arg in args[1:]:
163                self.data += arg;
164      #
165      # encode ATT_PREPARE_WRITE_REQUEST, <attribute_handle>, <value_offset>, <values>...
166      #       where <attribute_handle> 2 octets; <value_offset> 2 octets; <values> 1 octet each
167      #
168        elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_REQUEST ):
169            self.data = [ opcode ] + toArray( args[0], 2 ) + toArray( args[1], 2 );
170            for arg in args[2:]:
171                self.data += arg;
172      #
173      # encode ATT_EXECUTE_WRITE_REQUEST, <flags>
174      #       where <flags> 1 octet
175      #
176        elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_REQUEST ):
177            self.data = [ opcode, args[0] ];
178      #
179      # encode ATT_HANDLE_VALUE_CONFIRMATION
180      #
181        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_CONFIRMATION ):
182            self.data = [ opcode ];
183      #
184      # encode ATT_WRITE_COMMAND, <attribute_handle>, <values>...
185      #       where <attribute_handle> 2 octets; <values> 1 octet each
186      #
187        elif ( opcode == ATTOpcode.ATT_WRITE_COMMAND ):
188            self.data = [ opcode ] + toArray( args[0], 2 );
189            for arg in args[1:]:
190                self.data += arg;
191      #
192      # encode ATT_SIGNED_WRITE_COMMAND, <attribute_handle>, <signature>, <values>...
193      #       where <attribute_handle> 2 octets; <signature> 12 octets; <values> 1 octet each
194      #
195        elif ( opcode == ATTOpcode.ATT_SIGNED_WRITE_COMMAND ):
196            self.data = [ opcode ] + toArray( args[0], 2 ) + args[2:] + toArray( args[1], 12 );
197      #
198      # encode Illegal command...
199      #
200        else:
201            self.data = [ opcode ];
202            for arg in args:
203                self.data += arg;
204      #
205      # The first two octets in the L2CAP PDU contains the length of the entire L2CAP PDU in octets, excluding the Length and CID fields.
206      #
207        if len(self.data) > 0:
208            self.data = toArray( len(self.data), 2 ) + toArray( ATT_CID, 2 ) + self.data;
209        return self.data;
210
211    def decode(self, data):
212        self.data = data[:];
213        size = toNumber( data[:2] );
214        cid = toNumber( data[2:4] );
215        opcode = ATTOpcode(data[4]);
216
217        result = { "opcode": opcode };
218      #
219      # decode ATT_ERROR_RESPONSE: <request_opcode>, <attribute_handle>, <error_code>
220      #       where <request_opcode> 1 octet; <attribute_handle> 2 octets; <error_code> 1 octet
221      #
222        if   ( opcode == ATTOpcode.ATT_ERROR_RESPONSE ):
223            result["request opcode"] = ATTOpcode(data[5]);
224            result["handle"] = [ toNumber( data[6:8] ) ];
225            result["error"] = ATTError(data[8]);
226      #
227      # decode ATT_EXCH_MTU_RESPONSE: <mtu>
228      #       where <mtu> 2 octets
229      #
230        elif ( opcode == ATTOpcode.ATT_EXCH_MTU_RESPONSE ):
231            result["mtu"] = toNumber( data[5:7] );
232      #
233      # decode ATT_FIND_INFORMATION_RESPONSE: <format>, { <handle>, <uuid> }...
234      #       where <format> 1 octet; <handle> 2 octets; <uuid> 2 octets if <format> == 1 else 16 octets
235      #
236        elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_RESPONSE ):
237            result["handle"] = [];
238            result["uuid"] = [];
239            length = 4 if data[5] == 1 else 18;
240
241            n = 6;
242            while n < size+3:
243                result["handle"] += [ toNumber( data[n:n+2] ) ];
244                result["uuid"] += [ toNumber( data[n+2:n+length] ) ];
245                n += length;
246      #
247      # decode ATT_FIND_BY_TYPE_VALUE_RESPONSE: <handle>...
248      #       <handle> 2 octets
249      #
250        elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_RESPONSE ):
251            result["handle"] = [];
252
253            n = 5;
254            while n < size+3:
255                result["handle"] += [ toNumber( data[n:n+2] ) ];
256                n += 2;
257      #
258      # decode ATT_READ_BY_TYPE_RESPONSE: <length>, { <handle>, <value>... }
259      #       where <length> 1 octet holding the number of octets in each { <handle>, <value>... } set
260      #       <handle> 2 octets; <value> 1 octet each (<length>-2) octets in total
261      #
262        elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_RESPONSE ):
263            result["handle"] = [];
264            result["value"] = [];
265
266            n = 6;
267            while n < size+3:
268                result["handle"] += [ toNumber( data[n:n+2] ) ];
269                result["value"] += [ data[n+2:n+data[5]] ];
270                n += data[5];
271      #
272      # decode ATT_READ_RESPONSE: <value>...
273      #       where <value> 1 octet each
274      #
275        elif ( opcode == ATTOpcode.ATT_READ_RESPONSE ):
276            result["value"] = data[5:];
277      #
278      # decode ATT_READ_BLOB_RESPONSE: <value>...
279      #       where <value> 1 octet each
280      #
281        elif ( opcode == ATTOpcode.ATT_READ_BLOB_RESPONSE ):
282            result["value"] = data[5:];
283      #
284      # decode ATT_READ_MULTIPLE_RESPONSE: <value>...
285      #       where <value> 1 octet each
286      #
287        elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_RESPONSE ):
288            result["value"] = data[5:];
289      #
290      # decode ATT_READ_BY_GROUP_TYPE_RESPONSE: <length>, { <attribute_handle>, <end_group_handle>, <value>... }...
291      #       where <length> 1 octet; <attribute_handle> 2 octets; <end_group_handle> 2 octets; <value> 1 octet each (<length> - 4)
292      #
293        elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_RESPONSE ):
294            result["first_handle"] = [];
295            result["last_handle"] = [];
296            result["value"] = [];
297
298            n = 6;
299            while n < size+3:
300                result["first_handle"] += [ toNumber( data[n:n+2] ) ];
301                result["last_handle"] += [ toNumber( data[n+2:n+4] ) ];
302                result["value"] += [ data[n+4:n+data[5]] ];
303                n += data[5];
304      #
305      # decode ATT_WRITE_RESPONSE:
306      #       where
307      #
308      #  elif ( opcode == ATTOpcode.ATT_WRITE_RESPONSE ):
309
310      #
311      # decode ATT_PREPARE_WRITE_RESPONSE: <handle>, <offset>, <value>...
312      #       where <handle> 2 octets; <offset> 2 octets; <value> 1 octet each
313      #
314        elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_RESPONSE ):
315            result["handle"] = toNumber( data[5:7] );
316            result["offset"] = toNumber( data[7:9] );
317            result["value"] = data[9:];
318      #
319      # decode ATT_EXECUTE_WRITE_RESPONSE:
320      #       where
321      #
322      #  elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_RESPONSE ):
323
324      #
325      # decode ATT_HANDLE_VALUE_NOTIFICATION: <handle>, <value>...
326      #       where <handle> 2 octets; <value> 1 octet each
327      #
328        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_NOTIFICATION ):
329            result["handle"] = toNumber( data[5:7] );
330            result["value"] = data[7:];
331      #
332      # decode ATT_HANDLE_VALUE_INDICATION: <handle>, <value>...
333      #       where <handle> 2 octets; <value> 1 octet each
334      #
335        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_INDICATION ):
336            result["handle"] = toNumber( data[5:7] );
337            result["value"] = data[7:];
338
339        return result;
340
341    def __errorText(self, error):
342        result = ATTError(error).name;
343        result = '_'.join([_.lower().capitalize() if _ != 'ATT' else _ for _ in result.split('_')]);
344        return result;
345
346    def __opcodeName(self, opcode):
347        result = ATTOpcode(opcode).name;
348        result = '_'.join([_.lower().capitalize() if _ != 'ATT' else _ for _ in result.split('_')]);
349        return result;
350
351    def __hexByteArray(self, start, end):
352        result = '';
353        for n in range(start, min(len(self.data), end)):
354            if len(result) > 0:
355                result += ' ';
356            result += '%02X' % self.data[n];
357        return result;
358
359    def __hexWordArray(self, start, end):
360        result = '';
361        for n in range(start, end, 2):
362            if len(result) > 0:
363                result += ' ';
364            result += '%04X' % toNumber(self.data[n:n+2]);
365        return result;
366
367    def __str__(self):
368        size = toNumber( self.data[:2] );
369        cid = toNumber( self.data[2:4] );
370        opcode = ATTOpcode( self.data[4] );
371
372        result = self.__opcodeName( opcode );
373      #
374      # ATT_ERROR_RESPONSE: <request_opcode>, <attribute_handle>, <error_code>
375      #       where <request_opcode> 1 octet; <attribute_handle> 2 octets; <error_code> 1 octet
376      #
377        if   ( opcode == ATTOpcode.ATT_ERROR_RESPONSE ):
378            result += ' request=%s handle=0x%04X error=%s' % (self.__opcodeName(self.data[5]), toNumber(self.data[6:8]), self.__errorText(self.data[8]));
379      #
380      # ATT_EXCH_MTU_REQUEST: <mtu>
381      #       where <mtu> 2 octets
382      #
383        elif ( opcode == ATTOpcode.ATT_EXCH_MTU_REQUEST ):
384            result += ' mtu=%d' % toNumber(self.data[5:7]);
385      #
386      # ATT_EXCH_MTU_RESPONSE: <mtu>
387      #       where <mtu> 2 octets
388      #
389        elif ( opcode == ATTOpcode.ATT_EXCH_MTU_RESPONSE ):
390            result += ' mtu=%d' % toNumber(self.data[5:7]);
391      #
392      # ATT_FIND_INFORMATION_REQUEST: <start_handle>, <end_handle>
393      #       where <start_handle> 2 octets; <end_handle> 2 octets
394      #
395        elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_REQUEST ):
396            result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]));
397      #
398      # ATT_FIND_INFORMATION_RESPONSE: <format>, { <handle>, <uuid> }...
399      #       where <format> 1 octet; <handle> 2 octets; <uuid> 2 octets if <format> == 1 else 16 octets
400      #
401        elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_RESPONSE ):
402            result += ' format=%d' % self.data[5];
403            n, length = 6, 4 if self.data[5] == 1 else 18;
404            while n < size+3:
405                if length == 4:
406                    result += ' { 0x%04X 0x%04X }' % (toNumber(self.data[n:n+2]), toNumber(self.data[n+2:n+length]));
407                else:
408                    result += ' { 0x%04X %s }' % (toNumber(self.data[n:n+2]), self.uuid(toNumber(self.data[n+2:n+length])));
409                n += length;
410      #
411      # ATT_FIND_BY_TYPE_VALUE_REQUEST: <start_handle>, <end_handle>, <attribute_type>, <attribute_values>...
412      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_type> 2 octets UUID; <attribute_values> 1 octet each
413      #
414        elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_REQUEST ):
415            result += ' start=0x%04X end=0x%04X type=0x%04X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]), toNumber(self.data[9:11]), self.__hexByteArray(11,size+4));
416      #
417      # ATT_FIND_BY_TYPE_VALUE_RESPONSE: <handle>...
418      #       <handle> 2 octets
419      #
420        elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_RESPONSE ):
421            result += ' handles: %s' % self.__hexWordArray(5, size+4);
422      #
423      # ATT_READ_BY_TYPE_REQUEST: <start_handle>, <end_handle>, <attribute_group_type>
424      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_group_type> 2 or 16 octets UUID
425      #
426        elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_REQUEST ):
427            result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]));
428            if size > 7:
429                result += ' group type=%s' % self.uuid(toNumber(self.data[9:25]));
430            else:
431                result += ' group type=%04X' % toNumber(self.data[9:11]);
432      #
433      # ATT_READ_BY_TYPE_RESPONSE: <length>, { <handle>, <value>... }
434      #       where <length> 1 octet holding the number of octets in each { <handle>, <value>... } set
435      #       <handle> 2 octets; <value> 1 octet each (<length>-2) octets in total
436      #
437        elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_RESPONSE ):
438            result += ' length=%d' % self.data[5];
439            n = 6;
440            while n < size+3:
441                result += ' { handle=0x%04X values: %s }' % (toNumber(self.data[n:n+2]), self.__hexByteArray(n+2, n+self.data[5]));
442                n += self.data[5];
443      #
444      # ATT_READ_REQUEST: <attribute_handle>
445      #       where <attribute_handle> 2 octets
446      #
447        elif ( opcode == ATTOpcode.ATT_READ_REQUEST ):
448            result += ' handle=0x%04X' % toNumber(self.data[5:7]);
449      #
450      # ATT_READ_RESPONSE: <value>...
451      #       where <value> 1 octet each
452      #
453        elif ( opcode == ATTOpcode.ATT_READ_RESPONSE ):
454            result += ' values: %s' % self.__hexByteArray(5, size+4);
455      #
456      # ATT_READ_BLOB_REQUEST: <attribute_handle>, <value_offset>
457      #       where <attribute_handle> 2 octets; <value_offset> 2 octets
458      #
459        elif ( opcode == ATTOpcode.ATT_READ_BLOB_REQUEST ):
460            result += ' handle=0x%04X offset=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]));
461      #
462      # ATT_READ_BLOB_RESPONSE: <value>...
463      #       where <value> 1 octet each
464      #
465        elif ( opcode == ATTOpcode.ATT_READ_BLOB_RESPONSE ):
466            result += ' values: %s' % self.__hexByteArray(5, size+4);
467      #
468      # ATT_READ_MULTIPLE_REQUEST: <handles>...
469      #       where <handles> 2 octets each
470      #
471        elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_REQUEST ):
472            result += ' handles: %s' % self.__hexWordArray(5, size+4);
473      #
474      # ATT_READ_MULTIPLE_RESPONSE: <value>...
475      #       where <value> 1 octet each
476      #
477        elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_RESPONSE ):
478            result += ' values: %s' % self.__hexByteArray(5, size+4);
479      #
480      # ATT_READ_BY_GROUP_TYPE_REQUEST: <start_handle>, <end_handle>, <attribute_group_type>
481      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_group_type> 2 or 16 octets UUID
482      #
483        elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_REQUEST ):
484            result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]));
485            if size > 7:
486                result += ' group type=%s' % uuid(toNumber(self.data[9:25]));
487            else:
488                result += ' group type=%04X' % toNumber(self.data[9:11]);
489      #
490      # ATT_READ_BY_GROUP_TYPE_RESPONSE: <length>, { <attribute_handle>, <end_group_handle>, <value>... }...
491      #       where <length> 1 octet; <attribute_handle> 2 octets; <end_group_handle> 2 octets; <value> 1 octet each (<length> - 4)
492      #
493        elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_RESPONSE ):
494            result += ' length=%d' % self.data[5];
495            n, indent = 6, len(result);
496            while n < size+3:
497                if n > 6:
498                    result += '\n%*s' % (indent, ' ');
499                result += ' { handle=0x%04X end group=0x%04X values: %s }' % (toNumber(self.data[n:n+2]), toNumber(self.data[n+2:n+4]), self.__hexByteArray(n+4, n+self.data[5]));
500                n += self.data[5];
501      #
502      # ATT_WRITE_REQUEST: <attribute_handle>, <values>...
503      #       where <attribute_handle> 2 octets; <values> 1 octet each
504      #
505        elif ( opcode == ATTOpcode.ATT_WRITE_REQUEST ):
506            result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4));
507      #
508      # ATT_WRITE_RESPONSE:
509      #       where
510      #
511        elif ( opcode == ATTOpcode.ATT_WRITE_RESPONSE ):
512            pass;
513      #
514      # ATT_PREPARE_WRITE_REQUEST: <attribute_handle>, <value_offset>, <values>...
515      #       where <attribute_handle> 2 octets; <value_offset> 2 octets; <values> 1 octet each
516      #
517        elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_REQUEST ):
518            result += ' handle=0x%04X offset=0x%04X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]), self.__hexByteArray(9, size+4));
519      #
520      # ATT_PREPARE_WRITE_RESPONSE: <handle>, <offset>, <value>...
521      #       where <handle> 2 octets; <offset> 2 octets; <value> 1 octet each
522      #
523        elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_RESPONSE ):
524            result += ' handle=0x%04X offset=0x%04X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]), self.__hexByteArray(9, size+4));
525      #
526      # ATT_EXECUTE_WRITE_REQUEST: <flags>
527      #       where <flags> 1 octet
528      #
529        elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_REQUEST ):
530            result += ' flags=0x%02X' % self.data[5];
531      #
532      # ATT_EXECUTE_WRITE_RESPONSE:
533      #       where
534      #
535        elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_RESPONSE ):
536            pass;
537      #
538      # ATT_HANDLE_VALUE_NOTIFICATION: <handle>, <value>...
539      #       where <handle> 2 octets; <value> 1 octet each
540      #
541        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_NOTIFICATION ):
542            result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4));
543      #
544      # ATT_HANDLE_VALUE_INDICATION: <handle>, <value>...
545      #       where <handle> 2 octets; <value> 1 octet each
546      #
547        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_INDICATION ):
548            result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4));
549      #
550      # ATT_HANDLE_VALUE_CONFIRMATION:
551      #
552        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_CONFIRMATION ):
553            pass;
554      #
555      # ATT_WRITE_COMMAND: <attribute_handle>, <values>...
556      #       where <attribute_handle> 2 octets; <values> 1 octet each
557      #
558        elif ( opcode == ATTOpcode.ATT_WRITE_COMMAND ):
559            result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4));
560      #
561      # ATT_SIGNED_WRITE_COMMAND: <attribute_handle>, <signature>, <values>...
562      #       where <attribute_handle> 2 octets; <signature> 12 octets; <values> 1 octet each
563      #
564        elif ( opcode == ATTOpcode.ATT_SIGNED_WRITE_COMMAND ):
565            result += ' handle=0x%04X signature=0x%024X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:19]), self.__hexByteArray(19, size+4));
566      #
567      # ATT_INVALID_REQUEST:
568      #
569        elif ( opcode == ATTOpcode.ATT_INVALID_REQUEST ):
570            result += ' ' + self.__hexByteArray(5, size+4);
571      #
572      # ATT_INVALID_COMMAND:
573      #
574        elif ( opcode == ATTOpcode.ATT_INVALID_COMMAND ):
575            result += ' ' + self.__hexByteArray(5, size+4);
576
577        return result;
578
579    def uuid(self, data):
580        if isinstance(data, str):
581           uuid = 0;
582           octet = '';
583           for char in data:
584               if char in string.hexdigits:
585                   octet += char;
586                   if len(octet) == 2:
587                       uuid <<= 8;
588                       uuid += int(octet, 16);
589                       octet = '';
590        else:
591            if data <= 0xFFFFFFFF:
592                uuid = ('%08X' % data) + '00001000800000805F9B34FB'
593            else:
594                uuid = ('%032X' % data)
595            uuid = uuid[:8] + '-' + uuid[8:12] + '-' + uuid[12:16] + '-' + uuid[16:20] + '-' + uuid[20:];
596        return uuid;
597
598    def __formatEnumSet(self, value, texts):
599        txt = '';
600        for n in range(len(texts)):
601            if ( value & (1<<n) ):
602                if ( len(txt) ):
603                    txt += '|';
604                txt += texts[n];
605        return txt;
606
607    def property(self, data):
608        propertyTexts = [ 'BROADCAST', 'READ', 'WRITE WITHOUT RESPONSE', 'WRITE', 'NOTIFY', 'INDICATE', 'AUTHENTICATED SIGNED WRITES', 'EXTENDED PROPERTIES' ];
609        return self.__formatEnumSet(data, propertyTexts);
610
611    def permission(self, data):
612        permissionTexts = [ 'READ', 'WRITE', 'READ ENCRYPTED', 'WRITE ENCRYPTED', 'READ AUTHENTICATED', 'WRITE AUTHENTICATED', 'WRITE PREPARED', 'READ AUTHORIZED', 'WRITE AUTHORIZED' ];
613        return self.__formatEnumSet(data, permissionTexts);
614
615    def isPermissionError(self, code):
616        permissionErrors = [ ATTError.ATT_ERROR_READ_NOT_PERMITTED, ATTError.ATT_ERROR_INSUFFICIENT_AUTHENTICATION, ATTError.ATT_ERROR_INSUFFICIENT_AUTHORIZATION, ATTError.ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE, ATTError.ATT_ERROR_INSUFFICIENT_ENCRYPTION ];
617        return code in permissionErrors;
618
619    def error(self, code):
620        errorTexts = {
621            ATTError.ATT_ERROR_INVALID_HANDLE:
622                "The attribute handle given was not valid on this server.",
623            ATTError.ATT_ERROR_READ_NOT_PERMITTED:
624                "The attribute cannot be read.",
625            ATTError.ATT_ERROR_WRITE_NOT_PERMITTED:
626                "The attribute cannot be written.",
627            ATTError.ATT_ERROR_INVALID_PDU:
628                "The attribute PDU was invalid.",
629            ATTError.ATT_ERROR_INSUFFICIENT_AUTHENTICATION:
630                "The attribute requires authentication before it can be read or written.",
631            ATTError.ATT_ERROR_REQUEST_NOT_SUPPORTED:
632                "Attribute server does not support the request received from the client.",
633            ATTError.ATT_ERROR_INVALID_OFFSET:
634                "Offset specified was past the end of the attribute.",
635            ATTError.ATT_ERROR_INSUFFICIENT_AUTHORIZATION:
636                "The attribute requires authorization before it can be read or written.",
637            ATTError.ATT_ERROR_PREPARE_QUEUE_FULL:
638                "Too many prepare writes have been queued.",
639            ATTError.ATT_ERROR_ATTRIBUTE_NOT_FOUND:
640                "No attribute found within the given attribute handle range.",
641            ATTError.ATT_ERROR_ATTRIBUTE_NOT_LONG:
642                "The attribute cannot be read using the Read Blob Request.",
643            ATTError.ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
644                "The Encryption Key Size used for encrypting this link is insufficient.",
645            ATTError.ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH:
646                "The attribute value length is invalid for the operation.",
647            ATTError.ATT_ERROR_UNLIKELY_ERROR:
648                "The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested.",
649            ATTError.ATT_ERROR_INSUFFICIENT_ENCRYPTION:
650                "The attribute requires encryption before it can be read or written.",
651            ATTError.ATT_ERROR_UNSUPPORTED_GROUP_TYPE:
652                "The attribute type is not a supported grouping attribute as defined by a higher layer specification.",
653            ATTError.ATT_ERROR_INSUFFICIENT_RESOURCES:
654                "Insufficient Resources to complete the request.",
655            ATTError.ATT_ERROR_APPLICATION_ERROR:
656                "Application error code defined by a higher layer specification.",
657            ATTError.ATT_ERROR_WRITE_REQUEST_REJECTED:
658                "Write Request Rejected.",
659            ATTError.ATT_ERROR_CCC_DESCRIPTOR_IMPROPERLY_CONFIGURED:
660                "Client Characteristic Configuration Descriptor Improperly Configured.",
661            ATTError.ATT_ERROR_PROCEDURE_ALREADY_IN_PROGRESS:
662                "Procedure Already in Progress.",
663            ATTError.ATT_ERROR_OUT_OF_RANGE:
664                "Attribute value is out of range as defined by a profile or service specification."
665        };
666        if code in errorTexts:
667            return errorTexts[code];
668        else:
669            return "Unknown error code (%d)" % code;
670