1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/bluetooth/bluedroid/BluetoothOppManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1488 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "base/basictypes.h" 1.11 +#include "BluetoothOppManager.h" 1.12 + 1.13 +#include "BluetoothService.h" 1.14 +#include "BluetoothSocket.h" 1.15 +#include "BluetoothUtils.h" 1.16 +#include "BluetoothUuid.h" 1.17 +#include "ObexBase.h" 1.18 + 1.19 +#include "mozilla/dom/bluetooth/BluetoothTypes.h" 1.20 +#include "mozilla/RefPtr.h" 1.21 +#include "mozilla/Services.h" 1.22 +#include "mozilla/StaticPtr.h" 1.23 +#include "nsAutoPtr.h" 1.24 +#include "nsCExternalHandlerService.h" 1.25 +#include "nsIObserver.h" 1.26 +#include "nsIObserverService.h" 1.27 +#include "nsIDOMFile.h" 1.28 +#include "nsIFile.h" 1.29 +#include "nsIInputStream.h" 1.30 +#include "nsIMIMEService.h" 1.31 +#include "nsIOutputStream.h" 1.32 +#include "nsIVolumeService.h" 1.33 +#include "nsNetUtil.h" 1.34 +#include "nsServiceManagerUtils.h" 1.35 + 1.36 +#define TARGET_SUBDIR "Download/Bluetooth/" 1.37 + 1.38 +USING_BLUETOOTH_NAMESPACE 1.39 +using namespace mozilla; 1.40 +using namespace mozilla::ipc; 1.41 + 1.42 +namespace { 1.43 +// Sending system message "bluetooth-opp-update-progress" every 50kb 1.44 +static const uint32_t kUpdateProgressBase = 50 * 1024; 1.45 + 1.46 +/* 1.47 + * The format of the header of an PUT request is 1.48 + * [opcode:1][packet length:2][headerId:1][header length:2] 1.49 + */ 1.50 +static const uint32_t kPutRequestHeaderSize = 6; 1.51 + 1.52 +/* 1.53 + * The format of the appended header of an PUT request is 1.54 + * [headerId:1][header length:4] 1.55 + * P.S. Length of name header is 4 since unicode is 2 bytes per char. 1.56 + */ 1.57 +static const uint32_t kPutRequestAppendHeaderSize = 5; 1.58 + 1.59 +StaticRefPtr<BluetoothOppManager> sBluetoothOppManager; 1.60 +static bool sInShutdown = false; 1.61 +} 1.62 + 1.63 +class mozilla::dom::bluetooth::SendFileBatch { 1.64 +public: 1.65 + SendFileBatch(const nsAString& aDeviceAddress, nsIDOMBlob* aBlob) 1.66 + : mDeviceAddress(aDeviceAddress) 1.67 + { 1.68 + mBlobs.AppendElement(aBlob); 1.69 + } 1.70 + 1.71 + nsString mDeviceAddress; 1.72 + nsCOMArray<nsIDOMBlob> mBlobs; 1.73 +}; 1.74 + 1.75 +NS_IMETHODIMP 1.76 +BluetoothOppManager::Observe(nsISupports* aSubject, 1.77 + const char* aTopic, 1.78 + const char16_t* aData) 1.79 +{ 1.80 + MOZ_ASSERT(sBluetoothOppManager); 1.81 + 1.82 + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 1.83 + HandleShutdown(); 1.84 + return NS_OK; 1.85 + } 1.86 + 1.87 + MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!"); 1.88 + return NS_ERROR_UNEXPECTED; 1.89 +} 1.90 + 1.91 +class SendSocketDataTask : public nsRunnable 1.92 +{ 1.93 +public: 1.94 + SendSocketDataTask(uint8_t* aStream, uint32_t aSize) 1.95 + : mStream(aStream) 1.96 + , mSize(aSize) 1.97 + { 1.98 + MOZ_ASSERT(!NS_IsMainThread()); 1.99 + } 1.100 + 1.101 + NS_IMETHOD Run() 1.102 + { 1.103 + MOZ_ASSERT(NS_IsMainThread()); 1.104 + 1.105 + sBluetoothOppManager->SendPutRequest(mStream, mSize); 1.106 + 1.107 + return NS_OK; 1.108 + } 1.109 + 1.110 +private: 1.111 + nsAutoArrayPtr<uint8_t> mStream; 1.112 + uint32_t mSize; 1.113 +}; 1.114 + 1.115 +class ReadFileTask : public nsRunnable 1.116 +{ 1.117 +public: 1.118 + ReadFileTask(nsIInputStream* aInputStream, 1.119 + uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream) 1.120 + { 1.121 + MOZ_ASSERT(NS_IsMainThread()); 1.122 + 1.123 + mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize; 1.124 + } 1.125 + 1.126 + NS_IMETHOD Run() 1.127 + { 1.128 + MOZ_ASSERT(!NS_IsMainThread()); 1.129 + 1.130 + uint32_t numRead; 1.131 + nsAutoArrayPtr<char> buf(new char[mAvailablePacketSize]); 1.132 + 1.133 + // function inputstream->Read() only works on non-main thread 1.134 + nsresult rv = mInputStream->Read(buf, mAvailablePacketSize, &numRead); 1.135 + if (NS_FAILED(rv)) { 1.136 + // Needs error handling here 1.137 + BT_WARNING("Failed to read from input stream"); 1.138 + return NS_ERROR_FAILURE; 1.139 + } 1.140 + 1.141 + if (numRead > 0) { 1.142 + sBluetoothOppManager->CheckPutFinal(numRead); 1.143 + 1.144 + nsRefPtr<SendSocketDataTask> task = 1.145 + new SendSocketDataTask((uint8_t*)buf.forget(), numRead); 1.146 + if (NS_FAILED(NS_DispatchToMainThread(task))) { 1.147 + BT_WARNING("Failed to dispatch to main thread!"); 1.148 + return NS_ERROR_FAILURE; 1.149 + } 1.150 + } 1.151 + 1.152 + return NS_OK; 1.153 + }; 1.154 + 1.155 +private: 1.156 + nsCOMPtr<nsIInputStream> mInputStream; 1.157 + uint32_t mAvailablePacketSize; 1.158 +}; 1.159 + 1.160 +class CloseSocketTask : public Task 1.161 +{ 1.162 +public: 1.163 + CloseSocketTask(BluetoothSocket* aSocket) : mSocket(aSocket) 1.164 + { 1.165 + MOZ_ASSERT(aSocket); 1.166 + } 1.167 + 1.168 + void Run() MOZ_OVERRIDE 1.169 + { 1.170 + MOZ_ASSERT(NS_IsMainThread()); 1.171 + mSocket->CloseDroidSocket(); 1.172 + } 1.173 + 1.174 +private: 1.175 + nsRefPtr<BluetoothSocket> mSocket; 1.176 +}; 1.177 + 1.178 +BluetoothOppManager::BluetoothOppManager() : mConnected(false) 1.179 + , mRemoteObexVersion(0) 1.180 + , mRemoteConnectionFlags(0) 1.181 + , mRemoteMaxPacketLength(0) 1.182 + , mLastCommand(0) 1.183 + , mPacketLength(0) 1.184 + , mPutPacketReceivedLength(0) 1.185 + , mBodySegmentLength(0) 1.186 + , mAbortFlag(false) 1.187 + , mNewFileFlag(false) 1.188 + , mPutFinalFlag(false) 1.189 + , mSendTransferCompleteFlag(false) 1.190 + , mSuccessFlag(false) 1.191 + , mIsServer(true) 1.192 + , mWaitingForConfirmationFlag(false) 1.193 + , mFileLength(0) 1.194 + , mSentFileLength(0) 1.195 + , mWaitingToSendPutFinal(false) 1.196 + , mCurrentBlobIndex(-1) 1.197 +{ 1.198 + mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE); 1.199 +} 1.200 + 1.201 +BluetoothOppManager::~BluetoothOppManager() 1.202 +{ 1.203 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.204 + NS_ENSURE_TRUE_VOID(obs); 1.205 + if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) { 1.206 + BT_WARNING("Failed to remove shutdown observer!"); 1.207 + } 1.208 +} 1.209 + 1.210 +bool 1.211 +BluetoothOppManager::Init() 1.212 +{ 1.213 + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 1.214 + NS_ENSURE_TRUE(obs, false); 1.215 + if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) { 1.216 + BT_WARNING("Failed to add shutdown observer!"); 1.217 + return false; 1.218 + } 1.219 + 1.220 + /** 1.221 + * We don't start listening here as BluetoothServiceBluedroid calls Listen() 1.222 + * immediately when BT stops. 1.223 + * 1.224 + * If we start listening here, the listening fails when device boots up since 1.225 + * Listen() is called again and restarts server socket. The restart causes 1.226 + * absence of read events when device boots up. 1.227 + */ 1.228 + 1.229 + return true; 1.230 +} 1.231 + 1.232 +//static 1.233 +BluetoothOppManager* 1.234 +BluetoothOppManager::Get() 1.235 +{ 1.236 + MOZ_ASSERT(NS_IsMainThread()); 1.237 + 1.238 + // If sBluetoothOppManager already exists, exit early 1.239 + if (sBluetoothOppManager) { 1.240 + return sBluetoothOppManager; 1.241 + } 1.242 + 1.243 + // If we're in shutdown, don't create a new instance 1.244 + NS_ENSURE_FALSE(sInShutdown, nullptr); 1.245 + 1.246 + // Create a new instance, register, and return 1.247 + BluetoothOppManager *manager = new BluetoothOppManager(); 1.248 + NS_ENSURE_TRUE(manager->Init(), nullptr); 1.249 + 1.250 + sBluetoothOppManager = manager; 1.251 + return sBluetoothOppManager; 1.252 +} 1.253 + 1.254 +void 1.255 +BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress) 1.256 +{ 1.257 + MOZ_ASSERT(NS_IsMainThread()); 1.258 + 1.259 + // Stop listening because currently we only support one connection at a time. 1.260 + if (mServerSocket) { 1.261 + mServerSocket->Disconnect(); 1.262 + mServerSocket = nullptr; 1.263 + } 1.264 + 1.265 + mIsServer = false; 1.266 + 1.267 + BluetoothService* bs = BluetoothService::Get(); 1.268 + if (!bs || sInShutdown || mSocket) { 1.269 + OnSocketConnectError(mSocket); 1.270 + return; 1.271 + } 1.272 + 1.273 + mSocket = 1.274 + new BluetoothSocket(this, BluetoothSocketType::RFCOMM, false, true); 1.275 + mSocket->Connect(aDeviceAddress, -1); 1.276 +} 1.277 + 1.278 +void 1.279 +BluetoothOppManager::HandleShutdown() 1.280 +{ 1.281 + MOZ_ASSERT(NS_IsMainThread()); 1.282 + sInShutdown = true; 1.283 + Disconnect(nullptr); 1.284 + sBluetoothOppManager = nullptr; 1.285 +} 1.286 + 1.287 +bool 1.288 +BluetoothOppManager::Listen() 1.289 +{ 1.290 + MOZ_ASSERT(NS_IsMainThread()); 1.291 + 1.292 + if (mSocket) { 1.293 + BT_WARNING("mSocket exists. Failed to listen."); 1.294 + return false; 1.295 + } 1.296 + 1.297 + /** 1.298 + * Restart server socket since its underlying fd becomes invalid when 1.299 + * BT stops; otherwise no more read events would be received even if 1.300 + * BT restarts. 1.301 + */ 1.302 + if (mServerSocket) { 1.303 + mServerSocket->Disconnect(); 1.304 + mServerSocket = nullptr; 1.305 + } 1.306 + 1.307 + mServerSocket = 1.308 + new BluetoothSocket(this, BluetoothSocketType::RFCOMM, false, true); 1.309 + 1.310 + if (!mServerSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH)) { 1.311 + BT_WARNING("[OPP] Can't listen on RFCOMM socket!"); 1.312 + mServerSocket = nullptr; 1.313 + return false; 1.314 + } 1.315 + 1.316 + mIsServer = true; 1.317 + 1.318 + return true; 1.319 +} 1.320 + 1.321 +void 1.322 +BluetoothOppManager::StartSendingNextFile() 1.323 +{ 1.324 + MOZ_ASSERT(NS_IsMainThread()); 1.325 + 1.326 + MOZ_ASSERT(!IsConnected()); 1.327 + MOZ_ASSERT(!mBatches.IsEmpty()); 1.328 + MOZ_ASSERT((int)mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1); 1.329 + 1.330 + mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex]; 1.331 + 1.332 + // Before sending content, we have to send a header including 1.333 + // information such as file name, file length and content type. 1.334 + ExtractBlobHeaders(); 1.335 + StartFileTransfer(); 1.336 + 1.337 + if (mCurrentBlobIndex == 0) { 1.338 + // We may have more than one file waiting for transferring, but only one 1.339 + // CONNECT request would be sent. Therefore check if this is the very first 1.340 + // file at the head of queue. 1.341 + SendConnectRequest(); 1.342 + } else { 1.343 + SendPutHeaderRequest(mFileName, mFileLength); 1.344 + AfterFirstPut(); 1.345 + } 1.346 +} 1.347 + 1.348 +bool 1.349 +BluetoothOppManager::SendFile(const nsAString& aDeviceAddress, 1.350 + BlobParent* aActor) 1.351 +{ 1.352 + MOZ_ASSERT(NS_IsMainThread()); 1.353 + 1.354 + nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob(); 1.355 + 1.356 + return SendFile(aDeviceAddress, blob.get()); 1.357 +} 1.358 + 1.359 +bool 1.360 +BluetoothOppManager::SendFile(const nsAString& aDeviceAddress, 1.361 + nsIDOMBlob* aBlob) 1.362 +{ 1.363 + MOZ_ASSERT(NS_IsMainThread()); 1.364 + 1.365 + AppendBlobToSend(aDeviceAddress, aBlob); 1.366 + if (!mSocket) { 1.367 + ProcessNextBatch(); 1.368 + } 1.369 + 1.370 + return true; 1.371 +} 1.372 + 1.373 +void 1.374 +BluetoothOppManager::AppendBlobToSend(const nsAString& aDeviceAddress, 1.375 + nsIDOMBlob* aBlob) 1.376 +{ 1.377 + MOZ_ASSERT(NS_IsMainThread()); 1.378 + 1.379 + int indexTail = mBatches.Length() - 1; 1.380 + 1.381 + /** 1.382 + * Create a new batch if 1.383 + * - mBatches is empty, or 1.384 + * - aDeviceAddress differs from mDeviceAddress of the last batch 1.385 + */ 1.386 + if (mBatches.IsEmpty() || 1.387 + aDeviceAddress != mBatches[indexTail].mDeviceAddress) { 1.388 + SendFileBatch batch(aDeviceAddress, aBlob); 1.389 + mBatches.AppendElement(batch); 1.390 + } else { 1.391 + mBatches[indexTail].mBlobs.AppendElement(aBlob); 1.392 + } 1.393 +} 1.394 + 1.395 +void 1.396 +BluetoothOppManager::DiscardBlobsToSend() 1.397 +{ 1.398 + MOZ_ASSERT(NS_IsMainThread()); 1.399 + 1.400 + MOZ_ASSERT(!mBatches.IsEmpty()); 1.401 + MOZ_ASSERT(!mIsServer); 1.402 + 1.403 + int length = (int) mBatches[0].mBlobs.Length(); 1.404 + while (length > mCurrentBlobIndex + 1) { 1.405 + mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex]; 1.406 + 1.407 + BT_LOGR("idx %d", mCurrentBlobIndex); 1.408 + ExtractBlobHeaders(); 1.409 + StartFileTransfer(); 1.410 + FileTransferComplete(); 1.411 + } 1.412 +} 1.413 + 1.414 +bool 1.415 +BluetoothOppManager::ProcessNextBatch() 1.416 +{ 1.417 + MOZ_ASSERT(NS_IsMainThread()); 1.418 + 1.419 + // Remove the processed batch. 1.420 + // A batch is processed if we've incremented mCurrentBlobIndex for it. 1.421 + if (mCurrentBlobIndex >= 0) { 1.422 + ClearQueue(); 1.423 + mBatches.RemoveElementAt(0); 1.424 + BT_LOGR("REMOVE. %d remaining", mBatches.Length()); 1.425 + } 1.426 + 1.427 + // Process the next batch 1.428 + if (!mBatches.IsEmpty()) { 1.429 + ConnectInternal(mBatches[0].mDeviceAddress); 1.430 + return true; 1.431 + } 1.432 + 1.433 + // No more batch to process 1.434 + return false; 1.435 +} 1.436 + 1.437 +void 1.438 +BluetoothOppManager::ClearQueue() 1.439 +{ 1.440 + MOZ_ASSERT(NS_IsMainThread()); 1.441 + 1.442 + MOZ_ASSERT(!mIsServer); 1.443 + MOZ_ASSERT(!mBatches.IsEmpty()); 1.444 + MOZ_ASSERT(!mBatches[0].mBlobs.IsEmpty()); 1.445 + 1.446 + mCurrentBlobIndex = -1; 1.447 + mBlob = nullptr; 1.448 + mBatches[0].mBlobs.Clear(); 1.449 +} 1.450 + 1.451 +bool 1.452 +BluetoothOppManager::StopSendingFile() 1.453 +{ 1.454 + MOZ_ASSERT(NS_IsMainThread()); 1.455 + 1.456 + if (mIsServer) { 1.457 + mAbortFlag = true; 1.458 + } else { 1.459 + Disconnect(nullptr); 1.460 + } 1.461 + 1.462 + return true; 1.463 +} 1.464 + 1.465 +bool 1.466 +BluetoothOppManager::ConfirmReceivingFile(bool aConfirm) 1.467 +{ 1.468 + NS_ENSURE_TRUE(mConnected, false); 1.469 + NS_ENSURE_TRUE(mWaitingForConfirmationFlag, false); 1.470 + 1.471 + mWaitingForConfirmationFlag = false; 1.472 + 1.473 + // For the first packet of first file 1.474 + bool success = false; 1.475 + if (aConfirm) { 1.476 + StartFileTransfer(); 1.477 + if (CreateFile()) { 1.478 + success = WriteToFile(mBodySegment.get(), mBodySegmentLength); 1.479 + } 1.480 + } 1.481 + 1.482 + if (success && mPutFinalFlag) { 1.483 + mSuccessFlag = true; 1.484 + FileTransferComplete(); 1.485 + NotifyAboutFileChange(); 1.486 + } 1.487 + 1.488 + ReplyToPut(mPutFinalFlag, success); 1.489 + 1.490 + return true; 1.491 +} 1.492 + 1.493 +void 1.494 +BluetoothOppManager::AfterFirstPut() 1.495 +{ 1.496 + mUpdateProgressCounter = 1; 1.497 + mPutFinalFlag = false; 1.498 + mPutPacketReceivedLength = 0; 1.499 + mSentFileLength = 0; 1.500 + mWaitingToSendPutFinal = false; 1.501 + mSuccessFlag = false; 1.502 + mBodySegmentLength = 0; 1.503 +} 1.504 + 1.505 +void 1.506 +BluetoothOppManager::AfterOppConnected() 1.507 +{ 1.508 + MOZ_ASSERT(NS_IsMainThread()); 1.509 + 1.510 + mConnected = true; 1.511 + mAbortFlag = false; 1.512 + mWaitingForConfirmationFlag = true; 1.513 + AfterFirstPut(); 1.514 + // Get a mount lock to prevent the sdcard from being shared with 1.515 + // the PC while we're doing a OPP file transfer. After OPP transaction 1.516 + // were done, the mount lock will be freed. 1.517 + if (!AcquireSdcardMountLock()) { 1.518 + // If we fail to get a mount lock, abort this transaction 1.519 + // Directly sending disconnect-request is better than abort-request 1.520 + BT_WARNING("BluetoothOPPManager couldn't get a mount lock!"); 1.521 + Disconnect(nullptr); 1.522 + } 1.523 +} 1.524 + 1.525 +void 1.526 +BluetoothOppManager::AfterOppDisconnected() 1.527 +{ 1.528 + MOZ_ASSERT(NS_IsMainThread()); 1.529 + 1.530 + mConnected = false; 1.531 + mLastCommand = 0; 1.532 + mPutPacketReceivedLength = 0; 1.533 + mDsFile = nullptr; 1.534 + 1.535 + // We can't reset mSuccessFlag here since this function may be called 1.536 + // before we send system message of transfer complete 1.537 + // mSuccessFlag = false; 1.538 + 1.539 + if (mInputStream) { 1.540 + mInputStream->Close(); 1.541 + mInputStream = nullptr; 1.542 + } 1.543 + 1.544 + if (mOutputStream) { 1.545 + mOutputStream->Close(); 1.546 + mOutputStream = nullptr; 1.547 + } 1.548 + 1.549 + if (mReadFileThread) { 1.550 + mReadFileThread->Shutdown(); 1.551 + mReadFileThread = nullptr; 1.552 + } 1.553 + // Release the Mount lock if file transfer completed 1.554 + if (mMountLock) { 1.555 + // The mount lock will be implicitly unlocked 1.556 + mMountLock = nullptr; 1.557 + } 1.558 +} 1.559 + 1.560 +void 1.561 +BluetoothOppManager::DeleteReceivedFile() 1.562 +{ 1.563 + if (mOutputStream) { 1.564 + mOutputStream->Close(); 1.565 + mOutputStream = nullptr; 1.566 + } 1.567 + 1.568 + if (mDsFile && mDsFile->mFile) { 1.569 + mDsFile->mFile->Remove(false); 1.570 + mDsFile = nullptr; 1.571 + } 1.572 +} 1.573 + 1.574 +bool 1.575 +BluetoothOppManager::CreateFile() 1.576 +{ 1.577 + MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength); 1.578 + 1.579 + nsString path; 1.580 + path.AssignLiteral(TARGET_SUBDIR); 1.581 + path.Append(mFileName); 1.582 + 1.583 + mDsFile = DeviceStorageFile::CreateUnique( 1.584 + path, nsIFile::NORMAL_FILE_TYPE, 0644); 1.585 + NS_ENSURE_TRUE(mDsFile, false); 1.586 + 1.587 + nsCOMPtr<nsIFile> f; 1.588 + mDsFile->mFile->Clone(getter_AddRefs(f)); 1.589 + 1.590 + /* 1.591 + * The function CreateUnique() may create a file with a different file 1.592 + * name from the original mFileName. Therefore we have to retrieve 1.593 + * the file name again. 1.594 + */ 1.595 + f->GetLeafName(mFileName); 1.596 + 1.597 + NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f); 1.598 + NS_ENSURE_TRUE(mOutputStream, false); 1.599 + 1.600 + return true; 1.601 +} 1.602 + 1.603 +bool 1.604 +BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength) 1.605 +{ 1.606 + NS_ENSURE_TRUE(mOutputStream, false); 1.607 + 1.608 + uint32_t wrote = 0; 1.609 + mOutputStream->Write((const char*)aData, aDataLength, &wrote); 1.610 + NS_ENSURE_TRUE(aDataLength == (int) wrote, false); 1.611 + 1.612 + return true; 1.613 +} 1.614 + 1.615 +// Virtual function of class SocketConsumer 1.616 +void 1.617 +BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader) 1.618 +{ 1.619 + if (aHeader.Has(ObexHeaderId::Name)) { 1.620 + aHeader.GetName(mFileName); 1.621 + } 1.622 + 1.623 + if (aHeader.Has(ObexHeaderId::Type)) { 1.624 + aHeader.GetContentType(mContentType); 1.625 + } 1.626 + 1.627 + if (aHeader.Has(ObexHeaderId::Length)) { 1.628 + aHeader.GetLength(&mFileLength); 1.629 + } 1.630 + 1.631 + if (aHeader.Has(ObexHeaderId::Body) || 1.632 + aHeader.Has(ObexHeaderId::EndOfBody)) { 1.633 + uint8_t* bodyPtr; 1.634 + aHeader.GetBody(&bodyPtr); 1.635 + mBodySegment = bodyPtr; 1.636 + 1.637 + aHeader.GetBodyLength(&mBodySegmentLength); 1.638 + } 1.639 +} 1.640 + 1.641 +bool 1.642 +BluetoothOppManager::ExtractBlobHeaders() 1.643 +{ 1.644 + RetrieveSentFileName(); 1.645 + 1.646 + nsresult rv = mBlob->GetType(mContentType); 1.647 + if (NS_FAILED(rv)) { 1.648 + BT_WARNING("Can't get content type"); 1.649 + SendDisconnectRequest(); 1.650 + return false; 1.651 + } 1.652 + 1.653 + uint64_t fileLength; 1.654 + rv = mBlob->GetSize(&fileLength); 1.655 + if (NS_FAILED(rv)) { 1.656 + BT_WARNING("Can't get file size"); 1.657 + SendDisconnectRequest(); 1.658 + return false; 1.659 + } 1.660 + 1.661 + // Currently we keep the size of files which were sent/received via 1.662 + // Bluetooth not exceed UINT32_MAX because the Length header in OBEX 1.663 + // is only 4-byte long. Although it is possible to transfer a file 1.664 + // larger than UINT32_MAX, it needs to parse another OBEX Header 1.665 + // and I would like to leave it as a feature. 1.666 + if (fileLength > (uint64_t)UINT32_MAX) { 1.667 + BT_WARNING("The file size is too large for now"); 1.668 + SendDisconnectRequest(); 1.669 + return false; 1.670 + } 1.671 + 1.672 + mFileLength = fileLength; 1.673 + rv = NS_NewThread(getter_AddRefs(mReadFileThread)); 1.674 + if (NS_FAILED(rv)) { 1.675 + BT_WARNING("Can't create thread"); 1.676 + SendDisconnectRequest(); 1.677 + return false; 1.678 + } 1.679 + 1.680 + return true; 1.681 +} 1.682 + 1.683 +void 1.684 +BluetoothOppManager::RetrieveSentFileName() 1.685 +{ 1.686 + mFileName.Truncate(); 1.687 + 1.688 + nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob); 1.689 + if (file) { 1.690 + file->GetName(mFileName); 1.691 + } 1.692 + 1.693 + /** 1.694 + * We try our best to get the file extention to avoid interoperability issues. 1.695 + * However, once we found that we are unable to get suitable extension or 1.696 + * information about the content type, sending a pre-defined file name without 1.697 + * extension would be fine. 1.698 + */ 1.699 + if (mFileName.IsEmpty()) { 1.700 + mFileName.AssignLiteral("Unknown"); 1.701 + } 1.702 + 1.703 + int32_t offset = mFileName.RFindChar('/'); 1.704 + if (offset != kNotFound) { 1.705 + mFileName = Substring(mFileName, offset + 1); 1.706 + } 1.707 + 1.708 + offset = mFileName.RFindChar('.'); 1.709 + if (offset == kNotFound) { 1.710 + nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID); 1.711 + 1.712 + if (mimeSvc) { 1.713 + nsString mimeType; 1.714 + mBlob->GetType(mimeType); 1.715 + 1.716 + nsCString extension; 1.717 + nsresult rv = 1.718 + mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType), 1.719 + EmptyCString(), 1.720 + extension); 1.721 + if (NS_SUCCEEDED(rv)) { 1.722 + mFileName.AppendLiteral("."); 1.723 + AppendUTF8toUTF16(extension, mFileName); 1.724 + } 1.725 + } 1.726 + } 1.727 +} 1.728 + 1.729 +bool 1.730 +BluetoothOppManager::IsReservedChar(char16_t c) 1.731 +{ 1.732 + return (c < 0x0020 || 1.733 + c == char16_t('?') || c == char16_t('|') || c == char16_t('<') || 1.734 + c == char16_t('>') || c == char16_t('"') || c == char16_t(':') || 1.735 + c == char16_t('/') || c == char16_t('*') || c == char16_t('\\')); 1.736 +} 1.737 + 1.738 +void 1.739 +BluetoothOppManager::ValidateFileName() 1.740 +{ 1.741 + int length = mFileName.Length(); 1.742 + 1.743 + for (int i = 0; i < length; ++i) { 1.744 + // Replace reserved char of fat file system with '_' 1.745 + if (IsReservedChar(mFileName.CharAt(i))) { 1.746 + mFileName.Replace(i, 1, char16_t('_')); 1.747 + } 1.748 + } 1.749 +} 1.750 + 1.751 +bool 1.752 +BluetoothOppManager::ComposePacket(uint8_t aOpCode, UnixSocketRawData* aMessage) 1.753 +{ 1.754 + MOZ_ASSERT(NS_IsMainThread()); 1.755 + MOZ_ASSERT(aMessage); 1.756 + 1.757 + int frameHeaderLength = 0; 1.758 + 1.759 + // See if this is the first part of each Put packet 1.760 + if (mPutPacketReceivedLength == 0) { 1.761 + // Section 3.3.3 "Put", IrOBEX 1.2 1.762 + // [opcode:1][length:2][Headers:var] 1.763 + frameHeaderLength = 3; 1.764 + 1.765 + mPacketLength = ((((int)aMessage->mData[1]) << 8) | aMessage->mData[2]) - 1.766 + frameHeaderLength; 1.767 + /** 1.768 + * A PUT request from remote devices may be divided into multiple parts. 1.769 + * In other words, one request may need to be received multiple times, 1.770 + * so here we keep a variable mPutPacketReceivedLength to indicate if 1.771 + * current PUT request is done. 1.772 + */ 1.773 + mReceivedDataBuffer = new uint8_t[mPacketLength]; 1.774 + mPutFinalFlag = (aOpCode == ObexRequestCode::PutFinal); 1.775 + } 1.776 + 1.777 + int dataLength = aMessage->mSize - frameHeaderLength; 1.778 + 1.779 + // Check length before memcpy to prevent from memory pollution 1.780 + if (dataLength < 0 || 1.781 + mPutPacketReceivedLength + dataLength > mPacketLength) { 1.782 + BT_LOGR("Received packet size is unreasonable"); 1.783 + 1.784 + ReplyToPut(mPutFinalFlag, false); 1.785 + DeleteReceivedFile(); 1.786 + FileTransferComplete(); 1.787 + 1.788 + return false; 1.789 + } 1.790 + 1.791 + memcpy(mReceivedDataBuffer.get() + mPutPacketReceivedLength, 1.792 + &aMessage->mData[frameHeaderLength], dataLength); 1.793 + 1.794 + mPutPacketReceivedLength += dataLength; 1.795 + 1.796 + return (mPutPacketReceivedLength == mPacketLength); 1.797 +} 1.798 + 1.799 +void 1.800 +BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage) 1.801 +{ 1.802 + MOZ_ASSERT(NS_IsMainThread()); 1.803 + 1.804 + uint8_t opCode; 1.805 + int receivedLength = aMessage->mSize; 1.806 + 1.807 + if (mPutPacketReceivedLength > 0) { 1.808 + opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put; 1.809 + } else { 1.810 + opCode = aMessage->mData[0]; 1.811 + 1.812 + // When there's a Put packet right after a PutFinal packet, 1.813 + // which means it's the start point of a new file. 1.814 + if (mPutFinalFlag && 1.815 + (opCode == ObexRequestCode::Put || 1.816 + opCode == ObexRequestCode::PutFinal)) { 1.817 + mNewFileFlag = true; 1.818 + AfterFirstPut(); 1.819 + } 1.820 + } 1.821 + 1.822 + ObexHeaderSet pktHeaders(opCode); 1.823 + if (opCode == ObexRequestCode::Connect) { 1.824 + // Section 3.3.1 "Connect", IrOBEX 1.2 1.825 + // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2] 1.826 + // [Headers:var] 1.827 + if (!ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders)) { 1.828 + ReplyError(ObexResponseCode::BadRequest); 1.829 + return; 1.830 + } 1.831 + 1.832 + ReplyToConnect(); 1.833 + AfterOppConnected(); 1.834 + } else if (opCode == ObexRequestCode::Abort) { 1.835 + // Section 3.3.5 "Abort", IrOBEX 1.2 1.836 + // [opcode:1][length:2][Headers:var] 1.837 + if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) { 1.838 + ReplyError(ObexResponseCode::BadRequest); 1.839 + return; 1.840 + } 1.841 + 1.842 + ReplyToDisconnectOrAbort(); 1.843 + DeleteReceivedFile(); 1.844 + } else if (opCode == ObexRequestCode::Disconnect) { 1.845 + // Section 3.3.2 "Disconnect", IrOBEX 1.2 1.846 + // [opcode:1][length:2][Headers:var] 1.847 + if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) { 1.848 + ReplyError(ObexResponseCode::BadRequest); 1.849 + return; 1.850 + } 1.851 + 1.852 + ReplyToDisconnectOrAbort(); 1.853 + AfterOppDisconnected(); 1.854 + FileTransferComplete(); 1.855 + } else if (opCode == ObexRequestCode::Put || 1.856 + opCode == ObexRequestCode::PutFinal) { 1.857 + if (!ComposePacket(opCode, aMessage)) { 1.858 + return; 1.859 + } 1.860 + 1.861 + // A Put packet is received completely 1.862 + ParseHeaders(mReceivedDataBuffer.get(), 1.863 + mPutPacketReceivedLength, &pktHeaders); 1.864 + ExtractPacketHeaders(pktHeaders); 1.865 + ValidateFileName(); 1.866 + 1.867 + // When we cancel the transfer, delete the file and notify completion 1.868 + if (mAbortFlag) { 1.869 + ReplyToPut(mPutFinalFlag, false); 1.870 + mSentFileLength += mBodySegmentLength; 1.871 + DeleteReceivedFile(); 1.872 + FileTransferComplete(); 1.873 + return; 1.874 + } 1.875 + 1.876 + // Wait until get confirmation from user, then create file and write to it 1.877 + if (mWaitingForConfirmationFlag) { 1.878 + ReceivingFileConfirmation(); 1.879 + mSentFileLength += mBodySegmentLength; 1.880 + return; 1.881 + } 1.882 + 1.883 + // Already get confirmation from user, create a new file if needed and 1.884 + // write to output stream 1.885 + if (mNewFileFlag) { 1.886 + StartFileTransfer(); 1.887 + if (!CreateFile()) { 1.888 + ReplyToPut(mPutFinalFlag, false); 1.889 + return; 1.890 + } 1.891 + mNewFileFlag = false; 1.892 + } 1.893 + 1.894 + if (!WriteToFile(mBodySegment.get(), mBodySegmentLength)) { 1.895 + ReplyToPut(mPutFinalFlag, false); 1.896 + return; 1.897 + } 1.898 + 1.899 + ReplyToPut(mPutFinalFlag, true); 1.900 + 1.901 + // Send progress update 1.902 + mSentFileLength += mBodySegmentLength; 1.903 + if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) { 1.904 + UpdateProgress(); 1.905 + mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1; 1.906 + } 1.907 + 1.908 + // Success to receive a file and notify completion 1.909 + if (mPutFinalFlag) { 1.910 + mSuccessFlag = true; 1.911 + FileTransferComplete(); 1.912 + NotifyAboutFileChange(); 1.913 + } 1.914 + } else if (opCode == ObexRequestCode::Get || 1.915 + opCode == ObexRequestCode::GetFinal || 1.916 + opCode == ObexRequestCode::SetPath) { 1.917 + ReplyError(ObexResponseCode::BadRequest); 1.918 + BT_WARNING("Unsupported ObexRequestCode"); 1.919 + } else { 1.920 + ReplyError(ObexResponseCode::NotImplemented); 1.921 + BT_WARNING("Unrecognized ObexRequestCode"); 1.922 + } 1.923 +} 1.924 + 1.925 +void 1.926 +BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage) 1.927 +{ 1.928 + MOZ_ASSERT(NS_IsMainThread()); 1.929 + 1.930 + uint8_t opCode = aMessage->mData[0]; 1.931 + 1.932 + // Check response code and send out system message as finished if the response 1.933 + // code is somehow incorrect. 1.934 + uint8_t expectedOpCode = ObexResponseCode::Success; 1.935 + if (mLastCommand == ObexRequestCode::Put) { 1.936 + expectedOpCode = ObexResponseCode::Continue; 1.937 + } 1.938 + 1.939 + if (opCode != expectedOpCode) { 1.940 + if (mLastCommand == ObexRequestCode::Put || 1.941 + mLastCommand == ObexRequestCode::Abort || 1.942 + mLastCommand == ObexRequestCode::PutFinal) { 1.943 + SendDisconnectRequest(); 1.944 + } 1.945 + nsAutoCString str; 1.946 + str += "[OPP] 0x"; 1.947 + str.AppendInt(mLastCommand, 16); 1.948 + str += " failed"; 1.949 + BT_WARNING(str.get()); 1.950 + FileTransferComplete(); 1.951 + return; 1.952 + } 1.953 + 1.954 + if (mLastCommand == ObexRequestCode::PutFinal) { 1.955 + mSuccessFlag = true; 1.956 + FileTransferComplete(); 1.957 + 1.958 + if (mInputStream) { 1.959 + mInputStream->Close(); 1.960 + mInputStream = nullptr; 1.961 + } 1.962 + 1.963 + if (mCurrentBlobIndex + 1 == (int) mBatches[0].mBlobs.Length()) { 1.964 + SendDisconnectRequest(); 1.965 + } else { 1.966 + StartSendingNextFile(); 1.967 + } 1.968 + } else if (mLastCommand == ObexRequestCode::Abort) { 1.969 + SendDisconnectRequest(); 1.970 + FileTransferComplete(); 1.971 + } else if (mLastCommand == ObexRequestCode::Disconnect) { 1.972 + AfterOppDisconnected(); 1.973 + // Most devices will directly terminate connection after receiving 1.974 + // Disconnect request, so we make a delay here. If the socket hasn't been 1.975 + // disconnected, we will close it. 1.976 + if (mSocket) { 1.977 + MessageLoop::current()-> 1.978 + PostDelayedTask(FROM_HERE, new CloseSocketTask(mSocket), 1000); 1.979 + } 1.980 + } else if (mLastCommand == ObexRequestCode::Connect) { 1.981 + MOZ_ASSERT(!mFileName.IsEmpty()); 1.982 + MOZ_ASSERT(mBlob); 1.983 + 1.984 + AfterOppConnected(); 1.985 + 1.986 + // Keep remote information 1.987 + mRemoteObexVersion = aMessage->mData[3]; 1.988 + mRemoteConnectionFlags = aMessage->mData[4]; 1.989 + mRemoteMaxPacketLength = 1.990 + (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]); 1.991 + 1.992 + // The length of file name exceeds maximum length. 1.993 + int fileNameByteLen = (mFileName.Length() + 1) * 2; 1.994 + int headerLen = kPutRequestHeaderSize + kPutRequestAppendHeaderSize; 1.995 + if (fileNameByteLen > mRemoteMaxPacketLength - headerLen) { 1.996 + BT_WARNING("The length of file name is aberrant."); 1.997 + SendDisconnectRequest(); 1.998 + return; 1.999 + } 1.1000 + 1.1001 + SendPutHeaderRequest(mFileName, mFileLength); 1.1002 + } else if (mLastCommand == ObexRequestCode::Put) { 1.1003 + if (mWaitingToSendPutFinal) { 1.1004 + SendPutFinalRequest(); 1.1005 + return; 1.1006 + } 1.1007 + 1.1008 + if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) { 1.1009 + UpdateProgress(); 1.1010 + mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1; 1.1011 + } 1.1012 + 1.1013 + nsresult rv; 1.1014 + if (!mInputStream) { 1.1015 + rv = mBlob->GetInternalStream(getter_AddRefs(mInputStream)); 1.1016 + if (NS_FAILED(rv)) { 1.1017 + BT_WARNING("Can't get internal stream of blob"); 1.1018 + SendDisconnectRequest(); 1.1019 + return; 1.1020 + } 1.1021 + } 1.1022 + 1.1023 + nsRefPtr<ReadFileTask> task = new ReadFileTask(mInputStream, 1.1024 + mRemoteMaxPacketLength); 1.1025 + rv = mReadFileThread->Dispatch(task, NS_DISPATCH_NORMAL); 1.1026 + if (NS_FAILED(rv)) { 1.1027 + BT_WARNING("Cannot dispatch read file task!"); 1.1028 + SendDisconnectRequest(); 1.1029 + } 1.1030 + } else { 1.1031 + BT_WARNING("Unhandled ObexRequestCode"); 1.1032 + } 1.1033 +} 1.1034 + 1.1035 +// Virtual function of class SocketConsumer 1.1036 +void 1.1037 +BluetoothOppManager::ReceiveSocketData(BluetoothSocket* aSocket, 1.1038 + nsAutoPtr<UnixSocketRawData>& aMessage) 1.1039 +{ 1.1040 + if (mIsServer) { 1.1041 + ServerDataHandler(aMessage); 1.1042 + } else { 1.1043 + ClientDataHandler(aMessage); 1.1044 + } 1.1045 +} 1.1046 + 1.1047 +void 1.1048 +BluetoothOppManager::SendConnectRequest() 1.1049 +{ 1.1050 + if (mConnected) return; 1.1051 + 1.1052 + // Section 3.3.1 "Connect", IrOBEX 1.2 1.1053 + // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2] 1.1054 + // [Headers:var] 1.1055 + uint8_t req[255]; 1.1056 + int index = 7; 1.1057 + 1.1058 + req[3] = 0x10; // version=1.0 1.1059 + req[4] = 0x00; // flag=0x00 1.1060 + req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8; 1.1061 + req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH; 1.1062 + 1.1063 + SendObexData(req, ObexRequestCode::Connect, index); 1.1064 +} 1.1065 + 1.1066 +void 1.1067 +BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName, 1.1068 + int aFileSize) 1.1069 +{ 1.1070 + if (!mConnected) return; 1.1071 + 1.1072 + uint8_t* req = new uint8_t[mRemoteMaxPacketLength]; 1.1073 + 1.1074 + int len = aFileName.Length(); 1.1075 + uint8_t* fileName = new uint8_t[(len + 1) * 2]; 1.1076 + const char16_t* fileNamePtr = aFileName.BeginReading(); 1.1077 + 1.1078 + for (int i = 0; i < len; i++) { 1.1079 + fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8); 1.1080 + fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i]; 1.1081 + } 1.1082 + 1.1083 + fileName[len * 2] = 0x00; 1.1084 + fileName[len * 2 + 1] = 0x00; 1.1085 + 1.1086 + int index = 3; 1.1087 + index += AppendHeaderName(&req[index], mRemoteMaxPacketLength - index, 1.1088 + (char*)fileName, (len + 1) * 2); 1.1089 + index += AppendHeaderLength(&req[index], aFileSize); 1.1090 + 1.1091 + SendObexData(req, ObexRequestCode::Put, index); 1.1092 + 1.1093 + delete [] fileName; 1.1094 + delete [] req; 1.1095 +} 1.1096 + 1.1097 +void 1.1098 +BluetoothOppManager::SendPutRequest(uint8_t* aFileBody, 1.1099 + int aFileBodyLength) 1.1100 +{ 1.1101 + if (!mConnected) return; 1.1102 + int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize; 1.1103 + if (aFileBodyLength > packetLeftSpace) { 1.1104 + BT_WARNING("Not allowed such a small MaxPacketLength value"); 1.1105 + return; 1.1106 + } 1.1107 + 1.1108 + // Section 3.3.3 "Put", IrOBEX 1.2 1.1109 + // [opcode:1][length:2][Headers:var] 1.1110 + uint8_t* req = new uint8_t[mRemoteMaxPacketLength]; 1.1111 + 1.1112 + int index = 3; 1.1113 + index += AppendHeaderBody(&req[index], mRemoteMaxPacketLength - index, 1.1114 + aFileBody, aFileBodyLength); 1.1115 + 1.1116 + SendObexData(req, ObexRequestCode::Put, index); 1.1117 + delete [] req; 1.1118 + 1.1119 + mSentFileLength += aFileBodyLength; 1.1120 +} 1.1121 + 1.1122 +void 1.1123 +BluetoothOppManager::SendPutFinalRequest() 1.1124 +{ 1.1125 + if (!mConnected) return; 1.1126 + 1.1127 + /** 1.1128 + * Section 2.2.9, "End-of-Body", IrObex 1.2 1.1129 + * End-of-Body is used to identify the last chunk of the object body. 1.1130 + * For most platforms, a PutFinal packet is sent with an zero length 1.1131 + * End-of-Body header. 1.1132 + */ 1.1133 + 1.1134 + // [opcode:1][length:2] 1.1135 + int index = 3; 1.1136 + uint8_t* req = new uint8_t[mRemoteMaxPacketLength]; 1.1137 + index += AppendHeaderEndOfBody(&req[index]); 1.1138 + 1.1139 + SendObexData(req, ObexRequestCode::PutFinal, index); 1.1140 + delete [] req; 1.1141 + 1.1142 + mWaitingToSendPutFinal = false; 1.1143 +} 1.1144 + 1.1145 +void 1.1146 +BluetoothOppManager::SendDisconnectRequest() 1.1147 +{ 1.1148 + if (!mConnected) return; 1.1149 + 1.1150 + // Section 3.3.2 "Disconnect", IrOBEX 1.2 1.1151 + // [opcode:1][length:2][Headers:var] 1.1152 + uint8_t req[255]; 1.1153 + int index = 3; 1.1154 + 1.1155 + SendObexData(req, ObexRequestCode::Disconnect, index); 1.1156 +} 1.1157 + 1.1158 +void 1.1159 +BluetoothOppManager::CheckPutFinal(uint32_t aNumRead) 1.1160 +{ 1.1161 + if (mSentFileLength + aNumRead >= mFileLength) { 1.1162 + mWaitingToSendPutFinal = true; 1.1163 + } 1.1164 +} 1.1165 + 1.1166 +bool 1.1167 +BluetoothOppManager::IsConnected() 1.1168 +{ 1.1169 + return (mConnected && !mSendTransferCompleteFlag); 1.1170 +} 1.1171 + 1.1172 +void 1.1173 +BluetoothOppManager::GetAddress(nsAString& aDeviceAddress) 1.1174 +{ 1.1175 + return mSocket->GetAddress(aDeviceAddress); 1.1176 +} 1.1177 + 1.1178 +void 1.1179 +BluetoothOppManager::ReplyToConnect() 1.1180 +{ 1.1181 + if (mConnected) return; 1.1182 + 1.1183 + // Section 3.3.1 "Connect", IrOBEX 1.2 1.1184 + // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2] 1.1185 + // [Headers:var] 1.1186 + uint8_t req[255]; 1.1187 + int index = 7; 1.1188 + 1.1189 + req[3] = 0x10; // version=1.0 1.1190 + req[4] = 0x00; // flag=0x00 1.1191 + req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8; 1.1192 + req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH; 1.1193 + 1.1194 + SendObexData(req, ObexResponseCode::Success, index); 1.1195 +} 1.1196 + 1.1197 +void 1.1198 +BluetoothOppManager::ReplyToDisconnectOrAbort() 1.1199 +{ 1.1200 + if (!mConnected) return; 1.1201 + 1.1202 + // Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2 1.1203 + // The format of response packet of "Disconnect" and "Abort" are the same 1.1204 + // [opcode:1][length:2][Headers:var] 1.1205 + uint8_t req[255]; 1.1206 + int index = 3; 1.1207 + 1.1208 + SendObexData(req, ObexResponseCode::Success, index); 1.1209 +} 1.1210 + 1.1211 +void 1.1212 +BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue) 1.1213 +{ 1.1214 + if (!mConnected) return; 1.1215 + 1.1216 + // The received length can be reset here because this is where we reply to a 1.1217 + // complete put packet. 1.1218 + mPutPacketReceivedLength = 0; 1.1219 + 1.1220 + // Section 3.3.2 "Disconnect", IrOBEX 1.2 1.1221 + // [opcode:1][length:2][Headers:var] 1.1222 + uint8_t req[255]; 1.1223 + int index = 3; 1.1224 + uint8_t opcode; 1.1225 + 1.1226 + if (aContinue) { 1.1227 + opcode = (aFinal)? ObexResponseCode::Success : 1.1228 + ObexResponseCode::Continue; 1.1229 + } else { 1.1230 + opcode = (aFinal)? ObexResponseCode::Unauthorized : 1.1231 + ObexResponseCode::Unauthorized & (~FINAL_BIT); 1.1232 + } 1.1233 + 1.1234 + SendObexData(req, opcode, index); 1.1235 +} 1.1236 + 1.1237 +void 1.1238 +BluetoothOppManager::ReplyError(uint8_t aError) 1.1239 +{ 1.1240 + BT_LOGR("error: %d", aError); 1.1241 + 1.1242 + // Section 3.2 "Response Format", IrOBEX 1.2 1.1243 + // [opcode:1][length:2][Headers:var] 1.1244 + uint8_t req[255]; 1.1245 + int index = 3; 1.1246 + 1.1247 + SendObexData(req, aError, index); 1.1248 +} 1.1249 + 1.1250 +void 1.1251 +BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize) 1.1252 +{ 1.1253 + SetObexPacketInfo(aData, aOpcode, aSize); 1.1254 + 1.1255 + if (!mIsServer) { 1.1256 + mLastCommand = aOpcode; 1.1257 + } 1.1258 + 1.1259 + UnixSocketRawData* s = new UnixSocketRawData(aSize); 1.1260 + memcpy(s->mData, aData, s->mSize); 1.1261 + mSocket->SendDroidSocketData(s); 1.1262 +} 1.1263 + 1.1264 +void 1.1265 +BluetoothOppManager::FileTransferComplete() 1.1266 +{ 1.1267 + if (mSendTransferCompleteFlag) { 1.1268 + return; 1.1269 + } 1.1270 + 1.1271 + NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-complete"); 1.1272 + InfallibleTArray<BluetoothNamedValue> parameters; 1.1273 + 1.1274 + BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress); 1.1275 + BT_APPEND_NAMED_VALUE(parameters, "success", mSuccessFlag); 1.1276 + BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer); 1.1277 + BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName); 1.1278 + BT_APPEND_NAMED_VALUE(parameters, "fileLength", mSentFileLength); 1.1279 + BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType); 1.1280 + 1.1281 + BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters); 1.1282 + 1.1283 + mSendTransferCompleteFlag = true; 1.1284 +} 1.1285 + 1.1286 +void 1.1287 +BluetoothOppManager::StartFileTransfer() 1.1288 +{ 1.1289 + NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-start"); 1.1290 + InfallibleTArray<BluetoothNamedValue> parameters; 1.1291 + 1.1292 + BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress); 1.1293 + BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer); 1.1294 + BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName); 1.1295 + BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength); 1.1296 + BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType); 1.1297 + 1.1298 + BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters); 1.1299 + 1.1300 + mSendTransferCompleteFlag = false; 1.1301 +} 1.1302 + 1.1303 +void 1.1304 +BluetoothOppManager::UpdateProgress() 1.1305 +{ 1.1306 + NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-update-progress"); 1.1307 + InfallibleTArray<BluetoothNamedValue> parameters; 1.1308 + 1.1309 + BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress); 1.1310 + BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer); 1.1311 + BT_APPEND_NAMED_VALUE(parameters, "processedLength", mSentFileLength); 1.1312 + BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength); 1.1313 + 1.1314 + BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters); 1.1315 +} 1.1316 + 1.1317 +void 1.1318 +BluetoothOppManager::ReceivingFileConfirmation() 1.1319 +{ 1.1320 + NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-receiving-file-confirmation"); 1.1321 + InfallibleTArray<BluetoothNamedValue> parameters; 1.1322 + 1.1323 + BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress); 1.1324 + BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName); 1.1325 + BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength); 1.1326 + BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType); 1.1327 + 1.1328 + BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters); 1.1329 +} 1.1330 + 1.1331 +void 1.1332 +BluetoothOppManager::NotifyAboutFileChange() 1.1333 +{ 1.1334 + NS_NAMED_LITERAL_STRING(data, "modified"); 1.1335 + 1.1336 + nsCOMPtr<nsIObserverService> obs = 1.1337 + mozilla::services::GetObserverService(); 1.1338 + NS_ENSURE_TRUE_VOID(obs); 1.1339 + 1.1340 + obs->NotifyObservers(mDsFile, "file-watcher-notify", data.get()); 1.1341 +} 1.1342 + 1.1343 +void 1.1344 +BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket) 1.1345 +{ 1.1346 + BT_LOGR("[%s]", (mIsServer)? "server" : "client"); 1.1347 + MOZ_ASSERT(aSocket); 1.1348 + 1.1349 + /** 1.1350 + * If the created connection is an inbound connection, close server socket 1.1351 + * because currently only one file-transfer session is allowed. After that, 1.1352 + * we need to make sure that server socket would be nulled out. 1.1353 + * As for outbound connections, we just notify the controller that it's done. 1.1354 + */ 1.1355 + if (aSocket == mServerSocket) { 1.1356 + MOZ_ASSERT(!mSocket); 1.1357 + mServerSocket.swap(mSocket); 1.1358 + } 1.1359 + 1.1360 + // Cache device address since we can't get socket address when a remote 1.1361 + // device disconnect with us. 1.1362 + mSocket->GetAddress(mDeviceAddress); 1.1363 + 1.1364 + // Start sending file if we connect as a client 1.1365 + if (!mIsServer) { 1.1366 + StartSendingNextFile(); 1.1367 + } 1.1368 +} 1.1369 + 1.1370 +void 1.1371 +BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket) 1.1372 +{ 1.1373 + BT_LOGR("[%s]", (mIsServer)? "server" : "client"); 1.1374 + 1.1375 + mServerSocket = nullptr; 1.1376 + mSocket = nullptr; 1.1377 + 1.1378 + if (!mIsServer) { 1.1379 + // Inform gaia of remaining blobs' sending failure 1.1380 + DiscardBlobsToSend(); 1.1381 + } 1.1382 + 1.1383 + // Listen as a server if there's no more batch to process 1.1384 + if (!ProcessNextBatch()) { 1.1385 + Listen(); 1.1386 + } 1.1387 +} 1.1388 + 1.1389 +void 1.1390 +BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket) 1.1391 +{ 1.1392 + MOZ_ASSERT(aSocket); 1.1393 + if (aSocket != mSocket) { 1.1394 + // Do nothing when a listening server socket is closed. 1.1395 + return; 1.1396 + } 1.1397 + BT_LOGR("[%s]", (mIsServer) ? "server" : "client"); 1.1398 + 1.1399 + /** 1.1400 + * It is valid for a bluetooth device which is transfering file via OPP 1.1401 + * closing socket without sending OBEX disconnect request first. So we 1.1402 + * delete the broken file when we failed to receive a file from the remote, 1.1403 + * and notify the transfer has been completed (but failed). We also call 1.1404 + * AfterOppDisconnected here to ensure all variables will be cleaned. 1.1405 + */ 1.1406 + if (!mSuccessFlag) { 1.1407 + if (mIsServer) { 1.1408 + DeleteReceivedFile(); 1.1409 + } 1.1410 + 1.1411 + FileTransferComplete(); 1.1412 + if (!mIsServer) { 1.1413 + // Inform gaia of remaining blobs' sending failure 1.1414 + DiscardBlobsToSend(); 1.1415 + } 1.1416 + } 1.1417 + 1.1418 + AfterOppDisconnected(); 1.1419 + mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE); 1.1420 + mSuccessFlag = false; 1.1421 + 1.1422 + mSocket = nullptr; 1.1423 + // Listen as a server if there's no more batch to process 1.1424 + if (!ProcessNextBatch()) { 1.1425 + Listen(); 1.1426 + } 1.1427 +} 1.1428 + 1.1429 +void 1.1430 +BluetoothOppManager::Disconnect(BluetoothProfileController* aController) 1.1431 +{ 1.1432 + if (mSocket) { 1.1433 + mSocket->Disconnect(); 1.1434 + } else { 1.1435 + BT_WARNING("%s: No ongoing file transfer to stop", __FUNCTION__); 1.1436 + } 1.1437 +} 1.1438 + 1.1439 +NS_IMPL_ISUPPORTS(BluetoothOppManager, nsIObserver) 1.1440 + 1.1441 +bool 1.1442 +BluetoothOppManager::AcquireSdcardMountLock() 1.1443 +{ 1.1444 + nsCOMPtr<nsIVolumeService> volumeSrv = 1.1445 + do_GetService(NS_VOLUMESERVICE_CONTRACTID); 1.1446 + NS_ENSURE_TRUE(volumeSrv, false); 1.1447 + nsresult rv; 1.1448 + rv = volumeSrv->CreateMountLock(NS_LITERAL_STRING("sdcard"), 1.1449 + getter_AddRefs(mMountLock)); 1.1450 + NS_ENSURE_SUCCESS(rv, false); 1.1451 + return true; 1.1452 +} 1.1453 + 1.1454 +void 1.1455 +BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress, 1.1456 + const nsAString& aServiceUuid, 1.1457 + int aChannel) 1.1458 +{ 1.1459 + MOZ_ASSERT(false); 1.1460 +} 1.1461 + 1.1462 +void 1.1463 +BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress) 1.1464 +{ 1.1465 + MOZ_ASSERT(false); 1.1466 +} 1.1467 + 1.1468 +void 1.1469 +BluetoothOppManager::Connect(const nsAString& aDeviceAddress, 1.1470 + BluetoothProfileController* aController) 1.1471 +{ 1.1472 + MOZ_ASSERT(false); 1.1473 +} 1.1474 + 1.1475 +void 1.1476 +BluetoothOppManager::OnConnect(const nsAString& aErrorStr) 1.1477 +{ 1.1478 + MOZ_ASSERT(false); 1.1479 +} 1.1480 + 1.1481 +void 1.1482 +BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr) 1.1483 +{ 1.1484 + MOZ_ASSERT(false); 1.1485 +} 1.1486 + 1.1487 +void 1.1488 +BluetoothOppManager::Reset() 1.1489 +{ 1.1490 + MOZ_ASSERT(false); 1.1491 +}