• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/extensions/api/serial/serial_connection.h"
6 
7 #include <string>
8 
9 #include "base/files/file_path.h"
10 #include "base/lazy_instance.h"
11 #include "base/strings/string_util.h"
12 #include "chrome/common/extensions/api/serial.h"
13 #include "extensions/browser/api/api_resource_manager.h"
14 
15 namespace extensions {
16 
17 namespace {
18 
19 const int kDefaultBufferSize = 4096;
20 }
21 
22 static base::LazyInstance<
23     BrowserContextKeyedAPIFactory<ApiResourceManager<SerialConnection> > >
24     g_factory = LAZY_INSTANCE_INITIALIZER;
25 
26 // static
27 template <>
28 BrowserContextKeyedAPIFactory<ApiResourceManager<SerialConnection> >*
GetFactoryInstance()29 ApiResourceManager<SerialConnection>::GetFactoryInstance() {
30   return g_factory.Pointer();
31 }
32 
SerialConnection(const std::string & port,const std::string & owner_extension_id)33 SerialConnection::SerialConnection(const std::string& port,
34                                    const std::string& owner_extension_id)
35     : ApiResource(owner_extension_id),
36       port_(port),
37       persistent_(false),
38       buffer_size_(kDefaultBufferSize),
39       receive_timeout_(0),
40       send_timeout_(0),
41       paused_(false),
42       io_handler_(SerialIoHandler::Create()) {
43   DCHECK_CURRENTLY_ON(BrowserThread::IO);
44 }
45 
~SerialConnection()46 SerialConnection::~SerialConnection() {
47   DCHECK(open_complete_.is_null());
48   io_handler_->CancelRead(api::serial::RECEIVE_ERROR_DISCONNECTED);
49   io_handler_->CancelWrite(api::serial::SEND_ERROR_DISCONNECTED);
50   Close();
51 }
52 
IsPersistent() const53 bool SerialConnection::IsPersistent() const { return persistent(); }
54 
set_buffer_size(int buffer_size)55 void SerialConnection::set_buffer_size(int buffer_size) {
56   buffer_size_ = buffer_size;
57 }
58 
set_receive_timeout(int receive_timeout)59 void SerialConnection::set_receive_timeout(int receive_timeout) {
60   receive_timeout_ = receive_timeout;
61 }
62 
set_send_timeout(int send_timeout)63 void SerialConnection::set_send_timeout(int send_timeout) {
64   send_timeout_ = send_timeout;
65 }
66 
set_paused(bool paused)67 void SerialConnection::set_paused(bool paused) {
68   paused_ = paused;
69   if (paused) {
70     io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
71   }
72 }
73 
Open(const OpenCompleteCallback & callback)74 void SerialConnection::Open(const OpenCompleteCallback& callback) {
75   DCHECK_CURRENTLY_ON(BrowserThread::IO);
76   DCHECK(open_complete_.is_null());
77   open_complete_ = callback;
78   BrowserThread::PostTask(
79       BrowserThread::FILE,
80       FROM_HERE,
81       base::Bind(&SerialConnection::StartOpen, base::Unretained(this)));
82 }
83 
Close()84 void SerialConnection::Close() {
85   DCHECK(open_complete_.is_null());
86   DCHECK_CURRENTLY_ON(BrowserThread::IO);
87   if (file_.IsValid()) {
88     BrowserThread::PostTask(
89         BrowserThread::FILE,
90         FROM_HERE,
91         base::Bind(&SerialConnection::DoClose, Passed(file_.Pass())));
92   }
93 }
94 
Receive(const ReceiveCompleteCallback & callback)95 bool SerialConnection::Receive(const ReceiveCompleteCallback& callback) {
96   DCHECK_CURRENTLY_ON(BrowserThread::IO);
97   if (!receive_complete_.is_null())
98     return false;
99   receive_complete_ = callback;
100   io_handler_->Read(buffer_size_);
101   receive_timeout_task_.reset();
102   if (receive_timeout_ > 0) {
103     receive_timeout_task_.reset(new TimeoutTask(
104         base::Bind(&SerialConnection::OnReceiveTimeout, AsWeakPtr()),
105         base::TimeDelta::FromMilliseconds(receive_timeout_)));
106   }
107   return true;
108 }
109 
Send(const std::string & data,const SendCompleteCallback & callback)110 bool SerialConnection::Send(const std::string& data,
111                             const SendCompleteCallback& callback) {
112   DCHECK_CURRENTLY_ON(BrowserThread::IO);
113   if (!send_complete_.is_null())
114     return false;
115   send_complete_ = callback;
116   io_handler_->Write(data);
117   send_timeout_task_.reset();
118   if (send_timeout_ > 0) {
119     send_timeout_task_.reset(new TimeoutTask(
120         base::Bind(&SerialConnection::OnSendTimeout, AsWeakPtr()),
121         base::TimeDelta::FromMilliseconds(send_timeout_)));
122   }
123   return true;
124 }
125 
Configure(const api::serial::ConnectionOptions & options)126 bool SerialConnection::Configure(
127     const api::serial::ConnectionOptions& options) {
128   DCHECK_CURRENTLY_ON(BrowserThread::IO);
129   if (options.persistent.get())
130     set_persistent(*options.persistent);
131   if (options.name.get())
132     set_name(*options.name);
133   if (options.buffer_size.get())
134     set_buffer_size(*options.buffer_size);
135   if (options.receive_timeout.get())
136     set_receive_timeout(*options.receive_timeout);
137   if (options.send_timeout.get())
138     set_send_timeout(*options.send_timeout);
139   bool success = ConfigurePort(options);
140   io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
141   return success;
142 }
143 
SetIoHandlerForTest(scoped_refptr<SerialIoHandler> handler)144 void SerialConnection::SetIoHandlerForTest(
145     scoped_refptr<SerialIoHandler> handler) {
146   io_handler_ = handler;
147 }
148 
GetInfo(api::serial::ConnectionInfo * info) const149 bool SerialConnection::GetInfo(api::serial::ConnectionInfo* info) const {
150   DCHECK_CURRENTLY_ON(BrowserThread::IO);
151   info->paused = paused_;
152   info->persistent = persistent_;
153   info->name = name_;
154   info->buffer_size = buffer_size_;
155   info->receive_timeout = receive_timeout_;
156   info->send_timeout = send_timeout_;
157   return GetPortInfo(info);
158 }
159 
StartOpen()160 void SerialConnection::StartOpen() {
161   DCHECK(!open_complete_.is_null());
162   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
163   DCHECK(!file_.IsValid());
164   // It's the responsibility of the API wrapper around SerialConnection to
165   // validate the supplied path against the set of valid port names, and
166   // it is a reasonable assumption that serial port names are ASCII.
167   DCHECK(base::IsStringASCII(port_));
168   base::FilePath path(
169       base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port_)));
170   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
171               base::File::FLAG_EXCLUSIVE_READ | base::File::FLAG_WRITE |
172               base::File::FLAG_EXCLUSIVE_WRITE | base::File::FLAG_ASYNC |
173               base::File::FLAG_TERMINAL_DEVICE;
174   base::File file(path, flags);
175   BrowserThread::PostTask(
176       BrowserThread::IO,
177       FROM_HERE,
178       base::Bind(&SerialConnection::FinishOpen, base::Unretained(this),
179                  Passed(file.Pass())));
180 }
181 
FinishOpen(base::File file)182 void SerialConnection::FinishOpen(base::File file) {
183   DCHECK_CURRENTLY_ON(BrowserThread::IO);
184   DCHECK(!open_complete_.is_null());
185   DCHECK(!file_.IsValid());
186   OpenCompleteCallback callback = open_complete_;
187   open_complete_.Reset();
188 
189   if (!file.IsValid()) {
190     callback.Run(false);
191     return;
192   }
193 
194   // TODO(rvargas): crbug.com/351073. This is wrong. io_handler_ keeps a copy of
195   // the handler that is not in sync with this one.
196   file_ = file.Pass();
197   io_handler_->Initialize(
198       file_.GetPlatformFile(),
199       base::Bind(&SerialConnection::OnAsyncReadComplete, AsWeakPtr()),
200       base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()));
201 
202   bool success = PostOpen();
203   if (!success) {
204     Close();
205   }
206 
207   callback.Run(success);
208 }
209 
210 // static
DoClose(base::File port)211 void SerialConnection::DoClose(base::File port) {
212   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
213   // port closed by destructor.
214 }
215 
OnReceiveTimeout()216 void SerialConnection::OnReceiveTimeout() {
217   DCHECK_CURRENTLY_ON(BrowserThread::IO);
218   io_handler_->CancelRead(api::serial::RECEIVE_ERROR_TIMEOUT);
219 }
220 
OnSendTimeout()221 void SerialConnection::OnSendTimeout() {
222   DCHECK_CURRENTLY_ON(BrowserThread::IO);
223   io_handler_->CancelWrite(api::serial::SEND_ERROR_TIMEOUT);
224 }
225 
OnAsyncReadComplete(const std::string & data,api::serial::ReceiveError error)226 void SerialConnection::OnAsyncReadComplete(const std::string& data,
227                                            api::serial::ReceiveError error) {
228   DCHECK_CURRENTLY_ON(BrowserThread::IO);
229   DCHECK(!receive_complete_.is_null());
230   ReceiveCompleteCallback callback = receive_complete_;
231   receive_complete_.Reset();
232   receive_timeout_task_.reset();
233   callback.Run(data, error);
234 }
235 
OnAsyncWriteComplete(int bytes_sent,api::serial::SendError error)236 void SerialConnection::OnAsyncWriteComplete(int bytes_sent,
237                                             api::serial::SendError error) {
238   DCHECK_CURRENTLY_ON(BrowserThread::IO);
239   DCHECK(!send_complete_.is_null());
240   SendCompleteCallback callback = send_complete_;
241   send_complete_.Reset();
242   send_timeout_task_.reset();
243   callback.Run(bytes_sent, error);
244 }
245 
TimeoutTask(const base::Closure & closure,const base::TimeDelta & delay)246 SerialConnection::TimeoutTask::TimeoutTask(const base::Closure& closure,
247                                            const base::TimeDelta& delay)
248     : weak_factory_(this), closure_(closure), delay_(delay) {
249   base::MessageLoop::current()->PostDelayedTask(
250       FROM_HERE,
251       base::Bind(&TimeoutTask::Run, weak_factory_.GetWeakPtr()),
252       delay_);
253 }
254 
~TimeoutTask()255 SerialConnection::TimeoutTask::~TimeoutTask() {}
256 
Run() const257 void SerialConnection::TimeoutTask::Run() const { closure_.Run(); }
258 
259 }  // namespace extensions
260