blob: 4f51938136a21a70da03262843339e3ce9c89fc8 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/devtools/device/usb/usb_device_manager_helper.h"
#include <memory>
#include <utility>
#include "base/no_destructor.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
#include "services/device/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
using device::mojom::UsbTransferDirection;
using device::mojom::UsbTransferType;
namespace {
const int kAdbClass = 0xff;
const int kAdbSubclass = 0x42;
const int kAdbProtocol = 0x1;
std::unique_ptr<AndroidInterfaceInfo> FindAndroidInterface(
const device::mojom::UsbDeviceInfo& device_info) {
if (!device_info.active_configuration)
return nullptr;
// Find the active configuration object.
device::mojom::UsbConfigurationInfo* active_config = nullptr;
for (const auto& config : device_info.configurations) {
if (config->configuration_value == device_info.active_configuration) {
active_config = config.get();
break;
}
}
if (!active_config)
return nullptr;
for (const auto& interface : active_config->interfaces) {
for (const auto& alternate : interface->alternates) {
if (alternate->alternate_setting == 0 &&
alternate->class_code == kAdbClass &&
alternate->subclass_code == kAdbSubclass &&
alternate->protocol_code == kAdbProtocol &&
alternate->endpoints.size() == 2) {
return std::make_unique<AndroidInterfaceInfo>(
interface->interface_number, alternate.get());
}
}
}
return nullptr;
}
void GetAndroidDeviceInfoList(
AndroidDeviceInfoListCallback callback,
std::vector<device::mojom::UsbDeviceInfoPtr> usb_devices) {
std::vector<AndroidDeviceInfo> result;
for (auto& device_info : usb_devices) {
if (device_info->serial_number->empty())
continue;
auto interface_info = FindAndroidInterface(*device_info);
if (!interface_info)
continue;
int inbound_address = 0;
int outbound_address = 0;
int zero_mask = 0;
for (auto& endpoint : interface_info->alternate->endpoints) {
if (endpoint->type != UsbTransferType::BULK)
continue;
if (endpoint->direction == UsbTransferDirection::INBOUND)
inbound_address = endpoint->endpoint_number;
else
outbound_address = endpoint->endpoint_number;
zero_mask = endpoint->packet_size - 1;
}
if (inbound_address == 0 || outbound_address == 0)
continue;
result.push_back(AndroidDeviceInfo(
device_info->guid,
base::UTF16ToASCII(device_info->serial_number.value()),
interface_info->interface_number, inbound_address, outbound_address,
zero_mask));
}
std::move(callback).Run(std::move(result));
}
void CountAndroidDevices(base::OnceCallback<void(int)> callback,
std::vector<AndroidDeviceInfo> info_list) {
std::move(callback).Run(info_list.size());
}
void BindDeviceServiceOnUIThread(
device::mojom::UsbDeviceManagerRequest request) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Bind to the DeviceService for USB device manager.
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(device::mojom::kServiceName, std::move(request));
}
} // namespace
AndroidInterfaceInfo::AndroidInterfaceInfo(
uint8_t interface_number,
const device::mojom::UsbAlternateInterfaceInfo* alternate)
: interface_number(interface_number), alternate(alternate) {}
AndroidDeviceInfo::AndroidDeviceInfo(const std::string& guid,
const std::string& serial,
int interface_id,
int inbound_address,
int outbound_address,
int zero_mask)
: guid(guid),
serial(serial),
interface_id(interface_id),
inbound_address(inbound_address),
outbound_address(outbound_address),
zero_mask(zero_mask) {}
AndroidDeviceInfo::AndroidDeviceInfo(const AndroidDeviceInfo& other) = default;
// static
UsbDeviceManagerHelper* UsbDeviceManagerHelper::GetInstance() {
static base::NoDestructor<UsbDeviceManagerHelper> s_instance;
return s_instance.get();
}
// static
void UsbDeviceManagerHelper::CountDevices(
base::OnceCallback<void(int)> callback) {
GetInstance()->CountDevicesInternal(std::move(callback));
}
// static
void UsbDeviceManagerHelper::SetUsbManagerForTesting(
device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager) {
GetInstance()->SetUsbManagerForTestingInternal(std::move(fake_usb_manager));
}
UsbDeviceManagerHelper::UsbDeviceManagerHelper() : weak_factory_(this) {}
UsbDeviceManagerHelper::~UsbDeviceManagerHelper() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
void UsbDeviceManagerHelper::GetAndroidDevices(
AndroidDeviceInfoListCallback callback) {
EnsureUsbDeviceManagerConnection();
DCHECK(device_manager_);
device_manager_->GetDevices(
/*options=*/nullptr,
base::BindOnce(&GetAndroidDeviceInfoList, std::move(callback)));
}
void UsbDeviceManagerHelper::GetDevice(
const std::string& guid,
device::mojom::UsbDeviceRequest device_request) {
EnsureUsbDeviceManagerConnection();
DCHECK(device_manager_);
device_manager_->GetDevice(guid, std::move(device_request),
/*device_client=*/nullptr);
}
void UsbDeviceManagerHelper::EnsureUsbDeviceManagerConnection() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Ensure connection with the Device Service.
if (device_manager_)
return;
// Just for testing.
if (testing_device_manager_info_) {
device_manager_.Bind(std::move(testing_device_manager_info_));
device_manager_.set_connection_error_handler(
base::BindOnce(&UsbDeviceManagerHelper::OnDeviceManagerConnectionError,
weak_factory_.GetWeakPtr()));
return;
}
device::mojom::UsbDeviceManagerRequest request =
mojo::MakeRequest(&device_manager_);
device_manager_.set_connection_error_handler(
base::BindOnce(&UsbDeviceManagerHelper::OnDeviceManagerConnectionError,
weak_factory_.GetWeakPtr()));
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&BindDeviceServiceOnUIThread, std::move(request)));
}
void UsbDeviceManagerHelper::CountDevicesInternal(
base::OnceCallback<void(int)> callback) {
EnsureUsbDeviceManagerConnection();
DCHECK(device_manager_);
device_manager_->GetDevices(
/*options=*/nullptr, base::BindOnce(&GetAndroidDeviceInfoList,
base::BindOnce(&CountAndroidDevices,
std::move(callback))));
}
void UsbDeviceManagerHelper::SetUsbManagerForTestingInternal(
device::mojom::UsbDeviceManagerPtrInfo fake_usb_manager) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(fake_usb_manager);
testing_device_manager_info_ = std::move(fake_usb_manager);
}
void UsbDeviceManagerHelper::OnDeviceManagerConnectionError() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// TODO(donna.wu@intel.com): finish ongoing count/enumerate requests with some
// result for the connection error cases.
device_manager_.reset();
}