blob: a4985dcba92269c47738ab46c09dd12c6e42ca6e [file] [log] [blame]
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FEATURE_USAGE_FEATURE_USAGE_METRICS_H_
#define FEATURE_USAGE_FEATURE_USAGE_METRICS_H_
#include <memory>
#include <base/dcheck_is_on.h>
#include <base/power_monitor/power_observer.h>
#include <base/sequence_checker.h>
#include <base/time/time.h>
#include <base/timer/timer.h>
#include <metrics/metrics_library.h>
namespace base {
class Clock;
class TickClock;
} // namespace base
namespace feature_usage {
// Helper class to unify tracking features usage by users.
// It provides unified naming for the tracked events. Which reduces effort on
// the data analytics side to incorporate a new feature.
// This class also provides a way to report periodically if the device is
// eligible for the feature and whether user has it enabled.
class FeatureUsageMetrics final : public base::PowerSuspendObserver {
public:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class Event {
kEligible = 0,
kEnabled = 1,
kUsedWithSuccess = 2,
kUsedWithFailure = 3,
kAccessible = 4,
kMaxValue = kAccessible,
};
// Consumers should implement this interface to report periodically if the
// feature is eligible on the device and enabled.
class Delegate {
public:
// Whether the device is capable of running the feature.
virtual bool IsEligible() const = 0;
// Whether the feature is accessible to users (e.g. allowed by admins).
virtual std::optional<bool> IsAccessible() const;
// Whether the user has enabled the feature for themselves. If `IsEnabled`
// returns true `IsEligible` must return true too.
virtual bool IsEnabled() const = 0;
virtual ~Delegate() = default;
};
static const base::TimeDelta kInitialInterval;
static const base::TimeDelta kRepeatedInterval;
// `feature_name` must correspond to the entry of `FeaturesLoggingUsageEvents`
// and should never change.
FeatureUsageMetrics(const std::string& feature_name, Delegate* delegate);
// Custom `tick_clock` could be passed for testing purposes. Must not be
// nullptr.
FeatureUsageMetrics(const std::string& feature_name,
Delegate* delegate,
const base::Clock* clock,
const base::TickClock* tick_clock);
FeatureUsageMetrics(const FeatureUsageMetrics&) = delete;
FeatureUsageMetrics& operator=(const FeatureUsageMetrics&) = delete;
~FeatureUsageMetrics() final;
// `RecordUsage` should be called on every usage of the feature. `success`
// indicates whether the usage was successful. For example if user touches the
// fingerprint sensor and the finger was not recognized `RecordUsage` should
// be called with `false`.
void RecordUsage(bool success);
// Use `StartSuccessfulUsage` and `StopSuccessfulUsage` to record feature
// usage time. See ./README.md#Recording-usage-time for more details.
void StartSuccessfulUsage();
void StopSuccessfulUsage();
void SetMetricsLibraryForTesting(
std::unique_ptr<MetricsLibraryInterface> metrics_lib);
MetricsLibraryInterface* metrics_library_for_testing() {
return metrics_lib_.get();
}
private:
void OnSuspend() final;
void OnResume() final;
void SetupTimer(base::TimeDelta delta);
void MaybeReportPeriodicMetrics();
void ReportPeriodicMetrics();
void MaybeReportUseTime();
void RecordUsetime(base::TimeDelta usetime) const;
base::Time Now() const;
const std::string histogram_name_;
const Delegate* const delegate_ GUARDED_BY_CONTEXT(sequence_checker_);
base::Time last_time_enabled_reported_;
const base::Clock* const clock_;
base::Time start_usage_;
base::OneShotTimer timer_;
std::unique_ptr<MetricsLibraryInterface> metrics_lib_;
#if DCHECK_IS_ON()
std::optional<bool> last_record_usage_outcome_;
#endif
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace feature_usage
#endif // FEATURE_USAGE_FEATURE_USAGE_METRICS_H_