You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
730 lines
29 KiB
730 lines
29 KiB
#!/usr/bin/env python3
|
|
#
|
|
# Copyright 2020 - The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import logging
|
|
import bluetooth_packets_python3 as bt_packets
|
|
import logging
|
|
|
|
from bluetooth_packets_python3 import hci_packets
|
|
from bluetooth_packets_python3.hci_packets import EventCode
|
|
from bluetooth_packets_python3 import l2cap_packets
|
|
from bluetooth_packets_python3.l2cap_packets import CommandCode, LeCommandCode
|
|
from bluetooth_packets_python3.l2cap_packets import ConfigurationResponseResult
|
|
from bluetooth_packets_python3.l2cap_packets import ConnectionResponseResult
|
|
from bluetooth_packets_python3.l2cap_packets import InformationRequestInfoType
|
|
from bluetooth_packets_python3.l2cap_packets import LeCreditBasedConnectionResponseResult
|
|
|
|
|
|
class HciMatchers(object):
|
|
|
|
@staticmethod
|
|
def CommandComplete(opcode):
|
|
return lambda msg: HciMatchers._is_matching_command_complete(msg.payload, opcode)
|
|
|
|
@staticmethod
|
|
def ExtractMatchingCommandComplete(packet_bytes, opcode=None):
|
|
return HciMatchers._extract_matching_command_complete(packet_bytes, opcode)
|
|
|
|
@staticmethod
|
|
def _is_matching_command_complete(packet_bytes, opcode=None):
|
|
return HciMatchers._extract_matching_command_complete(packet_bytes, opcode) is not None
|
|
|
|
@staticmethod
|
|
def _extract_matching_command_complete(packet_bytes, opcode=None):
|
|
event = HciMatchers._extract_matching_event(packet_bytes, EventCode.COMMAND_COMPLETE)
|
|
if event is None:
|
|
return None
|
|
complete = hci_packets.CommandCompleteView(event)
|
|
if opcode is None or complete is None:
|
|
return complete
|
|
else:
|
|
if complete.GetCommandOpCode() != opcode:
|
|
return None
|
|
else:
|
|
return complete
|
|
|
|
@staticmethod
|
|
def CommandStatus(opcode=None):
|
|
return lambda msg: HciMatchers._is_matching_command_status(msg.payload, opcode)
|
|
|
|
@staticmethod
|
|
def ExtractMatchingCommandStatus(packet_bytes, opcode=None):
|
|
return HciMatchers._extract_matching_command_complete(packet_bytes, opcode)
|
|
|
|
@staticmethod
|
|
def _is_matching_command_status(packet_bytes, opcode=None):
|
|
return HciMatchers._extract_matching_command_status(packet_bytes, opcode) is not None
|
|
|
|
@staticmethod
|
|
def _extract_matching_command_status(packet_bytes, opcode=None):
|
|
event = HciMatchers._extract_matching_event(packet_bytes, EventCode.COMMAND_STATUS)
|
|
if event is None:
|
|
return None
|
|
complete = hci_packets.CommandStatusView(event)
|
|
if opcode is None or complete is None:
|
|
return complete
|
|
else:
|
|
if complete.GetCommandOpCode() != opcode:
|
|
return None
|
|
else:
|
|
return complete
|
|
|
|
@staticmethod
|
|
def EventWithCode(event_code):
|
|
return lambda msg: HciMatchers._is_matching_event(msg.payload, event_code)
|
|
|
|
@staticmethod
|
|
def ExtractEventWithCode(packet_bytes, event_code):
|
|
return HciMatchers._extract_matching_event(packet_bytes, event_code)
|
|
|
|
@staticmethod
|
|
def _is_matching_event(packet_bytes, event_code):
|
|
return HciMatchers._extract_matching_event(packet_bytes, event_code) is not None
|
|
|
|
@staticmethod
|
|
def _extract_matching_event(packet_bytes, event_code):
|
|
event = hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(packet_bytes)))
|
|
if event is None:
|
|
return None
|
|
if event_code is not None and event.GetEventCode() != event_code:
|
|
return None
|
|
return event
|
|
|
|
@staticmethod
|
|
def LeEventWithCode(subevent_code):
|
|
return lambda msg: HciMatchers._extract_matching_le_event(msg.payload, subevent_code) is not None
|
|
|
|
@staticmethod
|
|
def ExtractLeEventWithCode(packet_bytes, subevent_code):
|
|
return HciMatchers._extract_matching_le_event(packet_bytes, subevent_code)
|
|
|
|
@staticmethod
|
|
def _extract_matching_le_event(packet_bytes, subevent_code):
|
|
inner_event = HciMatchers._extract_matching_event(packet_bytes, hci_packets.EventCode.LE_META_EVENT)
|
|
if inner_event is None:
|
|
return None
|
|
event = hci_packets.LeMetaEventView(inner_event)
|
|
if event.GetSubeventCode() != subevent_code:
|
|
return None
|
|
return event
|
|
|
|
@staticmethod
|
|
def LeConnectionComplete():
|
|
return lambda msg: HciMatchers._extract_le_connection_complete(msg.payload) is not None
|
|
|
|
@staticmethod
|
|
def ExtractLeConnectionComplete(packet_bytes):
|
|
return HciMatchers._extract_le_connection_complete(packet_bytes)
|
|
|
|
@staticmethod
|
|
def _extract_le_connection_complete(packet_bytes):
|
|
inner_event = HciMatchers._extract_matching_le_event(packet_bytes, hci_packets.SubeventCode.CONNECTION_COMPLETE)
|
|
if inner_event is not None:
|
|
return hci_packets.LeConnectionCompleteView(inner_event)
|
|
|
|
inner_event = HciMatchers._extract_matching_le_event(packet_bytes,
|
|
hci_packets.SubeventCode.ENHANCED_CONNECTION_COMPLETE)
|
|
if inner_event is not None:
|
|
return hci_packets.LeEnhancedConnectionCompleteView(inner_event)
|
|
|
|
return None
|
|
|
|
@staticmethod
|
|
def LogEventCode():
|
|
return lambda event: logging.info("Received event: %x" % hci_packets.EventView(bt_packets.PacketViewLittleEndian(list(event.payload))).GetEventCode())
|
|
|
|
@staticmethod
|
|
def LinkKeyRequest():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.LINK_KEY_REQUEST)
|
|
|
|
@staticmethod
|
|
def IoCapabilityRequest():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.IO_CAPABILITY_REQUEST)
|
|
|
|
@staticmethod
|
|
def IoCapabilityResponse():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.IO_CAPABILITY_RESPONSE)
|
|
|
|
@staticmethod
|
|
def UserPasskeyNotification():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.USER_PASSKEY_NOTIFICATION)
|
|
|
|
@staticmethod
|
|
def UserPasskeyRequest():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.USER_PASSKEY_REQUEST)
|
|
|
|
@staticmethod
|
|
def UserConfirmationRequest():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.USER_CONFIRMATION_REQUEST)
|
|
|
|
@staticmethod
|
|
def RemoteHostSupportedFeaturesNotification():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION)
|
|
|
|
@staticmethod
|
|
def LinkKeyNotification():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.LINK_KEY_NOTIFICATION)
|
|
|
|
@staticmethod
|
|
def SimplePairingComplete():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.SIMPLE_PAIRING_COMPLETE)
|
|
|
|
@staticmethod
|
|
def Disconnect():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.DISCONNECT)
|
|
|
|
@staticmethod
|
|
def DisconnectionComplete():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.DISCONNECTION_COMPLETE)
|
|
|
|
@staticmethod
|
|
def RemoteOobDataRequest():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.REMOTE_OOB_DATA_REQUEST)
|
|
|
|
@staticmethod
|
|
def PinCodeRequest():
|
|
return lambda event: HciMatchers.EventWithCode(EventCode.PIN_CODE_REQUEST)
|
|
|
|
@staticmethod
|
|
def LoopbackOf(packet):
|
|
return HciMatchers.Exactly(hci_packets.LoopbackCommandBuilder(packet))
|
|
|
|
@staticmethod
|
|
def Exactly(packet):
|
|
data = bytes(packet.Serialize())
|
|
return lambda event: data == event.payload
|
|
|
|
|
|
class NeighborMatchers(object):
|
|
|
|
@staticmethod
|
|
def InquiryResult(address):
|
|
return lambda msg: NeighborMatchers._is_matching_inquiry_result(msg.packet, address)
|
|
|
|
@staticmethod
|
|
def _is_matching_inquiry_result(packet, address):
|
|
hci_event = HciMatchers.ExtractEventWithCode(packet, EventCode.INQUIRY_RESULT)
|
|
if hci_event is None:
|
|
return False
|
|
inquiry_view = hci_packets.InquiryResultView(hci_event)
|
|
if inquiry_view is None:
|
|
return False
|
|
results = inquiry_view.GetInquiryResults()
|
|
return any((address == result.bd_addr for result in results))
|
|
|
|
@staticmethod
|
|
def InquiryResultwithRssi(address):
|
|
return lambda msg: NeighborMatchers._is_matching_inquiry_result_with_rssi(msg.packet, address)
|
|
|
|
@staticmethod
|
|
def _is_matching_inquiry_result_with_rssi(packet, address):
|
|
hci_event = HciMatchers.ExtractEventWithCode(packet, EventCode.INQUIRY_RESULT_WITH_RSSI)
|
|
if hci_event is None:
|
|
return False
|
|
inquiry_view = hci_packets.InquiryResultWithRssiView(hci_event)
|
|
if inquiry_view is None:
|
|
return False
|
|
results = inquiry_view.GetInquiryResults()
|
|
return any((address == result.address for result in results))
|
|
|
|
@staticmethod
|
|
def ExtendedInquiryResult(address):
|
|
return lambda msg: NeighborMatchers._is_matching_extended_inquiry_result(msg.packet, address)
|
|
|
|
@staticmethod
|
|
def _is_matching_extended_inquiry_result(packet, address):
|
|
hci_event = HciMatchers.ExtractEventWithCode(packet, EventCode.EXTENDED_INQUIRY_RESULT)
|
|
if hci_event is None:
|
|
return False
|
|
extended_view = hci_packets.ExtendedInquiryResultView(hci_event)
|
|
if extended_view is None:
|
|
return False
|
|
return address == extended_view.GetAddress()
|
|
|
|
|
|
class L2capMatchers(object):
|
|
|
|
@staticmethod
|
|
def ConnectionRequest(psm):
|
|
return lambda packet: L2capMatchers._is_matching_connection_request(packet, psm)
|
|
|
|
@staticmethod
|
|
def ConnectionResponse(scid):
|
|
return lambda packet: L2capMatchers._is_matching_connection_response(packet, scid)
|
|
|
|
@staticmethod
|
|
def ConfigurationResponse(result=ConfigurationResponseResult.SUCCESS):
|
|
return lambda packet: L2capMatchers._is_matching_configuration_response(packet, result)
|
|
|
|
@staticmethod
|
|
def ConfigurationRequest(cid=None):
|
|
return lambda packet: L2capMatchers._is_matching_configuration_request_with_cid(packet, cid)
|
|
|
|
@staticmethod
|
|
def ConfigurationRequestWithErtm():
|
|
return lambda packet: L2capMatchers._is_matching_configuration_request_with_ertm(packet)
|
|
|
|
@staticmethod
|
|
def ConfigurationRequestView(dcid):
|
|
return lambda request_view: request_view.GetDestinationCid() == dcid
|
|
|
|
@staticmethod
|
|
def DisconnectionRequest(scid, dcid):
|
|
return lambda packet: L2capMatchers._is_matching_disconnection_request(packet, scid, dcid)
|
|
|
|
@staticmethod
|
|
def DisconnectionResponse(scid, dcid):
|
|
return lambda packet: L2capMatchers._is_matching_disconnection_response(packet, scid, dcid)
|
|
|
|
@staticmethod
|
|
def EchoResponse():
|
|
return lambda packet: L2capMatchers._is_control_frame_with_code(packet, CommandCode.ECHO_RESPONSE)
|
|
|
|
@staticmethod
|
|
def CommandReject():
|
|
return lambda packet: L2capMatchers._is_control_frame_with_code(packet, CommandCode.COMMAND_REJECT)
|
|
|
|
@staticmethod
|
|
def LeCommandReject():
|
|
return lambda packet: L2capMatchers._is_le_control_frame_with_code(packet, LeCommandCode.COMMAND_REJECT)
|
|
|
|
@staticmethod
|
|
def LeConnectionParameterUpdateRequest():
|
|
return lambda packet: L2capMatchers._is_le_control_frame_with_code(
|
|
packet, LeCommandCode.CONNECTION_PARAMETER_UPDATE_REQUEST)
|
|
|
|
@staticmethod
|
|
def LeConnectionParameterUpdateResponse(result=l2cap_packets.ConnectionParameterUpdateResponseResult.ACCEPTED):
|
|
return lambda packet: L2capMatchers._is_matching_connection_parameter_update_response(packet, result)
|
|
|
|
@staticmethod
|
|
def CreditBasedConnectionRequest(psm):
|
|
return lambda packet: L2capMatchers._is_matching_credit_based_connection_request(packet, psm)
|
|
|
|
@staticmethod
|
|
def CreditBasedConnectionResponse(result=LeCreditBasedConnectionResponseResult.SUCCESS):
|
|
return lambda packet: L2capMatchers._is_matching_credit_based_connection_response(packet, result)
|
|
|
|
@staticmethod
|
|
def CreditBasedConnectionResponseUsedCid():
|
|
return lambda packet: L2capMatchers._is_matching_credit_based_connection_response(
|
|
packet, LeCreditBasedConnectionResponseResult.SOURCE_CID_ALREADY_ALLOCATED
|
|
) or L2capMatchers._is_le_control_frame_with_code(packet, LeCommandCode.COMMAND_REJECT)
|
|
|
|
@staticmethod
|
|
def LeDisconnectionRequest(scid, dcid):
|
|
return lambda packet: L2capMatchers._is_matching_le_disconnection_request(packet, scid, dcid)
|
|
|
|
@staticmethod
|
|
def LeDisconnectionResponse(scid, dcid):
|
|
return lambda packet: L2capMatchers._is_matching_le_disconnection_response(packet, scid, dcid)
|
|
|
|
@staticmethod
|
|
def LeFlowControlCredit(cid):
|
|
return lambda packet: L2capMatchers._is_matching_le_flow_control_credit(packet, cid)
|
|
|
|
@staticmethod
|
|
def SFrame(req_seq=None, f=None, s=None, p=None):
|
|
return lambda packet: L2capMatchers._is_matching_supervisory_frame(packet, req_seq, f, s, p)
|
|
|
|
@staticmethod
|
|
def IFrame(tx_seq=None, payload=None, f=None):
|
|
return lambda packet: L2capMatchers._is_matching_information_frame(packet, tx_seq, payload, f, fcs=False)
|
|
|
|
@staticmethod
|
|
def IFrameWithFcs(tx_seq=None, payload=None, f=None):
|
|
return lambda packet: L2capMatchers._is_matching_information_frame(packet, tx_seq, payload, f, fcs=True)
|
|
|
|
@staticmethod
|
|
def IFrameStart(tx_seq=None, payload=None, f=None):
|
|
return lambda packet: L2capMatchers._is_matching_information_start_frame(packet, tx_seq, payload, f, fcs=False)
|
|
|
|
@staticmethod
|
|
def Data(payload):
|
|
return lambda packet: packet.GetPayload().GetBytes() == payload
|
|
|
|
@staticmethod
|
|
def FirstLeIFrame(payload, sdu_size):
|
|
return lambda packet: L2capMatchers._is_matching_first_le_i_frame(packet, payload, sdu_size)
|
|
|
|
# this is a hack - should be removed
|
|
@staticmethod
|
|
def PartialData(payload):
|
|
return lambda packet: payload in packet.GetPayload().GetBytes()
|
|
|
|
# this is a hack - should be removed
|
|
@staticmethod
|
|
def PacketPayloadRawData(payload):
|
|
return lambda packet: payload in packet.payload
|
|
|
|
# this is a hack - should be removed
|
|
@staticmethod
|
|
def PacketPayloadWithMatchingPsm(psm):
|
|
return lambda packet: None if psm != packet.psm else packet
|
|
|
|
# this is a hack - should be removed
|
|
@staticmethod
|
|
def PacketPayloadWithMatchingCid(cid):
|
|
return lambda packet: None if cid != packet.fixed_cid else packet
|
|
|
|
@staticmethod
|
|
def ExtractBasicFrame(scid):
|
|
return lambda packet: L2capMatchers._basic_frame_for(packet, scid)
|
|
|
|
@staticmethod
|
|
def ExtractBasicFrameWithFcs(scid):
|
|
return lambda packet: L2capMatchers._basic_frame_with_fcs_for(packet, scid)
|
|
|
|
@staticmethod
|
|
def InformationRequestWithType(info_type):
|
|
return lambda packet: L2capMatchers._information_request_with_type(packet, info_type)
|
|
|
|
@staticmethod
|
|
def InformationResponseExtendedFeatures(supports_ertm=None,
|
|
supports_streaming=None,
|
|
supports_fcs=None,
|
|
supports_fixed_channels=None):
|
|
return lambda packet: L2capMatchers._is_matching_information_response_extended_features(
|
|
packet, supports_ertm, supports_streaming, supports_fcs, supports_fixed_channels)
|
|
|
|
@staticmethod
|
|
def _basic_frame(packet):
|
|
if packet is None:
|
|
return None
|
|
return l2cap_packets.BasicFrameView(bt_packets.PacketViewLittleEndian(list(packet.payload)))
|
|
|
|
@staticmethod
|
|
def _basic_frame_with_fcs(packet):
|
|
if packet is None:
|
|
return None
|
|
return l2cap_packets.BasicFrameWithFcsView(bt_packets.PacketViewLittleEndian(list(packet.payload)))
|
|
|
|
@staticmethod
|
|
def _basic_frame_for(packet, scid):
|
|
frame = L2capMatchers._basic_frame(packet)
|
|
if frame.GetChannelId() != scid:
|
|
return None
|
|
return frame
|
|
|
|
@staticmethod
|
|
def _basic_frame_with_fcs_for(packet, scid):
|
|
frame = L2capMatchers._basic_frame(packet)
|
|
if frame.GetChannelId() != scid:
|
|
return None
|
|
frame = L2capMatchers._basic_frame_with_fcs(packet)
|
|
if frame is None:
|
|
return None
|
|
return frame
|
|
|
|
@staticmethod
|
|
def _information_frame(packet):
|
|
standard_frame = l2cap_packets.StandardFrameView(packet)
|
|
if standard_frame.GetFrameType() != l2cap_packets.FrameType.I_FRAME:
|
|
return None
|
|
return l2cap_packets.EnhancedInformationFrameView(standard_frame)
|
|
|
|
@staticmethod
|
|
def _information_frame_with_fcs(packet):
|
|
standard_frame = l2cap_packets.StandardFrameWithFcsView(packet)
|
|
if standard_frame is None:
|
|
return None
|
|
if standard_frame.GetFrameType() != l2cap_packets.FrameType.I_FRAME:
|
|
return None
|
|
return l2cap_packets.EnhancedInformationFrameWithFcsView(standard_frame)
|
|
|
|
@staticmethod
|
|
def _information_start_frame(packet):
|
|
start_frame = L2capMatchers._information_frame(packet)
|
|
if start_frame is None:
|
|
return None
|
|
return l2cap_packets.EnhancedInformationStartFrameView(start_frame)
|
|
|
|
@staticmethod
|
|
def _information_start_frame_with_fcs(packet):
|
|
start_frame = L2capMatchers._information_frame_with_fcs(packet)
|
|
if start_frame is None:
|
|
return None
|
|
return l2cap_packets.EnhancedInformationStartFrameWithFcsView(start_frame)
|
|
|
|
@staticmethod
|
|
def _supervisory_frame(packet):
|
|
standard_frame = l2cap_packets.StandardFrameView(packet)
|
|
if standard_frame.GetFrameType() != l2cap_packets.FrameType.S_FRAME:
|
|
return None
|
|
return l2cap_packets.EnhancedSupervisoryFrameView(standard_frame)
|
|
|
|
@staticmethod
|
|
def _is_matching_information_frame(packet, tx_seq, payload, f, fcs=False):
|
|
if fcs:
|
|
frame = L2capMatchers._information_frame_with_fcs(packet)
|
|
else:
|
|
frame = L2capMatchers._information_frame(packet)
|
|
if frame is None:
|
|
return False
|
|
if tx_seq is not None and frame.GetTxSeq() != tx_seq:
|
|
return False
|
|
if payload is not None and frame.GetPayload().GetBytes() != payload:
|
|
return False
|
|
if f is not None and frame.GetF() != f:
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def _is_matching_information_start_frame(packet, tx_seq, payload, f, fcs=False):
|
|
if fcs:
|
|
frame = L2capMatchers._information_start_frame_with_fcs(packet)
|
|
else:
|
|
frame = L2capMatchers._information_start_frame(packet)
|
|
if frame is None:
|
|
return False
|
|
if tx_seq is not None and frame.GetTxSeq() != tx_seq:
|
|
return False
|
|
if payload is not None and frame.GetPayload().GetBytes() != payload:
|
|
return False
|
|
if f is not None and frame.GetF() != f:
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def _is_matching_supervisory_frame(packet, req_seq, f, s, p):
|
|
frame = L2capMatchers._supervisory_frame(packet)
|
|
if frame is None:
|
|
return False
|
|
if req_seq is not None and frame.GetReqSeq() != req_seq:
|
|
return False
|
|
if f is not None and frame.GetF() != f:
|
|
return False
|
|
if s is not None and frame.GetS() != s:
|
|
return False
|
|
if p is not None and frame.GetP() != p:
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def _is_matching_first_le_i_frame(packet, payload, sdu_size):
|
|
first_le_i_frame = l2cap_packets.FirstLeInformationFrameView(packet)
|
|
return first_le_i_frame.GetPayload().GetBytes() == payload and first_le_i_frame.GetL2capSduLength() == sdu_size
|
|
|
|
@staticmethod
|
|
def _control_frame(packet):
|
|
if packet.GetChannelId() != 1:
|
|
return None
|
|
return l2cap_packets.ControlView(packet.GetPayload())
|
|
|
|
@staticmethod
|
|
def _le_control_frame(packet):
|
|
if packet.GetChannelId() != 5:
|
|
return None
|
|
return l2cap_packets.LeControlView(packet.GetPayload())
|
|
|
|
@staticmethod
|
|
def control_frame_with_code(packet, code):
|
|
frame = L2capMatchers._control_frame(packet)
|
|
if frame is None or frame.GetCode() != code:
|
|
return None
|
|
return frame
|
|
|
|
@staticmethod
|
|
def le_control_frame_with_code(packet, code):
|
|
frame = L2capMatchers._le_control_frame(packet)
|
|
if frame is None or frame.GetCode() != code:
|
|
return None
|
|
return frame
|
|
|
|
@staticmethod
|
|
def _is_control_frame_with_code(packet, code):
|
|
return L2capMatchers.control_frame_with_code(packet, code) is not None
|
|
|
|
@staticmethod
|
|
def _is_le_control_frame_with_code(packet, code):
|
|
return L2capMatchers.le_control_frame_with_code(packet, code) is not None
|
|
|
|
@staticmethod
|
|
def _is_matching_connection_request(packet, psm):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONNECTION_REQUEST)
|
|
if frame is None:
|
|
return False
|
|
request = l2cap_packets.ConnectionRequestView(frame)
|
|
return request.GetPsm() == psm
|
|
|
|
@staticmethod
|
|
def _is_matching_connection_response(packet, scid):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONNECTION_RESPONSE)
|
|
if frame is None:
|
|
return False
|
|
response = l2cap_packets.ConnectionResponseView(frame)
|
|
return response.GetSourceCid() == scid and response.GetResult(
|
|
) == ConnectionResponseResult.SUCCESS and response.GetDestinationCid() != 0
|
|
|
|
@staticmethod
|
|
def _is_matching_configuration_request_with_cid(packet, cid=None):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_REQUEST)
|
|
if frame is None:
|
|
return False
|
|
request = l2cap_packets.ConfigurationRequestView(frame)
|
|
dcid = request.GetDestinationCid()
|
|
return cid is None or cid == dcid
|
|
|
|
@staticmethod
|
|
def _is_matching_configuration_request_with_ertm(packet):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_REQUEST)
|
|
if frame is None:
|
|
return False
|
|
request = l2cap_packets.ConfigurationRequestView(frame)
|
|
config_bytes = request.GetBytes()
|
|
# TODO(b/153189503): Use packet struct parser.
|
|
return b"\x04\x09\x03" in config_bytes
|
|
|
|
@staticmethod
|
|
def _is_matching_configuration_response(packet, result=ConfigurationResponseResult.SUCCESS):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.CONFIGURATION_RESPONSE)
|
|
if frame is None:
|
|
return False
|
|
response = l2cap_packets.ConfigurationResponseView(frame)
|
|
return response.GetResult() == result
|
|
|
|
@staticmethod
|
|
def _is_matching_disconnection_request(packet, scid, dcid):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.DISCONNECTION_REQUEST)
|
|
if frame is None:
|
|
return False
|
|
request = l2cap_packets.DisconnectionRequestView(frame)
|
|
return request.GetSourceCid() == scid and request.GetDestinationCid() == dcid
|
|
|
|
@staticmethod
|
|
def _is_matching_disconnection_response(packet, scid, dcid):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.DISCONNECTION_RESPONSE)
|
|
if frame is None:
|
|
return False
|
|
response = l2cap_packets.DisconnectionResponseView(frame)
|
|
return response.GetSourceCid() == scid and response.GetDestinationCid() == dcid
|
|
|
|
@staticmethod
|
|
def _is_matching_le_disconnection_response(packet, scid, dcid):
|
|
frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.DISCONNECTION_RESPONSE)
|
|
if frame is None:
|
|
return False
|
|
response = l2cap_packets.LeDisconnectionResponseView(frame)
|
|
return response.GetSourceCid() == scid and response.GetDestinationCid() == dcid
|
|
|
|
@staticmethod
|
|
def _is_matching_le_disconnection_request(packet, scid, dcid):
|
|
frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.DISCONNECTION_REQUEST)
|
|
if frame is None:
|
|
return False
|
|
request = l2cap_packets.LeDisconnectionRequestView(frame)
|
|
return request.GetSourceCid() == scid and request.GetDestinationCid() == dcid
|
|
|
|
@staticmethod
|
|
def _is_matching_le_flow_control_credit(packet, cid):
|
|
frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_FLOW_CONTROL_CREDIT)
|
|
if frame is None:
|
|
return False
|
|
request = l2cap_packets.LeFlowControlCreditView(frame)
|
|
return request.GetCid() == cid
|
|
|
|
@staticmethod
|
|
def _information_request_with_type(packet, info_type):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.INFORMATION_REQUEST)
|
|
if frame is None:
|
|
return None
|
|
request = l2cap_packets.InformationRequestView(frame)
|
|
if request.GetInfoType() != info_type:
|
|
return None
|
|
return request
|
|
|
|
@staticmethod
|
|
def _information_response_with_type(packet, info_type):
|
|
frame = L2capMatchers.control_frame_with_code(packet, CommandCode.INFORMATION_RESPONSE)
|
|
if frame is None:
|
|
return None
|
|
response = l2cap_packets.InformationResponseView(frame)
|
|
if response.GetInfoType() != info_type:
|
|
return None
|
|
return response
|
|
|
|
@staticmethod
|
|
def _is_matching_information_response_extended_features(packet, supports_ertm, supports_streaming, supports_fcs,
|
|
supports_fixed_channels):
|
|
frame = L2capMatchers._information_response_with_type(packet,
|
|
InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED)
|
|
if frame is None:
|
|
return False
|
|
features = l2cap_packets.InformationResponseExtendedFeaturesView(frame)
|
|
if supports_ertm is not None and features.GetEnhancedRetransmissionMode() != supports_ertm:
|
|
return False
|
|
if supports_streaming is not None and features.GetStreamingMode != supports_streaming:
|
|
return False
|
|
if supports_fcs is not None and features.GetFcsOption() != supports_fcs:
|
|
return False
|
|
if supports_fixed_channels is not None and features.GetFixedChannels() != supports_fixed_channels:
|
|
return False
|
|
return True
|
|
|
|
@staticmethod
|
|
def _is_matching_connection_parameter_update_response(packet, result):
|
|
frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.CONNECTION_PARAMETER_UPDATE_RESPONSE)
|
|
if frame is None:
|
|
return False
|
|
return l2cap_packets.ConnectionParameterUpdateResponseView(frame).GetResult() == result
|
|
|
|
@staticmethod
|
|
def _is_matching_credit_based_connection_request(packet, psm):
|
|
frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_REQUEST)
|
|
if frame is None:
|
|
return False
|
|
request = l2cap_packets.LeCreditBasedConnectionRequestView(frame)
|
|
return request.GetLePsm() == psm
|
|
|
|
@staticmethod
|
|
def _is_matching_credit_based_connection_response(packet, result):
|
|
frame = L2capMatchers.le_control_frame_with_code(packet, LeCommandCode.LE_CREDIT_BASED_CONNECTION_RESPONSE)
|
|
if frame is None:
|
|
return False
|
|
response = l2cap_packets.LeCreditBasedConnectionResponseView(frame)
|
|
return response.GetResult() == result and (result != LeCreditBasedConnectionResponseResult.SUCCESS or
|
|
response.GetDestinationCid() != 0)
|
|
|
|
@staticmethod
|
|
def LinkSecurityInterfaceCallbackEvent(type):
|
|
return lambda event: True if event.event_type == type else False
|
|
|
|
|
|
class SecurityMatchers(object):
|
|
|
|
@staticmethod
|
|
def UiMsg(type, address=None):
|
|
return lambda event: True if event.message_type == type and (address == None or address == event.peer) else False
|
|
|
|
@staticmethod
|
|
def BondMsg(type, address=None, reason=None):
|
|
return lambda event: True if event.message_type == type and (address == None or address == event.peer) and (reason == None or reason == event.reason) else False
|
|
|
|
@staticmethod
|
|
def HelperMsg(type, address=None):
|
|
return lambda event: True if event.message_type == type and (address == None or address == event.peer) else False
|
|
|
|
|
|
class IsoMatchers(object):
|
|
|
|
@staticmethod
|
|
def Data(payload):
|
|
return lambda packet: packet.payload == payload
|
|
|
|
@staticmethod
|
|
def PacketPayloadWithMatchingCisHandle(cis_handle):
|
|
return lambda packet: None if cis_handle != packet.handle else packet
|