OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "device/bluetooth/bluetooth_socket_chromeos.h" | 5 #include "device/bluetooth/bluetooth_socket_chromeos.h" |
6 | 6 |
7 #include <errno.h> | |
8 #include <poll.h> | |
9 #include <unistd.h> | |
10 #include <sys/ioctl.h> | |
11 #include <sys/types.h> | |
12 #include <sys/socket.h> | |
13 | |
14 #include <string> | 7 #include <string> |
15 | 8 |
16 #include "base/logging.h" | 9 #include "base/logging.h" |
17 #include "base/memory/ref_counted.h" | 10 #include "base/memory/ref_counted.h" |
18 #include "base/posix/eintr_wrapper.h" | 11 #include "base/sequenced_task_runner.h" |
19 #include "base/safe_strerror_posix.h" | |
20 #include "base/threading/thread_restrictions.h" | 12 #include "base/threading/thread_restrictions.h" |
21 #include "dbus/file_descriptor.h" | 13 #include "dbus/file_descriptor.h" |
22 #include "device/bluetooth/bluetooth_socket.h" | 14 #include "device/bluetooth/bluetooth_socket.h" |
23 #include "net/base/io_buffer.h" | 15 #include "device/bluetooth/bluetooth_socket_net.h" |
| 16 #include "device/bluetooth/bluetooth_socket_thread.h" |
| 17 #include "net/base/ip_endpoint.h" |
| 18 #include "net/base/net_errors.h" |
| 19 |
| 20 namespace { |
| 21 |
| 22 const char kSocketAlreadyConnected[] = "Socket is already connected."; |
| 23 |
| 24 } // namespace |
24 | 25 |
25 namespace chromeos { | 26 namespace chromeos { |
26 | 27 |
27 BluetoothSocketChromeOS::BluetoothSocketChromeOS(int fd) | 28 // static |
28 : fd_(fd) { | 29 scoped_refptr<BluetoothSocketChromeOS> |
29 // Fetch the socket type so we read from it correctly. | 30 BluetoothSocketChromeOS::CreateBluetoothSocket( |
30 int optval; | 31 scoped_refptr<base::SequencedTaskRunner> ui_task_runner, |
31 socklen_t opt_len = sizeof optval; | 32 scoped_refptr<device::BluetoothSocketThread> socket_thread, |
32 if (getsockopt(fd_, SOL_SOCKET, SO_TYPE, &optval, &opt_len) < 0) { | 33 net::NetLog* net_log, |
33 // Sequenced packet is the safest assumption since it won't result in | 34 const net::NetLog::Source& source) { |
34 // truncated packets. | 35 DCHECK(ui_task_runner->RunsTasksOnCurrentThread()); |
35 LOG(WARNING) << "Unable to get socket type: " << safe_strerror(errno); | |
36 optval = SOCK_SEQPACKET; | |
37 } | |
38 | 36 |
39 if (optval == SOCK_DGRAM || optval == SOCK_SEQPACKET) { | 37 return make_scoped_refptr( |
40 socket_type_ = L2CAP; | 38 new BluetoothSocketChromeOS( |
41 } else { | 39 ui_task_runner, socket_thread, net_log, source)); |
42 socket_type_ = RFCOMM; | 40 } |
43 } | 41 |
| 42 BluetoothSocketChromeOS::BluetoothSocketChromeOS( |
| 43 scoped_refptr<base::SequencedTaskRunner> ui_task_runner, |
| 44 scoped_refptr<device::BluetoothSocketThread> socket_thread, |
| 45 net::NetLog* net_log, |
| 46 const net::NetLog::Source& source) |
| 47 : BluetoothSocketNet(ui_task_runner, socket_thread, net_log, source) { |
44 } | 48 } |
45 | 49 |
46 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() { | 50 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() { |
47 close(fd_); | |
48 } | 51 } |
49 | 52 |
50 void BluetoothSocketChromeOS::Close() { NOTIMPLEMENTED(); } | 53 void BluetoothSocketChromeOS::Connect( |
| 54 scoped_ptr<dbus::FileDescriptor> fd, |
| 55 const base::Closure& success_callback, |
| 56 const ErrorCompletionCallback& error_callback) { |
| 57 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); |
51 | 58 |
52 void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) { | 59 socket_thread()->task_runner()->PostTask( |
53 NOTIMPLEMENTED(); | 60 FROM_HERE, |
| 61 base::Bind( |
| 62 &BluetoothSocketChromeOS::DoConnect, |
| 63 this, |
| 64 base::Passed(&fd), |
| 65 base::Bind(&BluetoothSocketChromeOS::PostSuccess, |
| 66 this, |
| 67 success_callback), |
| 68 base::Bind(&BluetoothSocketChromeOS::PostErrorCompletion, |
| 69 this, |
| 70 error_callback))); |
54 } | 71 } |
55 | 72 |
56 void BluetoothSocketChromeOS::Receive( | 73 void BluetoothSocketChromeOS::DoConnect( |
57 int buffer_size, | 74 scoped_ptr<dbus::FileDescriptor> fd, |
58 const ReceiveCompletionCallback& success_callback, | 75 const base::Closure& success_callback, |
59 const ReceiveErrorCompletionCallback& error_callback) { | 76 const ErrorCompletionCallback& error_callback) { |
60 NOTIMPLEMENTED(); | 77 DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread()); |
61 } | 78 base::ThreadRestrictions::AssertIOAllowed(); |
| 79 DCHECK(fd->is_valid()); |
62 | 80 |
63 void BluetoothSocketChromeOS::Send( | 81 if (tcp_socket()) { |
64 scoped_refptr<net::IOBuffer> buffer, | 82 error_callback.Run(kSocketAlreadyConnected); |
65 int buffer_size, | 83 return; |
66 const SendCompletionCallback& success_callback, | |
67 const ErrorCompletionCallback& error_callback) { | |
68 NOTIMPLEMENTED(); | |
69 } | |
70 | |
71 #if 0 | |
72 bool BluetoothSocketChromeOS::Receive(net::GrowableIOBuffer *buffer) { | |
73 base::ThreadRestrictions::AssertIOAllowed(); | |
74 | |
75 if (socket_type_ == L2CAP) { | |
76 int count; | |
77 if (ioctl(fd_, FIONREAD, &count) < 0) { | |
78 error_message_ = safe_strerror(errno); | |
79 LOG(WARNING) << "Unable to get waiting data size: " << error_message_; | |
80 return true; | |
81 } | |
82 | |
83 // No bytes waiting can mean either nothing to read, or the other end has | |
84 // been closed, and reading zero bytes always returns zero. | |
85 // | |
86 // We can't do a short read for fear of a race where data arrives between | |
87 // calls and we trunctate it. So use poll() to check for the POLLHUP flag. | |
88 if (count == 0) { | |
89 struct pollfd pollfd; | |
90 | |
91 pollfd.fd = fd_; | |
92 pollfd.events = 0; | |
93 pollfd.revents = 0; | |
94 | |
95 // Timeout parameter set to 0 so this call will not block. | |
96 if (HANDLE_EINTR(poll(&pollfd, 1, 0)) < 0) { | |
97 error_message_ = safe_strerror(errno); | |
98 LOG(WARNING) << "Unable to check whether socket is closed: " | |
99 << error_message_; | |
100 return false; | |
101 } | |
102 | |
103 if (pollfd.revents & POLLHUP) { | |
104 // TODO(keybuk, youngki): Agree a common way to flag disconnected. | |
105 error_message_ = "Disconnected"; | |
106 return false; | |
107 } | |
108 } | |
109 | |
110 buffer->SetCapacity(count); | |
111 } else { | |
112 buffer->SetCapacity(1024); | |
113 } | 84 } |
114 | 85 |
115 ssize_t bytes_read; | 86 ResetTCPSocket(); |
116 do { | |
117 if (buffer->RemainingCapacity() == 0) | |
118 buffer->SetCapacity(buffer->capacity() * 2); | |
119 bytes_read = | |
120 HANDLE_EINTR(read(fd_, buffer->data(), buffer->RemainingCapacity())); | |
121 if (bytes_read > 0) | |
122 buffer->set_offset(buffer->offset() + bytes_read); | |
123 } while (socket_type_ == RFCOMM && bytes_read > 0); | |
124 | 87 |
125 // Ignore an error if at least one read() call succeeded; it'll be returned | 88 // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the |
126 // the next read() call. | 89 // TCPSocket implementation does not actually require one. |
127 if (buffer->offset() > 0) | 90 int net_result = tcp_socket()->AdoptConnectedSocket(fd->value(), |
128 return true; | 91 net::IPEndPoint()); |
129 | 92 if (net_result != net::OK) { |
130 if (bytes_read < 0) { | 93 error_callback.Run("Error connecting to socket: " + |
131 if (errno == ECONNRESET || errno == ENOTCONN) { | 94 std::string(net::ErrorToString(net_result))); |
132 // TODO(keybuk, youngki): Agree a common way to flag disconnected. | 95 return; |
133 error_message_ = "Disconnected"; | |
134 return false; | |
135 } else if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
136 error_message_ = safe_strerror(errno); | |
137 return false; | |
138 } | |
139 } | 96 } |
140 | 97 |
141 if (bytes_read == 0 && socket_type_ == RFCOMM) { | 98 fd->TakeValue(); |
142 // TODO(keybuk, youngki): Agree a common way to flag disconnected. | 99 success_callback.Run(); |
143 error_message_ = "Disconnected"; | |
144 return false; | |
145 } | |
146 | |
147 return true; | |
148 } | |
149 | |
150 bool BluetoothSocketChromeOS::Send(net::DrainableIOBuffer *buffer) { | |
151 base::ThreadRestrictions::AssertIOAllowed(); | |
152 | |
153 ssize_t bytes_written; | |
154 do { | |
155 bytes_written = | |
156 HANDLE_EINTR(write(fd_, buffer->data(), buffer->BytesRemaining())); | |
157 if (bytes_written > 0) | |
158 buffer->DidConsume(bytes_written); | |
159 } while (buffer->BytesRemaining() > 0 && bytes_written > 0); | |
160 | |
161 if (bytes_written < 0) { | |
162 if (errno == EPIPE || errno == ECONNRESET || errno == ENOTCONN) { | |
163 // TODO(keybuk, youngki): Agree a common way to flag disconnected. | |
164 error_message_ = "Disconnected"; | |
165 return false; | |
166 } else if (errno != EAGAIN && errno != EWOULDBLOCK) { | |
167 error_message_ = safe_strerror(errno); | |
168 return false; | |
169 } | |
170 } | |
171 | |
172 return true; | |
173 } | |
174 | |
175 std::string BluetoothSocketChromeOS::GetLastErrorMessage() const { | |
176 return error_message_; | |
177 } | |
178 #endif | |
179 | |
180 // static | |
181 scoped_refptr<device::BluetoothSocket> BluetoothSocketChromeOS::Create( | |
182 dbus::FileDescriptor* fd) { | |
183 DCHECK(fd->is_valid()); | |
184 | |
185 BluetoothSocketChromeOS* bluetooth_socket = | |
186 new BluetoothSocketChromeOS(fd->TakeValue()); | |
187 return scoped_refptr<BluetoothSocketChromeOS>(bluetooth_socket); | |
188 } | 100 } |
189 | 101 |
190 } // namespace chromeos | 102 } // namespace chromeos |
OLD | NEW |