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