chromium/ash/metrics/wm_feature_metrics_recorder.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "ash/metrics/wm_feature_metrics_recorder.h"

#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "chromeos/ui/base/app_types.h"
#include "chromeos/ui/base/window_properties.h"

namespace ash {

namespace {

// The Metrics prefix for WM features.
constexpr char kWMFeatureMetricPrefix[] = "Ash.Wm.";

// Pre-defined Window size ranges.
constexpr int kWidthRange[] = {0, 800, 1024, 1400};
constexpr int kHeightRange[] = {0, 600, 728, 900};

WMFeatureMetricsRecorder::WindowSizeRange GetWindowSizeRange(
    const gfx::Size& window_size) {
  const int width = window_size.width();
  const int height = window_size.height();

  int width_index = 0;
  for (size_t i = 0; i < sizeof(kWidthRange) / sizeof(int) - 1; ++i) {
    if (width > kWidthRange[i] && width <= kWidthRange[i + 1]) {
      width_index = i;
      break;
    }
    if (width > kWidthRange[i + 1]) {
      width_index = i + 1;
    }
  }

  int height_index = 0;
  for (size_t j = 0; j < sizeof(kHeightRange) / sizeof(int) - 1; ++j) {
    if (height > kHeightRange[j] && height <= kHeightRange[j + 1]) {
      height_index = j;
      break;
    }
    if (height > kHeightRange[j + 1]) {
      height_index = j + 1;
    }
  }

  return static_cast<WMFeatureMetricsRecorder::WindowSizeRange>(
      width_index * (sizeof(kHeightRange) / sizeof(int)) + height_index);
}

// Records the window state and layout related metrics for all windows and the
// current active window.
void RecordWindowLayoutAndStatePeriodically() {
  // Get all windows and their placement configuration.
  auto windows =
      Shell::Get()->mru_window_tracker()->BuildMruWindowList(kAllDesks);

  const std::string metrics_prefix =
      WMFeatureMetricsRecorder::GetFeatureMetricsPrefix(
          WMFeatureMetricsRecorder::WMFeatureType::kWindowLayoutState);
  // Report the number of the opened windows.
  base::UmaHistogramCounts100(metrics_prefix + "WindowNumbers", windows.size());

  aura::Window* active_window = window_util::GetActiveWindow();
  for (aura::Window* window : windows) {
    const bool is_active_window = window == active_window;
    std::vector<std::string> metrics_suffixes;

    // Report the window state types for all opened windows and the active
    // window.
    auto state_type = WindowState::Get(window)->GetStateType();
    metrics_suffixes.push_back("AllWindowStates");
    if (is_active_window) {
      metrics_suffixes.push_back("ActiveWindowState");
    }
    for (const std::string& metrics_suffix : metrics_suffixes) {
      base::UmaHistogramEnumeration(metrics_prefix + metrics_suffix,
                                    state_type);
    }

    // Report the app types for all opened windows and the active window.
    metrics_suffixes.clear();
    metrics_suffixes.push_back("AllAppTypes");
    if (is_active_window) {
      metrics_suffixes.push_back("ActiveWindowAppType");
    }
    for (const std::string& metrics_suffix : metrics_suffixes) {
      base::UmaHistogramEnumeration(metrics_prefix + metrics_suffix,
                                    window->GetProperty(chromeos::kAppTypeKey));
    }

    // Report the sizes for all windows and the active window.
    metrics_suffixes.clear();
    metrics_suffixes.push_back("AllWindowSizes");
    if (chromeos::IsNormalWindowStateType(state_type)) {
      metrics_suffixes.push_back("FreeformedWindowSizes");
    }
    if (is_active_window) {
      metrics_suffixes.push_back("ActiveWindowSize");
    }
    const WMFeatureMetricsRecorder::WindowSizeRange size_range =
        GetWindowSizeRange(window->bounds().size());
    for (const std::string& metrics_suffix : metrics_suffixes) {
      base::UmaHistogramEnumeration(metrics_prefix + metrics_suffix,
                                    size_range);
    }
  }
}

}  // namespace

WMFeatureMetricsRecorder::WMFeatureMetricsRecorder() = default;

WMFeatureMetricsRecorder::~WMFeatureMetricsRecorder() = default;

// static
std::string WMFeatureMetricsRecorder::GetFeatureMetricsPrefix(
    const WMFeatureType& wm_feature_type) {
  switch (wm_feature_type) {
    case WMFeatureType::kWindowLayoutState:
      return base::StrCat({kWMFeatureMetricPrefix, "WindowLayoutState."});
  }
}

void WMFeatureMetricsRecorder::RecordPeriodicalWMMetrics() {
  RecordWindowLayoutAndStatePeriodically();
}

}  // namespace ash