0

Mojofication on //device/hid.

In this CL:
1) Add the HidManager and HidConnection interfaces in hid.mojom, and
   implements those mojo interfaces in //device/hid.

2) Convert the clients in //extensions to use hid mojo interfaces.

3) Change the type of HidUsageAndPage::usage_page to uint16_t.

4) Rewrite the hid_apitest base on the new added mojo interfaces.

TODO:
1) Move client library files into device/hid/public/cpp.
2) Host HidService by DeviceService instead of DeviceClient.
3) Mojofy //device/u2f Or just move it into DeviceService.

BUG=728223

Change-Id: I1de1b5211f7ef37f54d4bc18c7995a3b47cc7da2
Reviewed-on: https://chromium-review.googlesource.com/648949
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Ke He <ke.he@intel.com>
Cr-Commit-Position: refs/heads/master@{#502109}
This commit is contained in:
Ke He
2017-09-15 00:41:19 +00:00
committed by Commit Bot
parent c3592928ad
commit d2eea93624
27 changed files with 734 additions and 471 deletions

@ -12,7 +12,6 @@
#include "chrome/test/base/testing_profile.h"
#include "device/base/mock_device_client.h"
#include "device/hid/hid_device_info.h"
#include "device/hid/mock_hid_service.h"
#include "device/hid/public/interfaces/hid.mojom.h"
#include "device/usb/mock_usb_device.h"
#include "device/usb/mock_usb_service.h"
@ -40,9 +39,17 @@ const uint64_t kTestDeviceIds[] = {1, 2, 3, 4};
const char* kTestDeviceIds[] = {"A", "B", "C", "D"};
#endif
class FakeHidDeviceManager : public HidDeviceManager {
public:
explicit FakeHidDeviceManager(content::BrowserContext* context)
: HidDeviceManager(context) {}
void LazyInitialize() override {}
};
std::unique_ptr<KeyedService> CreateHidDeviceManager(
content::BrowserContext* context) {
return base::MakeUnique<HidDeviceManager>(context);
return base::MakeUnique<FakeHidDeviceManager>(context);
}
} // namespace
@ -74,20 +81,15 @@ class DevicePermissionsManagerTest : public testing::Test {
device4_ = new HidDeviceInfo(
kTestDeviceIds[0], 0, 0, "Test HID Device", "abcde",
device::mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>());
device_client_.hid_service()->AddDevice(device4_);
device5_ = new HidDeviceInfo(kTestDeviceIds[1], 0, 0, "Test HID Device", "",
device::mojom::HidBusType::kHIDBusTypeUSB,
std::vector<uint8_t>());
device_client_.hid_service()->AddDevice(device5_);
device6_ = new HidDeviceInfo(
kTestDeviceIds[2], 0, 0, "Test HID Device", "67890",
device::mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>());
device_client_.hid_service()->AddDevice(device6_);
device7_ = new HidDeviceInfo(kTestDeviceIds[3], 0, 0, "Test HID Device", "",
device::mojom::HidBusType::kHIDBusTypeUSB,
std::vector<uint8_t>());
device_client_.hid_service()->AddDevice(device7_);
device_client_.hid_service()->FirstEnumerationComplete();
}
void TearDown() override { env_.reset(nullptr); }
@ -209,11 +211,8 @@ TEST_F(DevicePermissionsManagerTest, DisconnectDevice) {
device_client_.usb_service()->RemoveDevice(device0_);
device_client_.usb_service()->RemoveDevice(device1_);
// Wait until HidDeviceManager::GetDevicesCallback is run. HidService
// won't send notifications to its observers before that.
base::RunLoop().RunUntilIdle();
device_client_.hid_service()->RemoveDevice(device4_->platform_device_id());
device_client_.hid_service()->RemoveDevice(device5_->platform_device_id());
manager->RemoveEntryByHidDeviceGUID(device4_->device_guid());
manager->RemoveEntryByHidDeviceGUID(device5_->device_guid());
// Device 0 will be accessible when it is reconnected because it can be
// recognized by its serial number.

@ -77,6 +77,7 @@
"device": [
"device:battery_monitor",
"device:generic_sensor",
"device:hid",
"device:nfc",
"device:serial",
"device:vibration",

@ -11,6 +11,8 @@ source_set("hid") {
"hid_collection_info.h",
"hid_connection.cc",
"hid_connection.h",
"hid_connection_impl.cc",
"hid_connection_impl.h",
"hid_connection_linux.cc",
"hid_connection_linux.h",
"hid_connection_mac.cc",
@ -21,6 +23,8 @@ source_set("hid") {
"hid_device_filter.h",
"hid_device_info.cc",
"hid_device_info.h",
"hid_manager_impl.cc",
"hid_manager_impl.h",
"hid_report_descriptor.cc",
"hid_report_descriptor.h",
"hid_report_descriptor_item.cc",

@ -0,0 +1,113 @@
// 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 "device/hid/hid_connection_impl.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
namespace device {
HidConnectionImpl::HidConnectionImpl(
scoped_refptr<device::HidConnection> connection)
: hid_connection_(std::move(connection)), weak_factory_(this) {}
HidConnectionImpl::~HidConnectionImpl() {
DCHECK(hid_connection_);
// Close |hid_connection_| on destruction because this class is owned by a
// mojo::StrongBinding and will be destroyed when the pipe is closed.
hid_connection_->Close();
}
void HidConnectionImpl::Read(ReadCallback callback) {
DCHECK(hid_connection_);
hid_connection_->Read(base::BindOnce(&HidConnectionImpl::OnRead,
weak_factory_.GetWeakPtr(),
std::move(callback)));
}
void HidConnectionImpl::OnRead(ReadCallback callback,
bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size) {
if (!success) {
std::move(callback).Run(false, 0, base::nullopt);
return;
}
DCHECK(buffer);
std::vector<uint8_t> data(buffer->data() + 1, buffer->data() + size);
std::move(callback).Run(true, buffer->data()[0], data);
}
void HidConnectionImpl::Write(uint8_t report_id,
const std::vector<uint8_t>& buffer,
WriteCallback callback) {
DCHECK(hid_connection_);
auto io_buffer =
base::MakeRefCounted<net::IOBufferWithSize>(buffer.size() + 1);
io_buffer->data()[0] = report_id;
const char* data = reinterpret_cast<const char*>(buffer.data());
memcpy(io_buffer->data() + 1, data, buffer.size());
hid_connection_->Write(
io_buffer, io_buffer->size(),
base::BindOnce(&HidConnectionImpl::OnWrite, weak_factory_.GetWeakPtr(),
std::move(callback)));
}
void HidConnectionImpl::OnWrite(WriteCallback callback, bool success) {
std::move(callback).Run(success);
}
void HidConnectionImpl::GetFeatureReport(uint8_t report_id,
GetFeatureReportCallback callback) {
DCHECK(hid_connection_);
hid_connection_->GetFeatureReport(
report_id,
base::BindOnce(&HidConnectionImpl::OnGetFeatureReport,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void HidConnectionImpl::OnGetFeatureReport(GetFeatureReportCallback callback,
bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size) {
if (!success) {
std::move(callback).Run(false, base::nullopt);
return;
}
DCHECK(buffer);
std::vector<uint8_t> data(buffer->data(), buffer->data() + size);
std::move(callback).Run(true, data);
}
void HidConnectionImpl::SendFeatureReport(uint8_t report_id,
const std::vector<uint8_t>& buffer,
SendFeatureReportCallback callback) {
DCHECK(hid_connection_);
auto io_buffer =
base::MakeRefCounted<net::IOBufferWithSize>(buffer.size() + 1);
io_buffer->data()[0] = report_id;
const char* data = reinterpret_cast<const char*>(buffer.data());
memcpy(io_buffer->data() + 1, data, buffer.size());
hid_connection_->SendFeatureReport(
io_buffer, io_buffer->size(),
base::BindOnce(&HidConnectionImpl::OnSendFeatureReport,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void HidConnectionImpl::OnSendFeatureReport(SendFeatureReportCallback callback,
bool success) {
std::move(callback).Run(success);
}
} // namespace device

@ -0,0 +1,54 @@
// 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.
#ifndef DEVICE_HID_HID_CONNECTION_IMPL_H_
#define DEVICE_HID_HID_CONNECTION_IMPL_H_
#include "base/memory/ref_counted.h"
#include "device/hid/hid_connection.h"
#include "device/hid/public/interfaces/hid.mojom.h"
#include "net/base/io_buffer.h"
namespace device {
// HidConnectionImpl is reponsible for handling mojo communications from
// clients. It delegates to HidConnection the real work of creating
// connections in different platforms.
class HidConnectionImpl : public device::mojom::HidConnection {
public:
explicit HidConnectionImpl(scoped_refptr<device::HidConnection> connection);
~HidConnectionImpl() final;
// device::mojom::HidConnection implementation:
void Read(ReadCallback callback) override;
void Write(uint8_t report_id,
const std::vector<uint8_t>& buffer,
WriteCallback callback) override;
void GetFeatureReport(uint8_t report_id,
GetFeatureReportCallback callback) override;
void SendFeatureReport(uint8_t report_id,
const std::vector<uint8_t>& buffer,
SendFeatureReportCallback callback) override;
private:
void OnRead(ReadCallback callback,
bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size);
void OnWrite(WriteCallback callback, bool success);
void OnGetFeatureReport(GetFeatureReportCallback callback,
bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size);
void OnSendFeatureReport(SendFeatureReportCallback callback, bool success);
scoped_refptr<device::HidConnection> hid_connection_;
base::WeakPtrFactory<HidConnectionImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(HidConnectionImpl);
};
} // namespace device
#endif // DEVICE_HID_HID_CONNECTION_IMPL_H_

@ -0,0 +1,101 @@
// 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 "device/hid/hid_manager_impl.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/stl_util.h"
#include "device/base/device_client.h"
#include "device/hid/hid_connection_impl.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
namespace device {
HidManagerImpl::HidManagerImpl()
// TODO(ke.he@intel.com): Temporarily we still keep the HidService being
// owned and hosted in ChromeDeviceClient. The device service is shutdown
// earlier than ChromeDeviceClient, it is safe to hold a raw pointer of
// HidService here. After //device/u2f be mojofied or be moved into device
// service, we will remove HidService from the ChromeDeviceClient and let
// HidManagerImpl to own the HidService.
: hid_service_(DeviceClient::Get()->GetHidService()),
hid_service_observer_(this),
weak_factory_(this) {
DCHECK(hid_service_);
hid_service_observer_.Add(hid_service_);
}
HidManagerImpl::~HidManagerImpl() {}
void HidManagerImpl::AddBinding(device::mojom::HidManagerRequest request) {
bindings_.AddBinding(this, std::move(request));
}
void HidManagerImpl::GetDevicesAndSetClient(
device::mojom::HidManagerClientAssociatedPtrInfo client,
GetDevicesCallback callback) {
hid_service_->GetDevices(AdaptCallbackForRepeating(base::BindOnce(
&HidManagerImpl::CreateDeviceList, weak_factory_.GetWeakPtr(),
std::move(callback), std::move(client))));
}
void HidManagerImpl::GetDevices(GetDevicesCallback callback) {
hid_service_->GetDevices(AdaptCallbackForRepeating(base::BindOnce(
&HidManagerImpl::CreateDeviceList, weak_factory_.GetWeakPtr(),
std::move(callback), nullptr)));
}
void HidManagerImpl::CreateDeviceList(
GetDevicesCallback callback,
device::mojom::HidManagerClientAssociatedPtrInfo client,
std::vector<device::mojom::HidDeviceInfoPtr> devices) {
std::move(callback).Run(std::move(devices));
if (!client.is_valid())
return;
device::mojom::HidManagerClientAssociatedPtr client_ptr;
client_ptr.Bind(std::move(client));
clients_.AddPtr(std::move(client_ptr));
}
void HidManagerImpl::Connect(const std::string& device_guid,
ConnectCallback callback) {
hid_service_->Connect(device_guid,
AdaptCallbackForRepeating(base::BindOnce(
&HidManagerImpl::CreateConnection,
weak_factory_.GetWeakPtr(), std::move(callback))));
}
void HidManagerImpl::CreateConnection(
ConnectCallback callback,
scoped_refptr<device::HidConnection> connection) {
if (!connection) {
std::move(callback).Run(nullptr);
return;
}
device::mojom::HidConnectionPtr client;
mojo::MakeStrongBinding(base::MakeUnique<HidConnectionImpl>(connection),
mojo::MakeRequest(&client));
std::move(callback).Run(std::move(client));
}
void HidManagerImpl::OnDeviceAdded(device::mojom::HidDeviceInfoPtr device) {
device::mojom::HidDeviceInfo* device_info = device.get();
clients_.ForAllPtrs([device_info](device::mojom::HidManagerClient* client) {
client->DeviceAdded(device_info->Clone());
});
}
void HidManagerImpl::OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device) {
device::mojom::HidDeviceInfo* device_info = device.get();
clients_.ForAllPtrs([device_info](device::mojom::HidManagerClient* client) {
client->DeviceRemoved(device_info->Clone());
});
}
} // namespace device

@ -0,0 +1,63 @@
// 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.
#ifndef DEVICE_HID_HID_MANAGER_IMPL_H_
#define DEVICE_HID_HID_MANAGER_IMPL_H_
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observer.h"
#include "device/hid/hid_device_info.h"
#include "device/hid/hid_service.h"
#include "device/hid/public/interfaces/hid.mojom.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
namespace device {
// HidManagerImpl is owned by Device Service. It is reponsible for handling mojo
// communications from clients. It delegates to HidService the real work of
// talking with different platforms.
class HidManagerImpl : public device::mojom::HidManager,
public device::HidService::Observer {
public:
HidManagerImpl();
~HidManagerImpl() override;
void AddBinding(device::mojom::HidManagerRequest request);
// device::mojom::HidManager implementation:
void GetDevicesAndSetClient(
device::mojom::HidManagerClientAssociatedPtrInfo client,
GetDevicesCallback callback) override;
void GetDevices(GetDevicesCallback callback) override;
void Connect(const std::string& device_guid,
ConnectCallback callback) override;
private:
void CreateDeviceList(GetDevicesCallback callback,
device::mojom::HidManagerClientAssociatedPtrInfo client,
std::vector<device::mojom::HidDeviceInfoPtr> devices);
void CreateConnection(ConnectCallback callback,
scoped_refptr<HidConnection> connection);
// HidService::Observer:
void OnDeviceAdded(device::mojom::HidDeviceInfoPtr device_info) override;
void OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device_info) override;
HidService* hid_service_;
mojo::BindingSet<device::mojom::HidManager> bindings_;
mojo::AssociatedInterfacePtrSet<device::mojom::HidManagerClient> clients_;
ScopedObserver<device::HidService, device::HidService::Observer>
hid_service_observer_;
base::WeakPtrFactory<HidManagerImpl> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(HidManagerImpl);
};
} // namespace device
#endif // DEVICE_HID_HID_MANAGER_IMPL_H_

@ -34,9 +34,6 @@ class HidService {
// GetDevicesCallback. Earlier might cause OnDeviceAdded() and
// OnDeviceRemoved() to be called before the GetDevicesCallback, while later
// might cause missing OnDeviceAdded() and OnDeviceRemoved() notifications.
// TODO(ke.he@intel.com): In the mojofication of HidService, clients should
// pass an mojom::ObserverPtr in the GetDevices() interface, HidService adds
// the observer immediately after calling the GetDevicesCallback.
class Observer {
public:
virtual void OnDeviceAdded(device::mojom::HidDeviceInfoPtr info);

@ -120,12 +120,12 @@ struct HidUsageAndPage {
};
HidUsageAndPage() {}
HidUsageAndPage(uint16_t usage, Page usage_page)
HidUsageAndPage(uint16_t usage, uint16_t usage_page)
: usage(usage), usage_page(usage_page) {}
~HidUsageAndPage() {}
uint16_t usage;
Page usage_page;
uint16_t usage_page;
// Indicates whether this usage is protected by Chrome.
bool IsProtected() const;

@ -9,45 +9,9 @@ enum HidBusType {
kHIDBusTypeBluetooth = 1,
};
enum HidPage {
PageUndefined = 0x00,
PageGenericDesktop = 0x01,
PageSimulation = 0x02,
PageVirtualReality = 0x03,
PageSport = 0x04,
PageGame = 0x05,
PageKeyboard = 0x07,
PageLed = 0x08,
PageButton = 0x09,
PageOrdinal = 0x0A,
PageTelephony = 0x0B,
PageConsumer = 0x0C,
PageDigitizer = 0x0D,
PagePidPage = 0x0F,
PageUnicode = 0x10,
PageAlphanumericDisplay = 0x14,
PageMedicalInstruments = 0x40,
PageMonitor0 = 0x80,
PageMonitor1 = 0x81,
PageMonitor2 = 0x82,
PageMonitor3 = 0x83,
PagePower0 = 0x84,
PagePower1 = 0x85,
PagePower2 = 0x86,
PagePower3 = 0x87,
PageBarCodeScanner = 0x8C,
PageScale = 0x8D,
PageMagneticStripeReader = 0x8E,
PageReservedPointOfSale = 0x8F,
PageCameraControl = 0x90,
PageArcade = 0x91,
PageVendor = 0xFF00,
PageMediaCenter = 0xFFBC
};
struct HidUsageAndPage {
uint16 usage;
HidPage usage_page;
uint16 usage_page;
};
struct HidCollectionInfo {
@ -70,3 +34,43 @@ struct HidDeviceInfo {
uint64 max_feature_report_size;
string device_node;
};
interface HidManagerClient{
// Notifies the client that a device is added.
DeviceAdded(HidDeviceInfo device_info);
// Notifies the client that a device is being removed, called before
// removing the device from HidService.
DeviceRemoved(HidDeviceInfo device_info);
};
interface HidManager {
// Enumerates available devices and set as a client of HidManager.
// The implementation of HidManager guarantees that the returned callback
// will always be posted earlier than DeviceAdded() and DeviceRemoved().
GetDevicesAndSetClient(associated HidManagerClient client) =>
(array<HidDeviceInfo> devices);
// Enumerates available devices only.
GetDevices() => (array<HidDeviceInfo> devices);
// Opens a connection to a device by given guid. The callback will be run
// with null on failure.
Connect(string device_guid) => (HidConnection? connection);
};
interface HidConnection {
// The report_id is returned as 0 if not supported by the device.
Read() => (bool success, uint8 report_id, array<uint8>? buffer);
// Pass the report_id as 0 if not supported by the device.
Write(uint8 report_id, array<uint8> buffer) => (bool success);
// The buffer will contain whatever report data was received from the device.
// This may include the report ID. The report ID is not stripped because a
// device may respond with other data in place of the report ID.
GetFeatureReport(uint8 report_id) => (bool success, array<uint8>? buffer);
// Pass the report_id as 0 if not supported by the device.
SendFeatureReport(uint8 report_id, array<uint8> buffer) => (bool success);
};

@ -6,202 +6,13 @@
namespace mojo {
// static
device::mojom::HidPage
EnumTraits<device::mojom::HidPage, device::HidUsageAndPage::Page>::ToMojom(
device::HidUsageAndPage::Page input) {
switch (input) {
case device::HidUsageAndPage::Page::kPageUndefined:
return device::mojom::HidPage::PageUndefined;
case device::HidUsageAndPage::Page::kPageGenericDesktop:
return device::mojom::HidPage::PageGenericDesktop;
case device::HidUsageAndPage::Page::kPageSimulation:
return device::mojom::HidPage::PageSimulation;
case device::HidUsageAndPage::Page::kPageVirtualReality:
return device::mojom::HidPage::PageVirtualReality;
case device::HidUsageAndPage::Page::kPageSport:
return device::mojom::HidPage::PageSport;
case device::HidUsageAndPage::Page::kPageGame:
return device::mojom::HidPage::PageGame;
case device::HidUsageAndPage::Page::kPageKeyboard:
return device::mojom::HidPage::PageKeyboard;
case device::HidUsageAndPage::Page::kPageLed:
return device::mojom::HidPage::PageLed;
case device::HidUsageAndPage::Page::kPageButton:
return device::mojom::HidPage::PageButton;
case device::HidUsageAndPage::Page::kPageOrdinal:
return device::mojom::HidPage::PageOrdinal;
case device::HidUsageAndPage::Page::kPageTelephony:
return device::mojom::HidPage::PageTelephony;
case device::HidUsageAndPage::Page::kPageConsumer:
return device::mojom::HidPage::PageConsumer;
case device::HidUsageAndPage::Page::kPageDigitizer:
return device::mojom::HidPage::PageDigitizer;
case device::HidUsageAndPage::Page::kPagePidPage:
return device::mojom::HidPage::PagePidPage;
case device::HidUsageAndPage::Page::kPageUnicode:
return device::mojom::HidPage::PageUnicode;
case device::HidUsageAndPage::Page::kPageAlphanumericDisplay:
return device::mojom::HidPage::PageAlphanumericDisplay;
case device::HidUsageAndPage::Page::kPageMedicalInstruments:
return device::mojom::HidPage::PageMedicalInstruments;
case device::HidUsageAndPage::Page::kPageMonitor0:
return device::mojom::HidPage::PageMonitor0;
case device::HidUsageAndPage::Page::kPageMonitor1:
return device::mojom::HidPage::PageMonitor1;
case device::HidUsageAndPage::Page::kPageMonitor2:
return device::mojom::HidPage::PageMonitor2;
case device::HidUsageAndPage::Page::kPageMonitor3:
return device::mojom::HidPage::PageMonitor3;
case device::HidUsageAndPage::Page::kPagePower0:
return device::mojom::HidPage::PagePower0;
case device::HidUsageAndPage::Page::kPagePower1:
return device::mojom::HidPage::PagePower1;
case device::HidUsageAndPage::Page::kPagePower2:
return device::mojom::HidPage::PagePower2;
case device::HidUsageAndPage::Page::kPagePower3:
return device::mojom::HidPage::PagePower3;
case device::HidUsageAndPage::Page::kPageBarCodeScanner:
return device::mojom::HidPage::PageBarCodeScanner;
case device::HidUsageAndPage::Page::kPageScale:
return device::mojom::HidPage::PageScale;
case device::HidUsageAndPage::Page::kPageMagneticStripeReader:
return device::mojom::HidPage::PageMagneticStripeReader;
case device::HidUsageAndPage::Page::kPageReservedPointOfSale:
return device::mojom::HidPage::PageReservedPointOfSale;
case device::HidUsageAndPage::Page::kPageCameraControl:
return device::mojom::HidPage::PageCameraControl;
case device::HidUsageAndPage::Page::kPageArcade:
return device::mojom::HidPage::PageArcade;
case device::HidUsageAndPage::Page::kPageVendor:
return device::mojom::HidPage::PageVendor;
case device::HidUsageAndPage::Page::kPageMediaCenter:
return device::mojom::HidPage::PageMediaCenter;
}
NOTREACHED();
return device::mojom::HidPage::PageUndefined;
}
// static
bool EnumTraits<device::mojom::HidPage, device::HidUsageAndPage::Page>::
FromMojom(device::mojom::HidPage input,
device::HidUsageAndPage::Page* output) {
switch (input) {
case device::mojom::HidPage::PageUndefined:
*output = device::HidUsageAndPage::Page::kPageUndefined;
return true;
case device::mojom::HidPage::PageGenericDesktop:
*output = device::HidUsageAndPage::Page::kPageGenericDesktop;
return true;
case device::mojom::HidPage::PageSimulation:
*output = device::HidUsageAndPage::Page::kPageSimulation;
return true;
case device::mojom::HidPage::PageVirtualReality:
*output = device::HidUsageAndPage::Page::kPageVirtualReality;
return true;
case device::mojom::HidPage::PageSport:
*output = device::HidUsageAndPage::Page::kPageSport;
return true;
case device::mojom::HidPage::PageGame:
*output = device::HidUsageAndPage::Page::kPageGame;
return true;
case device::mojom::HidPage::PageKeyboard:
*output = device::HidUsageAndPage::Page::kPageKeyboard;
return true;
case device::mojom::HidPage::PageLed:
*output = device::HidUsageAndPage::Page::kPageLed;
return true;
case device::mojom::HidPage::PageButton:
*output = device::HidUsageAndPage::Page::kPageButton;
return true;
case device::mojom::HidPage::PageOrdinal:
*output = device::HidUsageAndPage::Page::kPageOrdinal;
return true;
case device::mojom::HidPage::PageTelephony:
*output = device::HidUsageAndPage::Page::kPageTelephony;
return true;
case device::mojom::HidPage::PageConsumer:
*output = device::HidUsageAndPage::Page::kPageConsumer;
return true;
case device::mojom::HidPage::PageDigitizer:
*output = device::HidUsageAndPage::Page::kPageDigitizer;
return true;
case device::mojom::HidPage::PagePidPage:
*output = device::HidUsageAndPage::Page::kPagePidPage;
return true;
case device::mojom::HidPage::PageUnicode:
*output = device::HidUsageAndPage::Page::kPageUnicode;
return true;
case device::mojom::HidPage::PageAlphanumericDisplay:
*output = device::HidUsageAndPage::Page::kPageAlphanumericDisplay;
return true;
case device::mojom::HidPage::PageMedicalInstruments:
*output = device::HidUsageAndPage::Page::kPageMedicalInstruments;
return true;
case device::mojom::HidPage::PageMonitor0:
*output = device::HidUsageAndPage::Page::kPageMonitor0;
return true;
case device::mojom::HidPage::PageMonitor1:
*output = device::HidUsageAndPage::Page::kPageMonitor1;
return true;
case device::mojom::HidPage::PageMonitor2:
*output = device::HidUsageAndPage::Page::kPageMonitor2;
return true;
case device::mojom::HidPage::PageMonitor3:
*output = device::HidUsageAndPage::Page::kPageMonitor3;
return true;
case device::mojom::HidPage::PagePower0:
*output = device::HidUsageAndPage::Page::kPagePower0;
return true;
case device::mojom::HidPage::PagePower1:
*output = device::HidUsageAndPage::Page::kPagePower1;
return true;
case device::mojom::HidPage::PagePower2:
*output = device::HidUsageAndPage::Page::kPagePower2;
return true;
case device::mojom::HidPage::PagePower3:
*output = device::HidUsageAndPage::Page::kPagePower3;
return true;
case device::mojom::HidPage::PageBarCodeScanner:
*output = device::HidUsageAndPage::Page::kPageBarCodeScanner;
return true;
case device::mojom::HidPage::PageScale:
*output = device::HidUsageAndPage::Page::kPageScale;
return true;
case device::mojom::HidPage::PageMagneticStripeReader:
*output = device::HidUsageAndPage::Page::kPageMagneticStripeReader;
return true;
case device::mojom::HidPage::PageReservedPointOfSale:
*output = device::HidUsageAndPage::Page::kPageReservedPointOfSale;
return true;
case device::mojom::HidPage::PageCameraControl:
*output = device::HidUsageAndPage::Page::kPageCameraControl;
return true;
case device::mojom::HidPage::PageArcade:
*output = device::HidUsageAndPage::Page::kPageArcade;
return true;
case device::mojom::HidPage::PageVendor:
*output = device::HidUsageAndPage::Page::kPageVendor;
return true;
case device::mojom::HidPage::PageMediaCenter:
*output = device::HidUsageAndPage::Page::kPageMediaCenter;
return true;
}
NOTREACHED();
return false;
}
// static
bool StructTraits<
device::mojom::HidUsageAndPageDataView,
device::HidUsageAndPage>::Read(device::mojom::HidUsageAndPageDataView data,
device::HidUsageAndPage* out) {
out->usage = data.usage();
if (!data.ReadUsagePage(&out->usage_page))
return false;
out->usage_page = data.usage_page();
return true;
}

@ -15,19 +15,11 @@
namespace mojo {
template <>
struct EnumTraits<device::mojom::HidPage, device::HidUsageAndPage::Page> {
static device::mojom::HidPage ToMojom(device::HidUsageAndPage::Page input);
static bool FromMojom(device::mojom::HidPage input,
device::HidUsageAndPage::Page* output);
};
template <>
struct StructTraits<device::mojom::HidUsageAndPageDataView,
device::HidUsageAndPage> {
static uint16_t usage(const device::HidUsageAndPage& r) { return r.usage; }
static const device::HidUsageAndPage::Page& usage_page(
const device::HidUsageAndPage& r) {
static uint16_t usage_page(const device::HidUsageAndPage& r) {
return r.usage_page;
}
static bool Read(device::mojom::HidUsageAndPageDataView data,

@ -366,6 +366,7 @@ source_set("browser_sources") {
"//extensions/strings",
"//google_apis",
"//ppapi/features",
"//services/device/public/interfaces",
"//services/preferences/public/cpp",
"//services/service_manager/public/cpp",
"//ui/display",
@ -433,7 +434,7 @@ source_set("browser_tests") {
"//content/test:test_support",
"//device/base:mocks",
"//device/bluetooth:mocks",
"//device/hid:mocks",
"//device/hid",
"//device/usb:test_support",
"//extensions:test_support",
"//extensions/common",
@ -441,6 +442,8 @@ source_set("browser_tests") {
"//extensions/shell:app_shell_lib",
"//extensions/shell:browser_tests",
"//net:test_support",
"//services/device/public/interfaces",
"//services/service_manager/public/cpp",
]
if (is_mac) {

@ -12,9 +12,10 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
#include "device/base/device_client.h"
#include "device/hid/hid_device_filter.h"
#include "device/hid/hid_service.h"
#include "device/hid/public/interfaces/hid.mojom.h"
#include "device/usb/public/cpp/filter_utils.h"
#include "device/usb/usb_device.h"
@ -22,6 +23,10 @@
#include "device/usb/usb_service.h"
#include "extensions/browser/api/device_permissions_manager.h"
#include "extensions/common/extension.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/device/public/interfaces/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "ui/base/l10n/l10n_util.h"
#if defined(OS_CHROMEOS)
@ -30,7 +35,6 @@
#endif // defined(OS_CHROMEOS)
using device::HidDeviceFilter;
using device::HidService;
using device::UsbDevice;
using device::mojom::UsbDeviceFilterPtr;
using device::UsbService;
@ -176,7 +180,7 @@ class HidDeviceInfo : public DevicePermissionsPrompt::Prompt::DeviceInfo {
};
class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
public device::HidService::Observer {
public device::mojom::HidManagerClient {
public:
HidDevicePermissionsPrompt(
const Extension* extension,
@ -185,9 +189,10 @@ class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
const std::vector<HidDeviceFilter>& filters,
const DevicePermissionsPrompt::HidDevicesCallback& callback)
: Prompt(extension, context, multiple),
initialized_(false),
filters_(filters),
callback_(callback),
service_observer_(this) {}
binding_(this) {}
private:
~HidDevicePermissionsPrompt() override {}
@ -197,13 +202,31 @@ class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
DevicePermissionsPrompt::Prompt::Observer* observer) override {
DevicePermissionsPrompt::Prompt::SetObserver(observer);
if (observer) {
HidService* service = device::DeviceClient::Get()->GetHidService();
if (service && !service_observer_.IsObserving(service)) {
service->GetDevices(base::Bind(
&HidDevicePermissionsPrompt::OnDevicesEnumerated, this, service));
}
if (observer)
LazyInitialize();
}
void LazyInitialize() {
if (initialized_) {
return;
}
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(content::ServiceManagerConnection::GetForProcess());
service_manager::Connector* connector =
content::ServiceManagerConnection::GetForProcess()->GetConnector();
connector->BindInterface(device::mojom::kServiceName,
mojo::MakeRequest(&hid_manager_));
device::mojom::HidManagerClientAssociatedPtrInfo client;
binding_.Bind(mojo::MakeRequest(&client));
hid_manager_->GetDevicesAndSetClient(
std::move(client),
base::BindOnce(&HidDevicePermissionsPrompt::OnDevicesEnumerated, this));
initialized_ = true;
}
void Dismissed() override {
@ -226,8 +249,8 @@ class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
callback_.Reset();
}
// device::HidService::Observer implementation:
void OnDeviceAdded(device::mojom::HidDeviceInfoPtr device) override {
// device::mojom::HidManagerClient implementation:
void DeviceAdded(device::mojom::HidDeviceInfoPtr device) override {
if (HasUnprotectedCollections(*device) &&
(filters_.empty() || HidDeviceFilter::MatchesAny(*device, filters_))) {
auto device_info = base::MakeUnique<HidDeviceInfo>(std::move(device));
@ -245,7 +268,7 @@ class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
}
}
void OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device) override {
void DeviceRemoved(device::mojom::HidDeviceInfoPtr device) override {
for (auto it = devices_.begin(); it != devices_.end(); ++it) {
HidDeviceInfo* entry = static_cast<HidDeviceInfo*>((*it).get());
if (entry->device()->guid == device->guid) {
@ -260,12 +283,9 @@ class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
}
void OnDevicesEnumerated(
HidService* service,
std::vector<device::mojom::HidDeviceInfoPtr> devices) {
for (auto& device : devices) {
OnDeviceAdded(std::move(device));
}
service_observer_.Add(service);
for (auto& device : devices)
DeviceAdded(std::move(device));
}
bool HasUnprotectedCollections(const device::mojom::HidDeviceInfo& device) {
@ -277,9 +297,11 @@ class HidDevicePermissionsPrompt : public DevicePermissionsPrompt::Prompt,
return false;
}
bool initialized_;
std::vector<HidDeviceFilter> filters_;
device::mojom::HidManagerPtr hid_manager_;
DevicePermissionsPrompt::HidDevicesCallback callback_;
ScopedObserver<HidService, HidService::Observer> service_observer_;
mojo::AssociatedBinding<device::mojom::HidManagerClient> binding_;
};
} // namespace

@ -12,21 +12,16 @@
#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "device/base/device_client.h"
#include "device/hid/hid_connection.h"
#include "device/hid/hid_device_filter.h"
#include "device/hid/hid_service.h"
#include "extensions/browser/api/api_resource_manager.h"
#include "extensions/browser/api/device_permissions_prompt.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/common/api/hid.h"
#include "net/base/io_buffer.h"
#include "extensions/utility/scoped_callback_runner.h"
namespace hid = extensions::api::hid;
using device::HidConnection;
using device::HidDeviceFilter;
using device::HidService;
namespace {
@ -36,9 +31,7 @@ const char kErrorFailedToOpenDevice[] = "Failed to open HID device.";
const char kErrorConnectionNotFound[] = "Connection not established.";
const char kErrorTransfer[] = "Transfer failed.";
std::unique_ptr<base::Value> PopulateHidConnection(
int connection_id,
scoped_refptr<HidConnection> connection) {
std::unique_ptr<base::Value> PopulateHidConnection(int connection_id) {
hid::HidConnectInfo connection_value;
connection_value.connection_id = connection_id;
return connection_value.ToValue();
@ -176,17 +169,14 @@ ExtensionFunction::ResponseAction HidConnectFunction::Run() {
return RespondNow(Error(kErrorPermissionDenied));
}
HidService* hid_service = device::DeviceClient::Get()->GetHidService();
CHECK(hid_service);
hid_service->Connect(
device_manager->Connect(
device_info->guid,
base::Bind(&HidConnectFunction::OnConnectComplete, this));
base::BindOnce(&HidConnectFunction::OnConnectComplete, this));
return RespondLater();
}
void HidConnectFunction::OnConnectComplete(
scoped_refptr<HidConnection> connection) {
device::mojom::HidConnectionPtr connection) {
if (!connection) {
Respond(Error(kErrorFailedToOpenDevice));
return;
@ -194,8 +184,8 @@ void HidConnectFunction::OnConnectComplete(
DCHECK(connection_manager_);
int connection_id = connection_manager_->Add(
new HidConnectionResource(extension_id(), connection));
Respond(OneArgument(PopulateHidConnection(connection_id, connection)));
new HidConnectionResource(extension_id(), std::move(connection)));
Respond(OneArgument(PopulateHidConnection(connection_id)));
}
HidDisconnectFunction::HidDisconnectFunction() {}
@ -241,7 +231,7 @@ ExtensionFunction::ResponseAction HidConnectionIoFunction::Run() {
return RespondNow(Error(kErrorConnectionNotFound));
}
StartWork(resource->connection().get());
StartWork(resource->connection());
return RespondLater();
}
@ -257,20 +247,24 @@ bool HidReceiveFunction::ReadParameters() {
return true;
}
void HidReceiveFunction::StartWork(HidConnection* connection) {
connection->Read(base::Bind(&HidReceiveFunction::OnFinished, this));
void HidReceiveFunction::StartWork(device::mojom::HidConnection* connection) {
connection->Read(ScopedCallbackRunner(
base::BindOnce(&HidReceiveFunction::OnFinished, this), false, 0,
base::nullopt));
}
void HidReceiveFunction::OnFinished(bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size) {
void HidReceiveFunction::OnFinished(
bool success,
uint8_t report_id,
const base::Optional<std::vector<uint8_t>>& buffer) {
if (success) {
DCHECK_GE(size, 1u);
int report_id = reinterpret_cast<uint8_t*>(buffer->data())[0];
DCHECK(buffer);
DCHECK_GE(buffer->size(), 0u);
Respond(TwoArguments(
std::make_unique<base::Value>(report_id),
base::Value::CreateWithCopiedBuffer(buffer->data() + 1, size - 1)));
base::Value::CreateWithCopiedBuffer(
reinterpret_cast<const char*>(buffer->data()), buffer->size())));
} else {
Respond(Error(kErrorTransfer));
}
@ -288,14 +282,14 @@ bool HidSendFunction::ReadParameters() {
return true;
}
void HidSendFunction::StartWork(HidConnection* connection) {
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(parameters_->data.size() + 1));
buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id);
memcpy(buffer->data() + 1, parameters_->data.data(),
parameters_->data.size());
connection->Write(buffer, buffer->size(),
base::Bind(&HidSendFunction::OnFinished, this));
void HidSendFunction::StartWork(device::mojom::HidConnection* connection) {
auto* data = reinterpret_cast<const uint8_t*>(parameters_->data.data());
std::vector<uint8_t> buffer(data, data + parameters_->data.size());
connection->Write(
static_cast<uint8_t>(parameters_->report_id), buffer,
ScopedCallbackRunner(base::BindOnce(&HidSendFunction::OnFinished, this),
false));
}
void HidSendFunction::OnFinished(bool success) {
@ -318,19 +312,22 @@ bool HidReceiveFeatureReportFunction::ReadParameters() {
return true;
}
void HidReceiveFeatureReportFunction::StartWork(HidConnection* connection) {
void HidReceiveFeatureReportFunction::StartWork(
device::mojom::HidConnection* connection) {
connection->GetFeatureReport(
static_cast<uint8_t>(parameters_->report_id),
base::Bind(&HidReceiveFeatureReportFunction::OnFinished, this));
ScopedCallbackRunner(
base::BindOnce(&HidReceiveFeatureReportFunction::OnFinished, this),
false, base::nullopt));
}
void HidReceiveFeatureReportFunction::OnFinished(
bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size) {
const base::Optional<std::vector<uint8_t>>& buffer) {
if (success) {
Respond(
OneArgument(base::Value::CreateWithCopiedBuffer(buffer->data(), size)));
DCHECK(buffer);
Respond(OneArgument(base::Value::CreateWithCopiedBuffer(
reinterpret_cast<const char*>(buffer->data()), buffer->size())));
} else {
Respond(Error(kErrorTransfer));
}
@ -348,15 +345,16 @@ bool HidSendFeatureReportFunction::ReadParameters() {
return true;
}
void HidSendFeatureReportFunction::StartWork(HidConnection* connection) {
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(parameters_->data.size() + 1));
buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id);
memcpy(buffer->data() + 1, parameters_->data.data(),
parameters_->data.size());
void HidSendFeatureReportFunction::StartWork(
device::mojom::HidConnection* connection) {
auto* data = reinterpret_cast<const uint8_t*>(parameters_->data.data());
std::vector<uint8_t> buffer(data, data + parameters_->data.size());
connection->SendFeatureReport(
buffer, buffer->size(),
base::Bind(&HidSendFeatureReportFunction::OnFinished, this));
static_cast<uint8_t>(parameters_->report_id), buffer,
ScopedCallbackRunner(
base::BindOnce(&HidSendFeatureReportFunction::OnFinished, this),
false));
}
void HidSendFeatureReportFunction::OnFinished(bool success) {

@ -19,14 +19,6 @@
#include "extensions/browser/extension_function.h"
#include "extensions/common/api/hid.h"
namespace device {
class HidConnection;
} // namespace device
namespace net {
class IOBuffer;
} // namespace net
namespace extensions {
class DevicePermissionsPrompt;
@ -80,7 +72,7 @@ class HidConnectFunction : public UIThreadExtensionFunction {
// ExtensionFunction:
ResponseAction Run() override;
void OnConnectComplete(scoped_refptr<device::HidConnection> connection);
void OnConnectComplete(device::mojom::HidConnectionPtr connection);
ApiResourceManager<HidConnectionResource>* connection_manager_;
@ -113,7 +105,7 @@ class HidConnectionIoFunction : public UIThreadExtensionFunction {
// Returns true if params were successfully read from |args_|.
virtual bool ReadParameters() = 0;
virtual void StartWork(device::HidConnection* connection) = 0;
virtual void StartWork(device::mojom::HidConnection* connection) = 0;
void set_connection_id(int connection_id) { connection_id_ = connection_id; }
@ -135,11 +127,11 @@ class HidReceiveFunction : public HidConnectionIoFunction {
// HidConnectionIoFunction:
bool ReadParameters() override;
void StartWork(device::HidConnection* connection) override;
void StartWork(device::mojom::HidConnection* connection) override;
void OnFinished(bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size);
uint8_t report_id,
const base::Optional<std::vector<uint8_t>>& buffer);
std::unique_ptr<api::hid::Receive::Params> parameters_;
@ -157,7 +149,7 @@ class HidSendFunction : public HidConnectionIoFunction {
// HidConnectionIoFunction:
bool ReadParameters() override;
void StartWork(device::HidConnection* connection) override;
void StartWork(device::mojom::HidConnection* connection) override;
void OnFinished(bool success);
@ -178,11 +170,10 @@ class HidReceiveFeatureReportFunction : public HidConnectionIoFunction {
// HidConnectionIoFunction:
bool ReadParameters() override;
void StartWork(device::HidConnection* connection) override;
void StartWork(device::mojom::HidConnection* connection) override;
void OnFinished(bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size);
const base::Optional<std::vector<uint8_t>>& buffer);
std::unique_ptr<api::hid::ReceiveFeatureReport::Params> parameters_;
@ -200,7 +191,7 @@ class HidSendFeatureReportFunction : public HidConnectionIoFunction {
// HidConnectionIoFunction:
bool ReadParameters() override;
void StartWork(device::HidConnection* connection) override;
void StartWork(device::mojom::HidConnection* connection) override;
void OnFinished(bool success);

@ -11,27 +11,24 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "device/base/mock_device_client.h"
#include "device/hid/hid_collection_info.h"
#include "device/hid/hid_connection.h"
#include "device/hid/hid_device_info.h"
#include "device/hid/hid_usage_and_page.h"
#include "device/hid/mock_hid_service.h"
#include "device/hid/public/interfaces/hid.mojom.h"
#include "extensions/browser/api/device_permissions_prompt.h"
#include "extensions/shell/browser/shell_extensions_api_client.h"
#include "extensions/shell/test/shell_apitest.h"
#include "extensions/test/extension_test_message_listener.h"
#include "net/base/io_buffer.h"
#include "mojo/public/cpp/bindings/binding_set.h"
#include "mojo/public/cpp/bindings/interface_ptr_set.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "services/device/public/interfaces/constants.mojom.h"
#include "services/service_manager/public/cpp/service_context.h"
using base::ThreadTaskRunnerHandle;
using device::HidCollectionInfo;
using device::HidDeviceInfo;
using device::HidPlatformDeviceId;
using device::HidUsageAndPage;
using device::MockDeviceClient;
using net::IOBuffer;
using testing::_;
#if defined(OS_MACOSX)
const uint64_t kTestDeviceIds[] = {1, 2, 3, 4, 5};
@ -54,75 +51,170 @@ const uint8_t kReportDescriptorWithIDs[] = {
namespace extensions {
class MockHidConnection : public device::HidConnection {
class FakeHidConnectionImpl : public device::mojom::HidConnection {
public:
explicit MockHidConnection(scoped_refptr<HidDeviceInfo> device_info)
: HidConnection(device_info) {}
explicit FakeHidConnectionImpl(scoped_refptr<device::HidDeviceInfo> device)
: device_(device) {}
void PlatformClose() override {}
~FakeHidConnectionImpl() override = default;
void PlatformRead(ReadCallback callback) override {
// device::mojom::HidConnection implemenation:
void Read(device::mojom::HidConnection::ReadCallback callback) override {
const char kResult[] = "This is a HID input report.";
uint8_t report_id = device_info()->has_report_id() ? 1 : 0;
scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
buffer->data()[0] = report_id;
memcpy(buffer->data() + 1, kResult, sizeof(kResult) - 1);
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), true, buffer, sizeof(kResult)));
uint8_t report_id = device_->has_report_id() ? 1 : 0;
std::vector<uint8_t> buffer(kResult, kResult + sizeof(kResult) - 1);
std::move(callback).Run(true, report_id, buffer);
}
void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
size_t size,
WriteCallback callback) override {
void Write(uint8_t report_id,
const std::vector<uint8_t>& buffer,
device::mojom::HidConnection::WriteCallback callback) override {
const char kExpected[] = "o-report"; // 8 bytes
bool result = false;
if (size == sizeof(kExpected)) {
uint8_t report_id = buffer->data()[0];
uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
if (report_id == expected_report_id) {
if (memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
result = true;
}
}
if (buffer.size() != sizeof(kExpected) - 1) {
std::move(callback).Run(false);
return;
}
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), result));
int expected_report_id = device_->has_report_id() ? 1 : 0;
if (report_id != expected_report_id) {
std::move(callback).Run(false);
return;
}
if (memcmp(buffer.data(), kExpected, sizeof(kExpected) - 1) != 0) {
std::move(callback).Run(false);
return;
}
std::move(callback).Run(true);
}
void PlatformGetFeatureReport(uint8_t report_id,
ReadCallback callback) override {
void GetFeatureReport(uint8_t report_id,
device::mojom::HidConnection::GetFeatureReportCallback
callback) override {
uint8_t expected_report_id = device_->has_report_id() ? 1 : 0;
if (report_id != expected_report_id) {
std::move(callback).Run(false, base::nullopt);
return;
}
const char kResult[] = "This is a HID feature report.";
scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
size_t offset = 0;
if (device_info()->has_report_id()) {
buffer->data()[offset++] = report_id;
}
memcpy(buffer->data() + offset, kResult, sizeof(kResult) - 1);
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), true, buffer,
sizeof(kResult) - 1 + offset));
std::vector<uint8_t> buffer;
if (device_->has_report_id())
buffer.push_back(report_id);
buffer.insert(buffer.end(), kResult, kResult + sizeof(kResult) - 1);
std::move(callback).Run(true, buffer);
}
void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
size_t size,
WriteCallback callback) override {
void SendFeatureReport(uint8_t report_id,
const std::vector<uint8_t>& buffer,
device::mojom::HidConnection::SendFeatureReportCallback
callback) override {
const char kExpected[] = "The app is setting this HID feature report.";
bool result = false;
if (size == sizeof(kExpected)) {
uint8_t report_id = buffer->data()[0];
uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
if (report_id == expected_report_id &&
memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
result = true;
}
if (buffer.size() != sizeof(kExpected) - 1) {
std::move(callback).Run(false);
return;
}
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), result));
int expected_report_id = device_->has_report_id() ? 1 : 0;
if (report_id != expected_report_id) {
std::move(callback).Run(false);
return;
}
if (memcmp(buffer.data(), kExpected, sizeof(kExpected) - 1) != 0) {
std::move(callback).Run(false);
return;
}
std::move(callback).Run(true);
}
private:
~MockHidConnection() override {}
scoped_refptr<device::HidDeviceInfo> device_;
};
class FakeHidManager : public device::mojom::HidManager {
public:
FakeHidManager() {}
~FakeHidManager() override = default;
void Bind(const std::string& interface_name,
mojo::ScopedMessagePipeHandle handle,
const service_manager::BindSourceInfo& source_info) {
bindings_.AddBinding(this,
device::mojom::HidManagerRequest(std::move(handle)));
}
// device::mojom::HidManager implementation:
void GetDevicesAndSetClient(
device::mojom::HidManagerClientAssociatedPtrInfo client,
device::mojom::HidManager::GetDevicesCallback callback) override {
std::vector<device::mojom::HidDeviceInfoPtr> device_list;
for (auto& map_entry : devices_)
device_list.push_back(map_entry.second->device()->Clone());
std::move(callback).Run(std::move(device_list));
device::mojom::HidManagerClientAssociatedPtr client_ptr;
client_ptr.Bind(std::move(client));
clients_.AddPtr(std::move(client_ptr));
}
void GetDevices(
device::mojom::HidManager::GetDevicesCallback callback) override {
// Clients of HidManager in extensions only use GetDevicesAndSetClient().
NOTREACHED();
}
void Connect(const std::string& device_guid,
device::mojom::HidManager::ConnectCallback callback) override {
scoped_refptr<device::HidDeviceInfo> device = devices_[device_guid];
// Strong binds a instance of FakeHidConnctionImpl.
device::mojom::HidConnectionPtr client;
mojo::MakeStrongBinding(base::MakeUnique<FakeHidConnectionImpl>(device),
mojo::MakeRequest(&client));
std::move(callback).Run(std::move(client));
}
void AddDevice(scoped_refptr<device::HidDeviceInfo> device) {
devices_[device->device_guid()] = device;
device::mojom::HidDeviceInfo* device_info = device->device().get();
clients_.ForAllPtrs([device_info](device::mojom::HidManagerClient* client) {
client->DeviceAdded(device_info->Clone());
});
}
void RemoveDevice(const HidPlatformDeviceId& platform_device_id) {
std::string guid;
scoped_refptr<device::HidDeviceInfo> device;
for (const auto& map_entry : devices_) {
if (map_entry.second->platform_device_id() == platform_device_id) {
guid = map_entry.first;
device = map_entry.second;
break;
}
}
if (!guid.empty()) {
device::mojom::HidDeviceInfo* device_info = device->device().get();
clients_.ForAllPtrs(
[device_info](device::mojom::HidManagerClient* client) {
client->DeviceRemoved(device_info->Clone());
});
devices_.erase(guid);
}
}
private:
std::map<std::string, scoped_refptr<device::HidDeviceInfo>> devices_;
mojo::AssociatedInterfacePtrSet<device::mojom::HidManagerClient> clients_;
mojo::BindingSet<device::mojom::HidManager> bindings_;
};
class TestDevicePermissionsPrompt
@ -180,34 +272,18 @@ class HidApiTest : public ShellApiTest {
void SetUpOnMainThread() override {
ShellApiTest::SetUpOnMainThread();
// MockDeviceClient replaces ShellDeviceClient.
device_client_.reset(new MockDeviceClient());
fake_hid_manager_ = base::MakeUnique<FakeHidManager>();
// Because Device Service also runs in this process(browser process), here
// we can directly set our binder to intercept interface requests against
// it.
service_manager::ServiceContext::SetGlobalBinderForTesting(
device::mojom::kServiceName, device::mojom::HidManager::Name_,
base::Bind(&FakeHidManager::Bind,
base::Unretained(fake_hid_manager_.get())));
ON_CALL(*device_client_->hid_service(), Connect(_, _))
.WillByDefault(Invoke(this, &HidApiTest::Connect));
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&HidApiTest::LazyFirstEnumeration, base::Unretained(this)));
}
void Connect(const std::string& device_guid,
const device::HidService::ConnectCallback& callback) {
const auto& devices = device_client_->hid_service()->devices();
const auto& device_entry = devices.find(device_guid);
scoped_refptr<MockHidConnection> connection;
if (device_entry != devices.end()) {
connection = new MockHidConnection(device_entry->second);
}
ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
base::Bind(callback, connection));
}
void LazyFirstEnumeration() {
AddDevice(kTestDeviceIds[0], 0x18D1, 0x58F0, false, "A");
AddDevice(kTestDeviceIds[1], 0x18D1, 0x58F0, true, "B");
AddDevice(kTestDeviceIds[2], 0x18D1, 0x58F1, false, "C");
device_client_->hid_service()->FirstEnumerationComplete();
}
void AddDevice(const HidPlatformDeviceId& platform_device_id,
@ -224,13 +300,15 @@ class HidApiTest : public ShellApiTest {
report_descriptor.insert(report_descriptor.begin(), kReportDescriptor,
kReportDescriptor + sizeof(kReportDescriptor));
}
device_client_->hid_service()->AddDevice(new HidDeviceInfo(
fake_hid_manager_->AddDevice(new device::HidDeviceInfo(
platform_device_id, vendor_id, product_id, "Test Device", serial_number,
device::mojom::HidBusType::kHIDBusTypeUSB, report_descriptor));
}
FakeHidManager* GetFakeHidManager() { return fake_hid_manager_.get(); }
protected:
std::unique_ptr<MockDeviceClient> device_client_;
std::unique_ptr<FakeHidManager> fake_hid_manager_;
};
IN_PROC_BROWSER_TEST_F(HidApiTest, HidApp) {
@ -263,9 +341,9 @@ IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceRemoved) {
// Device C was not returned by chrome.hid.getDevices, the app will not get
// a notification.
device_client_->hid_service()->RemoveDevice(kTestDeviceIds[2]);
GetFakeHidManager()->RemoveDevice(kTestDeviceIds[2]);
// Device A was returned, the app will get a notification.
device_client_->hid_service()->RemoveDevice(kTestDeviceIds[0]);
GetFakeHidManager()->RemoveDevice(kTestDeviceIds[0]);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
EXPECT_EQ("success", result_listener.message());
}
@ -278,7 +356,7 @@ IN_PROC_BROWSER_TEST_F(HidApiTest, GetUserSelectedDevices) {
ASSERT_TRUE(open_listener.WaitUntilSatisfied());
ExtensionTestMessageListener remove_listener("removed", false);
device_client_->hid_service()->RemoveDevice(kTestDeviceIds[0]);
GetFakeHidManager()->RemoveDevice(kTestDeviceIds[0]);
ASSERT_TRUE(remove_listener.WaitUntilSatisfied());
ExtensionTestMessageListener add_listener("added", false);

@ -26,11 +26,10 @@ ApiResourceManager<HidConnectionResource>::GetFactoryInstance() {
HidConnectionResource::HidConnectionResource(
const std::string& owner_extension_id,
scoped_refptr<device::HidConnection> connection)
: ApiResource(owner_extension_id), connection_(connection) {}
device::mojom::HidConnectionPtr connection)
: ApiResource(owner_extension_id), connection_(std::move(connection)) {}
HidConnectionResource::~HidConnectionResource() {
connection_->Close();
}
bool HidConnectionResource::IsPersistent() const {

@ -25,19 +25,17 @@ class HidConnectionResource : public ApiResource {
content::BrowserThread::UI;
HidConnectionResource(const std::string& owner_extension_id,
scoped_refptr<device::HidConnection> connection);
device::mojom::HidConnectionPtr connection);
~HidConnectionResource() override;
scoped_refptr<device::HidConnection> connection() const {
return connection_;
}
device::mojom::HidConnection* connection() const { return connection_.get(); }
bool IsPersistent() const override;
static const char* service_name() { return "HidConnectionResourceManager"; }
private:
scoped_refptr<device::HidConnection> connection_;
device::mojom::HidConnectionPtr connection_;
DISALLOW_COPY_AND_ASSIGN(HidConnectionResource);
};

@ -15,12 +15,17 @@
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
#include "device/base/device_client.h"
#include "device/hid/hid_device_filter.h"
#include "device/hid/hid_service.h"
#include "extensions/browser/api/device_permissions_manager.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/permissions/usb_device_permission.h"
#include "extensions/utility/scoped_callback_runner.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/device/public/interfaces/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace hid = extensions::api::hid;
@ -93,9 +98,7 @@ struct HidDeviceManager::GetApiDevicesParams {
};
HidDeviceManager::HidDeviceManager(content::BrowserContext* context)
: browser_context_(context),
hid_service_observer_(this),
weak_factory_(this) {
: browser_context_(context), binding_(this), weak_factory_(this) {
event_router_ = EventRouter::Get(context);
if (event_router_) {
event_router_->RegisterObserver(this, hid::OnDeviceAdded::kEventName);
@ -162,6 +165,14 @@ const device::mojom::HidDeviceInfo* HidDeviceManager::GetDeviceInfo(
return device_iter->second.get();
}
void HidDeviceManager::Connect(const std::string& device_guid,
ConnectCallback callback) {
DCHECK(initialized_);
hid_manager_->Connect(device_guid,
ScopedCallbackRunner(std::move(callback), nullptr));
}
bool HidDeviceManager::HasPermission(
const Extension* extension,
const device::mojom::HidDeviceInfo& device_info,
@ -210,7 +221,7 @@ void HidDeviceManager::Shutdown() {
void HidDeviceManager::OnListenerAdded(const EventListenerInfo& details) {
LazyInitialize();
}
void HidDeviceManager::OnDeviceAdded(device::mojom::HidDeviceInfoPtr device) {
void HidDeviceManager::DeviceAdded(device::mojom::HidDeviceInfoPtr device) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_LT(next_resource_id_, std::numeric_limits<int>::max());
int new_id = next_resource_id_++;
@ -234,7 +245,7 @@ void HidDeviceManager::OnDeviceAdded(device::mojom::HidDeviceInfoPtr device) {
}
}
void HidDeviceManager::OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device) {
void HidDeviceManager::DeviceRemoved(device::mojom::HidDeviceInfoPtr device) {
DCHECK(thread_checker_.CalledOnValidThread());
const auto& resource_entry = resource_ids_.find(device->guid);
DCHECK(resource_entry != resource_ids_.end());
@ -266,10 +277,27 @@ void HidDeviceManager::LazyInitialize() {
return;
}
HidService* hid_service = device::DeviceClient::Get()->GetHidService();
DCHECK(hid_service);
hid_service->GetDevices(base::Bind(&HidDeviceManager::OnEnumerationComplete,
weak_factory_.GetWeakPtr(), hid_service));
DCHECK(!hid_manager_);
// |hid_manager_| is initialized and safe to use whether or not the
// connection is successful.
device::mojom::HidManagerRequest request = mojo::MakeRequest(&hid_manager_);
device::mojom::HidManagerClientAssociatedPtrInfo client;
binding_.Bind(mojo::MakeRequest(&client));
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(content::ServiceManagerConnection::GetForProcess());
auto* connector =
content::ServiceManagerConnection::GetForProcess()->GetConnector();
connector->BindInterface(device::mojom::kServiceName, std::move(request));
std::vector<device::mojom::HidDeviceInfoPtr> empty_devices;
hid_manager_->GetDevicesAndSetClient(
std::move(client),
ScopedCallbackRunner(
base::BindOnce(&HidDeviceManager::OnEnumerationComplete,
weak_factory_.GetWeakPtr()),
std::move(empty_devices)));
initialized_ = true;
}
@ -305,12 +333,12 @@ std::unique_ptr<base::ListValue> HidDeviceManager::CreateApiDeviceList(
}
void HidDeviceManager::OnEnumerationComplete(
HidService* hid_service,
std::vector<device::mojom::HidDeviceInfoPtr> devices) {
DCHECK(resource_ids_.empty());
DCHECK(devices_.empty());
for (auto& device_info : devices) {
OnDeviceAdded(std::move(device_info));
DeviceAdded(std::move(device_info));
}
enumeration_ready_ = true;
@ -320,8 +348,6 @@ void HidDeviceManager::OnEnumerationComplete(
params->callback.Run(std::move(devices));
}
pending_enumerations_.clear();
hid_service_observer_.Add(hid_service);
}
void HidDeviceManager::DispatchEvent(

@ -13,12 +13,12 @@
#include "base/memory/ref_counted.h"
#include "base/scoped_observer.h"
#include "base/threading/thread_checker.h"
#include "device/hid/hid_service.h"
#include "device/hid/public/interfaces/hid.mojom.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_event_histogram_value.h"
#include "extensions/common/api/hid.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
namespace device {
class HidDeviceFilter;
@ -28,15 +28,17 @@ namespace extensions {
class Extension;
// This service maps devices enumerated by device::HidService to resource IDs
// This class maps devices enumerated by device::HidManager to resource IDs
// returned by the chrome.hid API.
class HidDeviceManager : public BrowserContextKeyedAPI,
public device::HidService::Observer,
public device::mojom::HidManagerClient,
public EventRouter::Observer {
public:
typedef base::Callback<void(std::unique_ptr<base::ListValue>)>
GetApiDevicesCallback;
using ConnectCallback = device::mojom::HidManager::ConnectCallback;
explicit HidDeviceManager(content::BrowserContext* context);
~HidDeviceManager() override;
@ -63,16 +65,17 @@ class HidDeviceManager : public BrowserContextKeyedAPI,
const device::mojom::HidDeviceInfo* GetDeviceInfo(int resource_id);
void Connect(const std::string& device_guid, ConnectCallback callback);
// Checks if |extension| has permission to open |device_info|. Set
// |update_last_used| to update the timestamp in the DevicePermissionsManager.
bool HasPermission(const Extension* extension,
const device::mojom::HidDeviceInfo& device_info,
bool update_last_used);
// Wait to perform an initial enumeration and register a HidService::Observer
// until the first API customer makes a request or registers an event
// listener.
void LazyInitialize();
// Lazily perform an initial enumeration and set client to HidManager when
// the first API customer makes a request or registers an event listener.
virtual void LazyInitialize();
private:
friend class BrowserContextKeyedAPIFactory<HidDeviceManager>;
@ -94,9 +97,9 @@ class HidDeviceManager : public BrowserContextKeyedAPI,
// EventRouter::Observer:
void OnListenerAdded(const EventListenerInfo& details) override;
// HidService::Observer:
void OnDeviceAdded(device::mojom::HidDeviceInfoPtr device) override;
void OnDeviceRemoved(device::mojom::HidDeviceInfoPtr device) override;
// device::mojom::HidManagerClient implementation:
void DeviceAdded(device::mojom::HidDeviceInfoPtr device) override;
void DeviceRemoved(device::mojom::HidDeviceInfoPtr device) override;
// Builds a list of device info objects representing the currently enumerated
// devices, taking into account the permissions held by the given extension
@ -105,7 +108,6 @@ class HidDeviceManager : public BrowserContextKeyedAPI,
const Extension* extension,
const std::vector<device::HidDeviceFilter>& filters);
void OnEnumerationComplete(
device::HidService* hid_service,
std::vector<device::mojom::HidDeviceInfoPtr> devices);
void DispatchEvent(events::HistogramValue histogram_value,
@ -117,8 +119,8 @@ class HidDeviceManager : public BrowserContextKeyedAPI,
content::BrowserContext* browser_context_ = nullptr;
EventRouter* event_router_ = nullptr;
bool initialized_ = false;
ScopedObserver<device::HidService, device::HidService::Observer>
hid_service_observer_;
device::mojom::HidManagerPtr hid_manager_;
mojo::AssociatedBinding<device::mojom::HidManagerClient> binding_;
bool enumeration_ready_ = false;
std::vector<std::unique_ptr<GetApiDevicesParams>> pending_enumerations_;
int next_resource_id_ = 0;

@ -23,11 +23,4 @@ device::UsbService* ShellDeviceClient::GetUsbService() {
return usb_service_.get();
}
device::HidService* ShellDeviceClient::GetHidService() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!hid_service_)
hid_service_ = device::HidService::Create();
return hid_service_.get();
}
} // namespace extensions

@ -21,10 +21,8 @@ class ShellDeviceClient : device::DeviceClient {
// device::DeviceClient implementation
device::UsbService* GetUsbService() override;
device::HidService* GetHidService() override;
private:
std::unique_ptr<device::HidService> hid_service_;
std::unique_ptr<device::UsbService> usb_service_;
DISALLOW_COPY_AND_ASSIGN(ShellDeviceClient);

@ -27,6 +27,7 @@ source_set("lib") {
deps = [
"//base",
"//device/hid",
"//device/sensors",
"//device/sensors/public/interfaces",
"//services/device/fingerprint",

@ -12,6 +12,7 @@
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "device/hid/hid_manager_impl.h"
#include "device/sensors/device_sensor_host.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "services/device/fingerprint/fingerprint.h"
@ -84,6 +85,8 @@ DeviceService::~DeviceService() {
void DeviceService::OnStart() {
registry_.AddInterface<mojom::Fingerprint>(base::Bind(
&DeviceService::BindFingerprintRequest, base::Unretained(this)));
registry_.AddInterface<mojom::HidManager>(base::Bind(
&DeviceService::BindHidManagerRequest, base::Unretained(this)));
registry_.AddInterface<mojom::OrientationSensor>(base::Bind(
&DeviceService::BindOrientationSensorRequest, base::Unretained(this)));
registry_.AddInterface<mojom::OrientationAbsoluteSensor>(
@ -148,6 +151,12 @@ void DeviceService::BindVibrationManagerRequest(
}
#endif
void DeviceService::BindHidManagerRequest(mojom::HidManagerRequest request) {
if (!hid_manager_)
hid_manager_ = base::MakeUnique<HidManagerImpl>();
hid_manager_->AddBinding(std::move(request));
}
void DeviceService::BindFingerprintRequest(mojom::FingerprintRequest request) {
Fingerprint::Create(std::move(request));
}

@ -6,6 +6,7 @@
#define SERVICES_DEVICE_DEVICE_SERVICE_H_
#include "base/memory/ref_counted.h"
#include "device/hid/public/interfaces/hid.mojom.h"
#include "device/screen_orientation/public/interfaces/screen_orientation.mojom.h"
#include "device/sensors/public/interfaces/orientation.mojom.h"
#include "mojo/public/cpp/bindings/binding_set.h"
@ -33,6 +34,7 @@ class SingleThreadTaskRunner;
namespace device {
class HidManagerImpl;
class PowerMonitorMessageBroadcaster;
class TimeZoneMonitor;
@ -73,6 +75,8 @@ class DeviceService : public service_manager::Service {
void BindFingerprintRequest(mojom::FingerprintRequest request);
void BindHidManagerRequest(mojom::HidManagerRequest request);
void BindOrientationSensorRequest(mojom::OrientationSensorRequest request);
void BindOrientationAbsoluteSensorRequest(
@ -100,6 +104,7 @@ class DeviceService : public service_manager::Service {
void BindSerialIoHandlerRequest(mojom::SerialIoHandlerRequest request);
std::unique_ptr<HidManagerImpl> hid_manager_;
std::unique_ptr<PowerMonitorMessageBroadcaster>
power_monitor_message_broadcaster_;
std::unique_ptr<TimeZoneMonitor> time_zone_monitor_;

@ -5,6 +5,7 @@
"service_manager:connector": {
"provides": {
"device:battery_monitor": [ "device::mojom::BatteryMonitor" ],
"device:hid": [ "device::mojom::HidManager" ],
"device:fingerprint": [ "device::mojom::Fingerprint" ],
"device:generic_sensor": [ "device::mojom::SensorProvider" ],
"device:nfc": [ "device::mojom::NFCProvider" ],