blob: 367190144b8e7c5af54140b81db86d6f59875366 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/api/serial/serial_connection.h"
#include <string>
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/strings/string_util.h"
#include "chrome/common/extensions/api/serial.h"
#include "extensions/browser/api/api_resource_manager.h"
namespace extensions {
namespace {
const int kDefaultBufferSize = 4096;
}
static base::LazyInstance<
BrowserContextKeyedAPIFactory<ApiResourceManager<SerialConnection> > >
g_factory = LAZY_INSTANCE_INITIALIZER;
// static
template <>
BrowserContextKeyedAPIFactory<ApiResourceManager<SerialConnection> >*
ApiResourceManager<SerialConnection>::GetFactoryInstance() {
return g_factory.Pointer();
}
SerialConnection::SerialConnection(const std::string& port,
const std::string& owner_extension_id)
: ApiResource(owner_extension_id),
port_(port),
persistent_(false),
buffer_size_(kDefaultBufferSize),
receive_timeout_(0),
send_timeout_(0),
paused_(false),
io_handler_(SerialIoHandler::Create()) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
SerialConnection::~SerialConnection() {
DCHECK(open_complete_.is_null());
io_handler_->CancelRead(api::serial::RECEIVE_ERROR_DISCONNECTED);
io_handler_->CancelWrite(api::serial::SEND_ERROR_DISCONNECTED);
Close();
}
bool SerialConnection::IsPersistent() const { return persistent(); }
void SerialConnection::set_buffer_size(int buffer_size) {
buffer_size_ = buffer_size;
}
void SerialConnection::set_receive_timeout(int receive_timeout) {
receive_timeout_ = receive_timeout;
}
void SerialConnection::set_send_timeout(int send_timeout) {
send_timeout_ = send_timeout;
}
void SerialConnection::set_paused(bool paused) {
paused_ = paused;
if (paused) {
io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
}
}
void SerialConnection::Open(const OpenCompleteCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(open_complete_.is_null());
open_complete_ = callback;
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&SerialConnection::StartOpen, base::Unretained(this)));
}
void SerialConnection::Close() {
DCHECK(open_complete_.is_null());
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (file_.IsValid()) {
BrowserThread::PostTask(
BrowserThread::FILE,
FROM_HERE,
base::Bind(&SerialConnection::DoClose, Passed(file_.Pass())));
}
}
bool SerialConnection::Receive(const ReceiveCompleteCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!receive_complete_.is_null())
return false;
receive_complete_ = callback;
io_handler_->Read(buffer_size_);
receive_timeout_task_.reset();
if (receive_timeout_ > 0) {
receive_timeout_task_.reset(new TimeoutTask(
base::Bind(&SerialConnection::OnReceiveTimeout, AsWeakPtr()),
base::TimeDelta::FromMilliseconds(receive_timeout_)));
}
return true;
}
bool SerialConnection::Send(const std::string& data,
const SendCompleteCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!send_complete_.is_null())
return false;
send_complete_ = callback;
io_handler_->Write(data);
send_timeout_task_.reset();
if (send_timeout_ > 0) {
send_timeout_task_.reset(new TimeoutTask(
base::Bind(&SerialConnection::OnSendTimeout, AsWeakPtr()),
base::TimeDelta::FromMilliseconds(send_timeout_)));
}
return true;
}
bool SerialConnection::Configure(
const api::serial::ConnectionOptions& options) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (options.persistent.get())
set_persistent(*options.persistent);
if (options.name.get())
set_name(*options.name);
if (options.buffer_size.get())
set_buffer_size(*options.buffer_size);
if (options.receive_timeout.get())
set_receive_timeout(*options.receive_timeout);
if (options.send_timeout.get())
set_send_timeout(*options.send_timeout);
bool success = ConfigurePort(options);
io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
return success;
}
void SerialConnection::SetIoHandlerForTest(
scoped_refptr<SerialIoHandler> handler) {
io_handler_ = handler;
}
bool SerialConnection::GetInfo(api::serial::ConnectionInfo* info) const {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
info->paused = paused_;
info->persistent = persistent_;
info->name = name_;
info->buffer_size = buffer_size_;
info->receive_timeout = receive_timeout_;
info->send_timeout = send_timeout_;
return GetPortInfo(info);
}
void SerialConnection::StartOpen() {
DCHECK(!open_complete_.is_null());
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
DCHECK(!file_.IsValid());
// It's the responsibility of the API wrapper around SerialConnection to
// validate the supplied path against the set of valid port names, and
// it is a reasonable assumption that serial port names are ASCII.
DCHECK(base::IsStringASCII(port_));
base::FilePath path(
base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port_)));
int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE | base::File::FLAG_ASYNC |
base::File::FLAG_TERMINAL_DEVICE;
base::File file(path, flags);
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&SerialConnection::FinishOpen, base::Unretained(this),
Passed(file.Pass())));
}
void SerialConnection::FinishOpen(base::File file) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!open_complete_.is_null());
DCHECK(!file_.IsValid());
OpenCompleteCallback callback = open_complete_;
open_complete_.Reset();
if (!file.IsValid()) {
callback.Run(false);
return;
}
// TODO(rvargas): crbug.com/351073. This is wrong. io_handler_ keeps a copy of
// the handler that is not in sync with this one.
file_ = file.Pass();
io_handler_->Initialize(
file_.GetPlatformFile(),
base::Bind(&SerialConnection::OnAsyncReadComplete, AsWeakPtr()),
base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()));
bool success = PostOpen();
if (!success) {
Close();
}
callback.Run(success);
}
// static
void SerialConnection::DoClose(base::File port) {
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
// port closed by destructor.
}
void SerialConnection::OnReceiveTimeout() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
io_handler_->CancelRead(api::serial::RECEIVE_ERROR_TIMEOUT);
}
void SerialConnection::OnSendTimeout() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
io_handler_->CancelWrite(api::serial::SEND_ERROR_TIMEOUT);
}
void SerialConnection::OnAsyncReadComplete(const std::string& data,
api::serial::ReceiveError error) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!receive_complete_.is_null());
ReceiveCompleteCallback callback = receive_complete_;
receive_complete_.Reset();
receive_timeout_task_.reset();
callback.Run(data, error);
}
void SerialConnection::OnAsyncWriteComplete(int bytes_sent,
api::serial::SendError error) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
DCHECK(!send_complete_.is_null());
SendCompleteCallback callback = send_complete_;
send_complete_.Reset();
send_timeout_task_.reset();
callback.Run(bytes_sent, error);
}
SerialConnection::TimeoutTask::TimeoutTask(const base::Closure& closure,
const base::TimeDelta& delay)
: weak_factory_(this), closure_(closure), delay_(delay) {
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&TimeoutTask::Run, weak_factory_.GetWeakPtr()),
delay_);
}
SerialConnection::TimeoutTask::~TimeoutTask() {}
void SerialConnection::TimeoutTask::Run() const { closure_.Run(); }
} // namespace extensions