Loading android/pandora/mmi2grpc/mmi2grpc/gatt.py +231 −40 Original line number Diff line number Diff line Loading @@ -42,6 +42,8 @@ class GATTProxy(ProfileProxy): self.characteristics = None self.descriptors = None self.read_response = None self.write_response = None self.written_over_length = False @assert_description def MMI_IUT_INITIATE_CONNECTION(self, test, pts_addr: bytes, **kwargs): Loading Loading @@ -75,6 +77,8 @@ class GATTProxy(ProfileProxy): self.characteristics = None self.descriptors = None self.read_response = None self.write_response = None self.written_over_length = False return "OK" @assert_description Loading Loading @@ -104,7 +108,7 @@ class GATTProxy(ProfileProxy): matches = re.findall("'([a0-Z9]*)'O and size = '([a0-Z9]*)'", description) handle = int(matches[0][0], 16) data = bytes([1]) * int(matches[0][1]) self.gatt.WriteCharacteristicFromHandle(connection=self.connection,\ self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" Loading Loading @@ -435,12 +439,12 @@ class GATTProxy(ProfileProxy): """ if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.INVALID_HANDLE assert self.read_response.status == AttStatusCode.INVALID_HANDLE elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert self.read_response.characteristics_read is not None assert AttStatusCode.INVALID_HANDLE in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) return "Yes" @assert_description Loading @@ -457,15 +461,14 @@ class GATTProxy(ProfileProxy): # Android read error doesn't return an error code so we have to also # compare to the generic error code here. if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.READ_NOT_PERMITTED or\ self.read_response.readValue.status == AttStatusCode.UNKNOWN_ERROR assert self.read_response.status == AttStatusCode.READ_NOT_PERMITTED or\ self.read_response.status == AttStatusCode.UNKNOWN_ERROR elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert AttStatusCode.READ_NOT_PERMITTED in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) or\ AttStatusCode.UNKNOWN_ERROR in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) assert self.read_response.characteristics_read is not None status_list = list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) assert AttStatusCode.READ_NOT_PERMITTED in status_list or\ AttStatusCode.UNKNOWN_ERROR in status_list return "Yes" @assert_description Loading @@ -480,12 +483,12 @@ class GATTProxy(ProfileProxy): """ if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.INSUFFICIENT_AUTHENTICATION assert self.read_response.status == AttStatusCode.INSUFFICIENT_AUTHENTICATION elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert self.read_response.characteristics_read is not None assert AttStatusCode.INSUFFICIENT_AUTHENTICATION in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) return "Yes" def MMI_IUT_SEND_READ_CHARACTERISTIC_UUID(self, description: str, **kwargs): Loading Loading @@ -519,15 +522,14 @@ class GATTProxy(ProfileProxy): # Android read error doesn't return an error code so we have to also # compare to the generic error code here. if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.ATTRIBUTE_NOT_FOUND or\ self.read_response.readValue.status == AttStatusCode.UNKNOWN_ERROR assert self.read_response.status == AttStatusCode.ATTRIBUTE_NOT_FOUND or\ self.read_response.status == AttStatusCode.UNKNOWN_ERROR elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert AttStatusCode.ATTRIBUTE_NOT_FOUND in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) or\ AttStatusCode.UNKNOWN_ERROR in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) assert self.read_response.characteristics_read is not None status_list = list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) assert AttStatusCode.ATTRIBUTE_NOT_FOUND in status_list or\ AttStatusCode.UNKNOWN_ERROR in status_list return "Yes" def MMI_IUT_SEND_READ_GREATER_OFFSET(self, description: str, **kwargs): Loading Loading @@ -572,12 +574,12 @@ class GATTProxy(ProfileProxy): """ if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.APPLICATION_ERROR assert self.read_response.status == AttStatusCode.APPLICATION_ERROR elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert self.read_response.characteristics_read is not None assert AttStatusCode.APPLICATION_ERROR in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) return "Yes" def MMI_IUT_CONFIRM_READ_CHARACTERISTIC_VALUE(self, description: str, **kwargs): Loading @@ -592,11 +594,13 @@ class GATTProxy(ProfileProxy): characteristic_value = bytes.fromhex(re.findall("'([a0-Z9]*)'O", description)[0]) if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert characteristic_value in self.read_response.readValue.value assert self.read_response.value is not None assert characteristic_value in self.read_response.value.value elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert characteristic_value in list(map(lambda read_value: read_value.value, self.read_response.readValues)) assert self.read_response.characteristics_read is not None assert characteristic_value in list(map(\ lambda characteristic_read: characteristic_read.value.value,\ self.read_response.characteristics_read)) return "Yes" def MMI_IUT_READ_BY_TYPE_UUID(self, description: str, **kwargs): Loading Loading @@ -643,12 +647,13 @@ class GATTProxy(ProfileProxy): bytes_value = bytes.fromhex(re.search("value='(.*)'O", description)[1]) if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.value == bytes_value assert self.read_response.value is not None assert self.read_response.value.value == bytes_value elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert bytes_value in\ list(map(lambda read_value: read_value.value, self.read_response.readValues)) assert self.read_response.characteristics_read is not None assert bytes_value in list(map(\ lambda characteristic_read: characteristic_read.value.value,\ self.read_response.characteristics_read)) return "Yes" def MMI_IUT_SEND_READ_DESCIPTOR_HANDLE(self, description: str, **kwargs): Loading @@ -674,11 +679,197 @@ class GATTProxy(ProfileProxy): send Read Descriptor to PTS random select adopted database. """ assert self.read_response.readValue is not None assert self.read_response.value is not None bytes_value = bytes.fromhex(re.search("value='(.*)'O", description)[1]) assert self.read_response.readValue.value == bytes_value assert self.read_response.value.value == bytes_value return "Yes" def MMI_IUT_SEND_WRITE_REQUEST(self, description: str, **kwargs): """ Please send write request with characteristic handle = 'XXXX'O with <= 'X' byte of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O with <= '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * int(matches[0][1]) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" @assert_description def MMI_IUT_CONFIRM_WRITE_INVALID_HANDLE(self, **kwargs): """ Please confirm IUT received Invalid handle error. Click Yes if IUT received it, otherwise click No. Description: Verify that the Implementation Under Test (IUT) indicate Invalid handle error when write a characteristic. """ assert self.write_response is not None assert self.write_response.status == AttStatusCode.INVALID_HANDLE return "Yes" @assert_description def MMI_IUT_CONFIRM_WRITE_NOT_PERMITTED(self, **kwargs): """ Please confirm IUT received write is not permitted error. Click Yes if IUT received it, otherwise click No. Description: Verify that the Implementation Under Test (IUT) indicate write is not permitted error when write a characteristic. """ assert self.write_response is not None assert self.write_response.status == AttStatusCode.WRITE_NOT_PERMITTED return "Yes" def MMI_IUT_SEND_PREPARE_WRITE(self, description: str, **kwargs): """ Please send prepare write request with handle = 'XXXX'O <= 'XX' byte of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send prepare write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O <= '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * int(matches[0][1]) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" def MMI_IUT_SEND_PREPARE_WRITE_GREATER_OFFSET(self, description: str, **kwargs): """ Please send prepare write request with handle = 'XXXX'O and offset greater than 'XX' byte to the PTS. Description: Verify that the Implementation Under Test (IUT) can send prepare write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O and offset greater than '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) # Android APIs does not permit offset write, however we can test this by writing a value # longer than the characteristic's value size. As sometimes this MMI description will ask # for values greater than 512 bytes, we have to check for this or Android Bluetooth will # crash. Setting written_over_length to True in order to perform the check in next MMI. offset = int(matches[0][1]) + 1 if offset <= 512: data = bytes([1]) * offset self.written_over_length = True else: data = bytes([1]) * 512 self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" @assert_description def MMI_IUT_SEND_EXECUTE_WRITE_REQUEST(self, **kwargs): """ Please send execute write request to the PTS. Description: Verify that the Implementation Under Test (IUT) can send execute write request. """ # PTS Sends this MMI after the MMI_IUT_SEND_PREPARE_WRITE_GREATER_OFFSET, # nothing to do as we already wrote. return "OK" @assert_description def MMI_IUT_CONFIRM_WRITE_INVALID_OFFSET(self, **kwargs): """ Please confirm IUT received Invalid offset error. Click Yes if IUT received it, otherwise click No. Description: Verify that the Implementation Under Test (IUT) indicate Invalid offset error when write a characteristic. """ assert self.write_response is not None # See MMI_IUT_SEND_PREPARE_WRITE_GREATER_OFFSET if self.written_over_length == True: assert self.write_response.status == AttStatusCode.INVALID_ATTRIBUTE_LENGTH return "OK" def MMI_IUT_SEND_WRITE_REQUEST_GREATER(self, description: str, **kwargs): """ Please send write request with characteristic handle = 'XXXX'O with greater than 'X' byte of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O with greater than '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * (int(matches[0][1]) + 1) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" @assert_description def MMI_IUT_CONFIRM_WRITE_INVALID_LENGTH(self, **kwargs): """ Please confirm IUT received Invalid attribute value length error. Click Yes if IUT received it, otherwise click No. Description: Verify that the Implementation Under Test (IUT) indicate Invalid attribute value length error when write a characteristic. """ assert self.write_response is not None assert self.write_response.status == AttStatusCode.INVALID_ATTRIBUTE_LENGTH return "OK" def MMI_IUT_SEND_PREPARE_WRITE_REQUEST_GREATER(self, description: str, **kwargs): """ Please send prepare write request with handle = 'XXXX'O with greater than 'XX' byte of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send prepare write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O with greater than '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * (int(matches[0][1]) + 1) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" def MMI_IUT_SEND_WRITE_COMMAND(self, description: str, **kwargs): """ Please send write command with handle = 'XXXX'O with <= 'X' bytes of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O with <= '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * int(matches[0][1]) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" common_uuid = "0000XXXX-0000-1000-8000-00805f9b34fb" Loading android/pandora/mmi2grpc/mmi2grpc/hogp.py +3 −3 Original line number Diff line number Diff line Loading @@ -70,7 +70,7 @@ class HOGPProxy(ProfileProxy): Properties: \[0x00(?P<properties>\S*)\] """ self.gatt.WriteCharacteristicDescriptorFromHandle( self.gatt.WriteAttFromHandle( connection=self.connection, handle=int(handle, base=16), value=bytes([int(f"0x{properties}", base=16), 0]), Loading Loading @@ -209,7 +209,7 @@ class HOGPProxy(ProfileProxy): Descriptor handle value: (?P<value>\S*) """ self.gatt.WriteCharacteristicDescriptorFromHandle( self.gatt.WriteAttFromHandle( connection=self.connection, handle=int(value, base=16), value=bytes([0x01, 0x00]), Loading Loading @@ -237,7 +237,7 @@ class HOGPProxy(ProfileProxy): self.characteristic_reads[handle] = action( connection=self.connection, handle=handle, ).readValue.value ).value.value return "OK" Loading android/pandora/server/configs/PtsBotTest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ <option name="profile" value="GATT/CL/GAC" /> <option name="profile" value="GATT/CL/GAD" /> <option name="profile" value="GATT/CL/GAR" /> <option name="profile" value="GATT/CL/GAW" /> <option name="profile" value="HFP/AG/DIS" /> <option name="profile" value="HFP/AG/HFI" /> <option name="profile" value="HFP/AG/SLC" /> Loading android/pandora/server/configs/pts_bot_tests_config.json +18 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,17 @@ "GATT/CL/GAR/BV-04-C", "GATT/CL/GAR/BV-06-C", "GATT/CL/GAR/BV-07-C", "GATT/CL/GAW/BI-02-C", "GATT/CL/GAW/BI-03-C", "GATT/CL/GAW/BI-07-C", "GATT/CL/GAW/BI-08-C", "GATT/CL/GAW/BI-09-C", "GATT/CL/GAW/BI-33-C", "GATT/CL/GAW/BI-34-C", "GATT/CL/GAW/BV-03-C", "GATT/CL/GAW/BV-05-C", "GATT/CL/GAW/BV-08-C", "GATT/CL/GAW/BV-09-C", "HOGP/RH/HGCF/BV-01-I", "HOGP/RH/HGDC/BV-01-I", "HOGP/RH/HGDC/BV-02-I", Loading Loading @@ -240,6 +251,13 @@ "GATT/CL/GAR/BI-16-C", "GATT/CL/GAR/BI-17-C", "GATT/CL/GAR/BV-03-C", "GATT/CL/GAW/BI-05-C", "GATT/CL/GAW/BI-06-C", "GATT/CL/GAW/BI-12-C", "GATT/CL/GAW/BI-13-C", "GATT/CL/GAW/BI-32-C", "GATT/CL/GAW/BV-01-C", "GATT/CL/GAW/BV-06-C", "HFP/AG/DIS/BV-01-I", "HFP/AG/HFI/BI-03-I", "HFP/AG/HFI/BV-02-I", Loading android/pandora/server/proto/pandora/gatt.proto +26 −30 Original line number Diff line number Diff line Loading @@ -9,13 +9,10 @@ import "google/protobuf/empty.proto"; service GATT { // Request an MTU size. rpc ExchangeMTU(ExchangeMTURequest) returns (google.protobuf.Empty); rpc ExchangeMTU(ExchangeMTURequest) returns (ExchangeMTUResponse); // Writes on a characteristic. rpc WriteCharacteristicFromHandle(WriteCharacteristicRequest) returns (google.protobuf.Empty); // Writes on a characteristic with given descriptor handle. rpc WriteCharacteristicDescriptorFromHandle(WriteCharacteristicDescriptorFromHandleRequest) returns (WriteCharacteristicDescriptorFromHandleResponse); // Writes on the given characteristic or descriptor with given handle. rpc WriteAttFromHandle(WriteRequest) returns (WriteResponse); // Starts service discovery for given uuid. rpc DiscoverServiceByUuid(DiscoverServiceByUuidRequest) returns (DiscoverServicesResponse); Loading @@ -27,7 +24,7 @@ service GATT { rpc DiscoverServicesSdp(DiscoverServicesSdpRequest) returns (DiscoverServicesSdpResponse); // Clears DUT GATT cache. rpc ClearCache(ClearCacheRequest) returns (google.protobuf.Empty); rpc ClearCache(ClearCacheRequest) returns (ClearCacheResponse); // Reads characteristic with given handle. rpc ReadCharacteristicFromHandle(ReadCharacteristicRequest) returns (ReadCharacteristicResponse); Loading @@ -44,9 +41,11 @@ enum AttStatusCode { UNKNOWN_ERROR = 0x101; INVALID_HANDLE = 0x01; READ_NOT_PERMITTED = 0x02; WRITE_NOT_PERMITTED = 0x03; INSUFFICIENT_AUTHENTICATION = 0x05; INVALID_OFFSET = 0x07; ATTRIBUTE_NOT_FOUND = 0x0A; INVALID_ATTRIBUTE_LENGTH = 0x0D; APPLICATION_ERROR = 0x80; } Loading @@ -65,20 +64,20 @@ message GattCharacteristic { uint32 permissions = 2; string uuid = 3; uint32 handle = 4; repeated GattDescriptor descriptors = 5; repeated GattCharacteristicDescriptor descriptors = 5; } // A message representing a GATT descriptors. message GattDescriptor { message GattCharacteristicDescriptor { uint32 handle = 1; uint32 permissions = 2; string uuid = 3; } message GattReadValue { message AttValue { // Descriptor handle or Characteristic handle (not Characteristic Value handle). uint32 handle = 1; bytes value = 2; AttStatusCode status = 3; } // Request for the `ExchangeMTU` rpc. Loading @@ -87,30 +86,22 @@ message ExchangeMTURequest { int32 mtu = 2; } // Request for the `WriteCharacteristicFromHandle` rpc. message WriteCharacteristicRequest { Connection connection = 1; uint32 handle = 2; bytes value = 3; } // Response for the `ExchangeMTU` rpc. message ExchangeMTUResponse {} // Request for the `WriteCharacteristicDescriptorFromHandle` rpc. message WriteCharacteristicDescriptorFromHandleRequest { // Request for the `WriteAttFromHandle` rpc. message WriteRequest { Connection connection = 1; uint32 handle = 2; bytes value = 3; } // Response for the `WriteCharacteristicDescriptorFromHandle` rpc. message WriteCharacteristicDescriptorFromHandleResponse { Connection connection = 1; uint32 handle = 2; bytes value = 3; // Request for the `WriteAttFromHandle` rpc. message WriteResponse { uint32 handle = 1; AttStatusCode status = 2; } // Response for the `WriteAttribute` rpc. message WriteAttributeResponse {} // Request for the `DiscoverServiceByUuid` rpc. message DiscoverServiceByUuidRequest { Connection connection = 1; Loading Loading @@ -142,6 +133,9 @@ message ClearCacheRequest { Connection connection = 1; } // Response for the `ClearCache` rpc. message ClearCacheResponse {} // Request for the `ReadCharacteristicFromHandle` rpc. message ReadCharacteristicRequest { Connection connection = 1; Loading @@ -158,12 +152,13 @@ message ReadCharacteristicsFromUuidRequest { // Response for the `ReadCharacteristicFromHandle` rpc. message ReadCharacteristicResponse { GattReadValue readValue = 1; AttValue value = 1; AttStatusCode status = 2; } // Response for the `ReadCharacteristicsFromUuid` rpc. message ReadCharacteristicsFromUuidResponse { repeated GattReadValue readValues = 1; repeated ReadCharacteristicResponse characteristics_read = 1; } // Request for the `ReadCharacteristicDescriptorFromHandle` rpc. Loading @@ -174,5 +169,6 @@ message ReadCharacteristicDescriptorRequest { // Response for the `ReadCharacteristicDescriptorFromHandle` rpc. message ReadCharacteristicDescriptorResponse { GattReadValue readValue = 1; AttValue value = 1; AttStatusCode status = 2; } Loading
android/pandora/mmi2grpc/mmi2grpc/gatt.py +231 −40 Original line number Diff line number Diff line Loading @@ -42,6 +42,8 @@ class GATTProxy(ProfileProxy): self.characteristics = None self.descriptors = None self.read_response = None self.write_response = None self.written_over_length = False @assert_description def MMI_IUT_INITIATE_CONNECTION(self, test, pts_addr: bytes, **kwargs): Loading Loading @@ -75,6 +77,8 @@ class GATTProxy(ProfileProxy): self.characteristics = None self.descriptors = None self.read_response = None self.write_response = None self.written_over_length = False return "OK" @assert_description Loading Loading @@ -104,7 +108,7 @@ class GATTProxy(ProfileProxy): matches = re.findall("'([a0-Z9]*)'O and size = '([a0-Z9]*)'", description) handle = int(matches[0][0], 16) data = bytes([1]) * int(matches[0][1]) self.gatt.WriteCharacteristicFromHandle(connection=self.connection,\ self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" Loading Loading @@ -435,12 +439,12 @@ class GATTProxy(ProfileProxy): """ if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.INVALID_HANDLE assert self.read_response.status == AttStatusCode.INVALID_HANDLE elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert self.read_response.characteristics_read is not None assert AttStatusCode.INVALID_HANDLE in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) return "Yes" @assert_description Loading @@ -457,15 +461,14 @@ class GATTProxy(ProfileProxy): # Android read error doesn't return an error code so we have to also # compare to the generic error code here. if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.READ_NOT_PERMITTED or\ self.read_response.readValue.status == AttStatusCode.UNKNOWN_ERROR assert self.read_response.status == AttStatusCode.READ_NOT_PERMITTED or\ self.read_response.status == AttStatusCode.UNKNOWN_ERROR elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert AttStatusCode.READ_NOT_PERMITTED in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) or\ AttStatusCode.UNKNOWN_ERROR in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) assert self.read_response.characteristics_read is not None status_list = list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) assert AttStatusCode.READ_NOT_PERMITTED in status_list or\ AttStatusCode.UNKNOWN_ERROR in status_list return "Yes" @assert_description Loading @@ -480,12 +483,12 @@ class GATTProxy(ProfileProxy): """ if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.INSUFFICIENT_AUTHENTICATION assert self.read_response.status == AttStatusCode.INSUFFICIENT_AUTHENTICATION elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert self.read_response.characteristics_read is not None assert AttStatusCode.INSUFFICIENT_AUTHENTICATION in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) return "Yes" def MMI_IUT_SEND_READ_CHARACTERISTIC_UUID(self, description: str, **kwargs): Loading Loading @@ -519,15 +522,14 @@ class GATTProxy(ProfileProxy): # Android read error doesn't return an error code so we have to also # compare to the generic error code here. if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.ATTRIBUTE_NOT_FOUND or\ self.read_response.readValue.status == AttStatusCode.UNKNOWN_ERROR assert self.read_response.status == AttStatusCode.ATTRIBUTE_NOT_FOUND or\ self.read_response.status == AttStatusCode.UNKNOWN_ERROR elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert AttStatusCode.ATTRIBUTE_NOT_FOUND in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) or\ AttStatusCode.UNKNOWN_ERROR in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) assert self.read_response.characteristics_read is not None status_list = list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) assert AttStatusCode.ATTRIBUTE_NOT_FOUND in status_list or\ AttStatusCode.UNKNOWN_ERROR in status_list return "Yes" def MMI_IUT_SEND_READ_GREATER_OFFSET(self, description: str, **kwargs): Loading Loading @@ -572,12 +574,12 @@ class GATTProxy(ProfileProxy): """ if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.status == AttStatusCode.APPLICATION_ERROR assert self.read_response.status == AttStatusCode.APPLICATION_ERROR elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert self.read_response.characteristics_read is not None assert AttStatusCode.APPLICATION_ERROR in\ list(map(lambda read_value: read_value.status, self.read_response.readValues)) list(map(lambda characteristic_read: characteristic_read.status,\ self.read_response.characteristics_read)) return "Yes" def MMI_IUT_CONFIRM_READ_CHARACTERISTIC_VALUE(self, description: str, **kwargs): Loading @@ -592,11 +594,13 @@ class GATTProxy(ProfileProxy): characteristic_value = bytes.fromhex(re.findall("'([a0-Z9]*)'O", description)[0]) if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert characteristic_value in self.read_response.readValue.value assert self.read_response.value is not None assert characteristic_value in self.read_response.value.value elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert characteristic_value in list(map(lambda read_value: read_value.value, self.read_response.readValues)) assert self.read_response.characteristics_read is not None assert characteristic_value in list(map(\ lambda characteristic_read: characteristic_read.value.value,\ self.read_response.characteristics_read)) return "Yes" def MMI_IUT_READ_BY_TYPE_UUID(self, description: str, **kwargs): Loading Loading @@ -643,12 +647,13 @@ class GATTProxy(ProfileProxy): bytes_value = bytes.fromhex(re.search("value='(.*)'O", description)[1]) if type(self.read_response) is ReadCharacteristicResponse: assert self.read_response.readValue is not None assert self.read_response.readValue.value == bytes_value assert self.read_response.value is not None assert self.read_response.value.value == bytes_value elif type(self.read_response) is ReadCharacteristicsFromUuidResponse: assert self.read_response.readValues is not None assert bytes_value in\ list(map(lambda read_value: read_value.value, self.read_response.readValues)) assert self.read_response.characteristics_read is not None assert bytes_value in list(map(\ lambda characteristic_read: characteristic_read.value.value,\ self.read_response.characteristics_read)) return "Yes" def MMI_IUT_SEND_READ_DESCIPTOR_HANDLE(self, description: str, **kwargs): Loading @@ -674,11 +679,197 @@ class GATTProxy(ProfileProxy): send Read Descriptor to PTS random select adopted database. """ assert self.read_response.readValue is not None assert self.read_response.value is not None bytes_value = bytes.fromhex(re.search("value='(.*)'O", description)[1]) assert self.read_response.readValue.value == bytes_value assert self.read_response.value.value == bytes_value return "Yes" def MMI_IUT_SEND_WRITE_REQUEST(self, description: str, **kwargs): """ Please send write request with characteristic handle = 'XXXX'O with <= 'X' byte of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O with <= '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * int(matches[0][1]) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" @assert_description def MMI_IUT_CONFIRM_WRITE_INVALID_HANDLE(self, **kwargs): """ Please confirm IUT received Invalid handle error. Click Yes if IUT received it, otherwise click No. Description: Verify that the Implementation Under Test (IUT) indicate Invalid handle error when write a characteristic. """ assert self.write_response is not None assert self.write_response.status == AttStatusCode.INVALID_HANDLE return "Yes" @assert_description def MMI_IUT_CONFIRM_WRITE_NOT_PERMITTED(self, **kwargs): """ Please confirm IUT received write is not permitted error. Click Yes if IUT received it, otherwise click No. Description: Verify that the Implementation Under Test (IUT) indicate write is not permitted error when write a characteristic. """ assert self.write_response is not None assert self.write_response.status == AttStatusCode.WRITE_NOT_PERMITTED return "Yes" def MMI_IUT_SEND_PREPARE_WRITE(self, description: str, **kwargs): """ Please send prepare write request with handle = 'XXXX'O <= 'XX' byte of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send prepare write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O <= '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * int(matches[0][1]) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" def MMI_IUT_SEND_PREPARE_WRITE_GREATER_OFFSET(self, description: str, **kwargs): """ Please send prepare write request with handle = 'XXXX'O and offset greater than 'XX' byte to the PTS. Description: Verify that the Implementation Under Test (IUT) can send prepare write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O and offset greater than '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) # Android APIs does not permit offset write, however we can test this by writing a value # longer than the characteristic's value size. As sometimes this MMI description will ask # for values greater than 512 bytes, we have to check for this or Android Bluetooth will # crash. Setting written_over_length to True in order to perform the check in next MMI. offset = int(matches[0][1]) + 1 if offset <= 512: data = bytes([1]) * offset self.written_over_length = True else: data = bytes([1]) * 512 self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" @assert_description def MMI_IUT_SEND_EXECUTE_WRITE_REQUEST(self, **kwargs): """ Please send execute write request to the PTS. Description: Verify that the Implementation Under Test (IUT) can send execute write request. """ # PTS Sends this MMI after the MMI_IUT_SEND_PREPARE_WRITE_GREATER_OFFSET, # nothing to do as we already wrote. return "OK" @assert_description def MMI_IUT_CONFIRM_WRITE_INVALID_OFFSET(self, **kwargs): """ Please confirm IUT received Invalid offset error. Click Yes if IUT received it, otherwise click No. Description: Verify that the Implementation Under Test (IUT) indicate Invalid offset error when write a characteristic. """ assert self.write_response is not None # See MMI_IUT_SEND_PREPARE_WRITE_GREATER_OFFSET if self.written_over_length == True: assert self.write_response.status == AttStatusCode.INVALID_ATTRIBUTE_LENGTH return "OK" def MMI_IUT_SEND_WRITE_REQUEST_GREATER(self, description: str, **kwargs): """ Please send write request with characteristic handle = 'XXXX'O with greater than 'X' byte of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O with greater than '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * (int(matches[0][1]) + 1) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" @assert_description def MMI_IUT_CONFIRM_WRITE_INVALID_LENGTH(self, **kwargs): """ Please confirm IUT received Invalid attribute value length error. Click Yes if IUT received it, otherwise click No. Description: Verify that the Implementation Under Test (IUT) indicate Invalid attribute value length error when write a characteristic. """ assert self.write_response is not None assert self.write_response.status == AttStatusCode.INVALID_ATTRIBUTE_LENGTH return "OK" def MMI_IUT_SEND_PREPARE_WRITE_REQUEST_GREATER(self, description: str, **kwargs): """ Please send prepare write request with handle = 'XXXX'O with greater than 'XX' byte of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send prepare write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O with greater than '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * (int(matches[0][1]) + 1) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" def MMI_IUT_SEND_WRITE_COMMAND(self, description: str, **kwargs): """ Please send write command with handle = 'XXXX'O with <= 'X' bytes of any octet value to the PTS. Description: Verify that the Implementation Under Test (IUT) can send write request. """ assert self.connection is not None matches = re.findall("'([a0-Z9]*)'O with <= '([a0-Z9]*)'", description) handle = stringHandleToInt(matches[0][0]) data = bytes([1]) * int(matches[0][1]) self.write_response = self.gatt.WriteAttFromHandle(connection=self.connection,\ handle=handle, value=data) return "OK" common_uuid = "0000XXXX-0000-1000-8000-00805f9b34fb" Loading
android/pandora/mmi2grpc/mmi2grpc/hogp.py +3 −3 Original line number Diff line number Diff line Loading @@ -70,7 +70,7 @@ class HOGPProxy(ProfileProxy): Properties: \[0x00(?P<properties>\S*)\] """ self.gatt.WriteCharacteristicDescriptorFromHandle( self.gatt.WriteAttFromHandle( connection=self.connection, handle=int(handle, base=16), value=bytes([int(f"0x{properties}", base=16), 0]), Loading Loading @@ -209,7 +209,7 @@ class HOGPProxy(ProfileProxy): Descriptor handle value: (?P<value>\S*) """ self.gatt.WriteCharacteristicDescriptorFromHandle( self.gatt.WriteAttFromHandle( connection=self.connection, handle=int(value, base=16), value=bytes([0x01, 0x00]), Loading Loading @@ -237,7 +237,7 @@ class HOGPProxy(ProfileProxy): self.characteristic_reads[handle] = action( connection=self.connection, handle=handle, ).readValue.value ).value.value return "OK" Loading
android/pandora/server/configs/PtsBotTest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ <option name="profile" value="GATT/CL/GAC" /> <option name="profile" value="GATT/CL/GAD" /> <option name="profile" value="GATT/CL/GAR" /> <option name="profile" value="GATT/CL/GAW" /> <option name="profile" value="HFP/AG/DIS" /> <option name="profile" value="HFP/AG/HFI" /> <option name="profile" value="HFP/AG/SLC" /> Loading
android/pandora/server/configs/pts_bot_tests_config.json +18 −0 Original line number Diff line number Diff line Loading @@ -77,6 +77,17 @@ "GATT/CL/GAR/BV-04-C", "GATT/CL/GAR/BV-06-C", "GATT/CL/GAR/BV-07-C", "GATT/CL/GAW/BI-02-C", "GATT/CL/GAW/BI-03-C", "GATT/CL/GAW/BI-07-C", "GATT/CL/GAW/BI-08-C", "GATT/CL/GAW/BI-09-C", "GATT/CL/GAW/BI-33-C", "GATT/CL/GAW/BI-34-C", "GATT/CL/GAW/BV-03-C", "GATT/CL/GAW/BV-05-C", "GATT/CL/GAW/BV-08-C", "GATT/CL/GAW/BV-09-C", "HOGP/RH/HGCF/BV-01-I", "HOGP/RH/HGDC/BV-01-I", "HOGP/RH/HGDC/BV-02-I", Loading Loading @@ -240,6 +251,13 @@ "GATT/CL/GAR/BI-16-C", "GATT/CL/GAR/BI-17-C", "GATT/CL/GAR/BV-03-C", "GATT/CL/GAW/BI-05-C", "GATT/CL/GAW/BI-06-C", "GATT/CL/GAW/BI-12-C", "GATT/CL/GAW/BI-13-C", "GATT/CL/GAW/BI-32-C", "GATT/CL/GAW/BV-01-C", "GATT/CL/GAW/BV-06-C", "HFP/AG/DIS/BV-01-I", "HFP/AG/HFI/BI-03-I", "HFP/AG/HFI/BV-02-I", Loading
android/pandora/server/proto/pandora/gatt.proto +26 −30 Original line number Diff line number Diff line Loading @@ -9,13 +9,10 @@ import "google/protobuf/empty.proto"; service GATT { // Request an MTU size. rpc ExchangeMTU(ExchangeMTURequest) returns (google.protobuf.Empty); rpc ExchangeMTU(ExchangeMTURequest) returns (ExchangeMTUResponse); // Writes on a characteristic. rpc WriteCharacteristicFromHandle(WriteCharacteristicRequest) returns (google.protobuf.Empty); // Writes on a characteristic with given descriptor handle. rpc WriteCharacteristicDescriptorFromHandle(WriteCharacteristicDescriptorFromHandleRequest) returns (WriteCharacteristicDescriptorFromHandleResponse); // Writes on the given characteristic or descriptor with given handle. rpc WriteAttFromHandle(WriteRequest) returns (WriteResponse); // Starts service discovery for given uuid. rpc DiscoverServiceByUuid(DiscoverServiceByUuidRequest) returns (DiscoverServicesResponse); Loading @@ -27,7 +24,7 @@ service GATT { rpc DiscoverServicesSdp(DiscoverServicesSdpRequest) returns (DiscoverServicesSdpResponse); // Clears DUT GATT cache. rpc ClearCache(ClearCacheRequest) returns (google.protobuf.Empty); rpc ClearCache(ClearCacheRequest) returns (ClearCacheResponse); // Reads characteristic with given handle. rpc ReadCharacteristicFromHandle(ReadCharacteristicRequest) returns (ReadCharacteristicResponse); Loading @@ -44,9 +41,11 @@ enum AttStatusCode { UNKNOWN_ERROR = 0x101; INVALID_HANDLE = 0x01; READ_NOT_PERMITTED = 0x02; WRITE_NOT_PERMITTED = 0x03; INSUFFICIENT_AUTHENTICATION = 0x05; INVALID_OFFSET = 0x07; ATTRIBUTE_NOT_FOUND = 0x0A; INVALID_ATTRIBUTE_LENGTH = 0x0D; APPLICATION_ERROR = 0x80; } Loading @@ -65,20 +64,20 @@ message GattCharacteristic { uint32 permissions = 2; string uuid = 3; uint32 handle = 4; repeated GattDescriptor descriptors = 5; repeated GattCharacteristicDescriptor descriptors = 5; } // A message representing a GATT descriptors. message GattDescriptor { message GattCharacteristicDescriptor { uint32 handle = 1; uint32 permissions = 2; string uuid = 3; } message GattReadValue { message AttValue { // Descriptor handle or Characteristic handle (not Characteristic Value handle). uint32 handle = 1; bytes value = 2; AttStatusCode status = 3; } // Request for the `ExchangeMTU` rpc. Loading @@ -87,30 +86,22 @@ message ExchangeMTURequest { int32 mtu = 2; } // Request for the `WriteCharacteristicFromHandle` rpc. message WriteCharacteristicRequest { Connection connection = 1; uint32 handle = 2; bytes value = 3; } // Response for the `ExchangeMTU` rpc. message ExchangeMTUResponse {} // Request for the `WriteCharacteristicDescriptorFromHandle` rpc. message WriteCharacteristicDescriptorFromHandleRequest { // Request for the `WriteAttFromHandle` rpc. message WriteRequest { Connection connection = 1; uint32 handle = 2; bytes value = 3; } // Response for the `WriteCharacteristicDescriptorFromHandle` rpc. message WriteCharacteristicDescriptorFromHandleResponse { Connection connection = 1; uint32 handle = 2; bytes value = 3; // Request for the `WriteAttFromHandle` rpc. message WriteResponse { uint32 handle = 1; AttStatusCode status = 2; } // Response for the `WriteAttribute` rpc. message WriteAttributeResponse {} // Request for the `DiscoverServiceByUuid` rpc. message DiscoverServiceByUuidRequest { Connection connection = 1; Loading Loading @@ -142,6 +133,9 @@ message ClearCacheRequest { Connection connection = 1; } // Response for the `ClearCache` rpc. message ClearCacheResponse {} // Request for the `ReadCharacteristicFromHandle` rpc. message ReadCharacteristicRequest { Connection connection = 1; Loading @@ -158,12 +152,13 @@ message ReadCharacteristicsFromUuidRequest { // Response for the `ReadCharacteristicFromHandle` rpc. message ReadCharacteristicResponse { GattReadValue readValue = 1; AttValue value = 1; AttStatusCode status = 2; } // Response for the `ReadCharacteristicsFromUuid` rpc. message ReadCharacteristicsFromUuidResponse { repeated GattReadValue readValues = 1; repeated ReadCharacteristicResponse characteristics_read = 1; } // Request for the `ReadCharacteristicDescriptorFromHandle` rpc. Loading @@ -174,5 +169,6 @@ message ReadCharacteristicDescriptorRequest { // Response for the `ReadCharacteristicDescriptorFromHandle` rpc. message ReadCharacteristicDescriptorResponse { GattReadValue readValue = 1; AttValue value = 1; AttStatusCode status = 2; }