• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 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/platform_file.h"
12 #include "base/strings/string_util.h"
13 #include "chrome/browser/extensions/api/api_resource_manager.h"
14 #include "chrome/browser/extensions/api/serial/serial_port_enumerator.h"
15 #include "chrome/common/extensions/api/serial.h"
16 
17 namespace extensions {
18 
19 namespace {
20 
21 const int kDefaultBufferSize = 4096;
22 
23 }
24 
25 static base::LazyInstance<ProfileKeyedAPIFactory<
26     ApiResourceManager<SerialConnection> > >
27         g_factory = LAZY_INSTANCE_INITIALIZER;
28 
29 // static
30 template <>
31 ProfileKeyedAPIFactory<ApiResourceManager<SerialConnection> >*
GetFactoryInstance()32 ApiResourceManager<SerialConnection>::GetFactoryInstance() {
33   return &g_factory.Get();
34 }
35 
SerialConnection(const std::string & port,const std::string & owner_extension_id)36 SerialConnection::SerialConnection(const std::string& port,
37                                    const std::string& owner_extension_id)
38     : ApiResource(owner_extension_id),
39       port_(port),
40       file_(base::kInvalidPlatformFileValue),
41       persistent_(false),
42       buffer_size_(kDefaultBufferSize),
43       receive_timeout_(0),
44       send_timeout_(0),
45       paused_(false),
46       io_handler_(SerialIoHandler::Create()) {
47   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
48 }
49 
~SerialConnection()50 SerialConnection::~SerialConnection() {
51   DCHECK(open_complete_.is_null());
52   io_handler_->CancelRead(api::serial::RECEIVE_ERROR_DISCONNECTED);
53   io_handler_->CancelWrite(api::serial::SEND_ERROR_DISCONNECTED);
54   Close();
55 }
56 
IsPersistent() const57 bool SerialConnection::IsPersistent() const {
58   return persistent();
59 }
60 
set_buffer_size(int buffer_size)61 void SerialConnection::set_buffer_size(int buffer_size) {
62   buffer_size_ = buffer_size;
63 }
64 
set_receive_timeout(int receive_timeout)65 void SerialConnection::set_receive_timeout(int receive_timeout) {
66   receive_timeout_ = receive_timeout;
67 }
68 
set_send_timeout(int send_timeout)69 void SerialConnection::set_send_timeout(int send_timeout) {
70   send_timeout_ = send_timeout;
71 }
72 
set_paused(bool paused)73 void SerialConnection::set_paused(bool paused) {
74   paused_ = paused;
75   if (paused) {
76     io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
77   }
78 }
79 
Open(const OpenCompleteCallback & callback)80 void SerialConnection::Open(const OpenCompleteCallback& callback) {
81   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
82   DCHECK(open_complete_.is_null());
83   open_complete_ = callback;
84   BrowserThread::PostTask(
85       BrowserThread::FILE, FROM_HERE,
86       base::Bind(&SerialConnection::StartOpen, base::Unretained(this)));
87 }
88 
Close()89 void SerialConnection::Close() {
90   DCHECK(open_complete_.is_null());
91   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
92   if (file_ != base::kInvalidPlatformFileValue) {
93     base::PlatformFile file = file_;
94     file_ = base::kInvalidPlatformFileValue;
95     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
96                             base::Bind(&SerialConnection::DoClose, file));
97   }
98 }
99 
Receive(const ReceiveCompleteCallback & callback)100 bool SerialConnection::Receive(const ReceiveCompleteCallback& callback) {
101   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
102   if (!receive_complete_.is_null())
103     return false;
104   receive_complete_ = callback;
105   io_handler_->Read(buffer_size_);
106   receive_timeout_task_.reset();
107   if (receive_timeout_ > 0) {
108     receive_timeout_task_.reset(new TimeoutTask(
109         base::Bind(&SerialConnection::OnReceiveTimeout, AsWeakPtr()),
110         base::TimeDelta::FromMilliseconds(receive_timeout_)));
111   }
112   return true;
113 }
114 
Send(const std::string & data,const SendCompleteCallback & callback)115 bool SerialConnection::Send(const std::string& data,
116                             const SendCompleteCallback& callback) {
117   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
118   if (!send_complete_.is_null())
119     return false;
120   send_complete_ = callback;
121   io_handler_->Write(data);
122   send_timeout_task_.reset();
123   if (send_timeout_ > 0) {
124     send_timeout_task_.reset(new TimeoutTask(
125         base::Bind(&SerialConnection::OnSendTimeout, AsWeakPtr()),
126         base::TimeDelta::FromMilliseconds(send_timeout_)));
127   }
128   return true;
129 }
130 
Configure(const api::serial::ConnectionOptions & options)131 bool SerialConnection::Configure(
132     const api::serial::ConnectionOptions& options) {
133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
134   if (options.persistent.get())
135     set_persistent(*options.persistent);
136   if (options.name.get())
137     set_name(*options.name);
138   if (options.buffer_size.get())
139     set_buffer_size(*options.buffer_size);
140   if (options.receive_timeout.get())
141     set_receive_timeout(*options.receive_timeout);
142   if (options.send_timeout.get())
143     set_send_timeout(*options.send_timeout);
144   bool success = ConfigurePort(options);
145   io_handler_->CancelRead(api::serial::RECEIVE_ERROR_NONE);
146   return success;
147 }
148 
SetIoHandlerForTest(scoped_refptr<SerialIoHandler> handler)149 void SerialConnection::SetIoHandlerForTest(
150     scoped_refptr<SerialIoHandler> handler) {
151   io_handler_ = handler;
152 }
153 
GetInfo(api::serial::ConnectionInfo * info) const154 bool SerialConnection::GetInfo(api::serial::ConnectionInfo* info) const {
155   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
156   info->paused = paused_;
157   info->persistent = persistent_;
158   info->name = name_;
159   info->buffer_size = buffer_size_;
160   info->receive_timeout = receive_timeout_;
161   info->send_timeout = send_timeout_;
162   return GetPortInfo(info);
163 }
164 
StartOpen()165 void SerialConnection::StartOpen() {
166   DCHECK(!open_complete_.is_null());
167   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
168   DCHECK_EQ(file_, base::kInvalidPlatformFileValue);
169   const SerialPortEnumerator::StringSet name_set(
170     SerialPortEnumerator::GenerateValidSerialPortNames());
171   base::PlatformFile file = base::kInvalidPlatformFileValue;
172   if (SerialPortEnumerator::DoesPortExist(name_set, port_)) {
173     // It's the responsibility of the API wrapper around SerialConnection to
174     // validate the supplied path against the set of valid port names, and
175     // it is a reasonable assumption that serial port names are ASCII.
176     DCHECK(IsStringASCII(port_));
177     base::FilePath path(
178         base::FilePath::FromUTF8Unsafe(MaybeFixUpPortName(port_)));
179     int flags = base::PLATFORM_FILE_OPEN |
180                 base::PLATFORM_FILE_READ |
181                 base::PLATFORM_FILE_EXCLUSIVE_READ |
182                 base::PLATFORM_FILE_WRITE |
183                 base::PLATFORM_FILE_EXCLUSIVE_WRITE |
184                 base::PLATFORM_FILE_ASYNC |
185                 base::PLATFORM_FILE_TERMINAL_DEVICE;
186     file = base::CreatePlatformFile(path, flags, NULL, NULL);
187   }
188   BrowserThread::PostTask(
189       BrowserThread::IO, FROM_HERE,
190       base::Bind(&SerialConnection::FinishOpen, base::Unretained(this), file));
191 }
192 
FinishOpen(base::PlatformFile file)193 void SerialConnection::FinishOpen(base::PlatformFile file) {
194   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
195   DCHECK(!open_complete_.is_null());
196   DCHECK_EQ(file_, base::kInvalidPlatformFileValue);
197   OpenCompleteCallback callback = open_complete_;
198   open_complete_.Reset();
199 
200   if (file == base::kInvalidPlatformFileValue) {
201     callback.Run(false);
202     return;
203   }
204 
205   file_ = file;
206   io_handler_->Initialize(
207       file_,
208       base::Bind(&SerialConnection::OnAsyncReadComplete, AsWeakPtr()),
209       base::Bind(&SerialConnection::OnAsyncWriteComplete, AsWeakPtr()));
210 
211   bool success = PostOpen();
212   if (!success) {
213     Close();
214   }
215 
216   callback.Run(success);
217 }
218 
219 // static
DoClose(base::PlatformFile port)220 void SerialConnection::DoClose(base::PlatformFile port) {
221   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
222   if (port != base::kInvalidPlatformFileValue) {
223     base::ClosePlatformFile(port);
224   }
225 }
226 
OnReceiveTimeout()227 void SerialConnection::OnReceiveTimeout() {
228   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
229   io_handler_->CancelRead(api::serial::RECEIVE_ERROR_TIMEOUT);
230 }
231 
OnSendTimeout()232 void SerialConnection::OnSendTimeout() {
233   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
234   io_handler_->CancelWrite(api::serial::SEND_ERROR_TIMEOUT);
235 }
236 
OnAsyncReadComplete(const std::string & data,api::serial::ReceiveError error)237 void SerialConnection::OnAsyncReadComplete(const std::string& data,
238                                            api::serial::ReceiveError error) {
239   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
240   DCHECK(!receive_complete_.is_null());
241   ReceiveCompleteCallback callback = receive_complete_;
242   receive_complete_.Reset();
243   receive_timeout_task_.reset();
244   callback.Run(data, error);
245 }
246 
OnAsyncWriteComplete(int bytes_sent,api::serial::SendError error)247 void SerialConnection::OnAsyncWriteComplete(int bytes_sent,
248                                             api::serial::SendError error) {
249   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
250   DCHECK(!send_complete_.is_null());
251   SendCompleteCallback callback = send_complete_;
252   send_complete_.Reset();
253   send_timeout_task_.reset();
254   callback.Run(bytes_sent, error);
255 }
256 
TimeoutTask(const base::Closure & closure,const base::TimeDelta & delay)257 SerialConnection::TimeoutTask::TimeoutTask(const base::Closure& closure,
258                                            const base::TimeDelta& delay)
259     : weak_factory_(this),
260       closure_(closure),
261       delay_(delay) {
262   base::MessageLoop::current()->PostDelayedTask(
263       FROM_HERE,
264       base::Bind(&TimeoutTask::Run, weak_factory_.GetWeakPtr()),
265       delay_);
266 }
267 
~TimeoutTask()268 SerialConnection::TimeoutTask::~TimeoutTask() {}
269 
Run() const270 void SerialConnection::TimeoutTask::Run() const {
271   closure_.Run();
272 }
273 
274 }  // namespace extensions
275