blob: 1dcb4e3e9ec13e3219c937e2979b314d903cb5ad [file] [log] [blame]
// Copyright 2017 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 "components/arc/usb/usb_host_bridge.h"
#include <unordered_set>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "chromeos/dbus/permission_broker/permission_broker_client.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_features.h"
#include "components/arc/session/arc_bridge_service.h"
#include "components/arc/usb/usb_host_ui_delegate.h"
#include "device/base/device_client.h"
#include "device/usb/mojo/type_converters.h"
#include "device/usb/usb_device_handle.h"
#include "device/usb/usb_device_linux.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace arc {
namespace {
// Singleton factory for ArcUsbHostBridge
class ArcUsbHostBridgeFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
ArcUsbHostBridge,
ArcUsbHostBridgeFactory> {
public:
// Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
static constexpr const char* kName = "ArcUsbHostBridgeFactory";
static ArcUsbHostBridgeFactory* GetInstance() {
return base::Singleton<ArcUsbHostBridgeFactory>::get();
}
private:
friend base::DefaultSingletonTraits<ArcUsbHostBridgeFactory>;
ArcUsbHostBridgeFactory() = default;
~ArcUsbHostBridgeFactory() override = default;
};
void OnDeviceOpened(mojom::UsbHostHost::OpenDeviceCallback callback,
base::ScopedFD fd) {
if (!fd.is_valid()) {
LOG(ERROR) << "Invalid USB device FD";
std::move(callback).Run(mojo::ScopedHandle());
return;
}
mojo::ScopedHandle wrapped_handle =
mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(fd)));
if (!wrapped_handle.is_valid()) {
LOG(ERROR) << "Failed to wrap device FD. Closing.";
std::move(callback).Run(mojo::ScopedHandle());
return;
}
std::move(callback).Run(std::move(wrapped_handle));
}
void OnDeviceOpenError(mojom::UsbHostHost::OpenDeviceCallback callback,
const std::string& error_name,
const std::string& error_message) {
LOG(WARNING) << "Cannot open USB device: " << error_name << ": "
<< error_message;
std::move(callback).Run(mojo::ScopedHandle());
}
using CheckedCallback =
base::RepeatingCallback<void(const std::string& guid, bool success)>;
void OnGetDevicesComplete(
const CheckedCallback& callback,
const std::vector<scoped_refptr<device::UsbDevice>>& devices) {
for (const scoped_refptr<device::UsbDevice>& device : devices)
device->CheckUsbAccess(base::BindOnce(callback, device.get()->guid()));
}
} // namespace
ArcUsbHostBridge* ArcUsbHostBridge::GetForBrowserContext(
content::BrowserContext* context) {
return ArcUsbHostBridgeFactory::GetForBrowserContext(context);
}
ArcUsbHostBridge::ArcUsbHostBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service)
: arc_bridge_service_(bridge_service),
usb_observer_(this),
weak_factory_(this) {
arc_bridge_service_->usb_host()->SetHost(this);
arc_bridge_service_->usb_host()->AddObserver(this);
usb_service_ = device::DeviceClient::Get()->GetUsbService();
if (usb_service_)
usb_observer_.Add(usb_service_);
}
ArcUsbHostBridge::~ArcUsbHostBridge() {
if (usb_service_)
usb_service_->RemoveObserver(this);
arc_bridge_service_->usb_host()->RemoveObserver(this);
arc_bridge_service_->usb_host()->SetHost(nullptr);
}
BrowserContextKeyedServiceFactory* ArcUsbHostBridge::GetFactory() {
return ArcUsbHostBridgeFactory::GetInstance();
}
void ArcUsbHostBridge::RequestPermission(const std::string& guid,
const std::string& package,
bool interactive,
RequestPermissionCallback callback) {
if (guid.empty()) {
HandleScanDeviceListRequest(package, std::move(callback));
return;
}
VLOG(2) << "USB RequestPermission " << guid << " package " << package;
// Permission already requested.
if (HasPermissionForDevice(guid, package)) {
std::move(callback).Run(true);
return;
}
// The other side was just checking, fail without asking the user.
if (!interactive) {
std::move(callback).Run(HasPermissionForDevice(guid, package));
return;
}
// Ask the authorization from the user.
DoRequestUserAuthorization(guid, package, std::move(callback));
}
void ArcUsbHostBridge::OpenDevice(const std::string& guid,
const base::Optional<std::string>& package,
OpenDeviceCallback callback) {
if (!usb_service_ || !package) {
std::move(callback).Run(mojo::ScopedHandle());
return;
}
device::UsbDeviceLinux* device =
static_cast<device::UsbDeviceLinux*>(usb_service_->GetDevice(guid).get());
if (!device) {
std::move(callback).Run(mojo::ScopedHandle());
return;
}
// The RequestPermission was never done, abort.
if (!HasPermissionForDevice(guid, package.value())) {
std::move(callback).Run(mojo::ScopedHandle());
return;
}
auto repeating_callback =
base::AdaptCallbackForRepeating(std::move(callback));
chromeos::PermissionBrokerClient::Get()->OpenPath(
device->device_path(),
base::BindOnce(&OnDeviceOpened, repeating_callback),
base::BindOnce(&OnDeviceOpenError, repeating_callback));
}
void ArcUsbHostBridge::OpenDeviceDeprecated(
const std::string& guid,
const base::Optional<std::string>& package,
OpenDeviceCallback callback) {
LOG(ERROR) << "ArcUsbHostBridge::OpenDeviceDeprecated is deprecated";
OpenDevice(guid, package, std::move(callback));
}
void ArcUsbHostBridge::GetDeviceInfo(const std::string& guid,
GetDeviceInfoCallback callback) {
if (!usb_service_) {
std::move(callback).Run(std::string(), nullptr);
return;
}
scoped_refptr<device::UsbDevice> device = usb_service_->GetDevice(guid);
if (!device.get()) {
LOG(WARNING) << "Unknown USB device " << guid;
std::move(callback).Run(std::string(), nullptr);
return;
}
device::mojom::UsbDeviceInfoPtr info =
device::mojom::UsbDeviceInfo::From(*device);
// b/69295049 the other side doesn't like optional strings.
for (const device::mojom::UsbConfigurationInfoPtr& cfg :
info->configurations) {
cfg->configuration_name =
cfg->configuration_name.value_or(base::string16());
for (const device::mojom::UsbInterfaceInfoPtr& iface : cfg->interfaces) {
for (const device::mojom::UsbAlternateInterfaceInfoPtr& alt :
iface->alternates) {
alt->interface_name = alt->interface_name.value_or(base::string16());
}
}
}
std::string path =
static_cast<device::UsbDeviceLinux*>(device.get())->device_path();
std::move(callback).Run(path, std::move(info));
}
// device::UsbService::Observer callbacks.
void ArcUsbHostBridge::OnDeviceAdded(scoped_refptr<device::UsbDevice> device) {
device->CheckUsbAccess(base::BindOnce(&ArcUsbHostBridge::OnDeviceChecked,
weak_factory_.GetWeakPtr(),
device.get()->guid()));
}
void ArcUsbHostBridge::OnDeviceRemoved(
scoped_refptr<device::UsbDevice> device) {
mojom::UsbHostInstance* usb_host_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->usb_host(), OnDeviceRemoved);
if (!usb_host_instance) {
VLOG(2) << "UsbInstance not ready yet";
return;
}
usb_host_instance->OnDeviceRemoved(device.get()->guid(),
GetEventReceiverPackages(device));
if (ui_delegate_)
ui_delegate_->DeviceRemoved(device.get()->guid());
}
// Notifies the observer that the UsbService it depends on is shutting down.
void ArcUsbHostBridge::WillDestroyUsbService() {
// Disconnect.
arc_bridge_service_->usb_host()->SetHost(nullptr);
}
void ArcUsbHostBridge::OnConnectionReady() {
if (!usb_service_)
return;
// Send the (filtered) list of already existing USB devices to the other side.
usb_service_->GetDevices(
base::Bind(&OnGetDevicesComplete,
base::BindRepeating(&ArcUsbHostBridge::OnDeviceChecked,
weak_factory_.GetWeakPtr())));
}
void ArcUsbHostBridge::OnConnectionClosed() {
if (ui_delegate_)
ui_delegate_->ClearPermissionRequests();
}
void ArcUsbHostBridge::Shutdown() {
ui_delegate_ = nullptr;
}
void ArcUsbHostBridge::SetUiDelegate(ArcUsbHostUiDelegate* ui_delegate) {
ui_delegate_ = ui_delegate;
}
std::vector<std::string> ArcUsbHostBridge::GetEventReceiverPackages(
scoped_refptr<device::UsbDevice> device) {
if (!device) {
LOG(WARNING) << "Unknown USB device.";
return std::vector<std::string>();
}
if (!ui_delegate_)
return std::vector<std::string>();
std::unordered_set<std::string> receivers = ui_delegate_->GetEventPackageList(
device->guid(), device->serial_number(), device->vendor_id(),
device->product_id());
return std::vector<std::string>(receivers.begin(), receivers.end());
}
void ArcUsbHostBridge::OnDeviceChecked(const std::string& guid, bool allowed) {
if (!base::FeatureList::IsEnabled(arc::kUsbHostFeature)) {
VLOG(1) << "AndroidUSBHost: feature is disabled; ignoring";
return;
}
if (!allowed)
return;
mojom::UsbHostInstance* usb_host_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->usb_host(), OnDeviceAdded);
if (!usb_host_instance || !usb_service_)
return;
usb_host_instance->OnDeviceAdded(
guid, GetEventReceiverPackages(usb_service_->GetDevice(guid)));
}
void ArcUsbHostBridge::DoRequestUserAuthorization(
const std::string& guid,
const std::string& package,
RequestPermissionCallback callback) {
if (!ui_delegate_) {
std::move(callback).Run(false);
return;
}
if (!usb_service_) {
std::move(callback).Run(false);
return;
}
scoped_refptr<device::UsbDevice> device = usb_service_->GetDevice(guid);
if (!device.get()) {
LOG(WARNING) << "Unknown USB device " << guid;
std::move(callback).Run(false);
return;
}
ui_delegate_->RequestUsbAccessPermission(
package, guid, device->serial_number(), device->manufacturer_string(),
device->product_string(), device->vendor_id(), device->product_id(),
std::move(callback));
}
bool ArcUsbHostBridge::HasPermissionForDevice(const std::string& guid,
const std::string& package) {
if (!ui_delegate_)
return false;
if (!usb_service_)
return false;
scoped_refptr<device::UsbDevice> device = usb_service_->GetDevice(guid);
if (!device.get()) {
LOG(WARNING) << "Unknown USB device " << guid;
return false;
}
return ui_delegate_->HasUsbAccessPermission(
package, guid, device->serial_number(), device->vendor_id(),
device->product_id());
}
void ArcUsbHostBridge::HandleScanDeviceListRequest(
const std::string& package,
RequestPermissionCallback callback) {
if (!ui_delegate_) {
std::move(callback).Run(false);
return;
}
VLOG(2) << "USB Request USB scan devicelist permission "
<< "package: " << package;
ui_delegate_->RequestUsbScanDeviceListPermission(package,
std::move(callback));
}
} // namespace arc