Table of Contents

Class MrpProtocol

Namespace
AppleTvControlLibrary.Mrp.Protocol
Assembly
AppleTvControlLibrary.Mrp.dll

Protocol logic related to MRP: connects, performs the initial DEVICE_INFORMATION exchange, enables encryption via pair-verify, sends the post-encryption bootstrap messages, and provides request/response correlation plus unsolicited-message dispatch on top of the underlying IMrpFrameConnection.

public sealed class MrpProtocol : IMrpConnectionListener, IDisposable
Inheritance
MrpProtocol
Implements
Inherited Members

Remarks

This type has no socket I/O of its own; a caller supplies AsyncSender to transmit framed bytes and feeds inbound bytes to the underlying IMrpFrameConnection (via Connection), mirroring the transport-agnostic design already used by AppleTv.Companion's CompanionProtocol.

Constructors

MrpProtocol(IMrpFrameConnection, SrpAuthHandler, MrpInfoSettings)

Initializes a new instance of the MrpProtocol class.

public MrpProtocol(IMrpFrameConnection connection, SrpAuthHandler srp, MrpInfoSettings info)

Parameters

connection IMrpFrameConnection

The underlying framed connection. May be an AirPlay-tunneled connection, or historically the retired raw-TCP MrpConnection (see archive/mrp-tcp-transport).

srp SrpAuthHandler

The SRP handler used for pair-verify and key derivation.

info MrpInfoSettings

Client information reported in the initial DEVICE_INFORMATION message.

Properties

AsyncSender

Gets or sets the asynchronous callback that transmits fully-built frames.

public Func<byte[], Task>? AsyncSender { get; set; }

Property Value

Func<byte[], Task>

Connection

Gets the underlying connection, so a transport can feed it received bytes.

public IMrpFrameConnection Connection { get; }

Property Value

IMrpFrameConnection

Credentials

Gets or sets the credentials used to enable encryption. Set externally when reusing previously-paired credentials rather than pairing fresh (mirrors pyatv checking self.service.credentials). pyatv/protocols/mrp/protocol.py — line 137-140 as of pyatv 0.18.0

public HapCredentials? Credentials { get; set; }

Property Value

HapCredentials

Listener

Gets or sets the listener notified when an unsolicited message is dispatched.

public IMrpProtocolListener? Listener { get; set; }

Property Value

IMrpProtocolListener

PowerState

Gets the last-known device power state, derived from DeviceInfoMessage.logicalDeviceCount on every DEVICE_INFO_MESSAGE / DEVICE_INFO_UPDATE_MESSAGE seen so far.

public MrpPowerState PowerState { get; }

Property Value

MrpPowerState

ResponseTimeout

Gets or sets how long to wait for a response before SendAndReceiveAsync(ProtocolMessage, bool, CancellationToken) throws a MrpProtocolException.

public TimeSpan ResponseTimeout { get; set; }

Property Value

TimeSpan

State

Gets the current protocol state.

public MrpProtocolState State { get; }

Property Value

MrpProtocolState

Methods

Dispose()

Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.

public void Dispose()

MessageReceived(byte[])

A complete, decrypted message was received from the device.

public void MessageReceived(byte[] data)

Parameters

data byte[]

The serialized protobuf message bytes.

SendAndReceiveAsync(ProtocolMessage, bool, CancellationToken)

Send a message and wait for a response.

public Task<ProtocolMessage> SendAndReceiveAsync(ProtocolMessage message, bool generateIdentifier = true, CancellationToken cancellationToken = default)

Parameters

message ProtocolMessage

The message to send.

generateIdentifier bool

Whether to generate and set a new identifier on the message before sending (some messages, like crypto pairing, never carry one, and are instead correlated by message type since only one such exchange can ever be outstanding).

cancellationToken CancellationToken

A token that cancels waiting to send or receive a response.

Returns

Task<ProtocolMessage>

The response message received from the device.

SendAsync(ProtocolMessage, CancellationToken)

Send a message and expect no response.

public Task SendAsync(ProtocolMessage message, CancellationToken cancellationToken = default)

Parameters

message ProtocolMessage

The message to send.

cancellationToken CancellationToken

A token that cancels waiting to send.

Returns

Task

StartAsync(bool, CancellationToken)

Send the initial DEVICE_INFORMATION message, enable encryption if credentials are set, and run the post-encryption bootstrap sequence.

public Task StartAsync(bool skipInitialMessages = false, CancellationToken cancellationToken = default)

Parameters

skipInitialMessages bool

If true, stop right after DEVICE_INFORMATION (used by proxy-style reuse of a protocol object). pyatv/protocols/mrp/protocol.py — line 154-155 as of pyatv 0.18.0

cancellationToken CancellationToken

A token that cancels the bootstrap exchanges.

Returns

Task

Stop()

Disconnect from the device, failing any outstanding requests.

public void Stop()

Events

PowerStateChanged

Raised when PowerState changes as a result of an inbound DEVICE_INFO_MESSAGE or DEVICE_INFO_UPDATE_MESSAGE. Carries the previous and new power state, in that order.

public event Action<MrpPowerState, MrpPowerState>? PowerStateChanged

Event Type

Action<MrpPowerState, MrpPowerState>

RawMessageReceived

Raised for every inbound message, before correlation/dispatch, carrying the raw message type number and full serialized payload.

public event Action<int, byte[]>? RawMessageReceived

Event Type

Action<int, byte[]>

Remarks

This is a diagnostic-only hook, not a port of anything in pyatv. Its purpose is to make message types absent from the vendored pyatv 0.18.0 .proto set observable instead of silently dropped: ProtocolMessage.Type declares SET_READY_STATE_MESSAGE = 36 and UPDATE_ACTIVE_SYSTEM_ENDPOINT_MESSAGE = 77 with no corresponding extension message/field anywhere in the tree, and the enum itself skips 13, 14 and 45 entirely, so pyatv has no name — and therefore no handler — for whatever Apple sends under those numbers. Listener/IMrpProtocolListener only ever surfaces ProtocolMessage instances decoded against Registry, which is sufficient for known extension fields but does not by itself make an unnamed type number easy to spot; this event exists purely so a consumer can log every (type, raw bytes) pair unconditionally while investigating unknown wire traffic.