chromium/chrome/browser/ash/dbus/arc_tracing_service_provider.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 "chrome/browser/ash/dbus/arc_tracing_service_provider.h"

#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/arc/tracing/arc_tracing_graphics_model.h"
#include "chrome/browser/ash/arc/tracing/overview_tracing_handler.h"
#include "chrome/browser/browser_process.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/aura/window.h"

namespace ash {
namespace {
constexpr int kMaxStatusMessagesCount = 20;
constexpr char kTraceStartedMsg[] = "Trace started";
}  // namespace

ArcTracingServiceProvider::ArcTracingServiceProvider() = default;

ArcTracingServiceProvider::~ArcTracingServiceProvider() = default;

void ArcTracingServiceProvider::Start(
    scoped_refptr<dbus::ExportedObject> exported_object) {
  exported_object->ExportMethod(
      arc::tracing::kArcTracingInterfaceName,
      arc::tracing::kArcTracingStartMethod,
      base::BindRepeating(&ArcTracingServiceProvider::StartTrace,
                          weak_ptr_factory_.GetWeakPtr()),
      base::BindOnce(&ArcTracingServiceProvider::OnExported,
                     weak_ptr_factory_.GetWeakPtr()));
  exported_object->ExportMethod(
      arc::tracing::kArcTracingInterfaceName,
      arc::tracing::kArcTracingGetStatusMethod,
      base::BindRepeating(&ArcTracingServiceProvider::GetStatus,
                          weak_ptr_factory_.GetWeakPtr()),
      base::BindOnce(&ArcTracingServiceProvider::OnExported,
                     weak_ptr_factory_.GetWeakPtr()));
}

void ArcTracingServiceProvider::AddStatusMessage(std::string_view status) {
  msgs_.emplace_back(status);
  if (msgs_.size() > kMaxStatusMessagesCount) {
    msgs_.pop_front();
  }
}

void ArcTracingServiceProvider::OnExported(const std::string& interface_name,
                                           const std::string& method_name,
                                           bool success) {
  LOG_IF(ERROR, !success) << "Failed to export " << interface_name << "."
                          << method_name;
}

void ArcTracingServiceProvider::OnTraceEnd(
    std::unique_ptr<arc::OverviewTracingResult> result) {
  if (result->path.empty()) {
    AddStatusMessage(result->status);
  } else if (auto* information =
                 result->model.GetDict().FindDict(arc::kKeyInformation);
             information) {
    auto pfps = information->FindDouble(arc::kKeyPerceivedFps);
    auto duration = information->FindDouble(arc::kKeyDuration);
    AddStatusMessage(base::StringPrintf(
        "%s: %s - perceived FPS=%.2f, duration=%.2fs", result->status.c_str(),
        result->path.value().c_str(), pfps.value_or(0),
        duration.value_or(0) / 1'000'000.0));
  }
  // Do this in a separate task because the handler may still have code to run
  // after we return.
  base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(
      FROM_HERE, std::move(handler_));
}

std::unique_ptr<arc::OverviewTracingHandler>
ArcTracingServiceProvider::NewHandler() {
  return std::make_unique<arc::OverviewTracingHandler>(
      arc::OverviewTracingHandler::ArcWindowFocusChangeCb());
}

void ArcTracingServiceProvider::StartTrace(
    dbus::MethodCall* method_call,
    dbus::ExportedObject::ResponseSender response_sender) {
  if (handler_) {
    std::move(response_sender)
        .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED,
                                                 "Trace already in progress"));
    return;
  }

  dbus::MessageReader reader(method_call);

  double max_trace_seconds;
  if (!reader.PopDouble(&max_trace_seconds)) {
    std::move(response_sender)
        .Run(dbus::ErrorResponse::FromMethodCall(
            method_call, DBUS_ERROR_INVALID_ARGS,
            "Expect max trace time as type double in seconds"));
    return;
  }
  auto handler = NewHandler();

  auto max_trace_time = base::Seconds(max_trace_seconds);
  if (max_trace_time < base::Seconds(1)) {
    std::move(response_sender)
        .Run(dbus::ErrorResponse::FromMethodCall(
            method_call, DBUS_ERROR_INVALID_ARGS,
            "Max trace seconds out of range; must be >= 1"));
    return;
  }

  if (!handler->arc_window_is_active()) {
    std::move(response_sender)
        .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED,
                                                 "ARC window isn't active"));
    return;
  }

  auto extra_windows = handler->NonTraceTargetWindows();
  if (!extra_windows.empty()) {
    std::vector<std::string> extra_win_msg = {
        "Extra windows are open. Close them and try the trace again: "};
    std::string_view delim = "";
    for (auto window : extra_windows) {
      extra_win_msg.emplace_back(delim);
      extra_win_msg.emplace_back("|");
      extra_win_msg.emplace_back(base::UTF16ToUTF8(window->GetTitle()));
      extra_win_msg.emplace_back("|");
      delim = ", ";
    }
    std::move(response_sender)
        .Run(dbus::ErrorResponse::FromMethodCall(method_call, DBUS_ERROR_FAILED,
                                                 base::StrCat(extra_win_msg)));
    return;
  }

  handler_ = std::move(handler);
  handler_->set_graphics_model_ready_cb(base::BindRepeating(
      &ArcTracingServiceProvider::OnTraceEnd, weak_ptr_factory_.GetWeakPtr()));
  handler_->set_start_build_model_cb(
      base::BindRepeating(&ArcTracingServiceProvider::AddStatusMessage,
                          weak_ptr_factory_.GetWeakPtr(), "Building model..."));
  handler_->StartTracing(trace_outdir_, max_trace_time);

  auto response = dbus::Response::FromMethodCall(method_call);
  dbus::MessageWriter writer(response.get());
  writer.AppendString(kTraceStartedMsg);
  AddStatusMessage(kTraceStartedMsg);
  std::move(response_sender).Run(std::move(response));
}

void ArcTracingServiceProvider::GetStatus(
    dbus::MethodCall* method_call,
    dbus::ExportedObject::ResponseSender response_sender) {
  auto response = dbus::Response::FromMethodCall(method_call);
  dbus::MessageWriter writer(response.get());
  for (const auto& msg : msgs_) {
    writer.AppendString(msg);
  }
  std::move(response_sender).Run(std::move(response));
}

}  // namespace ash