chromium/android_webview/browser/metrics/visibility_metrics_logger.h

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

#ifndef ANDROID_WEBVIEW_BROWSER_METRICS_VISIBILITY_METRICS_LOGGER_H_
#define ANDROID_WEBVIEW_BROWSER_METRICS_VISIBILITY_METRICS_LOGGER_H_

#include <map>
#include <string>
#include <vector>

#include "base/functional/callback.h"
#include "base/time/time.h"

namespace android_webview {

// Records how much of the screen is covered by WebViews. This helps us
// determine what WebView is being used for.
//
// Lifetime: Singleton
class VisibilityMetricsLogger {
 public:
  // These values are persisted to logs and must match the WebViewUrlScheme enum
  // defined in enums.xml. Entries should not be renumbered and numeric values
  // should never be reused.
  enum class Scheme {
    kEmpty = 0,
    kUnknown = 1,
    kHttp = 2,
    kHttps = 3,
    kFile = 4,
    kFtp = 5,
    kData = 6,
    kJavaScript = 7,
    kAbout = 8,
    kChrome = 9,
    kBlob = 10,
    kContent = 11,
    kIntent = 12,
    kMaxValue = kIntent,
  };

  static Scheme SchemeStringToEnum(const std::string& scheme);

  struct VisibilityInfo {
    bool view_attached = false;
    bool view_visible = false;
    bool window_visible = false;
    Scheme scheme = Scheme::kEmpty;

    bool IsVisible() const;
  };

  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class Visibility {
    kVisible = 0,
    kNotVisible = 1,
    kMaxValue = kNotVisible
  };

  class Client {
   public:
    virtual VisibilityInfo GetVisibilityInfo() = 0;
  };

  enum class ClientAction {
    kAdded = 0,
    kRemoved = 1,
    kVisibilityChanged = 2,
    kMaxValue = kVisibilityChanged,
  };

  VisibilityMetricsLogger();
  virtual ~VisibilityMetricsLogger();

  VisibilityMetricsLogger(const VisibilityMetricsLogger&) = delete;
  VisibilityMetricsLogger& operator=(const VisibilityMetricsLogger&) = delete;

  void AddClient(Client* client);
  void RemoveClient(Client* client);
  void ClientVisibilityChanged(Client* client);
  void UpdateScreenCoverage(int global_percentage,
                            const std::vector<Scheme>& schemes,
                            const std::vector<int>& scheme_percentages);

  void RecordMetrics();

  // Set a callback that is executed when global visibility changes, i.e. when:
  //  - false => true: no client was visible and one becomes visible.
  //  - true => false: >=1 clients were visible and all became hidden.
  using OnVisibilityChangedCallback =
      base::RepeatingCallback<void(bool /*visible*/)>;
  void SetOnVisibilityChangedCallback(OnVisibilityChangedCallback);

 private:
  void UpdateDurations();
  void ProcessClientUpdate(Client* client,
                           const VisibilityInfo& info,
                           ClientAction action);
  void RecordVisibilityMetrics();
  void RecordVisibleSchemeMetrics();
  void RecordScreenCoverageMetrics();

  // Counts the number of visible clients.
  size_t all_clients_visible_count_ = 0;
  // Counts the number of visible clients per scheme.
  size_t per_scheme_visible_counts_[static_cast<size_t>(Scheme::kMaxValue) +
                                    1] = {};

  struct WebViewDurationTracker {
    // Duration any WebView meets the tracking criteria
    base::TimeDelta any_webview_tracked_duration_ = base::Seconds(0);
    // Duration no WebViews meet the tracking criteria
    base::TimeDelta no_webview_tracked_duration_ = base::Seconds(0);
    // Total duration that WebViews meet the tracking criteria (i.e. if
    // 2x WebViews meet the criteria for 1 second then increment by 2 seconds)
    base::TimeDelta per_webview_duration_ = base::Seconds(0);
    // Total duration that WebViews exist but do not meet the tracking criteria
    base::TimeDelta per_webview_untracked_duration_ = base::Seconds(0);
  };

  WebViewDurationTracker all_clients_tracker_;
  WebViewDurationTracker
      per_scheme_trackers_[static_cast<size_t>(Scheme::kMaxValue) + 1] = {};

  base::TimeTicks last_update_time_;
  std::map<Client*, VisibilityInfo> client_visibility_;

  // The screen coverage percentage for all visible AwContents merged together.
  int global_coverage_percentage_ = 0;

  // The durations by screen coverage percentage for all visible AwContents
  // merged together.
  base::TimeDelta global_coverage_percentage_durations_[101] = {};

  // The currently visible schemes and their screen coverage percentages. A
  // scheme can occur more than once at a time so this uses a multimap.
  std::multimap<Scheme, int> schemes_to_coverage_percentages_;

  // The durations by screen coverage percentage and visible scheme.
  std::map<Scheme, std::map<int, base::TimeDelta>>
      schemes_to_percentages_to_durations_;

  OnVisibilityChangedCallback on_visibility_changed_callback_;
};

}  // namespace android_webview

#endif  // ANDROID_WEBVIEW_BROWSER_METRICS_VISIBILITY_METRICS_LOGGER_H_