910e62b5创建于 1月15日历史提交
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/device/usb/usb_device_linux.h"

#include <fcntl.h>
#include <stddef.h>

#include <algorithm>

#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "components/device_event_log/device_event_log.h"
#include "services/device/usb/usb_descriptors.h"
#include "services/device/usb/usb_device_handle_usbfs.h"
#include "services/device/usb/usb_service.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "base/memory/memory_pressure_monitor.h"
#include "base/process/process_metrics.h"
#include "chromeos/dbus/permission_broker/permission_broker_client.h"  // nogncheck
#include "components/crash/core/common/crash_key.h"
#include "services/device/public/cpp/device_features.h"
namespace {
constexpr uint32_t kAllInterfacesMask = ~0U;
}  // namespace
#endif  // BUILDFLAG(IS_CHROMEOS)

namespace device {

UsbDeviceLinux::UsbDeviceLinux(const std::string& device_path,
                               std::unique_ptr<UsbDeviceDescriptor> descriptor)
    : UsbDevice(std::move(descriptor->device_info)),
      device_path_(device_path) {}

UsbDeviceLinux::~UsbDeviceLinux() = default;

#if BUILDFLAG(IS_CHROMEOS)

void UsbDeviceLinux::CheckUsbAccess(ResultCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  chromeos::PermissionBrokerClient::Get()->CheckPathAccess(device_path_,
                                                           std::move(callback));
}

#endif  // BUILDFLAG(IS_CHROMEOS)

void UsbDeviceLinux::Open(OpenCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

#if BUILDFLAG(IS_CHROMEOS)
  if (base::FeatureList::IsEnabled(features::kUsbDeviceLinuxOpenCrashKey)) {
    // Opening a USB device on ChromeOS sometimes crashes with signatures seen
    // in crbug.com/40069034 and crbug.com/332722607. The crash is caused by a
    // CHECK failure in MessageWriter::AppendBasic when
    // dbus_message_iter_append_basic fails (returns false). DBus documentation
    // indicates this only happens on OOM, but it can also happen on file
    // descriptor exhaustion.
    //
    // Record crash keys with the current memory pressure and open file
    // descriptor counts to aid in debugging these crashes.
    static crash_reporter::CrashKeyString<6> memory_pressure_critical(
        "memory-pressure-critical");
    static crash_reporter::CrashKeyString<12> open_fd_count("open-fd-count");
    static crash_reporter::CrashKeyString<12> open_fd_soft_limit(
        "open-fd-soft-limit");
    auto* memory_pressure_monitor = base::MemoryPressureMonitor::Get();
    if (memory_pressure_monitor) {
      memory_pressure_critical.Set(
          (memory_pressure_monitor->GetCurrentPressureLevel(
               base::MemoryPressureMonitorTag::kUsbDeviceLinux) ==
           base::MEMORY_PRESSURE_LEVEL_CRITICAL)
              ? "true"
              : "false");
    }
    auto process_metrics = base::ProcessMetrics::CreateCurrentProcessMetrics();
    if (process_metrics) {
      open_fd_count.Set(
          base::NumberToString(process_metrics->GetOpenFdCount()));
      open_fd_soft_limit.Set(
          base::NumberToString(process_metrics->GetOpenFdSoftLimit()));
    }
  }
  // create the pipe used as a lifetime to re-attach the original kernel driver
  // to the USB device in permission_broker.
  base::ScopedFD read_end, write_end;
  if (!base::CreatePipe(&read_end, &write_end, /*non_blocking*/ true)) {
    LOG(ERROR) << "Couldn't create pipe for USB device " << device_path_;
    std::move(callback).Run(nullptr);
    return;
  }

  auto split_callback = base::SplitOnceCallback(std::move(callback));
  chromeos::PermissionBrokerClient::Get()->OpenPathAndRegisterClient(
      device_path_, kAllInterfacesMask, read_end.get(),
      base::BindOnce(&UsbDeviceLinux::OnOpenRequestComplete, this,
                     std::move(split_callback.first), std::move(write_end)),
      base::BindOnce(&UsbDeviceLinux::OnOpenRequestError, this,
                     std::move(split_callback.second)));
#else
  scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
      UsbService::CreateBlockingTaskRunner();
  blocking_task_runner->PostTask(
      FROM_HERE,
      base::BindOnce(&UsbDeviceLinux::OpenOnBlockingThread, this,
                     std::move(callback),
                     base::SingleThreadTaskRunner::GetCurrentDefault(),
                     blocking_task_runner));
#endif  // BUILDFLAG(IS_CHROMEOS)
}

#if BUILDFLAG(IS_CHROMEOS)

void UsbDeviceLinux::OnOpenRequestComplete(OpenCallback callback,
                                           base::ScopedFD lifeline_fd,
                                           const std::string& client_id,
                                           base::ScopedFD fd) {
  if (!fd.is_valid()) {
    USB_LOG(EVENT) << "Did not get valid device handle from permission broker.";
    std::move(callback).Run(nullptr);
    return;
  }
  Opened(std::move(fd), std::move(lifeline_fd), client_id, std::move(callback),
         UsbService::CreateBlockingTaskRunner());
}

void UsbDeviceLinux::OnOpenRequestError(OpenCallback callback,
                                        const std::string& error_name,
                                        const std::string& error_message) {
  USB_LOG(EVENT) << "Permission broker failed to open the device: "
                 << error_name << ": " << error_message;
  std::move(callback).Run(nullptr);
}

#else

void UsbDeviceLinux::OpenOnBlockingThread(
    OpenCallback callback,
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
  base::ScopedFD fd(HANDLE_EINTR(open(device_path_.c_str(), O_RDWR)));
  // Client id is only used for ChromeOS so pass empty string here to indicate
  // an invalid client id.
  std::string empty_client_id = "";
  if (fd.is_valid()) {
    task_runner->PostTask(
        FROM_HERE, base::BindOnce(&UsbDeviceLinux::Opened, this, std::move(fd),
                                  base::ScopedFD(), empty_client_id,
                                  std::move(callback), blocking_task_runner));
  } else {
    USB_PLOG(EVENT) << "Failed to open " << device_path_;
    task_runner->PostTask(FROM_HERE,
                          base::BindOnce(std::move(callback), nullptr));
  }
}

#endif  // BUILDFLAG(IS_CHROMEOS)

void UsbDeviceLinux::Opened(
    base::ScopedFD fd,
    base::ScopedFD lifeline_fd,
    const std::string& client_id,
    OpenCallback callback,
    scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  scoped_refptr<UsbDeviceHandle> device_handle =
      new UsbDeviceHandleUsbfs(this, std::move(fd), std::move(lifeline_fd),
                               client_id, blocking_task_runner);
  handles().push_back(device_handle.get());
  std::move(callback).Run(device_handle);
}

}  // namespace device