[go: nahoru, domu]

blob: 6290db8e296b736f43fb0f3a1e3faabbc762be15 [file] [log] [blame]
// Copyright 2014 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 <stddef.h>
#include <numeric>
#include "base/memory/ref_counted_memory.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/api/device_permissions_prompt.h"
#include "extensions/browser/api/usb/usb_api.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 "mojo/public/cpp/bindings/pending_remote.h"
#include "services/device/public/cpp/test/fake_usb_device_info.h"
#include "services/device/public/cpp/test/fake_usb_device_manager.h"
#include "services/device/public/cpp/test/mock_usb_mojo_device.h"
#include "services/device/public/mojom/usb_device.mojom.h"
using device::mojom::UsbControlTransferParams;
using device::mojom::UsbControlTransferRecipient;
using device::mojom::UsbControlTransferType;
using device::mojom::UsbIsochronousPacket;
using device::mojom::UsbOpenDeviceError;
using device::mojom::UsbTransferDirection;
using device::mojom::UsbTransferStatus;
using testing::_;
using testing::AnyNumber;
using testing::Invoke;
using testing::Return;
using testing::SaveArg;
namespace extensions {
namespace {
ACTION_TEMPLATE(InvokeCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(p1)) {
std::move(*std::get<k>(args)).Run(p1);
}
ACTION_TEMPLATE(BuildIsochronousTransferReturnValue,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_2_VALUE_PARAMS(transferred_length, success_packets)) {
const std::vector<uint32_t>& packet_lengths = std::get<k>(args);
std::vector<UsbIsochronousPacket> packets(packet_lengths.size());
for (size_t i = 0; i < packets.size(); ++i) {
packets[i].length = packet_lengths[i];
if (i < success_packets) {
packets[i].transferred_length = transferred_length;
packets[i].status = UsbTransferStatus::COMPLETED;
} else {
packets[i].transferred_length = 0;
packets[i].status = UsbTransferStatus::TRANSFER_ERROR;
}
}
return packets;
}
ACTION_P(SetConfiguration, fake_device) {
fake_device->SetActiveConfig(arg0);
std::move(*arg1).Run(true);
}
ACTION(InvokeClosureCallback) {
std::move(*arg0).Run();
}
MATCHER_P(BufferSizeIs, size, "") {
return arg.size() == size;
}
MATCHER_P(UsbControlTransferParamsEquals, expected, "") {
return arg.Equals(expected);
}
class TestDevicePermissionsPrompt
: public DevicePermissionsPrompt,
public DevicePermissionsPrompt::Prompt::Observer {
public:
explicit TestDevicePermissionsPrompt(content::WebContents* web_contents)
: DevicePermissionsPrompt(web_contents) {}
void ShowDialog() override { prompt()->SetObserver(this); }
void OnDevicesInitialized() override {
for (size_t i = 0; i < prompt()->GetDeviceCount(); ++i) {
prompt()->GrantDevicePermission(i);
if (!prompt()->multiple()) {
break;
}
}
prompt()->Dismissed();
}
void OnDeviceAdded(size_t index, const std::u16string& device_name) override {
}
void OnDeviceRemoved(size_t index,
const std::u16string& device_name) override {}
};
class TestExtensionsAPIClient : public ShellExtensionsAPIClient {
public:
TestExtensionsAPIClient() : ShellExtensionsAPIClient() {}
std::unique_ptr<DevicePermissionsPrompt> CreateDevicePermissionsPrompt(
content::WebContents* web_contents) const override {
return std::make_unique<TestDevicePermissionsPrompt>(web_contents);
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
bool ShouldAllowDetachingUsb(int vid, int pid) const override {
return vid == 1 && pid == 2;
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
};
class UsbApiTest : public ShellApiTest {
public:
void SetUpOnMainThread() override {
ShellApiTest::SetUpOnMainThread();
// Set fake USB device manager for extensions::UsbDeviceManager.
mojo::PendingRemote<device::mojom::UsbDeviceManager> usb_manager;
fake_usb_manager_.AddReceiver(usb_manager.InitWithNewPipeAndPassReceiver());
UsbDeviceManager::Get(browser_context())
->SetDeviceManagerForTesting(std::move(usb_manager));
base::RunLoop().RunUntilIdle();
std::vector<device::mojom::UsbConfigurationInfoPtr> configs;
configs.push_back(
device::FakeUsbDeviceInfo::CreateConfiguration(0xff, 0x00, 0x00, 1));
configs.push_back(
device::FakeUsbDeviceInfo::CreateConfiguration(0xff, 0x00, 0x00, 2));
fake_device_ = base::MakeRefCounted<device::FakeUsbDeviceInfo>(
0, 0, "Test Manufacturer", "Test Device", "ABC123", std::move(configs));
fake_usb_manager_.AddDevice(fake_device_);
fake_usb_manager_.SetMockForDevice(fake_device_->guid(), &mock_device_);
base::RunLoop().RunUntilIdle();
}
protected:
void SetActiveConfigForFakeDevice(uint8_t config_value) {
fake_device_->SetActiveConfig(config_value);
UsbDeviceManager::Get(browser_context())
->UpdateActiveConfig(fake_device_->guid(), config_value);
}
device::FakeUsbDeviceManager fake_usb_manager_;
scoped_refptr<device::FakeUsbDeviceInfo> fake_device_;
device::MockUsbMojoDevice mock_device_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(UsbApiTest, DeviceHandling) {
SetActiveConfigForFakeDevice(1);
EXPECT_CALL(mock_device_, OpenInternal(_))
.WillOnce(InvokeCallback<0>(UsbOpenDeviceError::OK))
.WillOnce(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, CloseInternal(_))
.WillOnce(InvokeClosureCallback())
.WillOnce(InvokeClosureCallback());
ASSERT_TRUE(RunAppTest("api_test/usb/device_handling"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, ResetDevice) {
EXPECT_CALL(mock_device_, OpenInternal(_))
.WillOnce(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, CloseInternal(_)).WillOnce(InvokeClosureCallback());
EXPECT_CALL(mock_device_, ResetInternal(_))
.WillOnce(InvokeCallback<0>(true))
.WillOnce(InvokeCallback<0>(false));
EXPECT_CALL(mock_device_,
GenericTransferOutInternal(2, BufferSizeIs(1u), _, _))
.WillOnce(InvokeCallback<3>(UsbTransferStatus::COMPLETED));
ASSERT_TRUE(RunAppTest("api_test/usb/reset_device"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, SetConfiguration) {
EXPECT_CALL(mock_device_, OpenInternal(_))
.WillOnce(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, CloseInternal(_)).WillOnce(InvokeClosureCallback());
EXPECT_CALL(mock_device_, SetConfigurationInternal(1, _))
.WillOnce(SetConfiguration(fake_device_.get()));
ASSERT_TRUE(RunAppTest("api_test/usb/set_configuration"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, ListInterfaces) {
SetActiveConfigForFakeDevice(1);
EXPECT_CALL(mock_device_, OpenInternal(_))
.WillOnce(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, CloseInternal(_)).WillOnce(InvokeClosureCallback());
ASSERT_TRUE(RunAppTest("api_test/usb/list_interfaces"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferEvent) {
auto expectedParams = UsbControlTransferParams::New();
expectedParams->type = UsbControlTransferType::STANDARD;
expectedParams->recipient = UsbControlTransferRecipient::DEVICE;
expectedParams->request = 1;
expectedParams->value = 2;
expectedParams->index = 3;
EXPECT_CALL(mock_device_, OpenInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, ControlTransferOutInternal(
UsbControlTransferParamsEquals(*expectedParams),
BufferSizeIs(1u), _, _))
.WillOnce(InvokeCallback<3>(UsbTransferStatus::COMPLETED));
EXPECT_CALL(mock_device_,
GenericTransferOutInternal(1, BufferSizeIs(1u), _, _))
.WillOnce(InvokeCallback<3>(UsbTransferStatus::COMPLETED));
EXPECT_CALL(mock_device_,
GenericTransferOutInternal(2, BufferSizeIs(1u), _, _))
.WillOnce(InvokeCallback<3>(UsbTransferStatus::COMPLETED));
EXPECT_CALL(mock_device_, IsochronousTransferOutInternal(3, _, _, _))
.WillOnce(BuildIsochronousTransferReturnValue<2>(1, 1u));
EXPECT_CALL(mock_device_, CloseInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeClosureCallback());
ASSERT_TRUE(RunAppTest("api_test/usb/transfer_event"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, ZeroLengthTransfer) {
EXPECT_CALL(mock_device_, OpenInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_,
GenericTransferOutInternal(_, BufferSizeIs(0u), _, _))
.WillOnce(InvokeCallback<3>(UsbTransferStatus::COMPLETED));
EXPECT_CALL(mock_device_, CloseInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeClosureCallback());
ASSERT_TRUE(RunAppTest("api_test/usb/zero_length_transfer"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferFailure) {
EXPECT_CALL(mock_device_, OpenInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, GenericTransferOutInternal(1, _, _, _))
.WillOnce(InvokeCallback<3>(UsbTransferStatus::COMPLETED))
.WillOnce(InvokeCallback<3>(UsbTransferStatus::TRANSFER_ERROR))
.WillOnce(InvokeCallback<3>(UsbTransferStatus::TIMEOUT));
EXPECT_CALL(mock_device_, IsochronousTransferInInternal(2, _, _))
.WillOnce(BuildIsochronousTransferReturnValue<1>(8, 10u))
.WillOnce(BuildIsochronousTransferReturnValue<1>(8, 5u));
EXPECT_CALL(mock_device_, CloseInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeClosureCallback());
ASSERT_TRUE(RunAppTest("api_test/usb/transfer_failure"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidLengthTransfer) {
EXPECT_CALL(mock_device_, OpenInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, CloseInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeClosureCallback());
ASSERT_TRUE(RunAppTest("api_test/usb/invalid_length_transfer"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidTimeout) {
EXPECT_CALL(mock_device_, OpenInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, CloseInternal(_))
.Times(AnyNumber())
.WillRepeatedly(InvokeClosureCallback());
ASSERT_TRUE(RunAppTest("api_test/usb/invalid_timeout"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, CallsAfterDisconnect) {
ExtensionTestMessageListener ready_listener("ready", false);
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
EXPECT_CALL(mock_device_, OpenInternal(_))
.WillOnce(InvokeCallback<0>(UsbOpenDeviceError::OK));
ASSERT_TRUE(LoadApp("api_test/usb/calls_after_disconnect"));
ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
fake_usb_manager_.RemoveDevice(fake_device_);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferFailureOnDisconnect) {
ExtensionTestMessageListener ready_listener("ready", false);
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
EXPECT_CALL(mock_device_, OpenInternal(_))
.WillOnce(InvokeCallback<0>(UsbOpenDeviceError::OK));
device::mojom::UsbDevice::GenericTransferInCallback saved_callback;
EXPECT_CALL(mock_device_, GenericTransferInInternal(_, _, _, _))
.WillOnce(
[&saved_callback](
uint8_t endpoint_number, uint32_t length, uint32_t timeout,
device::MockUsbMojoDevice::GenericTransferInCallback* callback) {
saved_callback = std::move(*callback);
});
ASSERT_TRUE(LoadApp("api_test/usb/transfer_failure_on_disconnect"));
ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
fake_usb_manager_.RemoveDevice(fake_device_);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceAdded) {
ExtensionTestMessageListener load_listener("loaded", false);
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
ASSERT_TRUE(LoadApp("api_test/usb/add_event"));
ASSERT_TRUE(load_listener.WaitUntilSatisfied());
fake_usb_manager_.CreateAndAddDevice(0x18D1, 0x58F0);
fake_usb_manager_.CreateAndAddDevice(0x18D1, 0x58F1);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceRemoved) {
ExtensionTestMessageListener load_listener("loaded", false);
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
ASSERT_TRUE(LoadApp("api_test/usb/remove_event"));
ASSERT_TRUE(load_listener.WaitUntilSatisfied());
fake_usb_manager_.RemoveDevice(fake_device_);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, GetUserSelectedDevices) {
ExtensionTestMessageListener ready_listener("opened_device", false);
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
EXPECT_CALL(mock_device_, OpenInternal(_))
.WillOnce(InvokeCallback<0>(UsbOpenDeviceError::OK));
EXPECT_CALL(mock_device_, CloseInternal(_)).WillOnce(InvokeClosureCallback());
TestExtensionsAPIClient test_api_client;
ASSERT_TRUE(LoadApp("api_test/usb/get_user_selected_devices"));
ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
fake_usb_manager_.RemoveDevice(fake_device_);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(UsbApiTest, MassStorage) {
ExtensionTestMessageListener ready_listener("ready", false);
ready_listener.set_failure_message("failure");
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
// Mass storage devices should be hidden unless allowed in policy.
// The TestExtensionsAPIClient allows only vid=1, pid=2.
TestExtensionsAPIClient test_api_client;
std::vector<device::mojom::UsbConfigurationInfoPtr> storage_configs;
auto storage_config = device::FakeUsbDeviceInfo::CreateConfiguration(
/* mass storage */ 0x08, 0x06, 0x50);
storage_configs.push_back(storage_config->Clone());
device::mojom::UsbDeviceInfoPtr device_1 =
fake_usb_manager_.CreateAndAddDevice(0x1, 0x2, 0x00,
std::move(storage_configs));
storage_configs.clear();
storage_configs.push_back(storage_config->Clone());
device::mojom::UsbDeviceInfoPtr device_2 =
fake_usb_manager_.CreateAndAddDevice(0x5, 0x6, 0x00,
std::move(storage_configs));
ASSERT_TRUE(LoadApp("api_test/usb/mass_storage"));
ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
fake_usb_manager_.RemoveDevice(device_2->guid);
fake_usb_manager_.RemoveDevice(device_1->guid);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
} // namespace extensions