/*
 * Copyright (C) 2023 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.
 */

syntax = "proto2";

package android.os.statsd.cronet;

import "frameworks/proto_logging/stats/atoms.proto";
import "frameworks/proto_logging/stats/atom_field_options.proto";

option java_package = "com.android.os.cronet";
option java_multiple_files = true;

extend Atom {
  optional CronetEngineCreated cronet_engine_created = 703
  [(module) = "cronet"];
  optional CronetTrafficReported cronet_traffic_reported = 704
  [(module) = "cronet"];
  optional CronetEngineBuilderInitialized cronet_engine_builder_initialized = 762 [(module) = "cronet"];
  optional CronetHttpFlagsInitialized cronet_http_flags_initialized = 763 [(module) = "cronet"];
  optional CronetInitialized cronet_initialized = 764 [(module) = "cronet"];
}

// Logged every time CronetEngine.Builder#build() returns.
message CronetEngineCreated {
  // A weak-ID-like reference to the instance of the Cronet engine being
  // created. The field can be used to join subsequent request metrics with the
  // engine details.
  //
  // The reference is NOT meant to be globally (or even on-device) unique, but
  // on device collisions should be unlikely.
  // Defaults to max positive int64 value which is equivalent to "not set".
  optional int64 engine_instance_ref = 1 [default = 9223372036854775807];
  // Deprecated, moved to CronetEngineBuilderInitialized
  optional int32 major_version = 2 [deprecated = true];
  // Deprecated, moved to CronetEngineBuilderInitialized
  optional int32 minor_version = 3 [deprecated = true];
  // Deprecated, moved to CronetEngineBuilderInitialized
  optional int32 build_version = 4 [deprecated = true];
  // Deprecated, moved to CronetEngineBuilderInitialized
  optional int32 patch_version = 5 [deprecated = true];

  // Deprecated, moved to CronetInitialized
  optional CronetSource source = 6 [deprecated = true];
  // Options that can be set on the Cronet builder. Only fields which are
  // actively set by the Cronet user are populated. When using the protos
  // default values should be considered as "null" - default semnantics can
  // change based on the Cronet version.
  //
  // Fields that can contain free text (even if just theoretically) should be
  // curated and/or replaced with fields that provide useful signal while not
  // being sensitive. For instance, instead of logging values of quic hint hosts
  // verbatim, prefer to log if a request was affected by a hint.
  //
  // For field names, follow the nomenclature below:
  // - fields set directly on the builder should have no prefix
  //   (e.g., enableBrotli() translates to enable_brotli)
  // - fields which are set in a sub-object or sub-structure should be prefixed
  //   with the path leading to that field. For example,
  //   builder.setExperimentalOptions("quic: { foo: { bar: 2 } }");
  //   translates to experimental_options_quic_foo_bar
  //
  //
  // In both cases, keep the existing names that are used in Cronet if at all
  // possible.

  // --- CronetEngine.Builder controls ---
  optional bool enable_brotli = 7;
  optional bool enable_http2 = 8;
  optional CronetHttpCacheMode http_cache_mode = 9;
  optional bool enable_public_key_pinning_bypass_for_local_trust_anchors = 10;
  optional bool enable_quic = 11;

  // --- ExperimentalCronetEngine.Builder controls ---
  optional bool enable_network_quality_estimator = 12;
  optional int32 thread_priority = 13;

  // --- JSON experimental options ---
  // The JSON config offers a lot of options but most of the atoms will have
  // a majority of the fields below unset. We considered lumping the boolean
  // flags into a single int32 field bud decided not to do so because
  //   a) having the standalone booleans makes the data set easier to understand
  //      and use
  //   b) we need to distinguish between three states (unset / true / false)
  //      which makes the compaction less efficient
  //   c) we expect the booleans to not be set in majority of the cases,
  //      in such a case, no space is saved.

  // QUIC
  // Lexicographically sorted, comma separated, curated list of QUIC connection
  // options.
  optional string experimental_options_quic_connection_options = 14;
  optional OptionalBoolean
      experimental_options_quic_store_server_configs_in_properties = 15;
  // Unset value is -1
  optional int32
      experimental_options_quic_max_server_configs_stored_in_properties = 16
      [default = -1];
  // Unset value is -1
  optional int32 experimental_options_quic_idle_connection_timeout_seconds = 17
      [default = -1];
  optional OptionalBoolean
      experimental_options_quic_goaway_sessions_on_ip_change = 18;
  optional OptionalBoolean
      experimental_options_quic_close_sessions_on_ip_change = 19;
  optional OptionalBoolean
      experimental_options_quic_migrate_sessions_on_network_change_v2 = 20;
  optional OptionalBoolean experimental_options_quic_migrate_sessions_early_v2 =
      21;
  optional OptionalBoolean
      experimental_options_quic_quic_disable_bidirectional_streams = 22;
  // Unset value is -1
  optional int32
      experimental_options_quic_max_time_before_crypto_handshake_seconds = 23
      [default = -1];
  // Unset value is -1
  optional int32
      experimental_options_quic_max_idle_time_before_crypto_handshake_seconds =
          24 [default = -1];
  optional OptionalBoolean
      experimental_options_quic_enable_socket_recv_optimization = 25;

  // DNS
  optional OptionalBoolean experimental_options_asyncdns_enable = 26;
  optional OptionalBoolean experimental_options_staledns_enable = 27;
  // Unset value is -1
  optional int32 experimental_options_staledns_delay_ms = 28 [default = -1];
  // Unset value is -1
  optional int32 experimental_options_staledns_max_expired_time_ms = 29
  [default = -1];
  // Unset value is -1
  optional int32 experimental_options_staledns_max_stale_uses = 30
  [default = -1];
  optional OptionalBoolean experimental_options_staledns_allow_other_network =
      31;
  optional OptionalBoolean experimental_options_staledns_persist_to_disk = 32;
  // Unset value is -1
  optional int32 experimental_options_staledns_persist_delay_ms = 33
  [default = -1];
  optional OptionalBoolean
      experimental_options_staledns_use_stale_on_name_not_resolved = 34;
  // Host resolver rules omitted
  optional OptionalBoolean experimental_options_disable_ipv6_on_wifi = 35;

  // Deprecated, moved to CronetEngineBuilderInitialized.
  enum CronetSource {
    option deprecated = true;
    // Safe default, don't use explicitly.
    CRONET_SOURCE_UNSPECIFIED = 0;
    // The library is bundled with the application.
    CRONET_SOURCE_STATICALLY_LINKED = 1;
    // The library is loaded from GMS Core
    CRONET_SOURCE_GMSCORE_DYNAMITE = 2;
    // The application is using the fallback implementation
    CRONET_SOURCE_FALLBACK = 3;
  }

  // A pointer to CronetInitialized.cronet_initialization_ref for the Cronet initialization
  // sequence that this engine relied on.
  // Note that CronetInitialized may be logged after CronetEngineCreated because initialization
  // continues in the background after the CronetEngine is instantiated.
  // One consequence is that this reference can be broken if a
  // crash occurs after the engine is created but before initialization is complete.
  optional int64 cronet_initialization_ref = 36;

  // See
  // https://developer.android.com/guide/topics/connectivity/cronet/reference/org/chromium/net/CronetEngine.Builder.html#constants
  // for detailed semantics.
  enum CronetHttpCacheMode {
    // Safe default, don't use explicitly.
    HTTP_CACHE_MODE_UNSPECIFIED = 0;

    HTTP_CACHE_DISABLED = 1;
    HTTP_CACHE_DISK = 2;
    HTTP_CACHE_DISK_NO_HTTP = 3;
    HTTP_CACHE_IN_MEMORY = 4;
  }
}

// Logged when a request has reached terminal state after the final user callback has returned
// and before the requestFinishedListeners has fired.
message CronetTrafficReported {
  // The Cronet engine that sent the trafic. See
  // CronetEngineCreated.engine_instance_ref for more details.
  // Defaults to max negative int64 value which is equivalent to "not set".
  // The default value is deliberately different from
  // CronetEngineCreated.engine_instance_ref to avoid unintentional joining.
  optional int64 engine_instance_ref = 1 [default = -9223372036854775808];

  // Bucketized sizes for request and response headers and body.
  optional CronetRequestHeadersSizeBucket request_headers_size = 2;
  optional CronetRequestBodySizeBucket request_body_size = 3;
  optional CronetResponseHeadersSizeBucket response_headers_size = 4;
  optional CronetResponseBodySizeBucket response_body_size = 5;

  // The status code of the response.
  optional int32 http_status_code = 6;
  // The Fingerprint2011 hash of the protocol that was negotiated for this
  // request (as returned by UrlResponseInfo.getNegotiatedProtocol()). The
  // possible values for the string are limited so the hash is sufficient
  // to identify known values while preventing accidental presence of
  // freeform text.
  // See go/cronet-negotiated-protocols for possible values.
  optional int64 negotiated_protocol_hash = 7;

  // The time it took from starting the request to receiving the full set of
  // response headers, in milliseconds.
  optional int32 headers_latency_millis = 8;

  // The time that elapsed from the point UrlRequest#start() was called to the point
  // we are ready to call the final user callback (e.g. onSucceeded).
  // Note: this includes time spent waiting for the user to issue an I/O request.
  // In other words this includes the effect of the user
  // throttling Cronet (a.k.a. flow control, a.k.a. backpressure).
  // See also total_idle_time_millis
  optional int32 overall_latency_millis = 9;

  // Whether a connection migration was attempted for this request.
  optional bool connection_migration_attempted = 10;
  // Whether a connection migration was attempted and successful for this
  // request.
  optional bool connection_migration_successful = 11;

  // Number of previous CronetTrafficReported records that were dropped due to rate limiting.
  optional int32 samples_rate_limited = 12;

  // Terminal state of the reported request.
  optional CronetRequestTerminalState terminal_state = 13;

  enum CronetRequestTerminalState {
    STATE_UNKNOWN = 0;
    STATE_SUCCEEDED = 1;
    STATE_ERROR = 2;
    STATE_CANCELLED = 3;
  }

  // count of exceptions thrown during the execution of a user callback.
  optional int32 user_callback_exception_count = 14;

  // Same as `overall_latency_millis`, but only including time spent with no
  // outstanding I/O request from the user. For example, the time that elapses between Cronet
  // becoming ready to call the onReadCompleted callback and the user making the
  // next UrlRequest#read() call is idle time: during that time
  // there is no outstanding read request.
  // If this value is large relative to overall_latency_millis, it may mean the request was
  // throttled by the user, i.e. they applied backpressure/flow control, intentionally or not.
  // Note that in this context, "idle" should be interpreted from the
  // perspective of the Cronet user. Internally, Cronet might not actually be "idle"
  // as it may still attempt to keep its internal buffers filled.
  // An example of Cronet idly waiting for the user would look like the following
  /*
              Cronet                          User
                |
                |
                |----- OnResponseStarted ------|
                                               |
     Cronet                                    |
     Idle                                      |
     Time                                      |
                                               |
Cronet          |------------------------------| User calls request.read()
Doing           |
Work            |
                |
OnReadCompleted |
                |
                |----- onReadCompleted -------|
                                              | Cronet Idle Time includes the time taken to call
                                              | Executor#execute() on the user's executor.
                                              |
      Cronet                                  | If the user is blocking the network
      Idle                                    | thread in Executor#execute(), then technically that
      Time                                    | counts as backpressure / flow control.
                                              |
                                              |
                                              |
                                              |
                                              |
                                              |
                                              |
                                              |
                                              |
                                              |
               |------------------------------|
   */
  // This field contains the sum of Cronet Idle time waiting for user calls.
  optional int64 total_idle_time_millis = 15;

  // The time it takes for the user's Executor#execute() method to return
  // Note: contrary to most other metrics in this atom, this also includes
  // the final user callback (e.g. onSucceeded).
  // Note: in the case of the native implementation, Executor#execute() is called on
  // the network thread, which is a shared resource. High values for this metric are
  // usually a red flag because they mean the network thread is unable to service any
  // other UrlRequests during this time (including those from other engines).
  optional int64 total_user_executor_execute_latency_millis = 16;

  // The total amount of times `UrlRequest#read()` was called.
  optional int32 read_count = 17;

  // The total amount of times `UploadDataProvider#read` has been fired.
  optional int32 on_upload_read_count = 18;

  // Indicates if the request is a bidirectional stream or not.
  optional OptionalBoolean is_bidi_stream = 19;


  enum CronetRequestHeadersSizeBucket {
    REQUEST_HEADERS_SIZE_BUCKET_UNSPECIFIED = 0;
    // It's impossible to send requests without any headers in Cronet
    REQUEST_HEADERS_SIZE_BUCKET_UNDER_ONE_KIB = 1;
    REQUEST_HEADERS_SIZE_BUCKET_ONE_TO_TEN_KIB = 2;
    REQUEST_HEADERS_SIZE_BUCKET_TEN_TO_TWENTY_FIVE_KIB = 3;
    REQUEST_HEADERS_SIZE_BUCKET_TWENTY_FIVE_TO_FIFTY_KIB = 4;
    REQUEST_HEADERS_SIZE_BUCKET_FIFTY_TO_HUNDRED_KIB = 5;
    REQUEST_HEADERS_SIZE_BUCKET_OVER_HUNDRED_KIB = 6;
  }

  enum CronetRequestBodySizeBucket {
    REQUEST_BODY_SIZE_BUCKET_UNSPECIFIED = 0;
    REQUEST_BODY_SIZE_BUCKET_ZERO = 1;
    REQUEST_BODY_SIZE_BUCKET_UNDER_TEN_KIB = 2;
    REQUEST_BODY_SIZE_BUCKET_TEN_TO_FIFTY_KIB = 3;
    REQUEST_BODY_SIZE_BUCKET_FIFTY_TO_TWO_HUNDRED_KIB = 4;
    REQUEST_BODY_SIZE_BUCKET_TWO_HUNDRED_TO_FIVE_HUNDRED_KIB = 5;
    REQUEST_BODY_SIZE_BUCKET_FIVE_HUNDRED_KIB_TO_ONE_MIB = 6;
    REQUEST_BODY_SIZE_BUCKET_ONE_TO_FIVE_MIB = 7;
    REQUEST_BODY_SIZE_BUCKET_OVER_FIVE_MIB = 8;
  }

  enum CronetResponseHeadersSizeBucket {
    RESPONSE_HEADERS_SIZE_BUCKET_UNSPECIFIED = 0;
    RESPONSE_HEADERS_SIZE_BUCKET_UNDER_ONE_KIB = 1;
    RESPONSE_HEADERS_SIZE_BUCKET_ONE_TO_TEN_KIB = 2;
    RESPONSE_HEADERS_SIZE_BUCKET_TEN_TO_TWENTY_FIVE_KIB = 3;
    RESPONSE_HEADERS_SIZE_BUCKET_TWENTY_FIVE_TO_FIFTY_KIB = 4;
    RESPONSE_HEADERS_SIZE_BUCKET_FIFTY_TO_HUNDRED_KIB = 5;
    RESPONSE_HEADERS_SIZE_BUCKET_OVER_HUNDRED_KIB = 6;
  }

  enum CronetResponseBodySizeBucket {
    RESPONSE_BODY_SIZE_BUCKET_UNSPECIFIED = 0;
    RESPONSE_BODY_SIZE_BUCKET_ZERO = 1;
    RESPONSE_BODY_SIZE_BUCKET_UNDER_TEN_KIB = 2;
    RESPONSE_BODY_SIZE_BUCKET_TEN_TO_FIFTY_KIB = 3;
    RESPONSE_BODY_SIZE_BUCKET_FIFTY_TO_TWO_HUNDRED_KIB = 4;
    RESPONSE_BODY_SIZE_BUCKET_TWO_HUNDRED_TO_FIVE_HUNDRED_KIB = 5;
    RESPONSE_BODY_SIZE_BUCKET_FIVE_HUNDRED_KIB_TO_ONE_MIB = 6;
    RESPONSE_BODY_SIZE_BUCKET_ONE_TO_FIVE_MIB = 7;
    RESPONSE_BODY_SIZE_BUCKET_OVER_FIVE_MIB = 8;
  }
}

// Logged when the HTTP flags have been read.
message CronetHttpFlagsInitialized {
  // A weak-ID-like reference to the http flags loading operation.
  // The field can be used to join with CronetEngineBuilderInitialized atom.
  optional int64 cronet_http_flags_ref = 1;
  // how long it took to load (or attempt to load) http flags.
  // This field is set to -1 if flag loading was not attempted
  // (e.g. because the app opted out of Cronet flag support).
  // Note: There is no support for unset values, hence -1 is used.
  optional int32 http_flags_latency_millis = 2 [default = -1];
  // Determine whether flag loading was successful or not.
  optional OptionalBoolean flags_successful = 3;

  // The first 8 bytes of the MD5 hashes, interpreted as signed little-endian integers,
  // of the HTTP flag names that the Cronet instance logging the atoms is
  // actually running with.
  repeated int64 http_flags_names = 4 [packed = true];

  // The effective values of the flags, in the same order as
  // http_flags_names.
  // For boolean flags, this is 0 or 1.
  // For integer flags, this is the value of the flag itself.
  // For float values, this is the floating point value multiplied by 1 billion,
  // rounded, and clamped to the int64 range. (This should result in distinct
  // values for most commonly used floating point inputs.)
  // For string or bytes flags, this is a hash of the value
  // calculated in the same way as http_flags_names above.
  repeated int64 http_flags_values = 5 [packed = true];
}

/*
  ---------------------                                  -----------------
  |                   |           createBuilder()        |               |  ================⦀
  |     Provider      | -------------------------------> |     Builder   |                  ⦀
  |                   |                                  |               |                  ⦀
  |-------------------|    engine_builder_created_lat    |----------------                  ⦀
                                                                 |                          ⦀
                                                       Build()   | engine_created_latency   ⦀
                                                                 |                          ⦀
                                                                 V                          ⦀
                          ------------------                 -------------------            ⦀
                          |                |                 |                 |            ⦀
                          |  REQ CAN START |  <------------- |      Engine     |            ⦀
                          |WITHOUT BLOCKING|                 |                 |            ⦀
                          |----------------|                 |-----------------|            ⦀
                          ⦀                                                                 ⦀
                          ==================================================================⦀
               engine_async_latency (This takes a variable amount of time depending on async jobs)

  engine_builder_created_latency: The wall time elapsed since createBuilder() has been called
  until a builder has been returned.

  engine_created_latency: The wall time elapsed since Build() has been called until a CronetEngine
  has been returned.

  engine_async_latency: The wall time elapsed since Build()
  has been called until the returned CronetEngine
  is usable (All Asynchronous setup calls have finished executing).
*/

// Logged once for each CronetSource when createBuilder() has returned.
// This atom should normally be logged from Cronet API code (as opposed to impl code).
// If the app is bundling a Cronet API version that is too old to know about this atom,
// the impl takes responsibility for logging this atom and attempts to do so soon as
// the impl is loaded. Some fields may be missing in this case.
message CronetEngineBuilderInitialized {
  // A weak-ID-like reference to the instance of the Cronet initialization.
  // The field can be used to join subsequent engine creation and request
  // metrics with the initialization details.
  optional int64 cronet_initialization_ref = 1;

  // How long it took to produce the first `CronetEngine.Builder` instance for the source specified.
  // This field can be set by Api layer or Impl Layer, If this field was set by the Impl layer then
  // the value of the field will be equal to -1.
  optional int32 engine_builder_created_latency_millis = 2 [default = -1];

  // Source of the Cronet being loaded
  optional Source source = 3;

  enum Source {
    // Safe default, don't use explicitly.
    CRONET_SOURCE_UNSPECIFIED = 0;
    // Native-based Cronet implementation, bundled within the client app.
    CRONET_SOURCE_EMBEDDED_NATIVE = 1;
    // Java-based Cronet implementation, bundled within the client app.
    CRONET_SOURCE_EMBEDDED_JAVA = 2;
    // Native-based Cronet implementation, loaded from GMS Core (Google Play Services).
    CRONET_SOURCE_GMSCORE_NATIVE = 3;
    // Native-based Cronet implementation, loaded from the bootclasspath
    // through the android.net.http.HttpEngine API.
    CRONET_SOURCE_HTTPENGINE_NATIVE = 4;
  }


  // Whether the Provider successfully created a builder or not.
  // This field can be set by Api layer or Impl Layer, If this field was set by the Impl layer then
  // the value of the field will be equal to `UNSET`.
  optional OptionalBoolean creation_successful = 4;

  // A weak-ID-like reference to the http flags loading operation.
  // The field can be used to join with CronetHttpFlagsInitialized atoms.
  // Note: at the time of writing, loading of flags happens after the builder creation
  // has returned. This means the corresponding CronetHttpFlagsLoaded is logged
  // after or might not be logged at all (if there was a crash or
  // the resulting builder was never used).
  optional int64 cronet_http_flags_ref = 5;

  // This field carries the value stored in the file below
  // https://source.chromium.org/chromium/chromium/src/+/main:components/cronet/android/api/src/org/chromium/net/ApiVersion.template
  optional int32 cronet_api_level = 6;

  // This field carries the value stored in the file below
  // https://source.chromium.org/chromium/chromium/src/+/main:components/cronet/android/java/src/org/chromium/net/impl/ImplVersion.template
  optional int32 cronet_impl_api_level = 7;

  // The following fields corresponds to the version of Chromium for which the implementation
  // was compiled against as seen https://source.chromium.org/chromium/chromium/src/+/main:chrome/VERSION;l=1;bpv=0;bpt=0
  optional int32 major_version = 8;
  optional int32 minor_version = 9;
  optional int32 build_version = 10;
  optional int32 patch_version = 11;
  // The Android UID of the process that Cronet was initialized in.
  optional int32 uid = 12 [(is_uid) = true];
}

// Logged once for each CronetSource when the first CronetEngine is ready to be used.
// Note that CronetInitialized may be logged after CronetEngineCreated because initialization
// may continue in the background after the CronetEngine is instantiated.
// One consequence is that cronet_initialization_ref can be broken if a
// crash occurs after the engine is created but before initialization is complete.
message CronetInitialized {
  // A weak-ID-like reference to the instance of the Cronet initialization.
  // The field can be used to join subsequent engine creation and request
  // metrics with the native details.
  optional int64 cronet_initialization_ref = 1;

  // The wall clock time spent in the execution of the first call to `CronetEngine.Builder#build`
  optional int32 engine_creation_latency_millis = 2 [default = -1];

  // How much wall clock time was spent from the moment the first `CronetEngine.Builder#build()`
  // was made and the point where Cronet is ready to send requests. Notably,
  // this includes the time it took for any asynchronous background initialization code to complete
  optional int32 engine_async_latency_millis = 3 [default = -1];
}

// Distinguishes between three states (unset / true / false)
enum OptionalBoolean {
  UNSET = 0;
  TRUE = 1;
  FALSE = 2;
}