| 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 |