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
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "base/basictypes.h"
8 #include "BluetoothOppManager.h"
10 #include "BluetoothService.h"
11 #include "BluetoothSocket.h"
12 #include "BluetoothUtils.h"
13 #include "BluetoothUuid.h"
14 #include "ObexBase.h"
16 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
17 #include "mozilla/RefPtr.h"
18 #include "mozilla/Services.h"
19 #include "mozilla/StaticPtr.h"
20 #include "nsAutoPtr.h"
21 #include "nsCExternalHandlerService.h"
22 #include "nsIObserver.h"
23 #include "nsIObserverService.h"
24 #include "nsIDOMFile.h"
25 #include "nsIFile.h"
26 #include "nsIInputStream.h"
27 #include "nsIMIMEService.h"
28 #include "nsIOutputStream.h"
29 #include "nsIVolumeService.h"
30 #include "nsNetUtil.h"
31 #include "nsServiceManagerUtils.h"
33 #define TARGET_SUBDIR "Download/Bluetooth/"
35 USING_BLUETOOTH_NAMESPACE
36 using namespace mozilla;
37 using namespace mozilla::ipc;
39 namespace {
40 // Sending system message "bluetooth-opp-update-progress" every 50kb
41 static const uint32_t kUpdateProgressBase = 50 * 1024;
43 /*
44 * The format of the header of an PUT request is
45 * [opcode:1][packet length:2][headerId:1][header length:2]
46 */
47 static const uint32_t kPutRequestHeaderSize = 6;
49 /*
50 * The format of the appended header of an PUT request is
51 * [headerId:1][header length:4]
52 * P.S. Length of name header is 4 since unicode is 2 bytes per char.
53 */
54 static const uint32_t kPutRequestAppendHeaderSize = 5;
56 StaticRefPtr<BluetoothOppManager> sBluetoothOppManager;
57 static bool sInShutdown = false;
58 }
60 class mozilla::dom::bluetooth::SendFileBatch {
61 public:
62 SendFileBatch(const nsAString& aDeviceAddress, nsIDOMBlob* aBlob)
63 : mDeviceAddress(aDeviceAddress)
64 {
65 mBlobs.AppendElement(aBlob);
66 }
68 nsString mDeviceAddress;
69 nsCOMArray<nsIDOMBlob> mBlobs;
70 };
72 NS_IMETHODIMP
73 BluetoothOppManager::Observe(nsISupports* aSubject,
74 const char* aTopic,
75 const char16_t* aData)
76 {
77 MOZ_ASSERT(sBluetoothOppManager);
79 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
80 HandleShutdown();
81 return NS_OK;
82 }
84 MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!");
85 return NS_ERROR_UNEXPECTED;
86 }
88 class SendSocketDataTask : public nsRunnable
89 {
90 public:
91 SendSocketDataTask(uint8_t* aStream, uint32_t aSize)
92 : mStream(aStream)
93 , mSize(aSize)
94 {
95 MOZ_ASSERT(!NS_IsMainThread());
96 }
98 NS_IMETHOD Run()
99 {
100 MOZ_ASSERT(NS_IsMainThread());
102 sBluetoothOppManager->SendPutRequest(mStream, mSize);
104 return NS_OK;
105 }
107 private:
108 nsAutoArrayPtr<uint8_t> mStream;
109 uint32_t mSize;
110 };
112 class ReadFileTask : public nsRunnable
113 {
114 public:
115 ReadFileTask(nsIInputStream* aInputStream,
116 uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream)
117 {
118 MOZ_ASSERT(NS_IsMainThread());
120 mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize;
121 }
123 NS_IMETHOD Run()
124 {
125 MOZ_ASSERT(!NS_IsMainThread());
127 uint32_t numRead;
128 nsAutoArrayPtr<char> buf(new char[mAvailablePacketSize]);
130 // function inputstream->Read() only works on non-main thread
131 nsresult rv = mInputStream->Read(buf, mAvailablePacketSize, &numRead);
132 if (NS_FAILED(rv)) {
133 // Needs error handling here
134 BT_WARNING("Failed to read from input stream");
135 return NS_ERROR_FAILURE;
136 }
138 if (numRead > 0) {
139 sBluetoothOppManager->CheckPutFinal(numRead);
141 nsRefPtr<SendSocketDataTask> task =
142 new SendSocketDataTask((uint8_t*)buf.forget(), numRead);
143 if (NS_FAILED(NS_DispatchToMainThread(task))) {
144 BT_WARNING("Failed to dispatch to main thread!");
145 return NS_ERROR_FAILURE;
146 }
147 }
149 return NS_OK;
150 };
152 private:
153 nsCOMPtr<nsIInputStream> mInputStream;
154 uint32_t mAvailablePacketSize;
155 };
157 class CloseSocketTask : public Task
158 {
159 public:
160 CloseSocketTask(BluetoothSocket* aSocket) : mSocket(aSocket)
161 {
162 MOZ_ASSERT(aSocket);
163 }
165 void Run() MOZ_OVERRIDE
166 {
167 MOZ_ASSERT(NS_IsMainThread());
168 mSocket->CloseDroidSocket();
169 }
171 private:
172 nsRefPtr<BluetoothSocket> mSocket;
173 };
175 BluetoothOppManager::BluetoothOppManager() : mConnected(false)
176 , mRemoteObexVersion(0)
177 , mRemoteConnectionFlags(0)
178 , mRemoteMaxPacketLength(0)
179 , mLastCommand(0)
180 , mPacketLength(0)
181 , mPutPacketReceivedLength(0)
182 , mBodySegmentLength(0)
183 , mAbortFlag(false)
184 , mNewFileFlag(false)
185 , mPutFinalFlag(false)
186 , mSendTransferCompleteFlag(false)
187 , mSuccessFlag(false)
188 , mIsServer(true)
189 , mWaitingForConfirmationFlag(false)
190 , mFileLength(0)
191 , mSentFileLength(0)
192 , mWaitingToSendPutFinal(false)
193 , mCurrentBlobIndex(-1)
194 {
195 mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
196 }
198 BluetoothOppManager::~BluetoothOppManager()
199 {
200 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
201 NS_ENSURE_TRUE_VOID(obs);
202 if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
203 BT_WARNING("Failed to remove shutdown observer!");
204 }
205 }
207 bool
208 BluetoothOppManager::Init()
209 {
210 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
211 NS_ENSURE_TRUE(obs, false);
212 if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
213 BT_WARNING("Failed to add shutdown observer!");
214 return false;
215 }
217 /**
218 * We don't start listening here as BluetoothServiceBluedroid calls Listen()
219 * immediately when BT stops.
220 *
221 * If we start listening here, the listening fails when device boots up since
222 * Listen() is called again and restarts server socket. The restart causes
223 * absence of read events when device boots up.
224 */
226 return true;
227 }
229 //static
230 BluetoothOppManager*
231 BluetoothOppManager::Get()
232 {
233 MOZ_ASSERT(NS_IsMainThread());
235 // If sBluetoothOppManager already exists, exit early
236 if (sBluetoothOppManager) {
237 return sBluetoothOppManager;
238 }
240 // If we're in shutdown, don't create a new instance
241 NS_ENSURE_FALSE(sInShutdown, nullptr);
243 // Create a new instance, register, and return
244 BluetoothOppManager *manager = new BluetoothOppManager();
245 NS_ENSURE_TRUE(manager->Init(), nullptr);
247 sBluetoothOppManager = manager;
248 return sBluetoothOppManager;
249 }
251 void
252 BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress)
253 {
254 MOZ_ASSERT(NS_IsMainThread());
256 // Stop listening because currently we only support one connection at a time.
257 if (mServerSocket) {
258 mServerSocket->Disconnect();
259 mServerSocket = nullptr;
260 }
262 mIsServer = false;
264 BluetoothService* bs = BluetoothService::Get();
265 if (!bs || sInShutdown || mSocket) {
266 OnSocketConnectError(mSocket);
267 return;
268 }
270 mSocket =
271 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, false, true);
272 mSocket->Connect(aDeviceAddress, -1);
273 }
275 void
276 BluetoothOppManager::HandleShutdown()
277 {
278 MOZ_ASSERT(NS_IsMainThread());
279 sInShutdown = true;
280 Disconnect(nullptr);
281 sBluetoothOppManager = nullptr;
282 }
284 bool
285 BluetoothOppManager::Listen()
286 {
287 MOZ_ASSERT(NS_IsMainThread());
289 if (mSocket) {
290 BT_WARNING("mSocket exists. Failed to listen.");
291 return false;
292 }
294 /**
295 * Restart server socket since its underlying fd becomes invalid when
296 * BT stops; otherwise no more read events would be received even if
297 * BT restarts.
298 */
299 if (mServerSocket) {
300 mServerSocket->Disconnect();
301 mServerSocket = nullptr;
302 }
304 mServerSocket =
305 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, false, true);
307 if (!mServerSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH)) {
308 BT_WARNING("[OPP] Can't listen on RFCOMM socket!");
309 mServerSocket = nullptr;
310 return false;
311 }
313 mIsServer = true;
315 return true;
316 }
318 void
319 BluetoothOppManager::StartSendingNextFile()
320 {
321 MOZ_ASSERT(NS_IsMainThread());
323 MOZ_ASSERT(!IsConnected());
324 MOZ_ASSERT(!mBatches.IsEmpty());
325 MOZ_ASSERT((int)mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1);
327 mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
329 // Before sending content, we have to send a header including
330 // information such as file name, file length and content type.
331 ExtractBlobHeaders();
332 StartFileTransfer();
334 if (mCurrentBlobIndex == 0) {
335 // We may have more than one file waiting for transferring, but only one
336 // CONNECT request would be sent. Therefore check if this is the very first
337 // file at the head of queue.
338 SendConnectRequest();
339 } else {
340 SendPutHeaderRequest(mFileName, mFileLength);
341 AfterFirstPut();
342 }
343 }
345 bool
346 BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
347 BlobParent* aActor)
348 {
349 MOZ_ASSERT(NS_IsMainThread());
351 nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
353 return SendFile(aDeviceAddress, blob.get());
354 }
356 bool
357 BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
358 nsIDOMBlob* aBlob)
359 {
360 MOZ_ASSERT(NS_IsMainThread());
362 AppendBlobToSend(aDeviceAddress, aBlob);
363 if (!mSocket) {
364 ProcessNextBatch();
365 }
367 return true;
368 }
370 void
371 BluetoothOppManager::AppendBlobToSend(const nsAString& aDeviceAddress,
372 nsIDOMBlob* aBlob)
373 {
374 MOZ_ASSERT(NS_IsMainThread());
376 int indexTail = mBatches.Length() - 1;
378 /**
379 * Create a new batch if
380 * - mBatches is empty, or
381 * - aDeviceAddress differs from mDeviceAddress of the last batch
382 */
383 if (mBatches.IsEmpty() ||
384 aDeviceAddress != mBatches[indexTail].mDeviceAddress) {
385 SendFileBatch batch(aDeviceAddress, aBlob);
386 mBatches.AppendElement(batch);
387 } else {
388 mBatches[indexTail].mBlobs.AppendElement(aBlob);
389 }
390 }
392 void
393 BluetoothOppManager::DiscardBlobsToSend()
394 {
395 MOZ_ASSERT(NS_IsMainThread());
397 MOZ_ASSERT(!mBatches.IsEmpty());
398 MOZ_ASSERT(!mIsServer);
400 int length = (int) mBatches[0].mBlobs.Length();
401 while (length > mCurrentBlobIndex + 1) {
402 mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
404 BT_LOGR("idx %d", mCurrentBlobIndex);
405 ExtractBlobHeaders();
406 StartFileTransfer();
407 FileTransferComplete();
408 }
409 }
411 bool
412 BluetoothOppManager::ProcessNextBatch()
413 {
414 MOZ_ASSERT(NS_IsMainThread());
416 // Remove the processed batch.
417 // A batch is processed if we've incremented mCurrentBlobIndex for it.
418 if (mCurrentBlobIndex >= 0) {
419 ClearQueue();
420 mBatches.RemoveElementAt(0);
421 BT_LOGR("REMOVE. %d remaining", mBatches.Length());
422 }
424 // Process the next batch
425 if (!mBatches.IsEmpty()) {
426 ConnectInternal(mBatches[0].mDeviceAddress);
427 return true;
428 }
430 // No more batch to process
431 return false;
432 }
434 void
435 BluetoothOppManager::ClearQueue()
436 {
437 MOZ_ASSERT(NS_IsMainThread());
439 MOZ_ASSERT(!mIsServer);
440 MOZ_ASSERT(!mBatches.IsEmpty());
441 MOZ_ASSERT(!mBatches[0].mBlobs.IsEmpty());
443 mCurrentBlobIndex = -1;
444 mBlob = nullptr;
445 mBatches[0].mBlobs.Clear();
446 }
448 bool
449 BluetoothOppManager::StopSendingFile()
450 {
451 MOZ_ASSERT(NS_IsMainThread());
453 if (mIsServer) {
454 mAbortFlag = true;
455 } else {
456 Disconnect(nullptr);
457 }
459 return true;
460 }
462 bool
463 BluetoothOppManager::ConfirmReceivingFile(bool aConfirm)
464 {
465 NS_ENSURE_TRUE(mConnected, false);
466 NS_ENSURE_TRUE(mWaitingForConfirmationFlag, false);
468 mWaitingForConfirmationFlag = false;
470 // For the first packet of first file
471 bool success = false;
472 if (aConfirm) {
473 StartFileTransfer();
474 if (CreateFile()) {
475 success = WriteToFile(mBodySegment.get(), mBodySegmentLength);
476 }
477 }
479 if (success && mPutFinalFlag) {
480 mSuccessFlag = true;
481 FileTransferComplete();
482 NotifyAboutFileChange();
483 }
485 ReplyToPut(mPutFinalFlag, success);
487 return true;
488 }
490 void
491 BluetoothOppManager::AfterFirstPut()
492 {
493 mUpdateProgressCounter = 1;
494 mPutFinalFlag = false;
495 mPutPacketReceivedLength = 0;
496 mSentFileLength = 0;
497 mWaitingToSendPutFinal = false;
498 mSuccessFlag = false;
499 mBodySegmentLength = 0;
500 }
502 void
503 BluetoothOppManager::AfterOppConnected()
504 {
505 MOZ_ASSERT(NS_IsMainThread());
507 mConnected = true;
508 mAbortFlag = false;
509 mWaitingForConfirmationFlag = true;
510 AfterFirstPut();
511 // Get a mount lock to prevent the sdcard from being shared with
512 // the PC while we're doing a OPP file transfer. After OPP transaction
513 // were done, the mount lock will be freed.
514 if (!AcquireSdcardMountLock()) {
515 // If we fail to get a mount lock, abort this transaction
516 // Directly sending disconnect-request is better than abort-request
517 BT_WARNING("BluetoothOPPManager couldn't get a mount lock!");
518 Disconnect(nullptr);
519 }
520 }
522 void
523 BluetoothOppManager::AfterOppDisconnected()
524 {
525 MOZ_ASSERT(NS_IsMainThread());
527 mConnected = false;
528 mLastCommand = 0;
529 mPutPacketReceivedLength = 0;
530 mDsFile = nullptr;
532 // We can't reset mSuccessFlag here since this function may be called
533 // before we send system message of transfer complete
534 // mSuccessFlag = false;
536 if (mInputStream) {
537 mInputStream->Close();
538 mInputStream = nullptr;
539 }
541 if (mOutputStream) {
542 mOutputStream->Close();
543 mOutputStream = nullptr;
544 }
546 if (mReadFileThread) {
547 mReadFileThread->Shutdown();
548 mReadFileThread = nullptr;
549 }
550 // Release the Mount lock if file transfer completed
551 if (mMountLock) {
552 // The mount lock will be implicitly unlocked
553 mMountLock = nullptr;
554 }
555 }
557 void
558 BluetoothOppManager::DeleteReceivedFile()
559 {
560 if (mOutputStream) {
561 mOutputStream->Close();
562 mOutputStream = nullptr;
563 }
565 if (mDsFile && mDsFile->mFile) {
566 mDsFile->mFile->Remove(false);
567 mDsFile = nullptr;
568 }
569 }
571 bool
572 BluetoothOppManager::CreateFile()
573 {
574 MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength);
576 nsString path;
577 path.AssignLiteral(TARGET_SUBDIR);
578 path.Append(mFileName);
580 mDsFile = DeviceStorageFile::CreateUnique(
581 path, nsIFile::NORMAL_FILE_TYPE, 0644);
582 NS_ENSURE_TRUE(mDsFile, false);
584 nsCOMPtr<nsIFile> f;
585 mDsFile->mFile->Clone(getter_AddRefs(f));
587 /*
588 * The function CreateUnique() may create a file with a different file
589 * name from the original mFileName. Therefore we have to retrieve
590 * the file name again.
591 */
592 f->GetLeafName(mFileName);
594 NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
595 NS_ENSURE_TRUE(mOutputStream, false);
597 return true;
598 }
600 bool
601 BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
602 {
603 NS_ENSURE_TRUE(mOutputStream, false);
605 uint32_t wrote = 0;
606 mOutputStream->Write((const char*)aData, aDataLength, &wrote);
607 NS_ENSURE_TRUE(aDataLength == (int) wrote, false);
609 return true;
610 }
612 // Virtual function of class SocketConsumer
613 void
614 BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
615 {
616 if (aHeader.Has(ObexHeaderId::Name)) {
617 aHeader.GetName(mFileName);
618 }
620 if (aHeader.Has(ObexHeaderId::Type)) {
621 aHeader.GetContentType(mContentType);
622 }
624 if (aHeader.Has(ObexHeaderId::Length)) {
625 aHeader.GetLength(&mFileLength);
626 }
628 if (aHeader.Has(ObexHeaderId::Body) ||
629 aHeader.Has(ObexHeaderId::EndOfBody)) {
630 uint8_t* bodyPtr;
631 aHeader.GetBody(&bodyPtr);
632 mBodySegment = bodyPtr;
634 aHeader.GetBodyLength(&mBodySegmentLength);
635 }
636 }
638 bool
639 BluetoothOppManager::ExtractBlobHeaders()
640 {
641 RetrieveSentFileName();
643 nsresult rv = mBlob->GetType(mContentType);
644 if (NS_FAILED(rv)) {
645 BT_WARNING("Can't get content type");
646 SendDisconnectRequest();
647 return false;
648 }
650 uint64_t fileLength;
651 rv = mBlob->GetSize(&fileLength);
652 if (NS_FAILED(rv)) {
653 BT_WARNING("Can't get file size");
654 SendDisconnectRequest();
655 return false;
656 }
658 // Currently we keep the size of files which were sent/received via
659 // Bluetooth not exceed UINT32_MAX because the Length header in OBEX
660 // is only 4-byte long. Although it is possible to transfer a file
661 // larger than UINT32_MAX, it needs to parse another OBEX Header
662 // and I would like to leave it as a feature.
663 if (fileLength > (uint64_t)UINT32_MAX) {
664 BT_WARNING("The file size is too large for now");
665 SendDisconnectRequest();
666 return false;
667 }
669 mFileLength = fileLength;
670 rv = NS_NewThread(getter_AddRefs(mReadFileThread));
671 if (NS_FAILED(rv)) {
672 BT_WARNING("Can't create thread");
673 SendDisconnectRequest();
674 return false;
675 }
677 return true;
678 }
680 void
681 BluetoothOppManager::RetrieveSentFileName()
682 {
683 mFileName.Truncate();
685 nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
686 if (file) {
687 file->GetName(mFileName);
688 }
690 /**
691 * We try our best to get the file extention to avoid interoperability issues.
692 * However, once we found that we are unable to get suitable extension or
693 * information about the content type, sending a pre-defined file name without
694 * extension would be fine.
695 */
696 if (mFileName.IsEmpty()) {
697 mFileName.AssignLiteral("Unknown");
698 }
700 int32_t offset = mFileName.RFindChar('/');
701 if (offset != kNotFound) {
702 mFileName = Substring(mFileName, offset + 1);
703 }
705 offset = mFileName.RFindChar('.');
706 if (offset == kNotFound) {
707 nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
709 if (mimeSvc) {
710 nsString mimeType;
711 mBlob->GetType(mimeType);
713 nsCString extension;
714 nsresult rv =
715 mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
716 EmptyCString(),
717 extension);
718 if (NS_SUCCEEDED(rv)) {
719 mFileName.AppendLiteral(".");
720 AppendUTF8toUTF16(extension, mFileName);
721 }
722 }
723 }
724 }
726 bool
727 BluetoothOppManager::IsReservedChar(char16_t c)
728 {
729 return (c < 0x0020 ||
730 c == char16_t('?') || c == char16_t('|') || c == char16_t('<') ||
731 c == char16_t('>') || c == char16_t('"') || c == char16_t(':') ||
732 c == char16_t('/') || c == char16_t('*') || c == char16_t('\\'));
733 }
735 void
736 BluetoothOppManager::ValidateFileName()
737 {
738 int length = mFileName.Length();
740 for (int i = 0; i < length; ++i) {
741 // Replace reserved char of fat file system with '_'
742 if (IsReservedChar(mFileName.CharAt(i))) {
743 mFileName.Replace(i, 1, char16_t('_'));
744 }
745 }
746 }
748 bool
749 BluetoothOppManager::ComposePacket(uint8_t aOpCode, UnixSocketRawData* aMessage)
750 {
751 MOZ_ASSERT(NS_IsMainThread());
752 MOZ_ASSERT(aMessage);
754 int frameHeaderLength = 0;
756 // See if this is the first part of each Put packet
757 if (mPutPacketReceivedLength == 0) {
758 // Section 3.3.3 "Put", IrOBEX 1.2
759 // [opcode:1][length:2][Headers:var]
760 frameHeaderLength = 3;
762 mPacketLength = ((((int)aMessage->mData[1]) << 8) | aMessage->mData[2]) -
763 frameHeaderLength;
764 /**
765 * A PUT request from remote devices may be divided into multiple parts.
766 * In other words, one request may need to be received multiple times,
767 * so here we keep a variable mPutPacketReceivedLength to indicate if
768 * current PUT request is done.
769 */
770 mReceivedDataBuffer = new uint8_t[mPacketLength];
771 mPutFinalFlag = (aOpCode == ObexRequestCode::PutFinal);
772 }
774 int dataLength = aMessage->mSize - frameHeaderLength;
776 // Check length before memcpy to prevent from memory pollution
777 if (dataLength < 0 ||
778 mPutPacketReceivedLength + dataLength > mPacketLength) {
779 BT_LOGR("Received packet size is unreasonable");
781 ReplyToPut(mPutFinalFlag, false);
782 DeleteReceivedFile();
783 FileTransferComplete();
785 return false;
786 }
788 memcpy(mReceivedDataBuffer.get() + mPutPacketReceivedLength,
789 &aMessage->mData[frameHeaderLength], dataLength);
791 mPutPacketReceivedLength += dataLength;
793 return (mPutPacketReceivedLength == mPacketLength);
794 }
796 void
797 BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
798 {
799 MOZ_ASSERT(NS_IsMainThread());
801 uint8_t opCode;
802 int receivedLength = aMessage->mSize;
804 if (mPutPacketReceivedLength > 0) {
805 opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
806 } else {
807 opCode = aMessage->mData[0];
809 // When there's a Put packet right after a PutFinal packet,
810 // which means it's the start point of a new file.
811 if (mPutFinalFlag &&
812 (opCode == ObexRequestCode::Put ||
813 opCode == ObexRequestCode::PutFinal)) {
814 mNewFileFlag = true;
815 AfterFirstPut();
816 }
817 }
819 ObexHeaderSet pktHeaders(opCode);
820 if (opCode == ObexRequestCode::Connect) {
821 // Section 3.3.1 "Connect", IrOBEX 1.2
822 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
823 // [Headers:var]
824 if (!ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders)) {
825 ReplyError(ObexResponseCode::BadRequest);
826 return;
827 }
829 ReplyToConnect();
830 AfterOppConnected();
831 } else if (opCode == ObexRequestCode::Abort) {
832 // Section 3.3.5 "Abort", IrOBEX 1.2
833 // [opcode:1][length:2][Headers:var]
834 if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
835 ReplyError(ObexResponseCode::BadRequest);
836 return;
837 }
839 ReplyToDisconnectOrAbort();
840 DeleteReceivedFile();
841 } else if (opCode == ObexRequestCode::Disconnect) {
842 // Section 3.3.2 "Disconnect", IrOBEX 1.2
843 // [opcode:1][length:2][Headers:var]
844 if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
845 ReplyError(ObexResponseCode::BadRequest);
846 return;
847 }
849 ReplyToDisconnectOrAbort();
850 AfterOppDisconnected();
851 FileTransferComplete();
852 } else if (opCode == ObexRequestCode::Put ||
853 opCode == ObexRequestCode::PutFinal) {
854 if (!ComposePacket(opCode, aMessage)) {
855 return;
856 }
858 // A Put packet is received completely
859 ParseHeaders(mReceivedDataBuffer.get(),
860 mPutPacketReceivedLength, &pktHeaders);
861 ExtractPacketHeaders(pktHeaders);
862 ValidateFileName();
864 // When we cancel the transfer, delete the file and notify completion
865 if (mAbortFlag) {
866 ReplyToPut(mPutFinalFlag, false);
867 mSentFileLength += mBodySegmentLength;
868 DeleteReceivedFile();
869 FileTransferComplete();
870 return;
871 }
873 // Wait until get confirmation from user, then create file and write to it
874 if (mWaitingForConfirmationFlag) {
875 ReceivingFileConfirmation();
876 mSentFileLength += mBodySegmentLength;
877 return;
878 }
880 // Already get confirmation from user, create a new file if needed and
881 // write to output stream
882 if (mNewFileFlag) {
883 StartFileTransfer();
884 if (!CreateFile()) {
885 ReplyToPut(mPutFinalFlag, false);
886 return;
887 }
888 mNewFileFlag = false;
889 }
891 if (!WriteToFile(mBodySegment.get(), mBodySegmentLength)) {
892 ReplyToPut(mPutFinalFlag, false);
893 return;
894 }
896 ReplyToPut(mPutFinalFlag, true);
898 // Send progress update
899 mSentFileLength += mBodySegmentLength;
900 if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
901 UpdateProgress();
902 mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
903 }
905 // Success to receive a file and notify completion
906 if (mPutFinalFlag) {
907 mSuccessFlag = true;
908 FileTransferComplete();
909 NotifyAboutFileChange();
910 }
911 } else if (opCode == ObexRequestCode::Get ||
912 opCode == ObexRequestCode::GetFinal ||
913 opCode == ObexRequestCode::SetPath) {
914 ReplyError(ObexResponseCode::BadRequest);
915 BT_WARNING("Unsupported ObexRequestCode");
916 } else {
917 ReplyError(ObexResponseCode::NotImplemented);
918 BT_WARNING("Unrecognized ObexRequestCode");
919 }
920 }
922 void
923 BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
924 {
925 MOZ_ASSERT(NS_IsMainThread());
927 uint8_t opCode = aMessage->mData[0];
929 // Check response code and send out system message as finished if the response
930 // code is somehow incorrect.
931 uint8_t expectedOpCode = ObexResponseCode::Success;
932 if (mLastCommand == ObexRequestCode::Put) {
933 expectedOpCode = ObexResponseCode::Continue;
934 }
936 if (opCode != expectedOpCode) {
937 if (mLastCommand == ObexRequestCode::Put ||
938 mLastCommand == ObexRequestCode::Abort ||
939 mLastCommand == ObexRequestCode::PutFinal) {
940 SendDisconnectRequest();
941 }
942 nsAutoCString str;
943 str += "[OPP] 0x";
944 str.AppendInt(mLastCommand, 16);
945 str += " failed";
946 BT_WARNING(str.get());
947 FileTransferComplete();
948 return;
949 }
951 if (mLastCommand == ObexRequestCode::PutFinal) {
952 mSuccessFlag = true;
953 FileTransferComplete();
955 if (mInputStream) {
956 mInputStream->Close();
957 mInputStream = nullptr;
958 }
960 if (mCurrentBlobIndex + 1 == (int) mBatches[0].mBlobs.Length()) {
961 SendDisconnectRequest();
962 } else {
963 StartSendingNextFile();
964 }
965 } else if (mLastCommand == ObexRequestCode::Abort) {
966 SendDisconnectRequest();
967 FileTransferComplete();
968 } else if (mLastCommand == ObexRequestCode::Disconnect) {
969 AfterOppDisconnected();
970 // Most devices will directly terminate connection after receiving
971 // Disconnect request, so we make a delay here. If the socket hasn't been
972 // disconnected, we will close it.
973 if (mSocket) {
974 MessageLoop::current()->
975 PostDelayedTask(FROM_HERE, new CloseSocketTask(mSocket), 1000);
976 }
977 } else if (mLastCommand == ObexRequestCode::Connect) {
978 MOZ_ASSERT(!mFileName.IsEmpty());
979 MOZ_ASSERT(mBlob);
981 AfterOppConnected();
983 // Keep remote information
984 mRemoteObexVersion = aMessage->mData[3];
985 mRemoteConnectionFlags = aMessage->mData[4];
986 mRemoteMaxPacketLength =
987 (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
989 // The length of file name exceeds maximum length.
990 int fileNameByteLen = (mFileName.Length() + 1) * 2;
991 int headerLen = kPutRequestHeaderSize + kPutRequestAppendHeaderSize;
992 if (fileNameByteLen > mRemoteMaxPacketLength - headerLen) {
993 BT_WARNING("The length of file name is aberrant.");
994 SendDisconnectRequest();
995 return;
996 }
998 SendPutHeaderRequest(mFileName, mFileLength);
999 } else if (mLastCommand == ObexRequestCode::Put) {
1000 if (mWaitingToSendPutFinal) {
1001 SendPutFinalRequest();
1002 return;
1003 }
1005 if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) {
1006 UpdateProgress();
1007 mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
1008 }
1010 nsresult rv;
1011 if (!mInputStream) {
1012 rv = mBlob->GetInternalStream(getter_AddRefs(mInputStream));
1013 if (NS_FAILED(rv)) {
1014 BT_WARNING("Can't get internal stream of blob");
1015 SendDisconnectRequest();
1016 return;
1017 }
1018 }
1020 nsRefPtr<ReadFileTask> task = new ReadFileTask(mInputStream,
1021 mRemoteMaxPacketLength);
1022 rv = mReadFileThread->Dispatch(task, NS_DISPATCH_NORMAL);
1023 if (NS_FAILED(rv)) {
1024 BT_WARNING("Cannot dispatch read file task!");
1025 SendDisconnectRequest();
1026 }
1027 } else {
1028 BT_WARNING("Unhandled ObexRequestCode");
1029 }
1030 }
1032 // Virtual function of class SocketConsumer
1033 void
1034 BluetoothOppManager::ReceiveSocketData(BluetoothSocket* aSocket,
1035 nsAutoPtr<UnixSocketRawData>& aMessage)
1036 {
1037 if (mIsServer) {
1038 ServerDataHandler(aMessage);
1039 } else {
1040 ClientDataHandler(aMessage);
1041 }
1042 }
1044 void
1045 BluetoothOppManager::SendConnectRequest()
1046 {
1047 if (mConnected) return;
1049 // Section 3.3.1 "Connect", IrOBEX 1.2
1050 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
1051 // [Headers:var]
1052 uint8_t req[255];
1053 int index = 7;
1055 req[3] = 0x10; // version=1.0
1056 req[4] = 0x00; // flag=0x00
1057 req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
1058 req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
1060 SendObexData(req, ObexRequestCode::Connect, index);
1061 }
1063 void
1064 BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
1065 int aFileSize)
1066 {
1067 if (!mConnected) return;
1069 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
1071 int len = aFileName.Length();
1072 uint8_t* fileName = new uint8_t[(len + 1) * 2];
1073 const char16_t* fileNamePtr = aFileName.BeginReading();
1075 for (int i = 0; i < len; i++) {
1076 fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
1077 fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i];
1078 }
1080 fileName[len * 2] = 0x00;
1081 fileName[len * 2 + 1] = 0x00;
1083 int index = 3;
1084 index += AppendHeaderName(&req[index], mRemoteMaxPacketLength - index,
1085 (char*)fileName, (len + 1) * 2);
1086 index += AppendHeaderLength(&req[index], aFileSize);
1088 SendObexData(req, ObexRequestCode::Put, index);
1090 delete [] fileName;
1091 delete [] req;
1092 }
1094 void
1095 BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
1096 int aFileBodyLength)
1097 {
1098 if (!mConnected) return;
1099 int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
1100 if (aFileBodyLength > packetLeftSpace) {
1101 BT_WARNING("Not allowed such a small MaxPacketLength value");
1102 return;
1103 }
1105 // Section 3.3.3 "Put", IrOBEX 1.2
1106 // [opcode:1][length:2][Headers:var]
1107 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
1109 int index = 3;
1110 index += AppendHeaderBody(&req[index], mRemoteMaxPacketLength - index,
1111 aFileBody, aFileBodyLength);
1113 SendObexData(req, ObexRequestCode::Put, index);
1114 delete [] req;
1116 mSentFileLength += aFileBodyLength;
1117 }
1119 void
1120 BluetoothOppManager::SendPutFinalRequest()
1121 {
1122 if (!mConnected) return;
1124 /**
1125 * Section 2.2.9, "End-of-Body", IrObex 1.2
1126 * End-of-Body is used to identify the last chunk of the object body.
1127 * For most platforms, a PutFinal packet is sent with an zero length
1128 * End-of-Body header.
1129 */
1131 // [opcode:1][length:2]
1132 int index = 3;
1133 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
1134 index += AppendHeaderEndOfBody(&req[index]);
1136 SendObexData(req, ObexRequestCode::PutFinal, index);
1137 delete [] req;
1139 mWaitingToSendPutFinal = false;
1140 }
1142 void
1143 BluetoothOppManager::SendDisconnectRequest()
1144 {
1145 if (!mConnected) return;
1147 // Section 3.3.2 "Disconnect", IrOBEX 1.2
1148 // [opcode:1][length:2][Headers:var]
1149 uint8_t req[255];
1150 int index = 3;
1152 SendObexData(req, ObexRequestCode::Disconnect, index);
1153 }
1155 void
1156 BluetoothOppManager::CheckPutFinal(uint32_t aNumRead)
1157 {
1158 if (mSentFileLength + aNumRead >= mFileLength) {
1159 mWaitingToSendPutFinal = true;
1160 }
1161 }
1163 bool
1164 BluetoothOppManager::IsConnected()
1165 {
1166 return (mConnected && !mSendTransferCompleteFlag);
1167 }
1169 void
1170 BluetoothOppManager::GetAddress(nsAString& aDeviceAddress)
1171 {
1172 return mSocket->GetAddress(aDeviceAddress);
1173 }
1175 void
1176 BluetoothOppManager::ReplyToConnect()
1177 {
1178 if (mConnected) return;
1180 // Section 3.3.1 "Connect", IrOBEX 1.2
1181 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
1182 // [Headers:var]
1183 uint8_t req[255];
1184 int index = 7;
1186 req[3] = 0x10; // version=1.0
1187 req[4] = 0x00; // flag=0x00
1188 req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
1189 req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
1191 SendObexData(req, ObexResponseCode::Success, index);
1192 }
1194 void
1195 BluetoothOppManager::ReplyToDisconnectOrAbort()
1196 {
1197 if (!mConnected) return;
1199 // Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
1200 // The format of response packet of "Disconnect" and "Abort" are the same
1201 // [opcode:1][length:2][Headers:var]
1202 uint8_t req[255];
1203 int index = 3;
1205 SendObexData(req, ObexResponseCode::Success, index);
1206 }
1208 void
1209 BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
1210 {
1211 if (!mConnected) return;
1213 // The received length can be reset here because this is where we reply to a
1214 // complete put packet.
1215 mPutPacketReceivedLength = 0;
1217 // Section 3.3.2 "Disconnect", IrOBEX 1.2
1218 // [opcode:1][length:2][Headers:var]
1219 uint8_t req[255];
1220 int index = 3;
1221 uint8_t opcode;
1223 if (aContinue) {
1224 opcode = (aFinal)? ObexResponseCode::Success :
1225 ObexResponseCode::Continue;
1226 } else {
1227 opcode = (aFinal)? ObexResponseCode::Unauthorized :
1228 ObexResponseCode::Unauthorized & (~FINAL_BIT);
1229 }
1231 SendObexData(req, opcode, index);
1232 }
1234 void
1235 BluetoothOppManager::ReplyError(uint8_t aError)
1236 {
1237 BT_LOGR("error: %d", aError);
1239 // Section 3.2 "Response Format", IrOBEX 1.2
1240 // [opcode:1][length:2][Headers:var]
1241 uint8_t req[255];
1242 int index = 3;
1244 SendObexData(req, aError, index);
1245 }
1247 void
1248 BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize)
1249 {
1250 SetObexPacketInfo(aData, aOpcode, aSize);
1252 if (!mIsServer) {
1253 mLastCommand = aOpcode;
1254 }
1256 UnixSocketRawData* s = new UnixSocketRawData(aSize);
1257 memcpy(s->mData, aData, s->mSize);
1258 mSocket->SendDroidSocketData(s);
1259 }
1261 void
1262 BluetoothOppManager::FileTransferComplete()
1263 {
1264 if (mSendTransferCompleteFlag) {
1265 return;
1266 }
1268 NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-complete");
1269 InfallibleTArray<BluetoothNamedValue> parameters;
1271 BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
1272 BT_APPEND_NAMED_VALUE(parameters, "success", mSuccessFlag);
1273 BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
1274 BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
1275 BT_APPEND_NAMED_VALUE(parameters, "fileLength", mSentFileLength);
1276 BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
1278 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
1280 mSendTransferCompleteFlag = true;
1281 }
1283 void
1284 BluetoothOppManager::StartFileTransfer()
1285 {
1286 NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-start");
1287 InfallibleTArray<BluetoothNamedValue> parameters;
1289 BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
1290 BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
1291 BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
1292 BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
1293 BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
1295 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
1297 mSendTransferCompleteFlag = false;
1298 }
1300 void
1301 BluetoothOppManager::UpdateProgress()
1302 {
1303 NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-update-progress");
1304 InfallibleTArray<BluetoothNamedValue> parameters;
1306 BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
1307 BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
1308 BT_APPEND_NAMED_VALUE(parameters, "processedLength", mSentFileLength);
1309 BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
1311 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
1312 }
1314 void
1315 BluetoothOppManager::ReceivingFileConfirmation()
1316 {
1317 NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-receiving-file-confirmation");
1318 InfallibleTArray<BluetoothNamedValue> parameters;
1320 BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
1321 BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
1322 BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
1323 BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
1325 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
1326 }
1328 void
1329 BluetoothOppManager::NotifyAboutFileChange()
1330 {
1331 NS_NAMED_LITERAL_STRING(data, "modified");
1333 nsCOMPtr<nsIObserverService> obs =
1334 mozilla::services::GetObserverService();
1335 NS_ENSURE_TRUE_VOID(obs);
1337 obs->NotifyObservers(mDsFile, "file-watcher-notify", data.get());
1338 }
1340 void
1341 BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
1342 {
1343 BT_LOGR("[%s]", (mIsServer)? "server" : "client");
1344 MOZ_ASSERT(aSocket);
1346 /**
1347 * If the created connection is an inbound connection, close server socket
1348 * because currently only one file-transfer session is allowed. After that,
1349 * we need to make sure that server socket would be nulled out.
1350 * As for outbound connections, we just notify the controller that it's done.
1351 */
1352 if (aSocket == mServerSocket) {
1353 MOZ_ASSERT(!mSocket);
1354 mServerSocket.swap(mSocket);
1355 }
1357 // Cache device address since we can't get socket address when a remote
1358 // device disconnect with us.
1359 mSocket->GetAddress(mDeviceAddress);
1361 // Start sending file if we connect as a client
1362 if (!mIsServer) {
1363 StartSendingNextFile();
1364 }
1365 }
1367 void
1368 BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
1369 {
1370 BT_LOGR("[%s]", (mIsServer)? "server" : "client");
1372 mServerSocket = nullptr;
1373 mSocket = nullptr;
1375 if (!mIsServer) {
1376 // Inform gaia of remaining blobs' sending failure
1377 DiscardBlobsToSend();
1378 }
1380 // Listen as a server if there's no more batch to process
1381 if (!ProcessNextBatch()) {
1382 Listen();
1383 }
1384 }
1386 void
1387 BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
1388 {
1389 MOZ_ASSERT(aSocket);
1390 if (aSocket != mSocket) {
1391 // Do nothing when a listening server socket is closed.
1392 return;
1393 }
1394 BT_LOGR("[%s]", (mIsServer) ? "server" : "client");
1396 /**
1397 * It is valid for a bluetooth device which is transfering file via OPP
1398 * closing socket without sending OBEX disconnect request first. So we
1399 * delete the broken file when we failed to receive a file from the remote,
1400 * and notify the transfer has been completed (but failed). We also call
1401 * AfterOppDisconnected here to ensure all variables will be cleaned.
1402 */
1403 if (!mSuccessFlag) {
1404 if (mIsServer) {
1405 DeleteReceivedFile();
1406 }
1408 FileTransferComplete();
1409 if (!mIsServer) {
1410 // Inform gaia of remaining blobs' sending failure
1411 DiscardBlobsToSend();
1412 }
1413 }
1415 AfterOppDisconnected();
1416 mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
1417 mSuccessFlag = false;
1419 mSocket = nullptr;
1420 // Listen as a server if there's no more batch to process
1421 if (!ProcessNextBatch()) {
1422 Listen();
1423 }
1424 }
1426 void
1427 BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
1428 {
1429 if (mSocket) {
1430 mSocket->Disconnect();
1431 } else {
1432 BT_WARNING("%s: No ongoing file transfer to stop", __FUNCTION__);
1433 }
1434 }
1436 NS_IMPL_ISUPPORTS(BluetoothOppManager, nsIObserver)
1438 bool
1439 BluetoothOppManager::AcquireSdcardMountLock()
1440 {
1441 nsCOMPtr<nsIVolumeService> volumeSrv =
1442 do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1443 NS_ENSURE_TRUE(volumeSrv, false);
1444 nsresult rv;
1445 rv = volumeSrv->CreateMountLock(NS_LITERAL_STRING("sdcard"),
1446 getter_AddRefs(mMountLock));
1447 NS_ENSURE_SUCCESS(rv, false);
1448 return true;
1449 }
1451 void
1452 BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
1453 const nsAString& aServiceUuid,
1454 int aChannel)
1455 {
1456 MOZ_ASSERT(false);
1457 }
1459 void
1460 BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
1461 {
1462 MOZ_ASSERT(false);
1463 }
1465 void
1466 BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
1467 BluetoothProfileController* aController)
1468 {
1469 MOZ_ASSERT(false);
1470 }
1472 void
1473 BluetoothOppManager::OnConnect(const nsAString& aErrorStr)
1474 {
1475 MOZ_ASSERT(false);
1476 }
1478 void
1479 BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
1480 {
1481 MOZ_ASSERT(false);
1482 }
1484 void
1485 BluetoothOppManager::Reset()
1486 {
1487 MOZ_ASSERT(false);
1488 }