Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ |
michael@0 | 2 | /* vim: set ts=2 et sw=2 tw=80: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "BluetoothSocket.h" |
michael@0 | 8 | |
michael@0 | 9 | #include <hardware/bluetooth.h> |
michael@0 | 10 | #include <hardware/bt_sock.h> |
michael@0 | 11 | #include <sys/socket.h> |
michael@0 | 12 | |
michael@0 | 13 | #include "base/message_loop.h" |
michael@0 | 14 | #include "BluetoothSocketObserver.h" |
michael@0 | 15 | #include "BluetoothUtils.h" |
michael@0 | 16 | #include "mozilla/FileUtils.h" |
michael@0 | 17 | #include "mozilla/RefPtr.h" |
michael@0 | 18 | #include "nsThreadUtils.h" |
michael@0 | 19 | #include "nsXULAppAPI.h" |
michael@0 | 20 | |
michael@0 | 21 | #define FIRST_SOCKET_INFO_MSG_LENGTH 4 |
michael@0 | 22 | #define TOTAL_SOCKET_INFO_LENGTH 20 |
michael@0 | 23 | |
michael@0 | 24 | using namespace mozilla::ipc; |
michael@0 | 25 | USING_BLUETOOTH_NAMESPACE |
michael@0 | 26 | |
michael@0 | 27 | static const size_t MAX_READ_SIZE = 1 << 16; |
michael@0 | 28 | static const uint8_t UUID_OBEX_OBJECT_PUSH[] = { |
michael@0 | 29 | 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00, |
michael@0 | 30 | 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB |
michael@0 | 31 | }; |
michael@0 | 32 | static const btsock_interface_t* sBluetoothSocketInterface = nullptr; |
michael@0 | 33 | |
michael@0 | 34 | // helper functions |
michael@0 | 35 | static bool |
michael@0 | 36 | EnsureBluetoothSocketHalLoad() |
michael@0 | 37 | { |
michael@0 | 38 | if (sBluetoothSocketInterface) { |
michael@0 | 39 | return true; |
michael@0 | 40 | } |
michael@0 | 41 | |
michael@0 | 42 | const bt_interface_t* btInf = GetBluetoothInterface(); |
michael@0 | 43 | NS_ENSURE_TRUE(btInf, false); |
michael@0 | 44 | |
michael@0 | 45 | sBluetoothSocketInterface = |
michael@0 | 46 | (btsock_interface_t *) btInf->get_profile_interface(BT_PROFILE_SOCKETS_ID); |
michael@0 | 47 | NS_ENSURE_TRUE(sBluetoothSocketInterface, false); |
michael@0 | 48 | |
michael@0 | 49 | return true; |
michael@0 | 50 | } |
michael@0 | 51 | |
michael@0 | 52 | static int16_t |
michael@0 | 53 | ReadInt16(const uint8_t* aData, size_t* aOffset) |
michael@0 | 54 | { |
michael@0 | 55 | int16_t value = (aData[*aOffset + 1] << 8) | aData[*aOffset]; |
michael@0 | 56 | |
michael@0 | 57 | *aOffset += 2; |
michael@0 | 58 | return value; |
michael@0 | 59 | } |
michael@0 | 60 | |
michael@0 | 61 | static int32_t |
michael@0 | 62 | ReadInt32(const uint8_t* aData, size_t* aOffset) |
michael@0 | 63 | { |
michael@0 | 64 | int32_t value = (aData[*aOffset + 3] << 24) | |
michael@0 | 65 | (aData[*aOffset + 2] << 16) | |
michael@0 | 66 | (aData[*aOffset + 1] << 8) | |
michael@0 | 67 | aData[*aOffset]; |
michael@0 | 68 | *aOffset += 4; |
michael@0 | 69 | return value; |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | static void |
michael@0 | 73 | ReadBdAddress(const uint8_t* aData, size_t* aOffset, nsAString& aDeviceAddress) |
michael@0 | 74 | { |
michael@0 | 75 | char bdstr[18]; |
michael@0 | 76 | sprintf(bdstr, "%02x:%02x:%02x:%02x:%02x:%02x", |
michael@0 | 77 | aData[*aOffset], aData[*aOffset + 1], aData[*aOffset + 2], |
michael@0 | 78 | aData[*aOffset + 3], aData[*aOffset + 4], aData[*aOffset + 5]); |
michael@0 | 79 | |
michael@0 | 80 | aDeviceAddress.AssignLiteral(bdstr); |
michael@0 | 81 | *aOffset += 6; |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | class mozilla::dom::bluetooth::DroidSocketImpl |
michael@0 | 85 | : public MessageLoopForIO::Watcher |
michael@0 | 86 | { |
michael@0 | 87 | public: |
michael@0 | 88 | DroidSocketImpl(BluetoothSocket* aConsumer, int aFd) |
michael@0 | 89 | : mConsumer(aConsumer) |
michael@0 | 90 | , mReadMsgForClientFd(false) |
michael@0 | 91 | , mIOLoop(nullptr) |
michael@0 | 92 | , mFd(aFd) |
michael@0 | 93 | , mShuttingDownOnIOThread(false) |
michael@0 | 94 | , mChannel(0) |
michael@0 | 95 | , mAuth(false) |
michael@0 | 96 | , mEncrypt(false) |
michael@0 | 97 | { |
michael@0 | 98 | } |
michael@0 | 99 | |
michael@0 | 100 | DroidSocketImpl(BluetoothSocket* aConsumer, |
michael@0 | 101 | int aChannel, bool aAuth, bool aEncrypt) |
michael@0 | 102 | : mConsumer(aConsumer) |
michael@0 | 103 | , mReadMsgForClientFd(false) |
michael@0 | 104 | , mIOLoop(nullptr) |
michael@0 | 105 | , mFd(-1) |
michael@0 | 106 | , mShuttingDownOnIOThread(false) |
michael@0 | 107 | , mChannel(aChannel) |
michael@0 | 108 | , mAuth(aAuth) |
michael@0 | 109 | , mEncrypt(aEncrypt) |
michael@0 | 110 | { } |
michael@0 | 111 | |
michael@0 | 112 | DroidSocketImpl(BluetoothSocket* aConsumer, const nsAString& aDeviceAddress, |
michael@0 | 113 | int aChannel, bool aAuth, bool aEncrypt) |
michael@0 | 114 | : mConsumer(aConsumer) |
michael@0 | 115 | , mReadMsgForClientFd(false) |
michael@0 | 116 | , mIOLoop(nullptr) |
michael@0 | 117 | , mFd(-1) |
michael@0 | 118 | , mShuttingDownOnIOThread(false) |
michael@0 | 119 | , mDeviceAddress(aDeviceAddress) |
michael@0 | 120 | , mChannel(aChannel) |
michael@0 | 121 | , mAuth(aAuth) |
michael@0 | 122 | , mEncrypt(aEncrypt) |
michael@0 | 123 | { |
michael@0 | 124 | MOZ_ASSERT(!mDeviceAddress.IsEmpty()); |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | ~DroidSocketImpl() |
michael@0 | 128 | { |
michael@0 | 129 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 130 | } |
michael@0 | 131 | |
michael@0 | 132 | void QueueWriteData(UnixSocketRawData* aData) |
michael@0 | 133 | { |
michael@0 | 134 | mOutgoingQ.AppendElement(aData); |
michael@0 | 135 | OnFileCanWriteWithoutBlocking(mFd); |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | bool IsShutdownOnMainThread() |
michael@0 | 139 | { |
michael@0 | 140 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 141 | return mConsumer == nullptr; |
michael@0 | 142 | } |
michael@0 | 143 | |
michael@0 | 144 | bool IsShutdownOnIOThread() |
michael@0 | 145 | { |
michael@0 | 146 | return mShuttingDownOnIOThread; |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | void ShutdownOnMainThread() |
michael@0 | 150 | { |
michael@0 | 151 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 152 | MOZ_ASSERT(!IsShutdownOnMainThread()); |
michael@0 | 153 | mConsumer = nullptr; |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | void ShutdownOnIOThread() |
michael@0 | 157 | { |
michael@0 | 158 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 159 | MOZ_ASSERT(!mShuttingDownOnIOThread); |
michael@0 | 160 | |
michael@0 | 161 | mReadWatcher.StopWatchingFileDescriptor(); |
michael@0 | 162 | mWriteWatcher.StopWatchingFileDescriptor(); |
michael@0 | 163 | |
michael@0 | 164 | mShuttingDownOnIOThread = true; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | void Connect(); |
michael@0 | 168 | void Listen(); |
michael@0 | 169 | |
michael@0 | 170 | void SetUpIO(bool aWrite) |
michael@0 | 171 | { |
michael@0 | 172 | MOZ_ASSERT(!mIOLoop); |
michael@0 | 173 | MOZ_ASSERT(mFd >= 0); |
michael@0 | 174 | mIOLoop = MessageLoopForIO::current(); |
michael@0 | 175 | |
michael@0 | 176 | // Set up a read watch |
michael@0 | 177 | mIOLoop->WatchFileDescriptor(mFd, |
michael@0 | 178 | true, |
michael@0 | 179 | MessageLoopForIO::WATCH_READ, |
michael@0 | 180 | &mReadWatcher, |
michael@0 | 181 | this); |
michael@0 | 182 | |
michael@0 | 183 | if (aWrite) { |
michael@0 | 184 | // Set up a write watch |
michael@0 | 185 | mIOLoop->WatchFileDescriptor(mFd.get(), |
michael@0 | 186 | false, |
michael@0 | 187 | MessageLoopForIO::WATCH_WRITE, |
michael@0 | 188 | &mWriteWatcher, |
michael@0 | 189 | this); |
michael@0 | 190 | } |
michael@0 | 191 | } |
michael@0 | 192 | |
michael@0 | 193 | void ConnectClientFd() |
michael@0 | 194 | { |
michael@0 | 195 | // Stop current read watch |
michael@0 | 196 | mReadWatcher.StopWatchingFileDescriptor(); |
michael@0 | 197 | mIOLoop = nullptr; |
michael@0 | 198 | |
michael@0 | 199 | // Restart read & write watch on client fd |
michael@0 | 200 | SetUpIO(true); |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | /** |
michael@0 | 204 | * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated |
michael@0 | 205 | * directly from main thread. All non-main-thread accesses should happen with |
michael@0 | 206 | * mImpl as container. |
michael@0 | 207 | */ |
michael@0 | 208 | RefPtr<BluetoothSocket> mConsumer; |
michael@0 | 209 | |
michael@0 | 210 | /** |
michael@0 | 211 | * If true, read message header to get client fd. |
michael@0 | 212 | */ |
michael@0 | 213 | bool mReadMsgForClientFd; |
michael@0 | 214 | |
michael@0 | 215 | private: |
michael@0 | 216 | /** |
michael@0 | 217 | * libevent triggered functions that reads data from socket when available and |
michael@0 | 218 | * guarenteed non-blocking. Only to be called on IO thread. |
michael@0 | 219 | * |
michael@0 | 220 | * @param aFd [in] File descriptor to read from |
michael@0 | 221 | */ |
michael@0 | 222 | virtual void OnFileCanReadWithoutBlocking(int aFd); |
michael@0 | 223 | |
michael@0 | 224 | /** |
michael@0 | 225 | * libevent or developer triggered functions that writes data to socket when |
michael@0 | 226 | * available and guarenteed non-blocking. Only to be called on IO thread. |
michael@0 | 227 | * |
michael@0 | 228 | * @param aFd [in] File descriptor to read from |
michael@0 | 229 | */ |
michael@0 | 230 | virtual void OnFileCanWriteWithoutBlocking(int aFd); |
michael@0 | 231 | |
michael@0 | 232 | /** |
michael@0 | 233 | * Read message to get data and client fd wrapped in message header |
michael@0 | 234 | * |
michael@0 | 235 | * @param aFd [in] File descriptor to read message from |
michael@0 | 236 | * @param aBuffer [out] Data buffer read |
michael@0 | 237 | * @param aLength [out] Number of bytes read |
michael@0 | 238 | */ |
michael@0 | 239 | ssize_t ReadMsg(int aFd, void *aBuffer, size_t aLength); |
michael@0 | 240 | |
michael@0 | 241 | /** |
michael@0 | 242 | * IO Loop pointer. Must be initalized and called from IO thread only. |
michael@0 | 243 | */ |
michael@0 | 244 | MessageLoopForIO* mIOLoop; |
michael@0 | 245 | |
michael@0 | 246 | /** |
michael@0 | 247 | * Raw data queue. Must be pushed/popped from IO thread only. |
michael@0 | 248 | */ |
michael@0 | 249 | typedef nsTArray<UnixSocketRawData* > UnixSocketRawDataQueue; |
michael@0 | 250 | UnixSocketRawDataQueue mOutgoingQ; |
michael@0 | 251 | |
michael@0 | 252 | /** |
michael@0 | 253 | * Read watcher for libevent. Only to be accessed on IO Thread. |
michael@0 | 254 | */ |
michael@0 | 255 | MessageLoopForIO::FileDescriptorWatcher mReadWatcher; |
michael@0 | 256 | |
michael@0 | 257 | /** |
michael@0 | 258 | * Write watcher for libevent. Only to be accessed on IO Thread. |
michael@0 | 259 | */ |
michael@0 | 260 | MessageLoopForIO::FileDescriptorWatcher mWriteWatcher; |
michael@0 | 261 | |
michael@0 | 262 | /** |
michael@0 | 263 | * File descriptor to read from/write to. Connection happens on user provided |
michael@0 | 264 | * thread. Read/write/close happens on IO thread. |
michael@0 | 265 | */ |
michael@0 | 266 | mozilla::ScopedClose mFd; |
michael@0 | 267 | |
michael@0 | 268 | /** |
michael@0 | 269 | * If true, do not requeue whatever task we're running |
michael@0 | 270 | */ |
michael@0 | 271 | bool mShuttingDownOnIOThread; |
michael@0 | 272 | |
michael@0 | 273 | nsString mDeviceAddress; |
michael@0 | 274 | int mChannel; |
michael@0 | 275 | bool mAuth; |
michael@0 | 276 | bool mEncrypt; |
michael@0 | 277 | }; |
michael@0 | 278 | |
michael@0 | 279 | template<class T> |
michael@0 | 280 | class DeleteInstanceRunnable : public nsRunnable |
michael@0 | 281 | { |
michael@0 | 282 | public: |
michael@0 | 283 | DeleteInstanceRunnable(T* aInstance) |
michael@0 | 284 | : mInstance(aInstance) |
michael@0 | 285 | { } |
michael@0 | 286 | |
michael@0 | 287 | NS_IMETHOD Run() |
michael@0 | 288 | { |
michael@0 | 289 | delete mInstance; |
michael@0 | 290 | |
michael@0 | 291 | return NS_OK; |
michael@0 | 292 | } |
michael@0 | 293 | |
michael@0 | 294 | private: |
michael@0 | 295 | T* mInstance; |
michael@0 | 296 | }; |
michael@0 | 297 | |
michael@0 | 298 | class RequestClosingSocketTask : public nsRunnable |
michael@0 | 299 | { |
michael@0 | 300 | public: |
michael@0 | 301 | RequestClosingSocketTask(DroidSocketImpl* aImpl) : mImpl(aImpl) |
michael@0 | 302 | { |
michael@0 | 303 | MOZ_ASSERT(aImpl); |
michael@0 | 304 | } |
michael@0 | 305 | |
michael@0 | 306 | NS_IMETHOD Run() |
michael@0 | 307 | { |
michael@0 | 308 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 309 | |
michael@0 | 310 | if (mImpl->IsShutdownOnMainThread()) { |
michael@0 | 311 | NS_WARNING("CloseSocket has already been called!"); |
michael@0 | 312 | // Since we've already explicitly closed and the close happened before |
michael@0 | 313 | // this, this isn't really an error. Since we've warned, return OK. |
michael@0 | 314 | return NS_OK; |
michael@0 | 315 | } |
michael@0 | 316 | |
michael@0 | 317 | // Start from here, same handling flow as calling CloseSocket() from |
michael@0 | 318 | // upper layer |
michael@0 | 319 | mImpl->mConsumer->CloseDroidSocket(); |
michael@0 | 320 | return NS_OK; |
michael@0 | 321 | } |
michael@0 | 322 | private: |
michael@0 | 323 | DroidSocketImpl* mImpl; |
michael@0 | 324 | }; |
michael@0 | 325 | |
michael@0 | 326 | class ShutdownSocketTask : public Task { |
michael@0 | 327 | virtual void Run() |
michael@0 | 328 | { |
michael@0 | 329 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 330 | |
michael@0 | 331 | // At this point, there should be no new events on the IO thread after this |
michael@0 | 332 | // one with the possible exception of a SocketAcceptTask that |
michael@0 | 333 | // ShutdownOnIOThread will cancel for us. We are now fully shut down, so we |
michael@0 | 334 | // can send a message to the main thread that will delete mImpl safely knowing |
michael@0 | 335 | // that no more tasks reference it. |
michael@0 | 336 | mImpl->ShutdownOnIOThread(); |
michael@0 | 337 | |
michael@0 | 338 | nsRefPtr<nsIRunnable> t(new DeleteInstanceRunnable< |
michael@0 | 339 | mozilla::dom::bluetooth::DroidSocketImpl>(mImpl)); |
michael@0 | 340 | nsresult rv = NS_DispatchToMainThread(t); |
michael@0 | 341 | NS_ENSURE_SUCCESS_VOID(rv); |
michael@0 | 342 | } |
michael@0 | 343 | |
michael@0 | 344 | DroidSocketImpl* mImpl; |
michael@0 | 345 | |
michael@0 | 346 | public: |
michael@0 | 347 | ShutdownSocketTask(DroidSocketImpl* aImpl) : mImpl(aImpl) { } |
michael@0 | 348 | }; |
michael@0 | 349 | |
michael@0 | 350 | class SocketReceiveTask : public nsRunnable |
michael@0 | 351 | { |
michael@0 | 352 | public: |
michael@0 | 353 | SocketReceiveTask(DroidSocketImpl* aImpl, UnixSocketRawData* aData) : |
michael@0 | 354 | mImpl(aImpl), |
michael@0 | 355 | mRawData(aData) |
michael@0 | 356 | { |
michael@0 | 357 | MOZ_ASSERT(aImpl); |
michael@0 | 358 | MOZ_ASSERT(aData); |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | NS_IMETHOD Run() |
michael@0 | 362 | { |
michael@0 | 363 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 364 | if (mImpl->IsShutdownOnMainThread()) { |
michael@0 | 365 | NS_WARNING("mConsumer is null, aborting receive!"); |
michael@0 | 366 | // Since we've already explicitly closed and the close happened before |
michael@0 | 367 | // this, this isn't really an error. Since we've warned, return OK. |
michael@0 | 368 | return NS_OK; |
michael@0 | 369 | } |
michael@0 | 370 | |
michael@0 | 371 | MOZ_ASSERT(mImpl->mConsumer); |
michael@0 | 372 | mImpl->mConsumer->ReceiveSocketData(mRawData); |
michael@0 | 373 | return NS_OK; |
michael@0 | 374 | } |
michael@0 | 375 | private: |
michael@0 | 376 | DroidSocketImpl* mImpl; |
michael@0 | 377 | nsAutoPtr<UnixSocketRawData> mRawData; |
michael@0 | 378 | }; |
michael@0 | 379 | |
michael@0 | 380 | class SocketSendTask : public Task |
michael@0 | 381 | { |
michael@0 | 382 | public: |
michael@0 | 383 | SocketSendTask(BluetoothSocket* aConsumer, DroidSocketImpl* aImpl, |
michael@0 | 384 | UnixSocketRawData* aData) |
michael@0 | 385 | : mConsumer(aConsumer), |
michael@0 | 386 | mImpl(aImpl), |
michael@0 | 387 | mData(aData) |
michael@0 | 388 | { |
michael@0 | 389 | MOZ_ASSERT(aConsumer); |
michael@0 | 390 | MOZ_ASSERT(aImpl); |
michael@0 | 391 | MOZ_ASSERT(aData); |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | void |
michael@0 | 395 | Run() |
michael@0 | 396 | { |
michael@0 | 397 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 398 | MOZ_ASSERT(!mImpl->IsShutdownOnIOThread()); |
michael@0 | 399 | |
michael@0 | 400 | mImpl->QueueWriteData(mData); |
michael@0 | 401 | } |
michael@0 | 402 | |
michael@0 | 403 | private: |
michael@0 | 404 | nsRefPtr<BluetoothSocket> mConsumer; |
michael@0 | 405 | DroidSocketImpl* mImpl; |
michael@0 | 406 | UnixSocketRawData* mData; |
michael@0 | 407 | }; |
michael@0 | 408 | |
michael@0 | 409 | class DroidSocketImplTask : public CancelableTask |
michael@0 | 410 | { |
michael@0 | 411 | public: |
michael@0 | 412 | DroidSocketImpl* GetDroidSocketImpl() const |
michael@0 | 413 | { |
michael@0 | 414 | return mDroidSocketImpl; |
michael@0 | 415 | } |
michael@0 | 416 | void Cancel() MOZ_OVERRIDE |
michael@0 | 417 | { |
michael@0 | 418 | mDroidSocketImpl = nullptr; |
michael@0 | 419 | } |
michael@0 | 420 | bool IsCanceled() const |
michael@0 | 421 | { |
michael@0 | 422 | return !mDroidSocketImpl; |
michael@0 | 423 | } |
michael@0 | 424 | protected: |
michael@0 | 425 | DroidSocketImplTask(DroidSocketImpl* aDroidSocketImpl) |
michael@0 | 426 | : mDroidSocketImpl(aDroidSocketImpl) |
michael@0 | 427 | { |
michael@0 | 428 | MOZ_ASSERT(mDroidSocketImpl); |
michael@0 | 429 | } |
michael@0 | 430 | private: |
michael@0 | 431 | DroidSocketImpl* mDroidSocketImpl; |
michael@0 | 432 | }; |
michael@0 | 433 | |
michael@0 | 434 | class SocketConnectTask : public DroidSocketImplTask |
michael@0 | 435 | { |
michael@0 | 436 | public: |
michael@0 | 437 | SocketConnectTask(DroidSocketImpl* aDroidSocketImpl) |
michael@0 | 438 | : DroidSocketImplTask(aDroidSocketImpl) |
michael@0 | 439 | { } |
michael@0 | 440 | |
michael@0 | 441 | void Run() MOZ_OVERRIDE |
michael@0 | 442 | { |
michael@0 | 443 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 444 | MOZ_ASSERT(!IsCanceled()); |
michael@0 | 445 | GetDroidSocketImpl()->Connect(); |
michael@0 | 446 | } |
michael@0 | 447 | }; |
michael@0 | 448 | |
michael@0 | 449 | class SocketListenTask : public DroidSocketImplTask |
michael@0 | 450 | { |
michael@0 | 451 | public: |
michael@0 | 452 | SocketListenTask(DroidSocketImpl* aDroidSocketImpl) |
michael@0 | 453 | : DroidSocketImplTask(aDroidSocketImpl) |
michael@0 | 454 | { } |
michael@0 | 455 | |
michael@0 | 456 | void Run() MOZ_OVERRIDE |
michael@0 | 457 | { |
michael@0 | 458 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 459 | if (!IsCanceled()) { |
michael@0 | 460 | GetDroidSocketImpl()->Listen(); |
michael@0 | 461 | } |
michael@0 | 462 | } |
michael@0 | 463 | }; |
michael@0 | 464 | |
michael@0 | 465 | class SocketConnectClientFdTask : public Task |
michael@0 | 466 | { |
michael@0 | 467 | virtual void Run() |
michael@0 | 468 | { |
michael@0 | 469 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 470 | mImpl->ConnectClientFd(); |
michael@0 | 471 | } |
michael@0 | 472 | |
michael@0 | 473 | DroidSocketImpl* mImpl; |
michael@0 | 474 | public: |
michael@0 | 475 | SocketConnectClientFdTask(DroidSocketImpl* aImpl) : mImpl(aImpl) { } |
michael@0 | 476 | }; |
michael@0 | 477 | |
michael@0 | 478 | void |
michael@0 | 479 | DroidSocketImpl::Connect() |
michael@0 | 480 | { |
michael@0 | 481 | MOZ_ASSERT(sBluetoothSocketInterface); |
michael@0 | 482 | |
michael@0 | 483 | bt_bdaddr_t remoteBdAddress; |
michael@0 | 484 | StringToBdAddressType(mDeviceAddress, &remoteBdAddress); |
michael@0 | 485 | |
michael@0 | 486 | // TODO: uuid as argument |
michael@0 | 487 | int fd = -1; |
michael@0 | 488 | bt_status_t status = |
michael@0 | 489 | sBluetoothSocketInterface->connect(&remoteBdAddress, |
michael@0 | 490 | BTSOCK_RFCOMM, |
michael@0 | 491 | UUID_OBEX_OBJECT_PUSH, |
michael@0 | 492 | mChannel, |
michael@0 | 493 | &fd, |
michael@0 | 494 | (BTSOCK_FLAG_ENCRYPT * mEncrypt) | |
michael@0 | 495 | (BTSOCK_FLAG_AUTH * mAuth)); |
michael@0 | 496 | NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS); |
michael@0 | 497 | NS_ENSURE_TRUE_VOID(fd >= 0); |
michael@0 | 498 | |
michael@0 | 499 | mFd = fd; |
michael@0 | 500 | |
michael@0 | 501 | MOZ_ASSERT(!mIOLoop); |
michael@0 | 502 | mIOLoop = MessageLoopForIO::current(); |
michael@0 | 503 | |
michael@0 | 504 | // Set up a read watch |
michael@0 | 505 | mIOLoop->WatchFileDescriptor(mFd.get(), |
michael@0 | 506 | true, |
michael@0 | 507 | MessageLoopForIO::WATCH_READ, |
michael@0 | 508 | &mReadWatcher, |
michael@0 | 509 | this); |
michael@0 | 510 | // Set up a write watch |
michael@0 | 511 | mIOLoop->WatchFileDescriptor(mFd.get(), |
michael@0 | 512 | false, |
michael@0 | 513 | MessageLoopForIO::WATCH_WRITE, |
michael@0 | 514 | &mWriteWatcher, |
michael@0 | 515 | this); |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | void |
michael@0 | 519 | DroidSocketImpl::Listen() |
michael@0 | 520 | { |
michael@0 | 521 | MOZ_ASSERT(sBluetoothSocketInterface); |
michael@0 | 522 | |
michael@0 | 523 | // TODO: uuid and service name as arguments |
michael@0 | 524 | |
michael@0 | 525 | int fd = -1; |
michael@0 | 526 | bt_status_t status = |
michael@0 | 527 | sBluetoothSocketInterface->listen(BTSOCK_RFCOMM, |
michael@0 | 528 | "OBEX Object Push", |
michael@0 | 529 | UUID_OBEX_OBJECT_PUSH, |
michael@0 | 530 | mChannel, |
michael@0 | 531 | &fd, |
michael@0 | 532 | (BTSOCK_FLAG_ENCRYPT * mEncrypt) | |
michael@0 | 533 | (BTSOCK_FLAG_AUTH * mAuth)); |
michael@0 | 534 | NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS); |
michael@0 | 535 | NS_ENSURE_TRUE_VOID(fd >= 0); |
michael@0 | 536 | |
michael@0 | 537 | mFd = fd; |
michael@0 | 538 | |
michael@0 | 539 | MOZ_ASSERT(!mIOLoop); |
michael@0 | 540 | mIOLoop = MessageLoopForIO::current(); |
michael@0 | 541 | |
michael@0 | 542 | // Set up a read watch |
michael@0 | 543 | mIOLoop->WatchFileDescriptor(mFd.get(), |
michael@0 | 544 | true, |
michael@0 | 545 | MessageLoopForIO::WATCH_READ, |
michael@0 | 546 | &mReadWatcher, |
michael@0 | 547 | this); |
michael@0 | 548 | } |
michael@0 | 549 | |
michael@0 | 550 | ssize_t |
michael@0 | 551 | DroidSocketImpl::ReadMsg(int aFd, void *aBuffer, size_t aLength) |
michael@0 | 552 | { |
michael@0 | 553 | ssize_t ret; |
michael@0 | 554 | struct msghdr msg; |
michael@0 | 555 | struct iovec iv; |
michael@0 | 556 | struct cmsghdr cmsgbuf[2 * sizeof(cmsghdr) + 0x100]; |
michael@0 | 557 | |
michael@0 | 558 | memset(&msg, 0, sizeof(msg)); |
michael@0 | 559 | memset(&iv, 0, sizeof(iv)); |
michael@0 | 560 | |
michael@0 | 561 | iv.iov_base = (unsigned char *)aBuffer; |
michael@0 | 562 | iv.iov_len = aLength; |
michael@0 | 563 | |
michael@0 | 564 | msg.msg_iov = &iv; |
michael@0 | 565 | msg.msg_iovlen = 1; |
michael@0 | 566 | msg.msg_control = cmsgbuf; |
michael@0 | 567 | msg.msg_controllen = sizeof(cmsgbuf); |
michael@0 | 568 | |
michael@0 | 569 | ret = recvmsg(mFd.get(), &msg, MSG_NOSIGNAL); |
michael@0 | 570 | if (ret < 0 && errno == EPIPE) { |
michael@0 | 571 | // Treat this as an end of stream |
michael@0 | 572 | return 0; |
michael@0 | 573 | } |
michael@0 | 574 | |
michael@0 | 575 | NS_ENSURE_FALSE(ret < 0, -1); |
michael@0 | 576 | NS_ENSURE_FALSE(msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE), -1); |
michael@0 | 577 | |
michael@0 | 578 | // Extract client fd from message header |
michael@0 | 579 | for (struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg); |
michael@0 | 580 | cmsgptr != nullptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { |
michael@0 | 581 | if (cmsgptr->cmsg_level != SOL_SOCKET) { |
michael@0 | 582 | continue; |
michael@0 | 583 | } |
michael@0 | 584 | if (cmsgptr->cmsg_type == SCM_RIGHTS) { |
michael@0 | 585 | int *pDescriptors = (int *)CMSG_DATA(cmsgptr); |
michael@0 | 586 | // Overwrite fd with client fd |
michael@0 | 587 | mFd.reset(pDescriptors[0]); |
michael@0 | 588 | break; |
michael@0 | 589 | } |
michael@0 | 590 | } |
michael@0 | 591 | |
michael@0 | 592 | return ret; |
michael@0 | 593 | } |
michael@0 | 594 | |
michael@0 | 595 | void |
michael@0 | 596 | DroidSocketImpl::OnFileCanReadWithoutBlocking(int aFd) |
michael@0 | 597 | { |
michael@0 | 598 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 599 | MOZ_ASSERT(!mShuttingDownOnIOThread); |
michael@0 | 600 | |
michael@0 | 601 | // Read all of the incoming data. |
michael@0 | 602 | while (true) { |
michael@0 | 603 | nsAutoPtr<UnixSocketRawData> incoming(new UnixSocketRawData(MAX_READ_SIZE)); |
michael@0 | 604 | |
michael@0 | 605 | ssize_t ret; |
michael@0 | 606 | if (!mReadMsgForClientFd) { |
michael@0 | 607 | ret = read(aFd, incoming->mData, incoming->mSize); |
michael@0 | 608 | } else { |
michael@0 | 609 | ret = ReadMsg(aFd, incoming->mData, incoming->mSize); |
michael@0 | 610 | } |
michael@0 | 611 | |
michael@0 | 612 | if (ret <= 0) { |
michael@0 | 613 | if (ret == -1) { |
michael@0 | 614 | if (errno == EINTR) { |
michael@0 | 615 | continue; // retry system call when interrupted |
michael@0 | 616 | } |
michael@0 | 617 | if (errno == EAGAIN || errno == EWOULDBLOCK) { |
michael@0 | 618 | return; // no data available: return and re-poll |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | BT_WARNING("Cannot read from network"); |
michael@0 | 622 | // else fall through to error handling on other errno's |
michael@0 | 623 | } |
michael@0 | 624 | |
michael@0 | 625 | // We're done with our descriptors. Ensure that spurious events don't |
michael@0 | 626 | // cause us to end up back here. |
michael@0 | 627 | mReadWatcher.StopWatchingFileDescriptor(); |
michael@0 | 628 | mWriteWatcher.StopWatchingFileDescriptor(); |
michael@0 | 629 | nsRefPtr<RequestClosingSocketTask> t = new RequestClosingSocketTask(this); |
michael@0 | 630 | NS_DispatchToMainThread(t); |
michael@0 | 631 | return; |
michael@0 | 632 | } |
michael@0 | 633 | |
michael@0 | 634 | incoming->mSize = ret; |
michael@0 | 635 | nsRefPtr<SocketReceiveTask> t = |
michael@0 | 636 | new SocketReceiveTask(this, incoming.forget()); |
michael@0 | 637 | NS_DispatchToMainThread(t); |
michael@0 | 638 | |
michael@0 | 639 | // If ret is less than MAX_READ_SIZE, there's no |
michael@0 | 640 | // more data in the socket for us to read now. |
michael@0 | 641 | if (ret < ssize_t(MAX_READ_SIZE)) { |
michael@0 | 642 | return; |
michael@0 | 643 | } |
michael@0 | 644 | } |
michael@0 | 645 | |
michael@0 | 646 | MOZ_CRASH("We returned early"); |
michael@0 | 647 | } |
michael@0 | 648 | |
michael@0 | 649 | void |
michael@0 | 650 | DroidSocketImpl::OnFileCanWriteWithoutBlocking(int aFd) |
michael@0 | 651 | { |
michael@0 | 652 | MOZ_ASSERT(!NS_IsMainThread()); |
michael@0 | 653 | MOZ_ASSERT(!mShuttingDownOnIOThread); |
michael@0 | 654 | MOZ_ASSERT(aFd >= 0); |
michael@0 | 655 | |
michael@0 | 656 | // Try to write the bytes of mCurrentRilRawData. If all were written, continue. |
michael@0 | 657 | // |
michael@0 | 658 | // Otherwise, save the byte position of the next byte to write |
michael@0 | 659 | // within mCurrentWriteOffset, and request another write when the |
michael@0 | 660 | // system won't block. |
michael@0 | 661 | // |
michael@0 | 662 | while (true) { |
michael@0 | 663 | UnixSocketRawData* data; |
michael@0 | 664 | if (mOutgoingQ.IsEmpty()) { |
michael@0 | 665 | return; |
michael@0 | 666 | } |
michael@0 | 667 | data = mOutgoingQ.ElementAt(0); |
michael@0 | 668 | const uint8_t *toWrite; |
michael@0 | 669 | toWrite = data->mData; |
michael@0 | 670 | |
michael@0 | 671 | while (data->mCurrentWriteOffset < data->mSize) { |
michael@0 | 672 | ssize_t write_amount = data->mSize - data->mCurrentWriteOffset; |
michael@0 | 673 | ssize_t written; |
michael@0 | 674 | written = write (aFd, toWrite + data->mCurrentWriteOffset, |
michael@0 | 675 | write_amount); |
michael@0 | 676 | if (written > 0) { |
michael@0 | 677 | data->mCurrentWriteOffset += written; |
michael@0 | 678 | } |
michael@0 | 679 | if (written != write_amount) { |
michael@0 | 680 | break; |
michael@0 | 681 | } |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | if (data->mCurrentWriteOffset != data->mSize) { |
michael@0 | 685 | MessageLoopForIO::current()->WatchFileDescriptor( |
michael@0 | 686 | aFd, |
michael@0 | 687 | false, |
michael@0 | 688 | MessageLoopForIO::WATCH_WRITE, |
michael@0 | 689 | &mWriteWatcher, |
michael@0 | 690 | this); |
michael@0 | 691 | return; |
michael@0 | 692 | } |
michael@0 | 693 | mOutgoingQ.RemoveElementAt(0); |
michael@0 | 694 | delete data; |
michael@0 | 695 | } |
michael@0 | 696 | } |
michael@0 | 697 | |
michael@0 | 698 | BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver, |
michael@0 | 699 | BluetoothSocketType aType, |
michael@0 | 700 | bool aAuth, |
michael@0 | 701 | bool aEncrypt) |
michael@0 | 702 | : mObserver(aObserver) |
michael@0 | 703 | , mImpl(nullptr) |
michael@0 | 704 | , mAuth(aAuth) |
michael@0 | 705 | , mEncrypt(aEncrypt) |
michael@0 | 706 | , mReceivedSocketInfoLength(0) |
michael@0 | 707 | { |
michael@0 | 708 | MOZ_ASSERT(aObserver); |
michael@0 | 709 | |
michael@0 | 710 | EnsureBluetoothSocketHalLoad(); |
michael@0 | 711 | mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE); |
michael@0 | 712 | } |
michael@0 | 713 | |
michael@0 | 714 | void |
michael@0 | 715 | BluetoothSocket::CloseDroidSocket() |
michael@0 | 716 | { |
michael@0 | 717 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 718 | if (!mImpl) { |
michael@0 | 719 | return; |
michael@0 | 720 | } |
michael@0 | 721 | |
michael@0 | 722 | // From this point on, we consider mImpl as being deleted. |
michael@0 | 723 | // We sever the relationship here so any future calls to listen or connect |
michael@0 | 724 | // will create a new implementation. |
michael@0 | 725 | mImpl->ShutdownOnMainThread(); |
michael@0 | 726 | XRE_GetIOMessageLoop()->PostTask(FROM_HERE, |
michael@0 | 727 | new ShutdownSocketTask(mImpl)); |
michael@0 | 728 | mImpl = nullptr; |
michael@0 | 729 | |
michael@0 | 730 | OnDisconnect(); |
michael@0 | 731 | } |
michael@0 | 732 | |
michael@0 | 733 | bool |
michael@0 | 734 | BluetoothSocket::Connect(const nsAString& aDeviceAddress, int aChannel) |
michael@0 | 735 | { |
michael@0 | 736 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 737 | NS_ENSURE_FALSE(mImpl, false); |
michael@0 | 738 | |
michael@0 | 739 | mIsServer = false; |
michael@0 | 740 | mImpl = new DroidSocketImpl(this, aDeviceAddress, aChannel, mAuth, mEncrypt); |
michael@0 | 741 | XRE_GetIOMessageLoop()->PostTask(FROM_HERE, |
michael@0 | 742 | new SocketConnectTask(mImpl)); |
michael@0 | 743 | |
michael@0 | 744 | return true; |
michael@0 | 745 | } |
michael@0 | 746 | |
michael@0 | 747 | bool |
michael@0 | 748 | BluetoothSocket::Listen(int aChannel) |
michael@0 | 749 | { |
michael@0 | 750 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 751 | NS_ENSURE_FALSE(mImpl, false); |
michael@0 | 752 | |
michael@0 | 753 | mIsServer = true; |
michael@0 | 754 | mImpl = new DroidSocketImpl(this, aChannel, mAuth, mEncrypt); |
michael@0 | 755 | XRE_GetIOMessageLoop()->PostTask(FROM_HERE, |
michael@0 | 756 | new SocketListenTask(mImpl)); |
michael@0 | 757 | return true; |
michael@0 | 758 | } |
michael@0 | 759 | |
michael@0 | 760 | bool |
michael@0 | 761 | BluetoothSocket::SendDroidSocketData(UnixSocketRawData* aData) |
michael@0 | 762 | { |
michael@0 | 763 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 764 | NS_ENSURE_TRUE(mImpl, false); |
michael@0 | 765 | |
michael@0 | 766 | MOZ_ASSERT(!mImpl->IsShutdownOnMainThread()); |
michael@0 | 767 | XRE_GetIOMessageLoop()->PostTask(FROM_HERE, |
michael@0 | 768 | new SocketSendTask(this, mImpl, aData)); |
michael@0 | 769 | return true; |
michael@0 | 770 | } |
michael@0 | 771 | |
michael@0 | 772 | bool |
michael@0 | 773 | BluetoothSocket::ReceiveSocketInfo(nsAutoPtr<UnixSocketRawData>& aMessage) |
michael@0 | 774 | { |
michael@0 | 775 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 776 | |
michael@0 | 777 | /** |
michael@0 | 778 | * 2 socket info messages (20 bytes) to receive at the beginning: |
michael@0 | 779 | * - 1st message: [channel:4] |
michael@0 | 780 | * - 2nd message: [size:2][bd address:6][channel:4][connection status:4] |
michael@0 | 781 | */ |
michael@0 | 782 | if (mReceivedSocketInfoLength >= TOTAL_SOCKET_INFO_LENGTH) { |
michael@0 | 783 | // We've got both socket info messages |
michael@0 | 784 | return false; |
michael@0 | 785 | } |
michael@0 | 786 | mReceivedSocketInfoLength += aMessage->mSize; |
michael@0 | 787 | |
michael@0 | 788 | size_t offset = 0; |
michael@0 | 789 | if (mReceivedSocketInfoLength == FIRST_SOCKET_INFO_MSG_LENGTH) { |
michael@0 | 790 | // 1st message: [channel:4] |
michael@0 | 791 | int32_t channel = ReadInt32(aMessage->mData, &offset); |
michael@0 | 792 | BT_LOGR("channel %d", channel); |
michael@0 | 793 | |
michael@0 | 794 | // If this is server socket, read header of next message for client fd |
michael@0 | 795 | mImpl->mReadMsgForClientFd = mIsServer; |
michael@0 | 796 | } else if (mReceivedSocketInfoLength == TOTAL_SOCKET_INFO_LENGTH) { |
michael@0 | 797 | // 2nd message: [size:2][bd address:6][channel:4][connection status:4] |
michael@0 | 798 | int16_t size = ReadInt16(aMessage->mData, &offset); |
michael@0 | 799 | ReadBdAddress(aMessage->mData, &offset, mDeviceAddress); |
michael@0 | 800 | int32_t channel = ReadInt32(aMessage->mData, &offset); |
michael@0 | 801 | int32_t connectionStatus = ReadInt32(aMessage->mData, &offset); |
michael@0 | 802 | |
michael@0 | 803 | BT_LOGR("size %d channel %d remote addr %s status %d", |
michael@0 | 804 | size, channel, NS_ConvertUTF16toUTF8(mDeviceAddress).get(), connectionStatus); |
michael@0 | 805 | |
michael@0 | 806 | if (connectionStatus != 0) { |
michael@0 | 807 | OnConnectError(); |
michael@0 | 808 | return true; |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | if (mIsServer) { |
michael@0 | 812 | mImpl->mReadMsgForClientFd = false; |
michael@0 | 813 | // Connect client fd on IO thread |
michael@0 | 814 | XRE_GetIOMessageLoop()->PostTask(FROM_HERE, |
michael@0 | 815 | new SocketConnectClientFdTask(mImpl)); |
michael@0 | 816 | } |
michael@0 | 817 | OnConnectSuccess(); |
michael@0 | 818 | } |
michael@0 | 819 | |
michael@0 | 820 | return true; |
michael@0 | 821 | } |
michael@0 | 822 | |
michael@0 | 823 | void |
michael@0 | 824 | BluetoothSocket::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage) |
michael@0 | 825 | { |
michael@0 | 826 | if (ReceiveSocketInfo(aMessage)) { |
michael@0 | 827 | return; |
michael@0 | 828 | } |
michael@0 | 829 | |
michael@0 | 830 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 831 | MOZ_ASSERT(mObserver); |
michael@0 | 832 | mObserver->ReceiveSocketData(this, aMessage); |
michael@0 | 833 | } |
michael@0 | 834 | |
michael@0 | 835 | void |
michael@0 | 836 | BluetoothSocket::OnConnectSuccess() |
michael@0 | 837 | { |
michael@0 | 838 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 839 | MOZ_ASSERT(mObserver); |
michael@0 | 840 | mObserver->OnSocketConnectSuccess(this); |
michael@0 | 841 | } |
michael@0 | 842 | |
michael@0 | 843 | void |
michael@0 | 844 | BluetoothSocket::OnConnectError() |
michael@0 | 845 | { |
michael@0 | 846 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 847 | MOZ_ASSERT(mObserver); |
michael@0 | 848 | mObserver->OnSocketConnectError(this); |
michael@0 | 849 | } |
michael@0 | 850 | |
michael@0 | 851 | void |
michael@0 | 852 | BluetoothSocket::OnDisconnect() |
michael@0 | 853 | { |
michael@0 | 854 | MOZ_ASSERT(NS_IsMainThread()); |
michael@0 | 855 | MOZ_ASSERT(mObserver); |
michael@0 | 856 | mObserver->OnSocketDisconnect(this); |
michael@0 | 857 | } |
michael@0 | 858 |