// Copyright 2011 Google Inc.
//
// 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.

// Specification of protocol buffers and the client-server protocol that are
// used by the clients of the system.
//
// Note: unless otherwise specified in a comment, all fields in all messages
// are required, even though they are listed as optional.

syntax = "proto2";

option optimize_for = LITE_RUNTIME;

package ipc.invalidation;

// Here is a high-level overview of the protocol. The protocol is designed in a
// "message-passing" style, i.e., the client (C) or the server (S) can send any
// message SPONTANEOUSLY at any time and both sides have to be prepared for any
// message from the other side. However, even with this style, there are some
// "flows" which are somewhat like requests and replies.

// 1. Initialization: When a client starts up, it needs a token to alow it to
//    perform any other operation with the server.
//    C -> S: InitializeMessage
//    S -> C: TokenControlMessage
//
// 2. Registration: When a client has to register or unregister for a set of
//    objects, the following flow occurs:
//    C -> S: RegistrationMessage
//    S -> C: RegistrationStatusMessage
//
// 3. Invalidation: The server sends an invalidation and the client sends back
//    an ack.
//    S -> C InvalidationMessage
//    C -> S InvalidationMessage
//
// 4. Registration sync: Once in a while the server may detect that the client
//    and server's registration state is out of sync (the server can detect this
//    since it gets the client's registration summary in the client's message
//    header). In that case, it asks the client some registration information
//    and the client sends it to the server.
//    S -> C: RegistrationSyncRequestMessage
//    C -> S: RegistrationSyncMessage
//
// 5. Information messages: The server can occasionally for client-side
//    information such as statistics, etc. The client responds with the
//    requested information
//    S -> C: InfoRequestMessage
//    C -> S: InfoMessage
//
// ------------------------------------------------------------------------

// A basic message type used for versioning public proto messages and/or
// types. The two fields are supposed to be used as follows:
//
// * The major version number is changed whenever an incompatible protocol
//   change or type has been made.  When a message/object with a particular
//   major version is received, the receiver needs to either know how to handle
//   this version or it needs to drop the message
//
// * The minor version can be changed (say) to document some internal change
//   for debugging purposes. When a message is received by a receiver, it MUST
//   ignore the minor version number for making any protocol/type
//   decisions. I.e., the minor version number is for debugging purposes only.
//
//   Versioning is used in various places - for entities that are part of a
//   protocol (e.g., message requests), for various client implementations, and
//   for other data types that change independently of the protocol (e.g.,
//   session tokens).  For each versioned entity, we define a specific message
//   type to encapsulate the version of that entity (e.g., ProtocolVersion,
//   ClientVersion, etc.).
message Version {
  optional int32 major_version = 1;
  optional int32 minor_version = 2;
}

// Message included in all client <-> server messages to indicate the version
// of the protocol in use by the sender.
message ProtocolVersion {
  optional Version version = 1;
}

// Defines a specific version of the client library (for information purposes
// only) May not be used to make decisions at the server (use ProtocolVersion
// instead).
message ClientVersion {

  // A client-specific version number.
  optional Version version = 1;

  // All fields below are for informational/debugging/monitoring purposes only.
  // No critical code decision is supposed to be made using them.

  // Information about the client operating system/platform, e.g., Windows,
  // ChromeOS.
  optional string platform = 2;

  // Language used for the library.
  optional string language = 3;

  // Extra information about the client (e.g., application name).
  optional string application_info = 4;
}

// Message indicating the result of an operation.
message StatusP {

  // Whether operation is successful or not
  enum Code {
    SUCCESS = 1;
    TRANSIENT_FAILURE = 2;
    PERMANENT_FAILURE = 3;
  }

  optional Code code = 1;

  // Textual description of the status or additional context about any
  // error. (Optional - Can be set for success also.)
  optional string description = 2;
}

// Identifies an object that a client can register for.
message ObjectIdP {

  // The source of the data.
  optional int32 source = 1;

  // The id of the object relative to the source. Must be <= 64 bytes.
  optional bytes name = 2;
}

// A message containing the part of the client's id that the application
// controls. This id is used for squelching invalidations on the server side.
// For example, if a client C1 modifies object x and informs the backend about
// C1's application client id as part of the invalidation. The backend can then
// avoid sending the invalidation unnecessarily to that client.
//
// If the application wishes to use this squelching feature, it must assign a
// globally unique client_name for a given client_type so that the particular
// instantation of the application can be identified.
message ApplicationClientIdP {
  // The type of the client.
  optional int32 client_type = 1;

  // A client name or unique id assigned by the application.  Application should
  // choose a unique name for different client instances if it wants to squelch
  // invalidations by name (as discussed above).
  optional bytes client_name = 2;
}

// Invalidation for a given object/version.
message InvalidationP {
  // The id of the object being invalidted.
  optional ObjectIdP object_id = 1;

  // Whether the invalidation is for a known version of the object as assigned
  // by an application backend (is_known_version == true) or an unknown system
  // version synthesized by the invalidation service.
  optional bool is_known_version = 2;

  // Version being invalidated (see comment on is_known_version). If the
  // is_known_version is false, the version corresponds to an internal "system
  // version" for *that* ojbect. An object's system version has no meaning to
  // the application other than the fact that these system versions are also
  // monotonically increasing and the client must ack such an invalidation with
  // this system version (and an ack for a later system version acknowledges an
  // invalidation for all earlier system version for *that* object.
  optional int64 version = 3;

  // Optional payload associated with this invalidation.
  optional bytes payload = 4;
}

// Specifies the intention to change a registration on a specific object.  To
// update registrations, a client sends a message containing repeated
// RegistrationP messages.
message RegistrationP {
  enum OpType {
    REGISTER = 1;
    UNREGISTER = 2;
  }

  // The object for which to (un)register.
  optional ObjectIdP object_id = 1;

  // Whether to register or unregister.
  optional OpType op_type = 2;
}

// Summary of the registration state associated with a particular client, sent
// in the header of client<->server messages. This summary has two different
// (but related) meanings depending on where it is used:
//
// 1) In a client->server message, it describes the DESIRED client state.
// 2) In a server->client message, it describes the ACTUAL state at the server
//    for that client.
message RegistrationSummary {
  // Number of registrations desired (client) or held (server).
  optional int32 num_registrations = 1;

  // Top-level digest over the registrations.
  //
  // The digest for an object id is computed as following (the digest chosen for
  // this method is SHA-1):
  //
  //  digest = new Digest();
  //  digest.update(Little endian encoding of object source type)
  //  digest.update(object name)
  //  digest.getDigestSummary()
  //
  // For a set of objects, digest is computing by sorting lexicographically
  // based on their digests and then performing the update process given above
  // (i.e., calling digest.update on each object's digest and then calling
  // getDigestSummary at the end).
  optional bytes registration_digest = 2;
}

// Header included on every client -> server message.
message ClientHeader {

  // Protocol version of this message.
  optional ProtocolVersion protocol_version = 1;

  // Token identifying the client. Tokens are issued by the server in response
  // to client requests (see InitializeMessage, below). In order to perform any
  // operation other than initialization, the client must supply a token. When
  // performing initialization, this field must be left unset.
  optional bytes client_token = 2;

  // Optional summary of the client's desired registration state. The client is
  // encouraged to provide this summary in every message once a "steady" state
  // of registrations/unregistrations has been reached. For example, it may not
  // want to send this summary during initialization (but after the initial set
  // has been registered, it should try to send it).
  optional RegistrationSummary registration_summary = 3;

  // Timestamp from the client's clock, expressed as ms since 00:00:00 UTC, 1
  // January 1970 (i.e., the UNIX epoch) - for debugging/monitoring purposes.
  optional int64 client_time_ms = 4;

  // Highest server timestamp observed by the client (the server includes its
  // time on every message to the client). Note: this time is NOT necessarily
  // expressed as relative to the UNIX epoch - for debugging/monitoring
  // purposes.
  optional int64 max_known_server_time_ms = 5;

  // Message id to identify the message -for debugging/monitoring purposes.
  optional string message_id = 6;
}

// A message from the client to the server.
message ClientToServerMessage {
  // Header.
  optional ClientHeader header = 1;

  // Any or all of the follow messages may be present.

  // Optional initialization message, used to obtain a new token. Note that, if
  // present, this message is always processed before the messages below, and
  // those messages will be interpreted relative to the new token assigned here.
  optional InitializeMessage initialize_message = 2;

  // Optional request to perform registrations.
  optional RegistrationMessage registration_message = 3;

  // Optional data for registration sync.
  optional RegistrationSyncMessage registration_sync_message = 4;

  // Optional invalidation acks.
  optional InvalidationMessage invalidation_ack_message = 5;

  // Optional information about the client.
  optional InfoMessage info_message = 6;
}

// Used to obtain a new token when the client does not have one.
message InitializeMessage {

  // Defines how clients serialize object ids when computing digests for
  // registrations.
  enum DigestSerializationType {

    // The digest for an object id is computed by serializing the object id into
    // bytes.
    BYTE_BASED = 1;

    // The digest for an object id is computed by serializing the object id into
    // an array of numbers.
    NUMBER_BASED = 2;
  }

  // Type of the client. This value is assigned by the backend notification
  // system (out-of-band) and the client must use the correct value.
  optional int32 client_type = 1;

  // Nonce. This value will be echoed as the existing token in the header of
  // the server message that supplies the new token (the new token itself will
  // be provided in a TokenControlMessage; see below).
  optional bytes nonce = 2;

  // Id of the client as assigned by the application.
  optional ApplicationClientIdP application_client_id = 3;

  // Type of registration digest used by this client.
  optional DigestSerializationType digest_serialization_type = 4;
}

// Registration operations to perform.
message RegistrationMessage {
  repeated RegistrationP registration = 1;
}

// Message from the client to the server.
message RegistrationSyncMessage {

  // Objects for which the client is registered.
  repeated RegistrationSubtree subtree = 1;
}

// Message sent from the client to the server about registered objects
// (typically) in response to a registration sync request.
//
// The name of the message implies a "tree" for future expansion where the
// intention is to not necessarily send the complete set of objects but to
// partition the object space into multiple ranges and then exchange Merkle-tree
// like data structures to determine which ranges are out-of-sync.
message RegistrationSubtree {
  // Registered objects
  repeated ObjectIdP registered_object = 1;
}

// A message from the client to the server with info such as performance
// counters, client os info, etc.
message InfoMessage {
  optional ClientVersion client_version = 1;

  // Config parameters used by the client.
  // Deprecated and removed - the client_config parameter is what is used now.
  repeated PropertyRecord config_parameter = 2;

  // Performance counters from the client.
  repeated PropertyRecord performance_counter = 3;

  // If 'true', indicates that the client does not know the server's
  // registration summary, so the server should respond with it even if the
  // client's summary matches the server's.
  optional bool server_registration_summary_requested = 4;

  // Configuration parameters for this client.
  optional ClientConfigP client_config = 5;
}

// Information about a single config/performance counter value in the
// InfoMessage.
message PropertyRecord {

  // Name of the performance counter/config parameter.
  optional string name = 1;

  // Value of the performance counter/config parameter.
  optional int32 value = 2;
}

message ServerHeader {
  // Protocol version of this message.
  optional ProtocolVersion protocol_version = 1;

  // Current token that the server expects the client to have. Clients must
  // ignore messages where this token field does not match their current token.
  // During initialization, the client's "token" is the nonce that it generates
  // and sends in the InitializeMessage.
  optional bytes client_token = 2;

  // Summary of registration state held by the server for the client.
  optional RegistrationSummary registration_summary = 3;

  // Timestamp from the server's clock. No guarantee on when this time is
  // relative to.
  optional int64 server_time_ms = 4;

  // Message id to identify the message (for debug purposes only).
  optional string message_id = 5;
}

message ServerToClientMessage {
  optional ServerHeader header = 1;

  // Message to assign a new client token or invalidate an existing one.  Note
  // that, if present, this message is always processed before the messages
  // below, and those messages will be interpreted relative to the new token
  // assigned here.
  optional TokenControlMessage token_control_message = 2;

  // Invalidations.
  optional InvalidationMessage invalidation_message = 3;

  // Registration operation replies.
  optional RegistrationStatusMessage registration_status_message = 4;

  // Request for client registration state.
  optional RegistrationSyncRequestMessage registration_sync_request_message = 5;

  // Request to change config from the server.
  optional ConfigChangeMessage config_change_message = 6;

  // Request for client information.
  optional InfoRequestMessage info_request_message = 7;

  // Asynchronous error information that the server sends to the client.
  optional ErrorMessage error_message = 8;
}

// Message used to supply a new client token or invalidate an existing one.
message TokenControlMessage {
  // If status is failure, new_token cannot be set.
  optional bytes new_token = 1;  // If missing, means destroy_token
}

// Status of a particular registration (could be sent spontaneously by the
// server or in response to a registration request).
message RegistrationStatus {
  optional RegistrationP registration = 1;
  optional StatusP status = 2;
}

// Registration status of several messages from the server to the client.
message RegistrationStatusMessage {
  repeated RegistrationStatus registration_status = 1;
}

// Request from the server to get the registration info from the client for
// sync purposes.
message RegistrationSyncRequestMessage {
}

// A set of invalidations from the client to the server or vice-versa
message InvalidationMessage {
  repeated InvalidationP invalidation = 1;
}

// A request from the server to the client for information such as
// performance counters, client os, etc
message InfoRequestMessage {
  enum InfoType {
    GET_PERFORMANCE_COUNTERS = 1;
  }
  repeated InfoType info_type = 1;
}

// A rate limit: a count of events and a window duration in which the events
// may occur.
message RateLimitP {

  // The size of the window over which the rate limit applies.
  optional int32 window_ms = 1;

  // The number of events allowed within a given window.
  optional int32 count = 2;
}

// Configuration parameters for the protocol handler in the Ticl.
message ProtocolHandlerConfigP {
  // Batching delay - certain messages (e.g., registrations, invalidation acks)
  // are sent to the server after this delay.
  optional int32 batching_delay_ms = 1 [default = 500];

  // Rate limits for sending messages. Only two levels allowed currently.
  repeated RateLimitP rate_limit = 2;
}

// Configuration parameters for the Ticl.
message ClientConfigP {

  optional Version version = 1;

  // The delay after which a network message sent to the server is considered
  // timed out.
  optional int32 network_timeout_delay_ms = 2 [default = 60000];

  // Retry delay for a persistent write if it fails
  optional int32 write_retry_delay_ms = 3 [default = 10000];

  // Delay for sending heartbeats to the server.
  optional int32 heartbeat_interval_ms = 4 [default = 1200000];

  // Delay after which performance counters are sent to the server.
  optional int32 perf_counter_delay_ms = 5 [default = 21600000];  // 6 hours.

  // The maximum exponential backoff factor used for network and persistence
  /// timeouts.
  optional int32 max_exponential_backoff_factor = 6 [default = 500];

  // Smearing percent for randomizing delays.
  optional int32 smear_percent = 7 [default = 20];

  // Whether the client is transient, that is, does not write its session
  // token to durable storage.
  // TODO(xiaolan): (BUG 5627144) need to expose to the clients.
  // For android the default is false. But for mtrx the default is true.
  optional bool is_transient = 8 [default = false];

  // Initial delay for a heartbeat after restarting from persistent state. We
  // use this so that the application has a chance to respond to the
  // reissueRegistrations call.
  optional int32 initial_persistent_heartbeat_delay_ms = 9 [default = 2000];

  // Configuration for the protocol client to control batching etc.
  optional ProtocolHandlerConfigP protocol_handler_config = 10;
}

// A message asking the client to change its configuration parameters
message ConfigChangeMessage {

  // On receipt of this value, do not send any new message to the server
  // for the specified delay (this message needs to be accepted without
  // any token check). A zero value is ignored by the client. So the lowest
  // value for this field is 1. This concept exists to allow the server
  // to tell the clients that they should not come back to the server
  // for some period of time.
  optional int64 next_message_delay_ms = 1;
}

// An error message that contains an enum for different types of failures with a
// textual description of the failure (as the need arises new error codes will
// be added to this message).
message ErrorMessage {

  enum Code {
    AUTH_FAILURE = 1;  // Authorization or authentication failure.
    UNKNOWN_FAILURE = 10000;  // Some failure which is not described above.
  };

  optional Code code = 1;

  // Textual description of the error
  optional string description = 2;
}
