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