dom/bluetooth/bluez/BluetoothOppManager.cpp

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

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

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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;
  1014     SendPutHeaderRequest(mFileName, mFileLength);
  1015   } else if (mLastCommand == ObexRequestCode::Put) {
  1016     if (mWaitingToSendPutFinal) {
  1017       SendPutFinalRequest();
  1018       return;
  1021     if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) {
  1022       UpdateProgress();
  1023       mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
  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;
  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();
  1043   } else {
  1044     BT_WARNING("Unhandled ObexRequestCode");
  1048 // Virtual function of class SocketConsumer
  1049 void
  1050 BluetoothOppManager::ReceiveSocketData(BluetoothSocket* aSocket,
  1051                                        nsAutoPtr<UnixSocketRawData>& aMessage)
  1053   if (mIsServer) {
  1054     ServerDataHandler(aMessage);
  1055   } else {
  1056     ClientDataHandler(aMessage);
  1060 void
  1061 BluetoothOppManager::SendConnectRequest()
  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);
  1079 void
  1080 BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
  1081                                           int aFileSize)
  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];
  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;
  1110 void
  1111 BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
  1112                                     int aFileBodyLength)
  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;
  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;
  1134 void
  1135 BluetoothOppManager::SendPutFinalRequest()
  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;
  1157 void
  1158 BluetoothOppManager::SendDisconnectRequest()
  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);
  1170 void
  1171 BluetoothOppManager::CheckPutFinal(uint32_t aNumRead)
  1173   if (mSentFileLength + aNumRead >= mFileLength) {
  1174     mWaitingToSendPutFinal = true;
  1178 bool
  1179 BluetoothOppManager::IsConnected()
  1181   return (mConnected && !mSendTransferCompleteFlag);
  1184 void
  1185 BluetoothOppManager::GetAddress(nsAString& aDeviceAddress)
  1187   return mSocket->GetAddress(aDeviceAddress);
  1190 void
  1191 BluetoothOppManager::ReplyToConnect()
  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);
  1209 void
  1210 BluetoothOppManager::ReplyToDisconnectOrAbort()
  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);
  1223 void
  1224 BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
  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);
  1246   SendObexData(req, opcode, index);
  1249 void
  1250 BluetoothOppManager::ReplyError(uint8_t aError)
  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);
  1262 void
  1263 BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize)
  1265   SetObexPacketInfo(aData, aOpcode, aSize);
  1267   if (!mIsServer) {
  1268     mLastCommand = aOpcode;
  1271   UnixSocketRawData* s = new UnixSocketRawData(aSize);
  1272   memcpy(s->mData, aData, s->mSize);
  1273   mSocket->SendSocketData(s);
  1276 void
  1277 BluetoothOppManager::FileTransferComplete()
  1279   if (mSendTransferCompleteFlag) {
  1280     return;
  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;
  1317   mSendTransferCompleteFlag = true;
  1320 void
  1321 BluetoothOppManager::StartFileTransfer()
  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;
  1353   mSendTransferCompleteFlag = false;
  1356 void
  1357 BluetoothOppManager::UpdateProgress()
  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;
  1386 void
  1387 BluetoothOppManager::ReceivingFileConfirmation()
  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;
  1416 void
  1417 BluetoothOppManager::NotifyAboutFileChange()
  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());
  1428 void
  1429 BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
  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;
  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();
  1464 void
  1465 BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
  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();
  1478   // Listen as a server if there's no more batch to process
  1479   if (!ProcessNextBatch() && !mIsServer) {
  1480     Listen();
  1484 void
  1485 BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
  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;
  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();
  1507     FileTransferComplete();
  1508     if (!mIsServer) {
  1509       // Inform gaia of remaining blobs' sending failure
  1510       DiscardBlobsToSend();
  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();
  1525 void
  1526 BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
  1528   if (mSocket) {
  1529     mSocket->Disconnect();
  1530   } else {
  1531     BT_WARNING("%s: No ongoing file transfer to stop", __FUNCTION__);
  1535 void
  1536 BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
  1537                                          const nsAString& aServiceUuid,
  1538                                          int aChannel)
  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);
  1554     return;
  1557   if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) {
  1558     OnSocketConnectError(mSocket);
  1562 void
  1563 BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
  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);
  1579 NS_IMPL_ISUPPORTS(BluetoothOppManager, nsIObserver)
  1581 bool
  1582 BluetoothOppManager::AcquireSdcardMountLock()
  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;
  1594 void
  1595 BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
  1596                              BluetoothProfileController* aController)
  1598   MOZ_ASSERT(false);
  1601 void
  1602 BluetoothOppManager::OnConnect(const nsAString& aErrorStr)
  1604   MOZ_ASSERT(false);
  1607 void
  1608 BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
  1610   MOZ_ASSERT(false);
  1613 void
  1614 BluetoothOppManager::Reset()
  1616   MOZ_ASSERT(false);

mercurial