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