blob: a542c8b46fecd4a5a3fb86ded7fd61388a96e8bd [file] [log] [blame]
// Copyright 2017 The Chromium OS 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 <midis/client.h>
#include <utility>
#include <base/bind.h>
#include <base/memory/ptr_util.h>
#include <base/posix/eintr_wrapper.h>
namespace midis {
namespace {
const int kMaxBufSize = 1024;
} // namespace
Client::Client(base::ScopedFD fd, DeviceTracker* device_tracker)
: client_fd_(std::move(fd)),
device_tracker_(device_tracker),
weak_factory_(this) {}
Client::~Client() { StopMonitoring(); }
std::unique_ptr<Client> Client::Create(base::ScopedFD fd,
DeviceTracker* device_tracker) {
auto cli = std::unique_ptr<Client>(new Client(std::move(fd), device_tracker));
if (!cli->StartMonitoring()) {
return nullptr;
}
LOG(INFO) << "New client created!";
return cli;
}
void Client::SendDevicesList() {
uint8_t payload_buf[kMaxBufSize];
struct MidisMessageHeader header;
uint32_t payload_size = PrepareDeviceListPayload(payload_buf, kMaxBufSize);
header.type = LIST_DEVICES_RESPONSE;
header.payload_size = payload_size;
int ret = HANDLE_EINTR(
write(client_fd_.get(), &header, sizeof(struct MidisMessageHeader)));
if (ret < 0) {
PLOG(ERROR) << "SendDevicesList() header: write to client_fd_failed.";
return;
}
ret = HANDLE_EINTR(write(client_fd_.get(), payload_buf, payload_size));
if (ret < 0) {
PLOG(ERROR) << "SendDevicesList() payload: write to client_fd_failed.";
}
}
uint32_t Client::PrepareDeviceListPayload(uint8_t* payload_buf,
size_t buf_len) {
std::vector<MidisDeviceInfo> list;
if (!device_tracker_) {
LOG(ERROR) << "Device tracker ptr is nullptr; something bad happened.";
return 0;
}
device_tracker_->ListDevices(&list);
memset(payload_buf, 0, buf_len);
// We'll fill num of devices once we are done.
uint8_t* payload_buf_ptr = &payload_buf[1];
uint8_t count = 0;
size_t bytes_written = 1;
// Prepare the payload to send back.
for (const auto& device : list) {
if (count == kMidisMaxDevices) {
LOG(WARNING) << "Number of devices exceeds max limit!.";
break;
}
// Make sure we avoid buffer overflows.
if (bytes_written + sizeof(struct MidisDeviceInfo) > buf_len) {
LOG(WARNING) << "Payload buffer can't accomodate more device entries.";
break;
}
count++;
bytes_written += sizeof(struct MidisDeviceInfo);
memcpy(payload_buf_ptr, &device, sizeof(struct MidisDeviceInfo));
payload_buf_ptr += sizeof(struct MidisDeviceInfo);
}
payload_buf[0] = count;
return bytes_written;
}
void Client::HandleClientMessages() {
int bytes;
struct MidisMessageHeader header;
if ((bytes = HANDLE_EINTR(read(client_fd_.get(), &header,
sizeof(struct MidisMessageHeader)))) > 0) {
switch (header.type) {
case REQUEST_LIST_DEVICES:
SendDevicesList();
break;
default:
LOG(ERROR) << "Unknown message: " << header.type;
break;
}
} else {
PLOG(ERROR) << "read() for client fd failed.";
}
}
bool Client::StartMonitoring() {
msg_taskid_ = brillo::MessageLoop::current()->WatchFileDescriptor(
FROM_HERE, client_fd_.get(), brillo::MessageLoop::kWatchRead, true,
base::Bind(&Client::HandleClientMessages, weak_factory_.GetWeakPtr()));
return msg_taskid_ != brillo::MessageLoop::kTaskIdNull;
}
void Client::StopMonitoring() {
brillo::MessageLoop::current()->CancelTask(msg_taskid_);
msg_taskid_ = brillo::MessageLoop::kTaskIdNull;
}
} // namespace midis