chromium/chromeos/ash/components/tether/hotspot_usage_duration_tracker.cc

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

#include "chromeos/ash/components/tether/hotspot_usage_duration_tracker.h"

#include "base/metrics/histogram_macros.h"
#include "base/time/clock.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"

namespace ash {

namespace tether {

namespace {

// Minimum value for the usage duration metric.
const int64_t kMinDurationSeconds = 1;

// Maximum value for the usage duration metric.
const int64_t kMaxDurationHours = 5;

// Number of buckets in the metric.
const int kNumMetricsBuckets = 100;

}  // namespace

HotspotUsageDurationTracker::HotspotUsageDurationTracker(
    ActiveHost* active_host,
    base::Clock* clock)
    : active_host_(active_host), clock_(clock) {
  active_host_->AddObserver(this);
}

HotspotUsageDurationTracker::~HotspotUsageDurationTracker() {
  active_host_->RemoveObserver(this);
}

void HotspotUsageDurationTracker::OnActiveHostChanged(
    const ActiveHost::ActiveHostChangeInfo& change_info) {
  // Handle the case that a session is being tracked but the active host's
  // status changes unexpectedly.
  HandleUnexpectedCurrentSession(change_info.new_status);

  switch (change_info.new_status) {
    case ActiveHost::ActiveHostStatus::CONNECTED:
      last_connection_start_ = clock_->Now();
      break;
    case ActiveHost::ActiveHostStatus::DISCONNECTED: {
      // If |last_connection_start_| has not been set, there was no active
      // connection before this status change; thus, there is nothing to do.
      if (last_connection_start_.is_null())
        break;

      base::TimeDelta duration = clock_->Now() - last_connection_start_;

      // Reset |last_connection_start_|; it will be set again the next time that
      // a connection is established.
      last_connection_start_ = base::Time();

      PA_LOG(VERBOSE) << "Connection to hotspot has ended. Duration was "
                      << duration.InSeconds() << " second(s).";
      UMA_HISTOGRAM_CUSTOM_TIMES("InstantTethering.HotspotUsageDuration",
                                 duration,
                                 base::Seconds(kMinDurationSeconds) /* min */,
                                 base::Hours(kMaxDurationHours) /* max */,
                                 kNumMetricsBuckets /* bucket_count */);
      break;
    }
    default:
      break;
  }
}

void HotspotUsageDurationTracker::HandleUnexpectedCurrentSession(
    const ActiveHost::ActiveHostStatus& active_host_status) {
  // If there is no start timestamp, no session is being tracked.
  if (last_connection_start_.is_null())
    return;

  // It is expected that when a current session ends, the active host's status
  // will change to DISCONNECTED.
  if (active_host_status == ActiveHost::ActiveHostStatus::DISCONNECTED)
    return;

  base::TimeDelta previous_duration = clock_->Now() - last_connection_start_;
  PA_LOG(ERROR) << "Active host status changed to "
                << ActiveHost::StatusToString(active_host_status) << ", but a "
                << "session was already being tracked ("
                << previous_duration.InSeconds() << " second(s)). "
                << "Not recording any metrics for this session since this "
                << "situation was reached in error.";

  // Delete the erroneously started session.
  last_connection_start_ = base::Time();
}

}  // namespace tether

}  // namespace ash