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 = str(ATTError( error ));
343        result = result.split('.')[1];
344        result = '_'.join([_.lower().capitalize() if _ != 'ATT' else _ for _ in result.split('_')]);
345        return result;
346
347    def __opcodeName(self, opcode):
348        result = str(ATTOpcode( opcode ));
349        result = result.split('.')[1];
350        result = '_'.join([_.lower().capitalize() if _ != 'ATT' else _ for _ in result.split('_')]);
351        return result;
352
353    def __hexByteArray(self, start, end):
354        result = '';
355        for n in range(start, min(len(self.data), end)):
356            if len(result) > 0:
357                result += ' ';
358            result += '%02X' % self.data[n];
359        return result;
360
361    def __hexWordArray(self, start, end):
362        result = '';
363        for n in range(start, end, 2):
364            if len(result) > 0:
365                result += ' ';
366            result += '%04X' % toNumber(self.data[n:n+2]);
367        return result;
368
369    def __str__(self):
370        size = toNumber( self.data[:2] );
371        cid = toNumber( self.data[2:4] );
372        opcode = ATTOpcode( self.data[4] );
373
374        result = self.__opcodeName( opcode );
375      #
376      # ATT_ERROR_RESPONSE: <request_opcode>, <attribute_handle>, <error_code>
377      #       where <request_opcode> 1 octet; <attribute_handle> 2 octets; <error_code> 1 octet
378      #
379        if   ( opcode == ATTOpcode.ATT_ERROR_RESPONSE ):
380            result += ' request=%s handle=0x%04X error=%s' % (self.__opcodeName(self.data[5]), toNumber(self.data[6:8]), self.__errorText(self.data[8]));
381      #
382      # ATT_EXCH_MTU_REQUEST: <mtu>
383      #       where <mtu> 2 octets
384      #
385        elif ( opcode == ATTOpcode.ATT_EXCH_MTU_REQUEST ):
386            result += ' mtu=%d' % toNumber(self.data[5:7]);
387      #
388      # ATT_EXCH_MTU_RESPONSE: <mtu>
389      #       where <mtu> 2 octets
390      #
391        elif ( opcode == ATTOpcode.ATT_EXCH_MTU_RESPONSE ):
392            result += ' mtu=%d' % toNumber(self.data[5:7]);
393      #
394      # ATT_FIND_INFORMATION_REQUEST: <start_handle>, <end_handle>
395      #       where <start_handle> 2 octets; <end_handle> 2 octets
396      #
397        elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_REQUEST ):
398            result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]));
399      #
400      # ATT_FIND_INFORMATION_RESPONSE: <format>, { <handle>, <uuid> }...
401      #       where <format> 1 octet; <handle> 2 octets; <uuid> 2 octets if <format> == 1 else 16 octets
402      #
403        elif ( opcode == ATTOpcode.ATT_FIND_INFORMATION_RESPONSE ):
404            result += ' format=%d' % self.data[5];
405            n, length = 6, 4 if self.data[5] == 1 else 18;
406            while n < size+3:
407                if length == 4:
408                    result += ' { 0x%04X 0x%04X }' % (toNumber(self.data[n:n+2]), toNumber(self.data[n+2:n+length]));
409                else:
410                    result += ' { 0x%04X %s }' % (toNumber(self.data[n:n+2]), self.uuid(toNumber(self.data[n+2:n+length])));
411                n += length;
412      #
413      # ATT_FIND_BY_TYPE_VALUE_REQUEST: <start_handle>, <end_handle>, <attribute_type>, <attribute_values>...
414      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_type> 2 octets UUID; <attribute_values> 1 octet each
415      #
416        elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_REQUEST ):
417            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));
418      #
419      # ATT_FIND_BY_TYPE_VALUE_RESPONSE: <handle>...
420      #       <handle> 2 octets
421      #
422        elif ( opcode == ATTOpcode.ATT_FIND_BY_TYPE_VALUE_RESPONSE ):
423            result += ' handles: %s' % self.__hexWordArray(5, size+4);
424      #
425      # ATT_READ_BY_TYPE_REQUEST: <start_handle>, <end_handle>, <attribute_group_type>
426      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_group_type> 2 or 16 octets UUID
427      #
428        elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_REQUEST ):
429            result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]));
430            if size > 7:
431                result += ' group type=%s' % self.uuid(toNumber(self.data[9:25]));
432            else:
433                result += ' group type=%04X' % toNumber(self.data[9:11]);
434      #
435      # ATT_READ_BY_TYPE_RESPONSE: <length>, { <handle>, <value>... }
436      #       where <length> 1 octet holding the number of octets in each { <handle>, <value>... } set
437      #       <handle> 2 octets; <value> 1 octet each (<length>-2) octets in total
438      #
439        elif ( opcode == ATTOpcode.ATT_READ_BY_TYPE_RESPONSE ):
440            result += ' length=%d' % self.data[5];
441            n = 6;
442            while n < size+3:
443                result += ' { handle=0x%04X values: %s }' % (toNumber(self.data[n:n+2]), self.__hexByteArray(n+2, n+self.data[5]));
444                n += self.data[5];
445      #
446      # ATT_READ_REQUEST: <attribute_handle>
447      #       where <attribute_handle> 2 octets
448      #
449        elif ( opcode == ATTOpcode.ATT_READ_REQUEST ):
450            result += ' handle=0x%04X' % toNumber(self.data[5:7]);
451      #
452      # ATT_READ_RESPONSE: <value>...
453      #       where <value> 1 octet each
454      #
455        elif ( opcode == ATTOpcode.ATT_READ_RESPONSE ):
456            result += ' values: %s' % self.__hexByteArray(5, size+4);
457      #
458      # ATT_READ_BLOB_REQUEST: <attribute_handle>, <value_offset>
459      #       where <attribute_handle> 2 octets; <value_offset> 2 octets
460      #
461        elif ( opcode == ATTOpcode.ATT_READ_BLOB_REQUEST ):
462            result += ' handle=0x%04X offset=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]));
463      #
464      # ATT_READ_BLOB_RESPONSE: <value>...
465      #       where <value> 1 octet each
466      #
467        elif ( opcode == ATTOpcode.ATT_READ_BLOB_RESPONSE ):
468            result += ' values: %s' % self.__hexByteArray(5, size+4);
469      #
470      # ATT_READ_MULTIPLE_REQUEST: <handles>...
471      #       where <handles> 2 octets each
472      #
473        elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_REQUEST ):
474            result += ' handles: %s' % self.__hexWordArray(5, size+4);
475      #
476      # ATT_READ_MULTIPLE_RESPONSE: <value>...
477      #       where <value> 1 octet each
478      #
479        elif ( opcode == ATTOpcode.ATT_READ_MULTIPLE_RESPONSE ):
480            result += ' values: %s' % self.__hexByteArray(5, size+4);
481      #
482      # ATT_READ_BY_GROUP_TYPE_REQUEST: <start_handle>, <end_handle>, <attribute_group_type>
483      #       where <start_handle> 2 octets; <end_handle> 2 octets; <attribute_group_type> 2 or 16 octets UUID
484      #
485        elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_REQUEST ):
486            result += ' start=0x%04X end=0x%04X' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]));
487            if size > 7:
488                result += ' group type=%s' % uuid(toNumber(self.data[9:25]));
489            else:
490                result += ' group type=%04X' % toNumber(self.data[9:11]);
491      #
492      # ATT_READ_BY_GROUP_TYPE_RESPONSE: <length>, { <attribute_handle>, <end_group_handle>, <value>... }...
493      #       where <length> 1 octet; <attribute_handle> 2 octets; <end_group_handle> 2 octets; <value> 1 octet each (<length> - 4)
494      #
495        elif ( opcode == ATTOpcode.ATT_READ_BY_GROUP_TYPE_RESPONSE ):
496            result += ' length=%d' % self.data[5];
497            n, indent = 6, len(result);
498            while n < size+3:
499                if n > 6:
500                    result += '\n%*s' % (indent, ' ');
501                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]));
502                n += self.data[5];
503      #
504      # ATT_WRITE_REQUEST: <attribute_handle>, <values>...
505      #       where <attribute_handle> 2 octets; <values> 1 octet each
506      #
507        elif ( opcode == ATTOpcode.ATT_WRITE_REQUEST ):
508            result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4));
509      #
510      # ATT_WRITE_RESPONSE:
511      #       where
512      #
513        elif ( opcode == ATTOpcode.ATT_WRITE_RESPONSE ):
514            pass;
515      #
516      # ATT_PREPARE_WRITE_REQUEST: <attribute_handle>, <value_offset>, <values>...
517      #       where <attribute_handle> 2 octets; <value_offset> 2 octets; <values> 1 octet each
518      #
519        elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_REQUEST ):
520            result += ' handle=0x%04X offset=0x%04X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]), self.__hexByteArray(9, size+4));
521      #
522      # ATT_PREPARE_WRITE_RESPONSE: <handle>, <offset>, <value>...
523      #       where <handle> 2 octets; <offset> 2 octets; <value> 1 octet each
524      #
525        elif ( opcode == ATTOpcode.ATT_PREPARE_WRITE_RESPONSE ):
526            result += ' handle=0x%04X offset=0x%04X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:9]), self.__hexByteArray(9, size+4));
527      #
528      # ATT_EXECUTE_WRITE_REQUEST: <flags>
529      #       where <flags> 1 octet
530      #
531        elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_REQUEST ):
532            result += ' flags=0x%02X' % self.data[5];
533      #
534      # ATT_EXECUTE_WRITE_RESPONSE:
535      #       where
536      #
537        elif ( opcode == ATTOpcode.ATT_EXECUTE_WRITE_RESPONSE ):
538            pass;
539      #
540      # ATT_HANDLE_VALUE_NOTIFICATION: <handle>, <value>...
541      #       where <handle> 2 octets; <value> 1 octet each
542      #
543        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_NOTIFICATION ):
544            result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4));
545      #
546      # ATT_HANDLE_VALUE_INDICATION: <handle>, <value>...
547      #       where <handle> 2 octets; <value> 1 octet each
548      #
549        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_INDICATION ):
550            result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4));
551      #
552      # ATT_HANDLE_VALUE_CONFIRMATION:
553      #
554        elif ( opcode == ATTOpcode.ATT_HANDLE_VALUE_CONFIRMATION ):
555            pass;
556      #
557      # ATT_WRITE_COMMAND: <attribute_handle>, <values>...
558      #       where <attribute_handle> 2 octets; <values> 1 octet each
559      #
560        elif ( opcode == ATTOpcode.ATT_WRITE_COMMAND ):
561            result += ' handle=0x%04X values: %s' % (toNumber(self.data[5:7]), self.__hexByteArray(7, size+4));
562      #
563      # ATT_SIGNED_WRITE_COMMAND: <attribute_handle>, <signature>, <values>...
564      #       where <attribute_handle> 2 octets; <signature> 12 octets; <values> 1 octet each
565      #
566        elif ( opcode == ATTOpcode.ATT_SIGNED_WRITE_COMMAND ):
567            result += ' handle=0x%04X signature=0x%024X values: %s' % (toNumber(self.data[5:7]), toNumber(self.data[7:19]), self.__hexByteArray(19, size+4));
568      #
569      # ATT_INVALID_REQUEST:
570      #
571        elif ( opcode == ATTOpcode.ATT_INVALID_REQUEST ):
572            result += ' ' + self.__hexByteArray(5, size+4);
573      #
574      # ATT_INVALID_COMMAND:
575      #
576        elif ( opcode == ATTOpcode.ATT_INVALID_COMMAND ):
577            result += ' ' + self.__hexByteArray(5, size+4);
578
579        return result;
580
581    def uuid(self, data):
582        if isinstance(data, str):
583           uuid = 0;
584           octet = '';
585           for char in data:
586               if char in string.hexdigits:
587                   octet += char;
588                   if len(octet) == 2:
589                       uuid <<= 8;
590                       uuid += int(octet, 16);
591                       octet = '';
592        else:
593            if data <= 0xFFFFFFFF:
594                uuid = ('%08X' % data) + '00001000800000805F9B34FB'
595            else:
596                uuid = ('%032X' % data)
597            uuid = uuid[:8] + '-' + uuid[8:12] + '-' + uuid[12:16] + '-' + uuid[16:20] + '-' + uuid[20:];
598        return uuid;
599
600    def __formatEnumSet(self, value, texts):
601        txt = '';
602        for n in range(len(texts)):
603            if ( value & (1<<n) ):
604                if ( len(txt) ):
605                    txt += '|';
606                txt += texts[n];
607        return txt;
608
609    def property(self, data):
610        propertyTexts = [ 'BROADCAST', 'READ', 'WRITE WITHOUT RESPONSE', 'WRITE', 'NOTIFY', 'INDICATE', 'AUTHENTICATED SIGNED WRITES', 'EXTENDED PROPERTIES' ];
611        return self.__formatEnumSet(data, propertyTexts);
612
613    def permission(self, data):
614        permissionTexts = [ 'READ', 'WRITE', 'READ ENCRYPTED', 'WRITE ENCRYPTED', 'READ AUTHENTICATED', 'WRITE AUTHENTICATED', 'WRITE PREPARED', 'READ AUTHORIZED', 'WRITE AUTHORIZED' ];
615        return self.__formatEnumSet(data, permissionTexts);
616
617    def isPermissionError(self, code):
618        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 ];
619        return code in permissionErrors;
620
621    def error(self, code):
622        errorTexts = {
623            ATTError.ATT_ERROR_INVALID_HANDLE:
624                "The attribute handle given was not valid on this server.",
625            ATTError.ATT_ERROR_READ_NOT_PERMITTED:
626                "The attribute cannot be read.",
627            ATTError.ATT_ERROR_WRITE_NOT_PERMITTED:
628                "The attribute cannot be written.",
629            ATTError.ATT_ERROR_INVALID_PDU:
630                "The attribute PDU was invalid.",
631            ATTError.ATT_ERROR_INSUFFICIENT_AUTHENTICATION:
632                "The attribute requires authentication before it can be read or written.",
633            ATTError.ATT_ERROR_REQUEST_NOT_SUPPORTED:
634                "Attribute server does not support the request received from the client.",
635            ATTError.ATT_ERROR_INVALID_OFFSET:
636                "Offset specified was past the end of the attribute.",
637            ATTError.ATT_ERROR_INSUFFICIENT_AUTHORIZATION:
638                "The attribute requires authorization before it can be read or written.",
639            ATTError.ATT_ERROR_PREPARE_QUEUE_FULL:
640                "Too many prepare writes have been queued.",
641            ATTError.ATT_ERROR_ATTRIBUTE_NOT_FOUND:
642                "No attribute found within the given attribute handle range.",
643            ATTError.ATT_ERROR_ATTRIBUTE_NOT_LONG:
644                "The attribute cannot be read using the Read Blob Request.",
645            ATTError.ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
646                "The Encryption Key Size used for encrypting this link is insufficient.",
647            ATTError.ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH:
648                "The attribute value length is invalid for the operation.",
649            ATTError.ATT_ERROR_UNLIKELY_ERROR:
650                "The attribute request that was requested has encountered an error that was unlikely, and therefore could not be completed as requested.",
651            ATTError.ATT_ERROR_INSUFFICIENT_ENCRYPTION:
652                "The attribute requires encryption before it can be read or written.",
653            ATTError.ATT_ERROR_UNSUPPORTED_GROUP_TYPE:
654                "The attribute type is not a supported grouping attribute as defined by a higher layer specification.",
655            ATTError.ATT_ERROR_INSUFFICIENT_RESOURCES:
656                "Insufficient Resources to complete the request.",
657            ATTError.ATT_ERROR_APPLICATION_ERROR:
658                "Application error code defined by a higher layer specification.",
659            ATTError.ATT_ERROR_WRITE_REQUEST_REJECTED:
660                "Write Request Rejected.",
661            ATTError.ATT_ERROR_CCC_DESCRIPTOR_IMPROPERLY_CONFIGURED:
662                "Client Characteristic Configuration Descriptor Improperly Configured.",
663            ATTError.ATT_ERROR_PROCEDURE_ALREADY_IN_PROGRESS:
664                "Procedure Already in Progress.",
665            ATTError.ATT_ERROR_OUT_OF_RANGE:
666                "Attribute value is out of range as defined by a profile or service specification."
667        };
668        if code in errorTexts:
669            return errorTexts[code];
670        else:
671            return "Unknown error code (%d)" % code;
672