dom/bluetooth/bluedroid/BluetoothSocket.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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

mercurial