blob: fc6ecbdb31047641ab753f5e058d2ab3ef3ff877 [file] [log] [blame]
syntax = "proto2";
package offworld;
// Offworld is a protocol implemented by the Android Emulator to enable
// controlling features of the emulator from the guest OS.
//
// The protocol is implemented on top of qemu pipe service, and the underlying
// encoding of the pipe is a stream of protobufs, with the length of each
// message encoded as a little-endian uint32 immediately before it:
//
// <uint32 length> <length bytes of binary-serialized protobuf>
//
// Upon receiving each message, the protobuf is deserialized from its binary
// form and dispatched.
//
// On connect, the guest is expected to first send a ConnectHandshake. For
// notational purposes, assume that the protobuf has been serialized with the
// above encoding. The guest then waits for the host to respond with a
// ConnectHandshakeResponse message.
//
// guest->host: ConnectHandshake { version: 1 }
// host->guest: ConnectHandshakeResponse { result: OK }
//
// The ConnectHandshakeResponse will either contain Result.OK or an error.
// If an error value is returned the host will not handle any further guest
// messages.
//
// After the connection is established, the guest may send Request messages, and
// will receive synchronous replies with a Response message. The response may
// include a pending_async_id, in which case there may be additional messages
// with an AsyncResponse payload corresponding to that async_id.
//
// For synchronous messages, the flow is:
//
// guest->host: Request { snapshot { create_checkpoint {
// snapshot_name: "Test" } } }
// host->guest: Response { result: OK, snapshot { create_checkpoint { ... } } }
//
//
// For asynchronous messages, a synchronous response is immediately returned
// with a pending_async_id, followed by one or more AsyncResponse messages with
// a matching async_id. Multiple async messages may be generated as a stream;
// to determine when the stream is complete check AsyncResponse.complete.
//
// guest->host: Request { automation { replay { event: "..." } } }
// host->guest: Response { result: OK, pending_async_id: 1 } }
// ...
// host->guest: Response { async { async_id: 1, complete: true, automation {
// replay_complete { } } } }
message ConnectHandshake {
optional uint32 version = 1;
}
message ConnectHandshakeResponse {
enum Result {
RESULT_NO_ERROR = 0;
RESULT_ERROR_UNKNOWN = 1;
RESULT_ERROR_VERSION_MISMATCH = 2;
}
optional Result result = 1;
}
//
// Requests
//
message Request {
oneof module {
SnapshotRequest snapshot = 1;
AutomationRequest automation = 2;
VideoInjectionRequest video_injection = 3;
SensorMockRequest sensor_mock = 4;
}
}
message SnapshotRequest {
message CreateCheckpoint {
optional string snapshot_name = 1;
}
message GotoCheckpoint {
enum ShareMode {
UNKNOWN = 0;
UNCHANGED = 1;
WRITABLE = 2;
READ_ONLY = 3;
};
optional string snapshot_name = 1;
optional bytes metadata = 2;
optional ShareMode share_mode = 3;
}
message ForkReadOnlyInstances {
optional int32 num_instances = 1;
}
message DoneInstance {
optional bytes metadata = 1;
}
oneof function {
CreateCheckpoint create_checkpoint = 1;
GotoCheckpoint goto_checkpoint = 2;
ForkReadOnlyInstances fork_read_only_instances = 3;
DoneInstance done_instance = 4;
}
}
message AutomationRequest {
message ReplayInitialState {
optional string state = 1;
}
message Replay {
optional string event = 1;
}
message Listen {}
message StopListening {}
oneof function {
ReplayInitialState replay_initial_state = 1;
Replay replay = 2;
Listen listen = 3;
StopListening stop_listening = 4;
}
}
message VideoInjectionRequest {
optional uint32 sequence_id = 1;
message Load {
optional bytes video_data = 1;
optional DatasetInfo dataset_info = 2;
}
message Play {
optional float offset_in_seconds = 1;
optional bool looping = 2;
optional float speed_factor = 3;
}
message Pause {
optional float offset_in_seconds = 1;
}
message Stop {
optional float delay_in_seconds = 1;
}
message DisplayDefaultFrame {}
oneof function {
Load load = 50;
Play play = 51;
Pause pause = 52;
Stop stop = 53;
DisplayDefaultFrame display_default_frame = 54;
}
}
message DatasetInfo {
optional DataStreamInfo location = 1;
optional DataStreamInfo accelerometer = 2;
optional DataStreamInfo gyroscope = 3;
optional DataStreamInfo magnetic_field = 4;
optional DataStreamInfo video_metadata = 5;
}
message DataStreamInfo {
optional uint32 stream_index = 1;
oneof packet_info {
SensorDataPacketInfo sensor_packet = 2;
LocationDataPacketInfo location_packet = 3;
VideoMetadataPacketInfo video_metadata_packet = 4;
}
}
message SensorDataPacketInfo {
optional FieldInfo timestamp = 1;
repeated FieldInfo value = 2;
}
message LocationDataPacketInfo {
optional FieldInfo timestamp = 1;
optional FieldInfo latitude = 2;
optional FieldInfo longitude = 3;
optional FieldInfo meters_elevation = 4;
optional FieldInfo knots_speed = 5;
optional FieldInfo degrees_heading = 6;
optional FieldInfo num_satellites = 7;
}
message VideoMetadataPacketInfo {
optional FieldInfo timestamp = 1;
optional FieldInfo frame_number = 2;
}
message FieldInfo {
enum Type {
UNDEF = 0;
INT_32 = 1;
INT_64 = 2;
FLOAT = 3;
DOUBLE = 4;
}
optional uint32 offset = 1;
optional Type type = 2;
}
message LocationData {
optional double latitude = 1;
optional double longitude = 2;
optional double meters_elevation = 3;
optional double knots_speed = 4;
optional double degrees_heading = 5;
optional uint32 num_satellites = 6;
}
message SensorMockRequest {
optional uint64 timestamp_us = 1;
message LocationMock {
optional LocationData location_data = 1;
}
oneof function {
LocationMock mock_location = 50;
}
}
//
// Responses
//
message Response {
enum Result {
RESULT_NO_ERROR = 0;
RESULT_ERROR_UNKNOWN = 1;
RESULT_ERROR_ACCESS_DENIED = 2;
RESULT_ERROR_NOT_IMPLEMENTED = 3;
}
optional Result result = 1;
optional string error_string = 2;
// Indicates that this is an async messages and there will be additional
// messages with a matching module.async.async_id.
optional uint32 pending_async_id = 3;
oneof module {
// Sync.
SnapshotResponse snapshot = 100;
AutomationResponse automation = 101;
VideoInjectionResponse video_injection = 102;
// Async.
// Async response.
AsyncResponse async = 201;
}
}
message AsyncResponse {
// An async id corresponding to the async_id from the original synchronous
// response.
optional uint32 async_id = 1;
// True if there will be no further messages with this async_id.
optional bool complete = 2;
oneof module {
AutomationAsyncResponse automation = 100;
VideoInjectionAsyncResponse video_injection = 101;
}
}
message SnapshotResponse {
message CreateCheckpoint {
optional bytes metadata = 1;
}
message GotoCheckpoint {}
message ForkReadOnlyInstances {
optional int32 instance_id = 1;
optional bytes metadata = 2;
}
message DoneInstance {}
oneof function {
CreateCheckpoint create_checkpoint = 1;
GotoCheckpoint goto_checkpoint = 2;
ForkReadOnlyInstances fork_read_only_instances = 3;
DoneInstance done_instance = 4;
}
}
message AutomationResponse {
message Listen {
// An text-encoded protobuf containing the initial state of the system.
optional string initial_state = 1;
}
oneof function {
Listen listen = 1;
}
}
message VideoInjectionResponse {
optional uint32 sequence_id = 1;
}
message AutomationAsyncResponse {
message ReplayComplete {}
message EventGenerated {
// A text-encoded protobuf containing a replayable event.
optional string event = 1;
}
oneof function {
ReplayComplete replay_complete = 1;
EventGenerated event_generated = 2;
}
}
message VideoInjectionAsyncResponse {
optional uint32 sequence_id = 1;
}