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