chromium/ash/wm/overview/overview_session_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.

#include "ash/wm/overview/overview_session_metrics_recorder.h"

#include "ash/shell.h"
#include "ash/wm/desks/desks_controller.h"
#include "ash/wm/desks/overview_desk_bar_view.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_grid.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/overview/overview_types.h"
#include "base/check.h"
#include "base/metrics/histogram_functions.h"
#include "base/trace_event/named_trigger.h"
#include "base/trace_event/trace_event.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/presentation_time_recorder.h"

namespace ash {

OverviewSessionMetricsRecorder::OverviewSessionMetricsRecorder(
    OverviewStartAction start_action,
    OverviewController* controller)
    : start_action_(start_action) {
  RecordOverviewStartAction(start_action);
  controller_observation_.Observe(controller);
}

OverviewSessionMetricsRecorder::~OverviewSessionMetricsRecorder() {
  // Corner case: If an overview session is started while one is in progress,
  // then the recorder for the first session is destroyed before the
  // `OnOverviewModeEndingAnimationComplete()` notification is received. This
  // case needs to be checked explicitly.
  if (!has_finished_exit_overview_trace_event_) {
    OnOverviewModeEndingAnimationComplete(/*canceled=*/true);
  }
}

void OverviewSessionMetricsRecorder::OnOverviewSessionInitializing() {
  overview_start_time_ = base::Time::Now();
  // We're only interested in chrometto traces where the user definitely intends
  // to see an overview of their windows. These two are the most common use
  // cases in the field, accounting for a combined 85% of overview sessions in
  // stable channel as of 8/29/24.
  if (start_action_ == OverviewStartAction::kAccelerator ||
      start_action_ == OverviewStartAction::kDragWindowFromShelf) {
    base::trace_event::EmitNamedTrigger("ash-overview-start");
  }
  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ui", "OverviewController::EnterOverview",
                                    this);

  enter_presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
      Shell::GetPrimaryRootWindow()->layer()->GetCompositor(),
      kEnterOverviewPresentationHistogram, "",
      kOverviewEnterExitPresentationMaxLatency, /*emit_trace_event=*/true);
  enter_presentation_time_recorder_->RequestNext();

  base::UmaHistogramCounts100("Ash.Overview.DeskCount",
                              DesksController::Get()->desks().size());
}

void OverviewSessionMetricsRecorder::OnOverviewSessionInitialized(
    OverviewSession* session) {
  session_ = session;
  CHECK(session_);
  desk_bar_shown_immediately_ = IsDeskBarOpen();
}

void OverviewSessionMetricsRecorder::OnOverviewSessionEnding() {
  RecordOverviewEndAction(session_->overview_end_action());

  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ui", "OverviewController::ExitOverview",
                                    this);

  const DeskBarVisibility desk_bar_visibility =
      desk_bar_shown_immediately_
          ? DeskBarVisibility::kShownImmediately
          : (IsDeskBarOpen() ? DeskBarVisibility::kShownAfterFirstFrame
                             : DeskBarVisibility::kNotShown);
  base::UmaHistogramEnumeration("Ash.Overview.DeskBarVisibility",
                                desk_bar_visibility);

  auto exit_presentation_time_recorder =
      CreatePresentationTimeHistogramRecorder(
          Shell::GetPrimaryRootWindow()->layer()->GetCompositor(),
          kExitOverviewPresentationHistogram, "",
          kOverviewEnterExitPresentationMaxLatency,
          /*emit_trace_event=*/true);
  exit_presentation_time_recorder->RequestNext();
  if (IsRenderingDeskBarWithMiniViews()) {
    SchedulePresentationTimeMetricsWithDeskBar(
        std::move(enter_presentation_time_recorder_),
        std::move(exit_presentation_time_recorder), desk_bar_visibility);
  }

  CHECK(!overview_start_time_.is_null());
  base::UmaHistogramMediumTimes("Ash.Overview.TimeInOverview",
                                base::Time::Now() - overview_start_time_);
  // Prevents dangling raw_ptr failures.
  session_ = nullptr;
}

void OverviewSessionMetricsRecorder::OnOverviewModeStartingAnimationComplete(
    bool canceled) {
  TRACE_EVENT_NESTABLE_ASYNC_END1("ui", "OverviewController::EnterOverview",
                                  this, "canceled", canceled);
}

void OverviewSessionMetricsRecorder::OnOverviewModeEndingAnimationComplete(
    bool canceled) {
  has_finished_exit_overview_trace_event_ = true;
  TRACE_EVENT_NESTABLE_ASYNC_END1("ui", "OverviewController::ExitOverview",
                                  this, "canceled", canceled);
}

bool OverviewSessionMetricsRecorder::IsDeskBarOpen() const {
  CHECK(session_);
  return session_->GetGridWithRootWindow(Shell::GetPrimaryRootWindow())
      ->desks_bar_view();
}

bool OverviewSessionMetricsRecorder::IsRenderingDeskBarWithMiniViews() const {
  return IsDeskBarOpen() &&
         !session_->GetGridWithRootWindow(Shell::GetPrimaryRootWindow())
              ->desks_bar_view()
              ->mini_views()
              .empty();
}

}  // namespace ash