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

mercurial