chromium/chromecast/metrics/cast_event_builder_impl.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromecast/metrics/cast_event_builder_impl.h"

#include "base/logging.h"
#include "base/metrics/metrics_hashes.h"
#include "base/ranges/algorithm.h"
#include "base/time/time.h"
#include "chromecast/base/hash_util.h"
#include "chromecast/metrics/cast_event_builder.h"
#include "chromecast/metrics/metrics_util.h"
#include "third_party/metrics_proto/cast_logs.pb.h"

namespace chromecast {

namespace {
// Bitmasks and values for the |transport_connection_id| field when recording
// discovery metrics for MDNS.
const uint32_t kDiscoverySenderMask = 0x0000FFFF;
const uint32_t kDiscoveryUnicastBit = 0x80000000;
}  // namespace

CastEventBuilderImpl::CastEventBuilderImpl()
    : event_proto_(new ::metrics::CastLogsProto_CastEventProto) {
  event_proto_->set_time_msec(
      (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds());
}

CastEventBuilderImpl::~CastEventBuilderImpl() {}

std::string CastEventBuilderImpl::GetName() {
  return unhashed_event_name_;
}

CastEventBuilder& CastEventBuilderImpl::SetName(const std::string& name) {
  unhashed_event_name_ = name;
  event_proto_->set_name_hash(base::HashMetricName(name));
  DVLOG(2) << "Hash metric " << name << " = " << std::hex
           << event_proto_->name_hash();
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetTime(const base::TimeTicks& time) {
  event_proto_->set_time_msec((time - base::TimeTicks()).InMilliseconds());
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetTimezoneId(
    const std::string& timezone_id) {
  event_proto_->set_timezone_id(timezone_id);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetAppId(const std::string& app_id) {
  event_proto_->set_app_id(HashAppId32(app_id));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetRemoteAppId(
    const std::string& remote_app_id) {
  event_proto_->set_remote_app_id(HashAppId32(remote_app_id));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetSessionId(
    const std::string& session_id) {
  event_proto_->set_application_session_id(HashSessionId64(session_id));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetSdkVersion(
    const std::string& sdk_version) {
  event_proto_->set_cast_receiver_version(HashSdkVersion64(sdk_version));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetMplVersion(
    const std::string& mpl_version) {
  event_proto_->set_cast_mpl_version(HashSdkVersion64(mpl_version));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetConnectionInfo(
    const std::string& transport_connection_id,
    const std::string& virtual_connection_id) {
  event_proto_->set_transport_connection_id(
      HashSocketId32(transport_connection_id));
  event_proto_->set_virtual_connection_id(
      HashConnectionId32(virtual_connection_id));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetGroupUuid(
    const std::string& group_uuid) {
  event_proto_->set_group_uuid(base::HashMetricName(group_uuid));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetExtraValue(int64_t extra_value) {
  event_proto_->set_value(extra_value);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetConversationKey(
    const std::string& conversation_key) {
  event_proto_->set_conversation_key(conversation_key);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetRequestId(int32_t request_id) {
  event_proto_->set_request_id(request_id);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetEventId(
    const std::string& event_id) {
  event_proto_->set_event_id(event_id);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetAoghRequestId(
    const std::string& request_id) {
  event_proto_->set_aogh_request_id(request_id);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetAoghLocalDeviceId(int64_t local_id) {
  event_proto_->set_aogh_local_device_id(local_id);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetAoghAgentId(
    const std::string& agent_id) {
  event_proto_->set_aogh_agent_id(agent_id);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetAoghStandardAgentId(
    const std::string& standard_agent_id) {
  event_proto_->set_aogh_standard_agent_id(standard_agent_id);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetUiVersion(const std::string& value) {
  event_proto_->set_ui_version(value);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetAuditReport(
    const std::string& audit_report) {
  event_proto_->set_selinux_audit_detail(audit_report);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetDuoCoreVersion(int64_t version) {
  event_proto_->set_duo_core_version(version);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetHotwordModelId(
    const std::string& model_id) {
  event_proto_->set_hotword_model_id(model_id);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetDiscoveryAppSubtype(
    const std::string& app_id) {
  // Application subtype can be directly added in full.
  event_proto_->set_app_id(HashAppId32(app_id));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetDiscoveryNamespaceSubtype(
    const std::string& namespace_hash) {
  // Namespace hash is a SHA-1 string that is 160-bits (20 bytes) of
  // information, so a 40 character long hex string. We only have 32-bits to
  // upload, so pull out the first 8 characters and hex decode it like an app-
  // id.
  event_proto_->set_app_id(HashAppId32(namespace_hash.substr(0, 8)));
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetDiscoverySender(
    const net::IPAddressBytes& sender_ip) {
  // Pack the last two bytes of the sender IP address into a fragment that takes
  // up the final two bytes.
  uint32_t sender_fragment = GetIPAddressFragmentForLogging(sender_ip);

  // Re-use the |transport_connection_id| field to store sender IP address info,
  // since discovery messages will not use this field normally. This gives
  // 32-bits of space to store sender IP info, although only the lower 16-bits
  // are used.
  //
  // Preserve the existing fields by only clearing the sender address (lower
  // 16-bits), then OR with new sender fragment.
  uint32_t value = event_proto_->transport_connection_id();
  value &= ~kDiscoverySenderMask;
  value |= (sender_fragment & kDiscoverySenderMask);
  event_proto_->set_transport_connection_id(value);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetDiscoveryUnicastFlag(
    bool uses_unicast) {
  // Re-use the highest bit in |transport_connection_id| field to store the
  // unicast status of the discovery event, since discovery messages will not
  // use this field normally. Other data may be stored in bits 0-30, so preserve
  // the existing bits by only modifying the unicast bit.
  uint32_t value = event_proto_->transport_connection_id();
  if (uses_unicast) {
    value |= kDiscoveryUnicastBit;
  } else {
    value &= ~kDiscoveryUnicastBit;
  }
  event_proto_->set_transport_connection_id(value);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetFeatureVector(
    const std::vector<float>& features) {
  event_proto_->mutable_feature_vector()->Resize(features.size(), 0);
  float* mutable_data = event_proto_->mutable_feature_vector()->mutable_data();
  base::ranges::copy(features, mutable_data);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::AddMetadata(const std::string& name,
                                                    int64_t value) {
  ::metrics::CastLogsProto_CastEventProto_Metadata* const metadata =
      event_proto_->add_metadata();
  metadata->set_name_hash(base::HashMetricName(name));
  metadata->set_value(value);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::SetLaunchFrom(LaunchFrom launch_from) {
  SetLaunchFromProto(event_proto_.get(), launch_from);
  return *this;
}

CastEventBuilder& CastEventBuilderImpl::MergeFrom(
    const ::metrics::CastLogsProto_CastEventProto* event_proto) {
  if (event_proto) {
    event_proto_->MergeFrom(*event_proto);
  }
  return *this;
}

::metrics::CastLogsProto_CastEventProto* CastEventBuilderImpl::Build() {
  return event_proto_.release();
}

}  // namespace chromecast