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());
169 if (mSocket->GetConnectionStatus() ==
170 SocketConnectionStatus::SOCKET_CONNECTED) {
171 mSocket->Disconnect();
172 }
173 }
175 private:
176 nsRefPtr<BluetoothSocket> mSocket;
177 };
179 BluetoothOppManager::BluetoothOppManager() : mConnected(false)
180 , mRemoteObexVersion(0)
181 , mRemoteConnectionFlags(0)
182 , mRemoteMaxPacketLength(0)
183 , mLastCommand(0)
184 , mPacketLength(0)
185 , mPutPacketReceivedLength(0)
186 , mBodySegmentLength(0)
187 , mAbortFlag(false)
188 , mNewFileFlag(false)
189 , mPutFinalFlag(false)
190 , mSendTransferCompleteFlag(false)
191 , mSuccessFlag(false)
192 , mIsServer(true)
193 , mWaitingForConfirmationFlag(false)
194 , mFileLength(0)
195 , mSentFileLength(0)
196 , mWaitingToSendPutFinal(false)
197 , mCurrentBlobIndex(-1)
198 {
199 mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
200 }
202 BluetoothOppManager::~BluetoothOppManager()
203 {
204 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
205 NS_ENSURE_TRUE_VOID(obs);
206 if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
207 BT_WARNING("Failed to remove shutdown observer!");
208 }
209 }
211 bool
212 BluetoothOppManager::Init()
213 {
214 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
215 NS_ENSURE_TRUE(obs, false);
216 if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
217 BT_WARNING("Failed to add shutdown observer!");
218 return false;
219 }
221 Listen();
223 return true;
224 }
226 //static
227 BluetoothOppManager*
228 BluetoothOppManager::Get()
229 {
230 MOZ_ASSERT(NS_IsMainThread());
232 // If sBluetoothOppManager already exists, exit early
233 if (sBluetoothOppManager) {
234 return sBluetoothOppManager;
235 }
237 // If we're in shutdown, don't create a new instance
238 NS_ENSURE_FALSE(sInShutdown, nullptr);
240 // Create a new instance, register, and return
241 BluetoothOppManager *manager = new BluetoothOppManager();
242 NS_ENSURE_TRUE(manager->Init(), nullptr);
244 sBluetoothOppManager = manager;
245 return sBluetoothOppManager;
246 }
248 void
249 BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress)
250 {
251 MOZ_ASSERT(NS_IsMainThread());
253 // Stop listening because currently we only support one connection at a time.
254 if (mRfcommSocket) {
255 mRfcommSocket->Disconnect();
256 mRfcommSocket = nullptr;
257 }
259 if (mL2capSocket) {
260 mL2capSocket->Disconnect();
261 mL2capSocket = nullptr;
262 }
264 mIsServer = false;
266 BluetoothService* bs = BluetoothService::Get();
267 if (!bs || sInShutdown || mSocket) {
268 OnSocketConnectError(mSocket);
269 return;
270 }
272 mNeedsUpdatingSdpRecords = true;
274 nsString uuid;
275 BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
277 if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
278 OnSocketConnectError(mSocket);
279 return;
280 }
282 mSocket =
283 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
284 }
286 void
287 BluetoothOppManager::HandleShutdown()
288 {
289 MOZ_ASSERT(NS_IsMainThread());
290 sInShutdown = true;
291 Disconnect(nullptr);
292 sBluetoothOppManager = nullptr;
293 }
295 bool
296 BluetoothOppManager::Listen()
297 {
298 MOZ_ASSERT(NS_IsMainThread());
300 if (mSocket) {
301 BT_WARNING("mSocket exists. Failed to listen.");
302 return false;
303 }
305 if (!mRfcommSocket) {
306 mRfcommSocket =
307 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
309 if (!mRfcommSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH)) {
310 BT_WARNING("[OPP] Can't listen on RFCOMM socket!");
311 mRfcommSocket = nullptr;
312 return false;
313 }
314 }
316 if (!mL2capSocket) {
317 mL2capSocket =
318 new BluetoothSocket(this, BluetoothSocketType::EL2CAP, true, true);
320 if (!mL2capSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH_L2CAP)) {
321 BT_WARNING("[OPP] Can't listen on L2CAP socket!");
322 mRfcommSocket->Disconnect();
323 mRfcommSocket = nullptr;
324 mL2capSocket = nullptr;
325 return false;
326 }
327 }
329 mIsServer = true;
331 return true;
332 }
334 void
335 BluetoothOppManager::StartSendingNextFile()
336 {
337 MOZ_ASSERT(NS_IsMainThread());
339 MOZ_ASSERT(!IsConnected());
340 MOZ_ASSERT(!mBatches.IsEmpty());
341 MOZ_ASSERT((int)mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1);
343 mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
345 // Before sending content, we have to send a header including
346 // information such as file name, file length and content type.
347 ExtractBlobHeaders();
348 StartFileTransfer();
350 if (mCurrentBlobIndex == 0) {
351 // We may have more than one file waiting for transferring, but only one
352 // CONNECT request would be sent. Therefore check if this is the very first
353 // file at the head of queue.
354 SendConnectRequest();
355 } else {
356 SendPutHeaderRequest(mFileName, mFileLength);
357 AfterFirstPut();
358 }
359 }
361 bool
362 BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
363 BlobParent* aActor)
364 {
365 MOZ_ASSERT(NS_IsMainThread());
367 nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
369 return SendFile(aDeviceAddress, blob.get());
370 }
372 bool
373 BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
374 nsIDOMBlob* aBlob)
375 {
376 MOZ_ASSERT(NS_IsMainThread());
378 AppendBlobToSend(aDeviceAddress, aBlob);
379 if (!mSocket) {
380 ProcessNextBatch();
381 }
383 return true;
384 }
386 void
387 BluetoothOppManager::AppendBlobToSend(const nsAString& aDeviceAddress,
388 nsIDOMBlob* aBlob)
389 {
390 MOZ_ASSERT(NS_IsMainThread());
392 int indexTail = mBatches.Length() - 1;
394 /**
395 * Create a new batch if
396 * - mBatches is empty, or
397 * - aDeviceAddress differs from mDeviceAddress of the last batch
398 */
399 if (mBatches.IsEmpty() ||
400 aDeviceAddress != mBatches[indexTail].mDeviceAddress) {
401 SendFileBatch batch(aDeviceAddress, aBlob);
402 mBatches.AppendElement(batch);
403 } else {
404 mBatches[indexTail].mBlobs.AppendElement(aBlob);
405 }
406 }
408 void
409 BluetoothOppManager::DiscardBlobsToSend()
410 {
411 MOZ_ASSERT(NS_IsMainThread());
413 MOZ_ASSERT(!mBatches.IsEmpty());
414 MOZ_ASSERT(!mIsServer);
416 int length = (int) mBatches[0].mBlobs.Length();
417 while (length > mCurrentBlobIndex + 1) {
418 mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
420 BT_LOGR("idx %d", mCurrentBlobIndex);
421 ExtractBlobHeaders();
422 StartFileTransfer();
423 FileTransferComplete();
424 }
425 }
427 bool
428 BluetoothOppManager::ProcessNextBatch()
429 {
430 MOZ_ASSERT(NS_IsMainThread());
432 // Remove the processed batch.
433 // A batch is processed if we've incremented mCurrentBlobIndex for it.
434 if (mCurrentBlobIndex >= 0) {
435 ClearQueue();
436 mBatches.RemoveElementAt(0);
437 BT_LOGR("REMOVE. %d remaining", mBatches.Length());
438 }
440 // Process the next batch
441 if (!mBatches.IsEmpty()) {
442 ConnectInternal(mBatches[0].mDeviceAddress);
443 return true;
444 }
446 // No more batch to process
447 return false;
448 }
450 void
451 BluetoothOppManager::ClearQueue()
452 {
453 MOZ_ASSERT(NS_IsMainThread());
455 MOZ_ASSERT(!mIsServer);
456 MOZ_ASSERT(!mBatches.IsEmpty());
457 MOZ_ASSERT(!mBatches[0].mBlobs.IsEmpty());
459 mCurrentBlobIndex = -1;
460 mBlob = nullptr;
461 mBatches[0].mBlobs.Clear();
462 }
464 bool
465 BluetoothOppManager::StopSendingFile()
466 {
467 MOZ_ASSERT(NS_IsMainThread());
469 if (mIsServer) {
470 mAbortFlag = true;
471 } else {
472 Disconnect(nullptr);
473 }
475 return true;
476 }
478 bool
479 BluetoothOppManager::ConfirmReceivingFile(bool aConfirm)
480 {
481 NS_ENSURE_TRUE(mConnected, false);
482 NS_ENSURE_TRUE(mWaitingForConfirmationFlag, false);
484 mWaitingForConfirmationFlag = false;
486 // For the first packet of first file
487 bool success = false;
488 if (aConfirm) {
489 StartFileTransfer();
490 if (CreateFile()) {
491 success = WriteToFile(mBodySegment.get(), mBodySegmentLength);
492 }
493 }
495 if (success && mPutFinalFlag) {
496 mSuccessFlag = true;
497 FileTransferComplete();
498 NotifyAboutFileChange();
499 }
501 ReplyToPut(mPutFinalFlag, success);
503 return true;
504 }
506 void
507 BluetoothOppManager::AfterFirstPut()
508 {
509 mUpdateProgressCounter = 1;
510 mPutFinalFlag = false;
511 mPutPacketReceivedLength = 0;
512 mSentFileLength = 0;
513 mWaitingToSendPutFinal = false;
514 mSuccessFlag = false;
515 mBodySegmentLength = 0;
516 }
518 void
519 BluetoothOppManager::AfterOppConnected()
520 {
521 MOZ_ASSERT(NS_IsMainThread());
523 mConnected = true;
524 mAbortFlag = false;
525 mWaitingForConfirmationFlag = true;
526 AfterFirstPut();
527 // Get a mount lock to prevent the sdcard from being shared with
528 // the PC while we're doing a OPP file transfer. After OPP transaction
529 // were done, the mount lock will be freed.
530 if (!AcquireSdcardMountLock()) {
531 // If we fail to get a mount lock, abort this transaction
532 // Directly sending disconnect-request is better than abort-request
533 BT_WARNING("BluetoothOPPManager couldn't get a mount lock!");
534 Disconnect(nullptr);
535 }
536 }
538 void
539 BluetoothOppManager::AfterOppDisconnected()
540 {
541 MOZ_ASSERT(NS_IsMainThread());
543 mConnected = false;
544 mLastCommand = 0;
545 mPutPacketReceivedLength = 0;
546 mDsFile = nullptr;
548 // We can't reset mSuccessFlag here since this function may be called
549 // before we send system message of transfer complete
550 // mSuccessFlag = false;
552 if (mInputStream) {
553 mInputStream->Close();
554 mInputStream = nullptr;
555 }
557 if (mOutputStream) {
558 mOutputStream->Close();
559 mOutputStream = nullptr;
560 }
562 if (mReadFileThread) {
563 mReadFileThread->Shutdown();
564 mReadFileThread = nullptr;
565 }
566 // Release the Mount lock if file transfer completed
567 if (mMountLock) {
568 // The mount lock will be implicitly unlocked
569 mMountLock = nullptr;
570 }
571 }
573 void
574 BluetoothOppManager::DeleteReceivedFile()
575 {
576 if (mOutputStream) {
577 mOutputStream->Close();
578 mOutputStream = nullptr;
579 }
581 if (mDsFile && mDsFile->mFile) {
582 mDsFile->mFile->Remove(false);
583 mDsFile = nullptr;
584 }
585 }
587 bool
588 BluetoothOppManager::CreateFile()
589 {
590 MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength);
592 nsString path;
593 path.AssignLiteral(TARGET_SUBDIR);
594 path.Append(mFileName);
596 mDsFile = DeviceStorageFile::CreateUnique(
597 path, nsIFile::NORMAL_FILE_TYPE, 0644);
598 NS_ENSURE_TRUE(mDsFile, false);
600 nsCOMPtr<nsIFile> f;
601 mDsFile->mFile->Clone(getter_AddRefs(f));
603 /*
604 * The function CreateUnique() may create a file with a different file
605 * name from the original mFileName. Therefore we have to retrieve
606 * the file name again.
607 */
608 f->GetLeafName(mFileName);
610 NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
611 NS_ENSURE_TRUE(mOutputStream, false);
613 return true;
614 }
616 bool
617 BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
618 {
619 NS_ENSURE_TRUE(mOutputStream, false);
621 uint32_t wrote = 0;
622 mOutputStream->Write((const char*)aData, aDataLength, &wrote);
623 NS_ENSURE_TRUE(aDataLength == (int) wrote, false);
625 return true;
626 }
628 // Virtual function of class SocketConsumer
629 void
630 BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
631 {
632 if (aHeader.Has(ObexHeaderId::Name)) {
633 aHeader.GetName(mFileName);
634 }
636 if (aHeader.Has(ObexHeaderId::Type)) {
637 aHeader.GetContentType(mContentType);
638 }
640 if (aHeader.Has(ObexHeaderId::Length)) {
641 aHeader.GetLength(&mFileLength);
642 }
644 if (aHeader.Has(ObexHeaderId::Body) ||
645 aHeader.Has(ObexHeaderId::EndOfBody)) {
646 uint8_t* bodyPtr;
647 aHeader.GetBody(&bodyPtr);
648 mBodySegment = bodyPtr;
650 aHeader.GetBodyLength(&mBodySegmentLength);
651 }
652 }
654 bool
655 BluetoothOppManager::ExtractBlobHeaders()
656 {
657 RetrieveSentFileName();
659 nsresult rv = mBlob->GetType(mContentType);
660 if (NS_FAILED(rv)) {
661 BT_WARNING("Can't get content type");
662 SendDisconnectRequest();
663 return false;
664 }
666 uint64_t fileLength;
667 rv = mBlob->GetSize(&fileLength);
668 if (NS_FAILED(rv)) {
669 BT_WARNING("Can't get file size");
670 SendDisconnectRequest();
671 return false;
672 }
674 // Currently we keep the size of files which were sent/received via
675 // Bluetooth not exceed UINT32_MAX because the Length header in OBEX
676 // is only 4-byte long. Although it is possible to transfer a file
677 // larger than UINT32_MAX, it needs to parse another OBEX Header
678 // and I would like to leave it as a feature.
679 if (fileLength > (uint64_t)UINT32_MAX) {
680 BT_WARNING("The file size is too large for now");
681 SendDisconnectRequest();
682 return false;
683 }
685 mFileLength = fileLength;
686 rv = NS_NewThread(getter_AddRefs(mReadFileThread));
687 if (NS_FAILED(rv)) {
688 BT_WARNING("Can't create thread");
689 SendDisconnectRequest();
690 return false;
691 }
693 return true;
694 }
696 void
697 BluetoothOppManager::RetrieveSentFileName()
698 {
699 mFileName.Truncate();
701 nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
702 if (file) {
703 file->GetName(mFileName);
704 }
706 /**
707 * We try our best to get the file extention to avoid interoperability issues.
708 * However, once we found that we are unable to get suitable extension or
709 * information about the content type, sending a pre-defined file name without
710 * extension would be fine.
711 */
712 if (mFileName.IsEmpty()) {
713 mFileName.AssignLiteral("Unknown");
714 }
716 int32_t offset = mFileName.RFindChar('/');
717 if (offset != kNotFound) {
718 mFileName = Substring(mFileName, offset + 1);
719 }
721 offset = mFileName.RFindChar('.');
722 if (offset == kNotFound) {
723 nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
725 if (mimeSvc) {
726 nsString mimeType;
727 mBlob->GetType(mimeType);
729 nsCString extension;
730 nsresult rv =
731 mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
732 EmptyCString(),
733 extension);
734 if (NS_SUCCEEDED(rv)) {
735 mFileName.AppendLiteral(".");
736 AppendUTF8toUTF16(extension, mFileName);
737 }
738 }
739 }
740 }
742 bool
743 BluetoothOppManager::IsReservedChar(char16_t c)
744 {
745 return (c < 0x0020 ||
746 c == char16_t('?') || c == char16_t('|') || c == char16_t('<') ||
747 c == char16_t('>') || c == char16_t('"') || c == char16_t(':') ||
748 c == char16_t('/') || c == char16_t('*') || c == char16_t('\\'));
749 }
751 void
752 BluetoothOppManager::ValidateFileName()
753 {
754 int length = mFileName.Length();
756 for (int i = 0; i < length; ++i) {
757 // Replace reserved char of fat file system with '_'
758 if (IsReservedChar(mFileName.CharAt(i))) {
759 mFileName.Replace(i, 1, char16_t('_'));
760 }
761 }
762 }
764 bool
765 BluetoothOppManager::ComposePacket(uint8_t aOpCode, UnixSocketRawData* aMessage)
766 {
767 MOZ_ASSERT(NS_IsMainThread());
768 MOZ_ASSERT(aMessage);
770 int frameHeaderLength = 0;
772 // See if this is the first part of each Put packet
773 if (mPutPacketReceivedLength == 0) {
774 // Section 3.3.3 "Put", IrOBEX 1.2
775 // [opcode:1][length:2][Headers:var]
776 frameHeaderLength = 3;
778 mPacketLength = ((((int)aMessage->mData[1]) << 8) | aMessage->mData[2]) -
779 frameHeaderLength;
780 /**
781 * A PUT request from remote devices may be divided into multiple parts.
782 * In other words, one request may need to be received multiple times,
783 * so here we keep a variable mPutPacketReceivedLength to indicate if
784 * current PUT request is done.
785 */
786 mReceivedDataBuffer = new uint8_t[mPacketLength];
787 mPutFinalFlag = (aOpCode == ObexRequestCode::PutFinal);
788 }
790 int dataLength = aMessage->mSize - frameHeaderLength;
792 // Check length before memcpy to prevent from memory pollution
793 if (dataLength < 0 ||
794 mPutPacketReceivedLength + dataLength > mPacketLength) {
795 BT_LOGR("Received packet size is unreasonable");
797 ReplyToPut(mPutFinalFlag, false);
798 DeleteReceivedFile();
799 FileTransferComplete();
801 return false;
802 }
804 memcpy(mReceivedDataBuffer.get() + mPutPacketReceivedLength,
805 &aMessage->mData[frameHeaderLength], dataLength);
807 mPutPacketReceivedLength += dataLength;
809 return (mPutPacketReceivedLength == mPacketLength);
810 }
812 void
813 BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
814 {
815 MOZ_ASSERT(NS_IsMainThread());
817 uint8_t opCode;
818 int receivedLength = aMessage->mSize;
820 if (mPutPacketReceivedLength > 0) {
821 opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
822 } else {
823 opCode = aMessage->mData[0];
825 // When there's a Put packet right after a PutFinal packet,
826 // which means it's the start point of a new file.
827 if (mPutFinalFlag &&
828 (opCode == ObexRequestCode::Put ||
829 opCode == ObexRequestCode::PutFinal)) {
830 mNewFileFlag = true;
831 AfterFirstPut();
832 }
833 }
835 ObexHeaderSet pktHeaders(opCode);
836 if (opCode == ObexRequestCode::Connect) {
837 // Section 3.3.1 "Connect", IrOBEX 1.2
838 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
839 // [Headers:var]
840 if (!ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders)) {
841 ReplyError(ObexResponseCode::BadRequest);
842 return;
843 }
845 ReplyToConnect();
846 AfterOppConnected();
847 } else if (opCode == ObexRequestCode::Abort) {
848 // Section 3.3.5 "Abort", IrOBEX 1.2
849 // [opcode:1][length:2][Headers:var]
850 if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
851 ReplyError(ObexResponseCode::BadRequest);
852 return;
853 }
855 ReplyToDisconnectOrAbort();
856 DeleteReceivedFile();
857 } else if (opCode == ObexRequestCode::Disconnect) {
858 // Section 3.3.2 "Disconnect", IrOBEX 1.2
859 // [opcode:1][length:2][Headers:var]
860 if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
861 ReplyError(ObexResponseCode::BadRequest);
862 return;
863 }
865 ReplyToDisconnectOrAbort();
866 AfterOppDisconnected();
867 FileTransferComplete();
868 } else if (opCode == ObexRequestCode::Put ||
869 opCode == ObexRequestCode::PutFinal) {
870 if (!ComposePacket(opCode, aMessage)) {
871 return;
872 }
874 // A Put packet is received completely
875 ParseHeaders(mReceivedDataBuffer.get(),
876 mPutPacketReceivedLength, &pktHeaders);
877 ExtractPacketHeaders(pktHeaders);
878 ValidateFileName();
880 // When we cancel the transfer, delete the file and notify completion
881 if (mAbortFlag) {
882 ReplyToPut(mPutFinalFlag, false);
883 mSentFileLength += mBodySegmentLength;
884 DeleteReceivedFile();
885 FileTransferComplete();
886 return;
887 }
889 // Wait until get confirmation from user, then create file and write to it
890 if (mWaitingForConfirmationFlag) {
891 ReceivingFileConfirmation();
892 mSentFileLength += mBodySegmentLength;
893 return;
894 }
896 // Already get confirmation from user, create a new file if needed and
897 // write to output stream
898 if (mNewFileFlag) {
899 StartFileTransfer();
900 if (!CreateFile()) {
901 ReplyToPut(mPutFinalFlag, false);
902 return;
903 }
904 mNewFileFlag = false;
905 }
907 if (!WriteToFile(mBodySegment.get(), mBodySegmentLength)) {
908 ReplyToPut(mPutFinalFlag, false);
909 return;
910 }
912 ReplyToPut(mPutFinalFlag, true);
914 // Send progress update
915 mSentFileLength += mBodySegmentLength;
916 if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
917 UpdateProgress();
918 mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
919 }
921 // Success to receive a file and notify completion
922 if (mPutFinalFlag) {
923 mSuccessFlag = true;
924 FileTransferComplete();
925 NotifyAboutFileChange();
926 }
927 } else if (opCode == ObexRequestCode::Get ||
928 opCode == ObexRequestCode::GetFinal ||
929 opCode == ObexRequestCode::SetPath) {
930 ReplyError(ObexResponseCode::BadRequest);
931 BT_WARNING("Unsupported ObexRequestCode");
932 } else {
933 ReplyError(ObexResponseCode::NotImplemented);
934 BT_WARNING("Unrecognized ObexRequestCode");
935 }
936 }
938 void
939 BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
940 {
941 MOZ_ASSERT(NS_IsMainThread());
943 uint8_t opCode = aMessage->mData[0];
945 // Check response code and send out system message as finished if the response
946 // code is somehow incorrect.
947 uint8_t expectedOpCode = ObexResponseCode::Success;
948 if (mLastCommand == ObexRequestCode::Put) {
949 expectedOpCode = ObexResponseCode::Continue;
950 }
952 if (opCode != expectedOpCode) {
953 if (mLastCommand == ObexRequestCode::Put ||
954 mLastCommand == ObexRequestCode::Abort ||
955 mLastCommand == ObexRequestCode::PutFinal) {
956 SendDisconnectRequest();
957 }
958 nsAutoCString str;
959 str += "[OPP] 0x";
960 str.AppendInt(mLastCommand, 16);
961 str += " failed";
962 BT_WARNING(str.get());
963 FileTransferComplete();
964 return;
965 }
967 if (mLastCommand == ObexRequestCode::PutFinal) {
968 mSuccessFlag = true;
969 FileTransferComplete();
971 if (mInputStream) {
972 mInputStream->Close();
973 mInputStream = nullptr;
974 }
976 if (mCurrentBlobIndex + 1 == (int) mBatches[0].mBlobs.Length()) {
977 SendDisconnectRequest();
978 } else {
979 StartSendingNextFile();
980 }
981 } else if (mLastCommand == ObexRequestCode::Abort) {
982 SendDisconnectRequest();
983 FileTransferComplete();
984 } else if (mLastCommand == ObexRequestCode::Disconnect) {
985 AfterOppDisconnected();
986 // Most devices will directly terminate connection after receiving
987 // Disconnect request, so we make a delay here. If the socket hasn't been
988 // disconnected, we will close it.
989 if (mSocket) {
990 MessageLoop::current()->
991 PostDelayedTask(FROM_HERE, new CloseSocketTask(mSocket), 1000);
992 }
993 } else if (mLastCommand == ObexRequestCode::Connect) {
994 MOZ_ASSERT(!mFileName.IsEmpty());
995 MOZ_ASSERT(mBlob);
997 AfterOppConnected();
999 // Keep remote information
1000 mRemoteObexVersion = aMessage->mData[3];
1001 mRemoteConnectionFlags = aMessage->mData[4];
1002 mRemoteMaxPacketLength =
1003 (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
1005 // The length of file name exceeds maximum length.
1006 int fileNameByteLen = (mFileName.Length() + 1) * 2;
1007 int headerLen = kPutRequestHeaderSize + kPutRequestAppendHeaderSize;
1008 if (fileNameByteLen > mRemoteMaxPacketLength - headerLen) {
1009 BT_WARNING("The length of file name is aberrant.");
1010 SendDisconnectRequest();
1011 return;
1012 }
1014 SendPutHeaderRequest(mFileName, mFileLength);
1015 } else if (mLastCommand == ObexRequestCode::Put) {
1016 if (mWaitingToSendPutFinal) {
1017 SendPutFinalRequest();
1018 return;
1019 }
1021 if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) {
1022 UpdateProgress();
1023 mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
1024 }
1026 nsresult rv;
1027 if (!mInputStream) {
1028 rv = mBlob->GetInternalStream(getter_AddRefs(mInputStream));
1029 if (NS_FAILED(rv)) {
1030 BT_WARNING("Can't get internal stream of blob");
1031 SendDisconnectRequest();
1032 return;
1033 }
1034 }
1036 nsRefPtr<ReadFileTask> task = new ReadFileTask(mInputStream,
1037 mRemoteMaxPacketLength);
1038 rv = mReadFileThread->Dispatch(task, NS_DISPATCH_NORMAL);
1039 if (NS_FAILED(rv)) {
1040 BT_WARNING("Cannot dispatch read file task!");
1041 SendDisconnectRequest();
1042 }
1043 } else {
1044 BT_WARNING("Unhandled ObexRequestCode");
1045 }
1046 }
1048 // Virtual function of class SocketConsumer
1049 void
1050 BluetoothOppManager::ReceiveSocketData(BluetoothSocket* aSocket,
1051 nsAutoPtr<UnixSocketRawData>& aMessage)
1052 {
1053 if (mIsServer) {
1054 ServerDataHandler(aMessage);
1055 } else {
1056 ClientDataHandler(aMessage);
1057 }
1058 }
1060 void
1061 BluetoothOppManager::SendConnectRequest()
1062 {
1063 if (mConnected) return;
1065 // Section 3.3.1 "Connect", IrOBEX 1.2
1066 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
1067 // [Headers:var]
1068 uint8_t req[255];
1069 int index = 7;
1071 req[3] = 0x10; // version=1.0
1072 req[4] = 0x00; // flag=0x00
1073 req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
1074 req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
1076 SendObexData(req, ObexRequestCode::Connect, index);
1077 }
1079 void
1080 BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
1081 int aFileSize)
1082 {
1083 if (!mConnected) return;
1085 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
1087 int len = aFileName.Length();
1088 uint8_t* fileName = new uint8_t[(len + 1) * 2];
1089 const char16_t* fileNamePtr = aFileName.BeginReading();
1091 for (int i = 0; i < len; i++) {
1092 fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
1093 fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i];
1094 }
1096 fileName[len * 2] = 0x00;
1097 fileName[len * 2 + 1] = 0x00;
1099 int index = 3;
1100 index += AppendHeaderName(&req[index], mRemoteMaxPacketLength - index,
1101 (char*)fileName, (len + 1) * 2);
1102 index += AppendHeaderLength(&req[index], aFileSize);
1104 SendObexData(req, ObexRequestCode::Put, index);
1106 delete [] fileName;
1107 delete [] req;
1108 }
1110 void
1111 BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
1112 int aFileBodyLength)
1113 {
1114 if (!mConnected) return;
1115 int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
1116 if (aFileBodyLength > packetLeftSpace) {
1117 BT_WARNING("Not allowed such a small MaxPacketLength value");
1118 return;
1119 }
1121 // Section 3.3.3 "Put", IrOBEX 1.2
1122 // [opcode:1][length:2][Headers:var]
1123 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
1125 int index = 3;
1126 index += AppendHeaderBody(&req[index], mRemoteMaxPacketLength - index,
1127 aFileBody, aFileBodyLength);
1129 SendObexData(req, ObexRequestCode::Put, index);
1130 delete [] req;
1131 mSentFileLength += aFileBodyLength;
1132 }
1134 void
1135 BluetoothOppManager::SendPutFinalRequest()
1136 {
1137 if (!mConnected) return;
1139 /**
1140 * Section 2.2.9, "End-of-Body", IrObex 1.2
1141 * End-of-Body is used to identify the last chunk of the object body.
1142 * For most platforms, a PutFinal packet is sent with an zero length
1143 * End-of-Body header.
1144 */
1146 // [opcode:1][length:2]
1147 int index = 3;
1148 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
1149 index += AppendHeaderEndOfBody(&req[index]);
1151 SendObexData(req, ObexRequestCode::PutFinal, index);
1152 delete [] req;
1154 mWaitingToSendPutFinal = false;
1155 }
1157 void
1158 BluetoothOppManager::SendDisconnectRequest()
1159 {
1160 if (!mConnected) return;
1162 // Section 3.3.2 "Disconnect", IrOBEX 1.2
1163 // [opcode:1][length:2][Headers:var]
1164 uint8_t req[255];
1165 int index = 3;
1167 SendObexData(req, ObexRequestCode::Disconnect, index);
1168 }
1170 void
1171 BluetoothOppManager::CheckPutFinal(uint32_t aNumRead)
1172 {
1173 if (mSentFileLength + aNumRead >= mFileLength) {
1174 mWaitingToSendPutFinal = true;
1175 }
1176 }
1178 bool
1179 BluetoothOppManager::IsConnected()
1180 {
1181 return (mConnected && !mSendTransferCompleteFlag);
1182 }
1184 void
1185 BluetoothOppManager::GetAddress(nsAString& aDeviceAddress)
1186 {
1187 return mSocket->GetAddress(aDeviceAddress);
1188 }
1190 void
1191 BluetoothOppManager::ReplyToConnect()
1192 {
1193 if (mConnected) return;
1195 // Section 3.3.1 "Connect", IrOBEX 1.2
1196 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
1197 // [Headers:var]
1198 uint8_t req[255];
1199 int index = 7;
1201 req[3] = 0x10; // version=1.0
1202 req[4] = 0x00; // flag=0x00
1203 req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
1204 req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
1206 SendObexData(req, ObexResponseCode::Success, index);
1207 }
1209 void
1210 BluetoothOppManager::ReplyToDisconnectOrAbort()
1211 {
1212 if (!mConnected) return;
1214 // Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
1215 // The format of response packet of "Disconnect" and "Abort" are the same
1216 // [opcode:1][length:2][Headers:var]
1217 uint8_t req[255];
1218 int index = 3;
1220 SendObexData(req, ObexResponseCode::Success, index);
1221 }
1223 void
1224 BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
1225 {
1226 if (!mConnected) return;
1228 // The received length can be reset here because this is where we reply to a
1229 // complete put packet.
1230 mPutPacketReceivedLength = 0;
1232 // Section 3.3.2 "Disconnect", IrOBEX 1.2
1233 // [opcode:1][length:2][Headers:var]
1234 uint8_t req[255];
1235 int index = 3;
1236 uint8_t opcode;
1238 if (aContinue) {
1239 opcode = (aFinal)? ObexResponseCode::Success :
1240 ObexResponseCode::Continue;
1241 } else {
1242 opcode = (aFinal)? ObexResponseCode::Unauthorized :
1243 ObexResponseCode::Unauthorized & (~FINAL_BIT);
1244 }
1246 SendObexData(req, opcode, index);
1247 }
1249 void
1250 BluetoothOppManager::ReplyError(uint8_t aError)
1251 {
1252 BT_LOGR("error: %d", aError);
1254 // Section 3.2 "Response Format", IrOBEX 1.2
1255 // [opcode:1][length:2][Headers:var]
1256 uint8_t req[255];
1257 int index = 3;
1259 SendObexData(req, aError, index);
1260 }
1262 void
1263 BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize)
1264 {
1265 SetObexPacketInfo(aData, aOpcode, aSize);
1267 if (!mIsServer) {
1268 mLastCommand = aOpcode;
1269 }
1271 UnixSocketRawData* s = new UnixSocketRawData(aSize);
1272 memcpy(s->mData, aData, s->mSize);
1273 mSocket->SendSocketData(s);
1274 }
1276 void
1277 BluetoothOppManager::FileTransferComplete()
1278 {
1279 if (mSendTransferCompleteFlag) {
1280 return;
1281 }
1283 nsString type, name;
1284 BluetoothValue v;
1285 InfallibleTArray<BluetoothNamedValue> parameters;
1286 type.AssignLiteral("bluetooth-opp-transfer-complete");
1288 name.AssignLiteral("address");
1289 v = mConnectedDeviceAddress;
1290 parameters.AppendElement(BluetoothNamedValue(name, v));
1292 name.AssignLiteral("success");
1293 v = mSuccessFlag;
1294 parameters.AppendElement(BluetoothNamedValue(name, v));
1296 name.AssignLiteral("received");
1297 v = mIsServer;
1298 parameters.AppendElement(BluetoothNamedValue(name, v));
1300 name.AssignLiteral("fileName");
1301 v = mFileName;
1302 parameters.AppendElement(BluetoothNamedValue(name, v));
1304 name.AssignLiteral("fileLength");
1305 v = mSentFileLength;
1306 parameters.AppendElement(BluetoothNamedValue(name, v));
1308 name.AssignLiteral("contentType");
1309 v = mContentType;
1310 parameters.AppendElement(BluetoothNamedValue(name, v));
1312 if (!BroadcastSystemMessage(type, parameters)) {
1313 BT_WARNING("Failed to broadcast [bluetooth-opp-transfer-complete]");
1314 return;
1315 }
1317 mSendTransferCompleteFlag = true;
1318 }
1320 void
1321 BluetoothOppManager::StartFileTransfer()
1322 {
1323 nsString type, name;
1324 BluetoothValue v;
1325 InfallibleTArray<BluetoothNamedValue> parameters;
1326 type.AssignLiteral("bluetooth-opp-transfer-start");
1328 name.AssignLiteral("address");
1329 v = mConnectedDeviceAddress;
1330 parameters.AppendElement(BluetoothNamedValue(name, v));
1332 name.AssignLiteral("received");
1333 v = mIsServer;
1334 parameters.AppendElement(BluetoothNamedValue(name, v));
1336 name.AssignLiteral("fileName");
1337 v = mFileName;
1338 parameters.AppendElement(BluetoothNamedValue(name, v));
1340 name.AssignLiteral("fileLength");
1341 v = mFileLength;
1342 parameters.AppendElement(BluetoothNamedValue(name, v));
1344 name.AssignLiteral("contentType");
1345 v = mContentType;
1346 parameters.AppendElement(BluetoothNamedValue(name, v));
1348 if (!BroadcastSystemMessage(type, parameters)) {
1349 BT_WARNING("Failed to broadcast [bluetooth-opp-transfer-start]");
1350 return;
1351 }
1353 mSendTransferCompleteFlag = false;
1354 }
1356 void
1357 BluetoothOppManager::UpdateProgress()
1358 {
1359 nsString type, name;
1360 BluetoothValue v;
1361 InfallibleTArray<BluetoothNamedValue> parameters;
1362 type.AssignLiteral("bluetooth-opp-update-progress");
1364 name.AssignLiteral("address");
1365 v = mConnectedDeviceAddress;
1366 parameters.AppendElement(BluetoothNamedValue(name, v));
1368 name.AssignLiteral("received");
1369 v = mIsServer;
1370 parameters.AppendElement(BluetoothNamedValue(name, v));
1372 name.AssignLiteral("processedLength");
1373 v = mSentFileLength;
1374 parameters.AppendElement(BluetoothNamedValue(name, v));
1376 name.AssignLiteral("fileLength");
1377 v = mFileLength;
1378 parameters.AppendElement(BluetoothNamedValue(name, v));
1380 if (!BroadcastSystemMessage(type, parameters)) {
1381 BT_WARNING("Failed to broadcast [bluetooth-opp-update-progress]");
1382 return;
1383 }
1384 }
1386 void
1387 BluetoothOppManager::ReceivingFileConfirmation()
1388 {
1389 nsString type, name;
1390 BluetoothValue v;
1391 InfallibleTArray<BluetoothNamedValue> parameters;
1392 type.AssignLiteral("bluetooth-opp-receiving-file-confirmation");
1394 name.AssignLiteral("address");
1395 v = mConnectedDeviceAddress;
1396 parameters.AppendElement(BluetoothNamedValue(name, v));
1398 name.AssignLiteral("fileName");
1399 v = mFileName;
1400 parameters.AppendElement(BluetoothNamedValue(name, v));
1402 name.AssignLiteral("fileLength");
1403 v = mFileLength;
1404 parameters.AppendElement(BluetoothNamedValue(name, v));
1406 name.AssignLiteral("contentType");
1407 v = mContentType;
1408 parameters.AppendElement(BluetoothNamedValue(name, v));
1410 if (!BroadcastSystemMessage(type, parameters)) {
1411 BT_WARNING("Failed to send [bluetooth-opp-receiving-file-confirmation]");
1412 return;
1413 }
1414 }
1416 void
1417 BluetoothOppManager::NotifyAboutFileChange()
1418 {
1419 NS_NAMED_LITERAL_STRING(data, "modified");
1421 nsCOMPtr<nsIObserverService> obs =
1422 mozilla::services::GetObserverService();
1423 NS_ENSURE_TRUE_VOID(obs);
1425 obs->NotifyObservers(mDsFile, "file-watcher-notify", data.get());
1426 }
1428 void
1429 BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
1430 {
1431 BT_LOGR("[%s]", (mIsServer)? "server" : "client");
1432 MOZ_ASSERT(aSocket);
1434 /**
1435 * If the created connection is an inbound connection, close another server
1436 * socket because currently only one file-transfer session is allowed. After
1437 * that, we need to make sure that both server socket would be nulled out.
1438 * As for outbound connections, we just notify the controller that it's done.
1439 */
1440 if (aSocket == mRfcommSocket) {
1441 MOZ_ASSERT(!mSocket);
1442 mRfcommSocket.swap(mSocket);
1444 mL2capSocket->Disconnect();
1445 mL2capSocket = nullptr;
1446 } else if (aSocket == mL2capSocket) {
1447 MOZ_ASSERT(!mSocket);
1448 mL2capSocket.swap(mSocket);
1450 mRfcommSocket->Disconnect();
1451 mRfcommSocket = nullptr;
1452 }
1454 // Cache device address since we can't get socket address when a remote
1455 // device disconnect with us.
1456 mSocket->GetAddress(mConnectedDeviceAddress);
1458 // Start sending file if we connect as a client
1459 if (!mIsServer) {
1460 StartSendingNextFile();
1461 }
1462 }
1464 void
1465 BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
1466 {
1467 BT_LOGR("[%s]", (mIsServer)? "server" : "client");
1469 mRfcommSocket = nullptr;
1470 mL2capSocket = nullptr;
1471 mSocket = nullptr;
1473 if (!mIsServer) {
1474 // Inform gaia of remaining blobs' sending failure
1475 DiscardBlobsToSend();
1476 }
1478 // Listen as a server if there's no more batch to process
1479 if (!ProcessNextBatch() && !mIsServer) {
1480 Listen();
1481 }
1482 }
1484 void
1485 BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
1486 {
1487 BT_LOGR("[%s]", (mIsServer)? "server" : "client");
1488 MOZ_ASSERT(aSocket);
1490 if (aSocket != mSocket) {
1491 // Do nothing when a listening server socket is closed.
1492 return;
1493 }
1495 /**
1496 * It is valid for a bluetooth device which is transfering file via OPP
1497 * closing socket without sending OBEX disconnect request first. So we
1498 * delete the broken file when we failed to receive a file from the remote,
1499 * and notify the transfer has been completed (but failed). We also call
1500 * AfterOppDisconnected here to ensure all variables will be cleaned.
1501 */
1502 if (!mSuccessFlag) {
1503 if (mIsServer) {
1504 DeleteReceivedFile();
1505 }
1507 FileTransferComplete();
1508 if (!mIsServer) {
1509 // Inform gaia of remaining blobs' sending failure
1510 DiscardBlobsToSend();
1511 }
1512 }
1514 AfterOppDisconnected();
1515 mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
1516 mSuccessFlag = false;
1518 mSocket = nullptr;
1519 // Listen as a server if there's no more batch to process
1520 if (!ProcessNextBatch()) {
1521 Listen();
1522 }
1523 }
1525 void
1526 BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
1527 {
1528 if (mSocket) {
1529 mSocket->Disconnect();
1530 } else {
1531 BT_WARNING("%s: No ongoing file transfer to stop", __FUNCTION__);
1532 }
1533 }
1535 void
1536 BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
1537 const nsAString& aServiceUuid,
1538 int aChannel)
1539 {
1540 MOZ_ASSERT(NS_IsMainThread());
1541 MOZ_ASSERT(!aDeviceAddress.IsEmpty());
1543 BluetoothService* bs = BluetoothService::Get();
1544 NS_ENSURE_TRUE_VOID(bs);
1546 if (aChannel < 0) {
1547 if (mNeedsUpdatingSdpRecords) {
1548 mNeedsUpdatingSdpRecords = false;
1549 bs->UpdateSdpRecords(aDeviceAddress, this);
1550 } else {
1551 OnSocketConnectError(mSocket);
1552 }
1554 return;
1555 }
1557 if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) {
1558 OnSocketConnectError(mSocket);
1559 }
1560 }
1562 void
1563 BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
1564 {
1565 MOZ_ASSERT(NS_IsMainThread());
1566 MOZ_ASSERT(!aDeviceAddress.IsEmpty());
1568 BluetoothService* bs = BluetoothService::Get();
1569 NS_ENSURE_TRUE_VOID(bs);
1571 nsString uuid;
1572 BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
1574 if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
1575 OnSocketConnectError(mSocket);
1576 }
1577 }
1579 NS_IMPL_ISUPPORTS(BluetoothOppManager, nsIObserver)
1581 bool
1582 BluetoothOppManager::AcquireSdcardMountLock()
1583 {
1584 nsCOMPtr<nsIVolumeService> volumeSrv =
1585 do_GetService(NS_VOLUMESERVICE_CONTRACTID);
1586 NS_ENSURE_TRUE(volumeSrv, false);
1587 nsresult rv;
1588 rv = volumeSrv->CreateMountLock(NS_LITERAL_STRING("sdcard"),
1589 getter_AddRefs(mMountLock));
1590 NS_ENSURE_SUCCESS(rv, false);
1591 return true;
1592 }
1594 void
1595 BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
1596 BluetoothProfileController* aController)
1597 {
1598 MOZ_ASSERT(false);
1599 }
1601 void
1602 BluetoothOppManager::OnConnect(const nsAString& aErrorStr)
1603 {
1604 MOZ_ASSERT(false);
1605 }
1607 void
1608 BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
1609 {
1610 MOZ_ASSERT(false);
1611 }
1613 void
1614 BluetoothOppManager::Reset()
1615 {
1616 MOZ_ASSERT(false);
1617 }