chromium/extensions/browser/api/feedback_private/feedback_service.cc

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

#include "extensions/browser/api/feedback_private/feedback_service.h"

#include <memory>
#include <string>
#include <utility>

#include "base/barrier_closure.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/statistics_recorder.h"
#include "base/strings/strcat.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "build/chromeos_buildflags.h"
#include "components/feedback/feedback_data.h"
#include "components/feedback/feedback_report.h"
#include "components/feedback/redaction_tool/redaction_tool.h"
#include "components/feedback/system_logs/system_logs_fetcher.h"
#include "components/feedback/system_logs/system_logs_source.h"
#include "content/public/browser/browser_context.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/api/feedback_private/feedback_private_delegate.h"
#include "extensions/browser/blob_reader.h"
#include "net/base/network_change_notifier.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/public/cpp/assistant/controller/assistant_controller.h"
#include "chromeos/ash/components/cryptohome/cryptohome_parameters.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_service.h"
#include "components/account_id/account_id.h"
#include "components/user_manager/user_manager.h"
#include "third_party/cros_system_api/dbus/debugd/dbus-constants.h"
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

namespace extensions {

SysLogsFetcherCallback;
SystemLogsFetcher;
SystemLogsResponse;

namespace {

#if BUILDFLAG(IS_CHROMEOS_ASH)
// The paths are relative to "/var/log/" by default, which can be overwritten
// for testing purpose.
constexpr base::FilePath::CharType kBluetoothLogsFilePath[] =
    FILE_PATH_LITERAL("bluetooth/log.bz2");
constexpr base::FilePath::CharType kBluetoothLogsFilePathOld[] =
    FILE_PATH_LITERAL("bluetooth/log.bz2.old");
constexpr base::FilePath::CharType kBluetoothQualityReportFilePath[] =
    FILE_PATH_LITERAL("bluetooth/bluetooth_quality_report");

constexpr char kBluetoothLogsAttachmentName[] = "bluetooth_logs.bz2";
constexpr char kBluetoothLogsAttachmentNameOld[] = "bluetooth_logs.old.bz2";
constexpr char kBluetoothQualityReportAttachmentName[] =
    "bluetooth_quality_report";

constexpr char kLacrosHistogramsFilename[] = "lacros_histograms.zip";

void AddAttachment(scoped_refptr<feedback::FeedbackData> feedback_data,
                   const base::FilePath& root_path,
                   const std::string& file_path,
                   const std::string& attachment_name) {
  std::string temp_log_content;
  if (base::ReadFileToString(root_path.Append(file_path), &temp_log_content)) {
    feedback_data->AddFile(attachment_name, std::move(temp_log_content));
  } else {
    LOG(WARNING) << "failed to add attachment " << attachment_name
                 << ": could not read file: " << file_path << " in "
                 << root_path.value();
  }
}

void AttachBluetoothLogs(scoped_refptr<feedback::FeedbackData> feedback_data,
                         const base::FilePath& root_path) {
  AddAttachment(feedback_data, root_path, kBluetoothLogsFilePath,
                kBluetoothLogsAttachmentName);
  AddAttachment(feedback_data, root_path, kBluetoothLogsFilePathOld,
                kBluetoothLogsAttachmentNameOld);
  AddAttachment(feedback_data, root_path, kBluetoothQualityReportFilePath,
                kBluetoothQualityReportAttachmentName);
}

// A new case must be added for every new log type. Otherwise the code should
// not compile.
std::string_view GetAttachmentName(debugd::FeedbackBinaryLogType log_type) {
  switch (log_type) {
    case debugd::WIFI_FIRMWARE_DUMP:
      return "wifi_firmware_dumps.tar.zst";
    case debugd::BLUETOOTH_FIRMWARE_DUMP:
      return "bluetooth_firmware_dumps.tar.zst";
  }
}
#endif

constexpr char kLacrosLogEntryPrefix[] =;

void RedactFeedbackData(scoped_refptr<feedback::FeedbackData> feedback_data) {}

}  // namespace

FeedbackService::FeedbackService(content::BrowserContext* browser_context)
    :{}

FeedbackService::FeedbackService(content::BrowserContext* browser_context,
                                 FeedbackPrivateDelegate* delegate)
    :{}

FeedbackService::~FeedbackService() = default;

void FeedbackService::RedactThenSendFeedback(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    SendFeedbackCallback callback) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
void FeedbackService::SetLogFilesRootPathForTesting(
    const base::FilePath& log_file_root) {
  log_file_root_ = log_file_root;
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

// After the attached file and screenshot if available are fetched, the callback
// will be invoked. Other further processing will be done in background. The
// report will be sent out once all data are in place.
void FeedbackService::SendFeedback(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    SendFeedbackCallback callback) {}

void FeedbackService::FetchAttachedFileAndScreenshot(
    scoped_refptr<feedback::FeedbackData> feedback_data,
    base::OnceClosure callback) {}

void FeedbackService::OnAttachedFileAndScreenshotFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    SendFeedbackCallback callback) {}

void FeedbackService::FetchSystemInformation(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data) {}

void FeedbackService::OnSystemInformationFetched(
    base::TimeTicks fetch_start_time,
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    std::unique_ptr<system_logs::SystemLogsResponse> sys_info) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
void FeedbackService::FetchExtraLogs(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data) {
  delegate_->FetchExtraLogs(
      feedback_data,
      base::BindOnce(&FeedbackService::OnExtraLogsFetched, this, params));
}

void FeedbackService::OnExtraLogsFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data) {
  delegate_->GetLacrosHistograms(
      base::BindOnce(&FeedbackService::OnLacrosHistogramsFetched, this, params,
                     feedback_data));
}

void FeedbackService::OnLacrosHistogramsFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    const std::string& compressed_histograms) {
  if (!compressed_histograms.empty()) {
    feedback_data->AddFile(kLacrosHistogramsFilename,
                           std::move(compressed_histograms));
  }

  auto barrier_closure =
      base::BarrierClosure((params.send_bluetooth_logs ? 2 : 0) +
                               (params.send_wifi_debug_logs ? 1 : 0),
                           base::BindOnce(&FeedbackService::OnAllLogsFetched,
                                          this, params, feedback_data));
  const user_manager::User* user =
      user_manager::UserManager::Get()->GetActiveUser();
  const auto account_identifier =
      cryptohome::CreateAccountIdentifierFromAccountId(
          user ? user->GetAccountId() : EmptyAccountId());

  // If bluetooth logs are requested, invoke AttachBluetoothLogs to add
  // them in a separate thread to avoid blocking the UI thread.
  if (params.send_bluetooth_logs) {
    base::ThreadPool::PostTaskAndReply(
        FROM_HERE, {base::MayBlock()},
        base::BindOnce(&AttachBluetoothLogs, feedback_data, log_file_root_),
        barrier_closure);

    binary_log_files_reader_.GetFeedbackBinaryLogs(
        account_identifier,
        debugd::FeedbackBinaryLogType::BLUETOOTH_FIRMWARE_DUMP,
        base::BindOnce(&FeedbackService::OnBinaryLogFilesFetched, this, params,
                       feedback_data, barrier_closure));
  }

  if (params.send_wifi_debug_logs) {
    binary_log_files_reader_.GetFeedbackBinaryLogs(
        account_identifier, debugd::FeedbackBinaryLogType::WIFI_FIRMWARE_DUMP,
        base::BindOnce(&FeedbackService::OnBinaryLogFilesFetched, this, params,
                       feedback_data, barrier_closure));
  }
}

void FeedbackService::OnBinaryLogFilesFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data,
    base::RepeatingClosure barrier_closure_callback,
    feedback::BinaryLogFilesReader::BinaryLogsResponse binary_logs_response) {
  if (binary_logs_response) {
    for (auto& item : *binary_logs_response) {
      feedback_data->AddFile(GetAttachmentName(item.first).data(),
                             std::move(item.second));
    }
  }
  std::move(barrier_closure_callback).Run();
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

void FeedbackService::OnAllLogsFetched(
    const FeedbackParams& params,
    scoped_refptr<feedback::FeedbackData> feedback_data) {}

}  // namespace extensions