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