dom/bluetooth/bluedroid/BluetoothOppManager.cpp

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

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

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

michael@0 1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "base/basictypes.h"
michael@0 8 #include "BluetoothOppManager.h"
michael@0 9
michael@0 10 #include "BluetoothService.h"
michael@0 11 #include "BluetoothSocket.h"
michael@0 12 #include "BluetoothUtils.h"
michael@0 13 #include "BluetoothUuid.h"
michael@0 14 #include "ObexBase.h"
michael@0 15
michael@0 16 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
michael@0 17 #include "mozilla/RefPtr.h"
michael@0 18 #include "mozilla/Services.h"
michael@0 19 #include "mozilla/StaticPtr.h"
michael@0 20 #include "nsAutoPtr.h"
michael@0 21 #include "nsCExternalHandlerService.h"
michael@0 22 #include "nsIObserver.h"
michael@0 23 #include "nsIObserverService.h"
michael@0 24 #include "nsIDOMFile.h"
michael@0 25 #include "nsIFile.h"
michael@0 26 #include "nsIInputStream.h"
michael@0 27 #include "nsIMIMEService.h"
michael@0 28 #include "nsIOutputStream.h"
michael@0 29 #include "nsIVolumeService.h"
michael@0 30 #include "nsNetUtil.h"
michael@0 31 #include "nsServiceManagerUtils.h"
michael@0 32
michael@0 33 #define TARGET_SUBDIR "Download/Bluetooth/"
michael@0 34
michael@0 35 USING_BLUETOOTH_NAMESPACE
michael@0 36 using namespace mozilla;
michael@0 37 using namespace mozilla::ipc;
michael@0 38
michael@0 39 namespace {
michael@0 40 // Sending system message "bluetooth-opp-update-progress" every 50kb
michael@0 41 static const uint32_t kUpdateProgressBase = 50 * 1024;
michael@0 42
michael@0 43 /*
michael@0 44 * The format of the header of an PUT request is
michael@0 45 * [opcode:1][packet length:2][headerId:1][header length:2]
michael@0 46 */
michael@0 47 static const uint32_t kPutRequestHeaderSize = 6;
michael@0 48
michael@0 49 /*
michael@0 50 * The format of the appended header of an PUT request is
michael@0 51 * [headerId:1][header length:4]
michael@0 52 * P.S. Length of name header is 4 since unicode is 2 bytes per char.
michael@0 53 */
michael@0 54 static const uint32_t kPutRequestAppendHeaderSize = 5;
michael@0 55
michael@0 56 StaticRefPtr<BluetoothOppManager> sBluetoothOppManager;
michael@0 57 static bool sInShutdown = false;
michael@0 58 }
michael@0 59
michael@0 60 class mozilla::dom::bluetooth::SendFileBatch {
michael@0 61 public:
michael@0 62 SendFileBatch(const nsAString& aDeviceAddress, nsIDOMBlob* aBlob)
michael@0 63 : mDeviceAddress(aDeviceAddress)
michael@0 64 {
michael@0 65 mBlobs.AppendElement(aBlob);
michael@0 66 }
michael@0 67
michael@0 68 nsString mDeviceAddress;
michael@0 69 nsCOMArray<nsIDOMBlob> mBlobs;
michael@0 70 };
michael@0 71
michael@0 72 NS_IMETHODIMP
michael@0 73 BluetoothOppManager::Observe(nsISupports* aSubject,
michael@0 74 const char* aTopic,
michael@0 75 const char16_t* aData)
michael@0 76 {
michael@0 77 MOZ_ASSERT(sBluetoothOppManager);
michael@0 78
michael@0 79 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
michael@0 80 HandleShutdown();
michael@0 81 return NS_OK;
michael@0 82 }
michael@0 83
michael@0 84 MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!");
michael@0 85 return NS_ERROR_UNEXPECTED;
michael@0 86 }
michael@0 87
michael@0 88 class SendSocketDataTask : public nsRunnable
michael@0 89 {
michael@0 90 public:
michael@0 91 SendSocketDataTask(uint8_t* aStream, uint32_t aSize)
michael@0 92 : mStream(aStream)
michael@0 93 , mSize(aSize)
michael@0 94 {
michael@0 95 MOZ_ASSERT(!NS_IsMainThread());
michael@0 96 }
michael@0 97
michael@0 98 NS_IMETHOD Run()
michael@0 99 {
michael@0 100 MOZ_ASSERT(NS_IsMainThread());
michael@0 101
michael@0 102 sBluetoothOppManager->SendPutRequest(mStream, mSize);
michael@0 103
michael@0 104 return NS_OK;
michael@0 105 }
michael@0 106
michael@0 107 private:
michael@0 108 nsAutoArrayPtr<uint8_t> mStream;
michael@0 109 uint32_t mSize;
michael@0 110 };
michael@0 111
michael@0 112 class ReadFileTask : public nsRunnable
michael@0 113 {
michael@0 114 public:
michael@0 115 ReadFileTask(nsIInputStream* aInputStream,
michael@0 116 uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream)
michael@0 117 {
michael@0 118 MOZ_ASSERT(NS_IsMainThread());
michael@0 119
michael@0 120 mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize;
michael@0 121 }
michael@0 122
michael@0 123 NS_IMETHOD Run()
michael@0 124 {
michael@0 125 MOZ_ASSERT(!NS_IsMainThread());
michael@0 126
michael@0 127 uint32_t numRead;
michael@0 128 nsAutoArrayPtr<char> buf(new char[mAvailablePacketSize]);
michael@0 129
michael@0 130 // function inputstream->Read() only works on non-main thread
michael@0 131 nsresult rv = mInputStream->Read(buf, mAvailablePacketSize, &numRead);
michael@0 132 if (NS_FAILED(rv)) {
michael@0 133 // Needs error handling here
michael@0 134 BT_WARNING("Failed to read from input stream");
michael@0 135 return NS_ERROR_FAILURE;
michael@0 136 }
michael@0 137
michael@0 138 if (numRead > 0) {
michael@0 139 sBluetoothOppManager->CheckPutFinal(numRead);
michael@0 140
michael@0 141 nsRefPtr<SendSocketDataTask> task =
michael@0 142 new SendSocketDataTask((uint8_t*)buf.forget(), numRead);
michael@0 143 if (NS_FAILED(NS_DispatchToMainThread(task))) {
michael@0 144 BT_WARNING("Failed to dispatch to main thread!");
michael@0 145 return NS_ERROR_FAILURE;
michael@0 146 }
michael@0 147 }
michael@0 148
michael@0 149 return NS_OK;
michael@0 150 };
michael@0 151
michael@0 152 private:
michael@0 153 nsCOMPtr<nsIInputStream> mInputStream;
michael@0 154 uint32_t mAvailablePacketSize;
michael@0 155 };
michael@0 156
michael@0 157 class CloseSocketTask : public Task
michael@0 158 {
michael@0 159 public:
michael@0 160 CloseSocketTask(BluetoothSocket* aSocket) : mSocket(aSocket)
michael@0 161 {
michael@0 162 MOZ_ASSERT(aSocket);
michael@0 163 }
michael@0 164
michael@0 165 void Run() MOZ_OVERRIDE
michael@0 166 {
michael@0 167 MOZ_ASSERT(NS_IsMainThread());
michael@0 168 mSocket->CloseDroidSocket();
michael@0 169 }
michael@0 170
michael@0 171 private:
michael@0 172 nsRefPtr<BluetoothSocket> mSocket;
michael@0 173 };
michael@0 174
michael@0 175 BluetoothOppManager::BluetoothOppManager() : mConnected(false)
michael@0 176 , mRemoteObexVersion(0)
michael@0 177 , mRemoteConnectionFlags(0)
michael@0 178 , mRemoteMaxPacketLength(0)
michael@0 179 , mLastCommand(0)
michael@0 180 , mPacketLength(0)
michael@0 181 , mPutPacketReceivedLength(0)
michael@0 182 , mBodySegmentLength(0)
michael@0 183 , mAbortFlag(false)
michael@0 184 , mNewFileFlag(false)
michael@0 185 , mPutFinalFlag(false)
michael@0 186 , mSendTransferCompleteFlag(false)
michael@0 187 , mSuccessFlag(false)
michael@0 188 , mIsServer(true)
michael@0 189 , mWaitingForConfirmationFlag(false)
michael@0 190 , mFileLength(0)
michael@0 191 , mSentFileLength(0)
michael@0 192 , mWaitingToSendPutFinal(false)
michael@0 193 , mCurrentBlobIndex(-1)
michael@0 194 {
michael@0 195 mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
michael@0 196 }
michael@0 197
michael@0 198 BluetoothOppManager::~BluetoothOppManager()
michael@0 199 {
michael@0 200 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
michael@0 201 NS_ENSURE_TRUE_VOID(obs);
michael@0 202 if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
michael@0 203 BT_WARNING("Failed to remove shutdown observer!");
michael@0 204 }
michael@0 205 }
michael@0 206
michael@0 207 bool
michael@0 208 BluetoothOppManager::Init()
michael@0 209 {
michael@0 210 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
michael@0 211 NS_ENSURE_TRUE(obs, false);
michael@0 212 if (NS_FAILED(obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false))) {
michael@0 213 BT_WARNING("Failed to add shutdown observer!");
michael@0 214 return false;
michael@0 215 }
michael@0 216
michael@0 217 /**
michael@0 218 * We don't start listening here as BluetoothServiceBluedroid calls Listen()
michael@0 219 * immediately when BT stops.
michael@0 220 *
michael@0 221 * If we start listening here, the listening fails when device boots up since
michael@0 222 * Listen() is called again and restarts server socket. The restart causes
michael@0 223 * absence of read events when device boots up.
michael@0 224 */
michael@0 225
michael@0 226 return true;
michael@0 227 }
michael@0 228
michael@0 229 //static
michael@0 230 BluetoothOppManager*
michael@0 231 BluetoothOppManager::Get()
michael@0 232 {
michael@0 233 MOZ_ASSERT(NS_IsMainThread());
michael@0 234
michael@0 235 // If sBluetoothOppManager already exists, exit early
michael@0 236 if (sBluetoothOppManager) {
michael@0 237 return sBluetoothOppManager;
michael@0 238 }
michael@0 239
michael@0 240 // If we're in shutdown, don't create a new instance
michael@0 241 NS_ENSURE_FALSE(sInShutdown, nullptr);
michael@0 242
michael@0 243 // Create a new instance, register, and return
michael@0 244 BluetoothOppManager *manager = new BluetoothOppManager();
michael@0 245 NS_ENSURE_TRUE(manager->Init(), nullptr);
michael@0 246
michael@0 247 sBluetoothOppManager = manager;
michael@0 248 return sBluetoothOppManager;
michael@0 249 }
michael@0 250
michael@0 251 void
michael@0 252 BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress)
michael@0 253 {
michael@0 254 MOZ_ASSERT(NS_IsMainThread());
michael@0 255
michael@0 256 // Stop listening because currently we only support one connection at a time.
michael@0 257 if (mServerSocket) {
michael@0 258 mServerSocket->Disconnect();
michael@0 259 mServerSocket = nullptr;
michael@0 260 }
michael@0 261
michael@0 262 mIsServer = false;
michael@0 263
michael@0 264 BluetoothService* bs = BluetoothService::Get();
michael@0 265 if (!bs || sInShutdown || mSocket) {
michael@0 266 OnSocketConnectError(mSocket);
michael@0 267 return;
michael@0 268 }
michael@0 269
michael@0 270 mSocket =
michael@0 271 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, false, true);
michael@0 272 mSocket->Connect(aDeviceAddress, -1);
michael@0 273 }
michael@0 274
michael@0 275 void
michael@0 276 BluetoothOppManager::HandleShutdown()
michael@0 277 {
michael@0 278 MOZ_ASSERT(NS_IsMainThread());
michael@0 279 sInShutdown = true;
michael@0 280 Disconnect(nullptr);
michael@0 281 sBluetoothOppManager = nullptr;
michael@0 282 }
michael@0 283
michael@0 284 bool
michael@0 285 BluetoothOppManager::Listen()
michael@0 286 {
michael@0 287 MOZ_ASSERT(NS_IsMainThread());
michael@0 288
michael@0 289 if (mSocket) {
michael@0 290 BT_WARNING("mSocket exists. Failed to listen.");
michael@0 291 return false;
michael@0 292 }
michael@0 293
michael@0 294 /**
michael@0 295 * Restart server socket since its underlying fd becomes invalid when
michael@0 296 * BT stops; otherwise no more read events would be received even if
michael@0 297 * BT restarts.
michael@0 298 */
michael@0 299 if (mServerSocket) {
michael@0 300 mServerSocket->Disconnect();
michael@0 301 mServerSocket = nullptr;
michael@0 302 }
michael@0 303
michael@0 304 mServerSocket =
michael@0 305 new BluetoothSocket(this, BluetoothSocketType::RFCOMM, false, true);
michael@0 306
michael@0 307 if (!mServerSocket->Listen(BluetoothReservedChannels::CHANNEL_OPUSH)) {
michael@0 308 BT_WARNING("[OPP] Can't listen on RFCOMM socket!");
michael@0 309 mServerSocket = nullptr;
michael@0 310 return false;
michael@0 311 }
michael@0 312
michael@0 313 mIsServer = true;
michael@0 314
michael@0 315 return true;
michael@0 316 }
michael@0 317
michael@0 318 void
michael@0 319 BluetoothOppManager::StartSendingNextFile()
michael@0 320 {
michael@0 321 MOZ_ASSERT(NS_IsMainThread());
michael@0 322
michael@0 323 MOZ_ASSERT(!IsConnected());
michael@0 324 MOZ_ASSERT(!mBatches.IsEmpty());
michael@0 325 MOZ_ASSERT((int)mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1);
michael@0 326
michael@0 327 mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
michael@0 328
michael@0 329 // Before sending content, we have to send a header including
michael@0 330 // information such as file name, file length and content type.
michael@0 331 ExtractBlobHeaders();
michael@0 332 StartFileTransfer();
michael@0 333
michael@0 334 if (mCurrentBlobIndex == 0) {
michael@0 335 // We may have more than one file waiting for transferring, but only one
michael@0 336 // CONNECT request would be sent. Therefore check if this is the very first
michael@0 337 // file at the head of queue.
michael@0 338 SendConnectRequest();
michael@0 339 } else {
michael@0 340 SendPutHeaderRequest(mFileName, mFileLength);
michael@0 341 AfterFirstPut();
michael@0 342 }
michael@0 343 }
michael@0 344
michael@0 345 bool
michael@0 346 BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
michael@0 347 BlobParent* aActor)
michael@0 348 {
michael@0 349 MOZ_ASSERT(NS_IsMainThread());
michael@0 350
michael@0 351 nsCOMPtr<nsIDOMBlob> blob = aActor->GetBlob();
michael@0 352
michael@0 353 return SendFile(aDeviceAddress, blob.get());
michael@0 354 }
michael@0 355
michael@0 356 bool
michael@0 357 BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
michael@0 358 nsIDOMBlob* aBlob)
michael@0 359 {
michael@0 360 MOZ_ASSERT(NS_IsMainThread());
michael@0 361
michael@0 362 AppendBlobToSend(aDeviceAddress, aBlob);
michael@0 363 if (!mSocket) {
michael@0 364 ProcessNextBatch();
michael@0 365 }
michael@0 366
michael@0 367 return true;
michael@0 368 }
michael@0 369
michael@0 370 void
michael@0 371 BluetoothOppManager::AppendBlobToSend(const nsAString& aDeviceAddress,
michael@0 372 nsIDOMBlob* aBlob)
michael@0 373 {
michael@0 374 MOZ_ASSERT(NS_IsMainThread());
michael@0 375
michael@0 376 int indexTail = mBatches.Length() - 1;
michael@0 377
michael@0 378 /**
michael@0 379 * Create a new batch if
michael@0 380 * - mBatches is empty, or
michael@0 381 * - aDeviceAddress differs from mDeviceAddress of the last batch
michael@0 382 */
michael@0 383 if (mBatches.IsEmpty() ||
michael@0 384 aDeviceAddress != mBatches[indexTail].mDeviceAddress) {
michael@0 385 SendFileBatch batch(aDeviceAddress, aBlob);
michael@0 386 mBatches.AppendElement(batch);
michael@0 387 } else {
michael@0 388 mBatches[indexTail].mBlobs.AppendElement(aBlob);
michael@0 389 }
michael@0 390 }
michael@0 391
michael@0 392 void
michael@0 393 BluetoothOppManager::DiscardBlobsToSend()
michael@0 394 {
michael@0 395 MOZ_ASSERT(NS_IsMainThread());
michael@0 396
michael@0 397 MOZ_ASSERT(!mBatches.IsEmpty());
michael@0 398 MOZ_ASSERT(!mIsServer);
michael@0 399
michael@0 400 int length = (int) mBatches[0].mBlobs.Length();
michael@0 401 while (length > mCurrentBlobIndex + 1) {
michael@0 402 mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
michael@0 403
michael@0 404 BT_LOGR("idx %d", mCurrentBlobIndex);
michael@0 405 ExtractBlobHeaders();
michael@0 406 StartFileTransfer();
michael@0 407 FileTransferComplete();
michael@0 408 }
michael@0 409 }
michael@0 410
michael@0 411 bool
michael@0 412 BluetoothOppManager::ProcessNextBatch()
michael@0 413 {
michael@0 414 MOZ_ASSERT(NS_IsMainThread());
michael@0 415
michael@0 416 // Remove the processed batch.
michael@0 417 // A batch is processed if we've incremented mCurrentBlobIndex for it.
michael@0 418 if (mCurrentBlobIndex >= 0) {
michael@0 419 ClearQueue();
michael@0 420 mBatches.RemoveElementAt(0);
michael@0 421 BT_LOGR("REMOVE. %d remaining", mBatches.Length());
michael@0 422 }
michael@0 423
michael@0 424 // Process the next batch
michael@0 425 if (!mBatches.IsEmpty()) {
michael@0 426 ConnectInternal(mBatches[0].mDeviceAddress);
michael@0 427 return true;
michael@0 428 }
michael@0 429
michael@0 430 // No more batch to process
michael@0 431 return false;
michael@0 432 }
michael@0 433
michael@0 434 void
michael@0 435 BluetoothOppManager::ClearQueue()
michael@0 436 {
michael@0 437 MOZ_ASSERT(NS_IsMainThread());
michael@0 438
michael@0 439 MOZ_ASSERT(!mIsServer);
michael@0 440 MOZ_ASSERT(!mBatches.IsEmpty());
michael@0 441 MOZ_ASSERT(!mBatches[0].mBlobs.IsEmpty());
michael@0 442
michael@0 443 mCurrentBlobIndex = -1;
michael@0 444 mBlob = nullptr;
michael@0 445 mBatches[0].mBlobs.Clear();
michael@0 446 }
michael@0 447
michael@0 448 bool
michael@0 449 BluetoothOppManager::StopSendingFile()
michael@0 450 {
michael@0 451 MOZ_ASSERT(NS_IsMainThread());
michael@0 452
michael@0 453 if (mIsServer) {
michael@0 454 mAbortFlag = true;
michael@0 455 } else {
michael@0 456 Disconnect(nullptr);
michael@0 457 }
michael@0 458
michael@0 459 return true;
michael@0 460 }
michael@0 461
michael@0 462 bool
michael@0 463 BluetoothOppManager::ConfirmReceivingFile(bool aConfirm)
michael@0 464 {
michael@0 465 NS_ENSURE_TRUE(mConnected, false);
michael@0 466 NS_ENSURE_TRUE(mWaitingForConfirmationFlag, false);
michael@0 467
michael@0 468 mWaitingForConfirmationFlag = false;
michael@0 469
michael@0 470 // For the first packet of first file
michael@0 471 bool success = false;
michael@0 472 if (aConfirm) {
michael@0 473 StartFileTransfer();
michael@0 474 if (CreateFile()) {
michael@0 475 success = WriteToFile(mBodySegment.get(), mBodySegmentLength);
michael@0 476 }
michael@0 477 }
michael@0 478
michael@0 479 if (success && mPutFinalFlag) {
michael@0 480 mSuccessFlag = true;
michael@0 481 FileTransferComplete();
michael@0 482 NotifyAboutFileChange();
michael@0 483 }
michael@0 484
michael@0 485 ReplyToPut(mPutFinalFlag, success);
michael@0 486
michael@0 487 return true;
michael@0 488 }
michael@0 489
michael@0 490 void
michael@0 491 BluetoothOppManager::AfterFirstPut()
michael@0 492 {
michael@0 493 mUpdateProgressCounter = 1;
michael@0 494 mPutFinalFlag = false;
michael@0 495 mPutPacketReceivedLength = 0;
michael@0 496 mSentFileLength = 0;
michael@0 497 mWaitingToSendPutFinal = false;
michael@0 498 mSuccessFlag = false;
michael@0 499 mBodySegmentLength = 0;
michael@0 500 }
michael@0 501
michael@0 502 void
michael@0 503 BluetoothOppManager::AfterOppConnected()
michael@0 504 {
michael@0 505 MOZ_ASSERT(NS_IsMainThread());
michael@0 506
michael@0 507 mConnected = true;
michael@0 508 mAbortFlag = false;
michael@0 509 mWaitingForConfirmationFlag = true;
michael@0 510 AfterFirstPut();
michael@0 511 // Get a mount lock to prevent the sdcard from being shared with
michael@0 512 // the PC while we're doing a OPP file transfer. After OPP transaction
michael@0 513 // were done, the mount lock will be freed.
michael@0 514 if (!AcquireSdcardMountLock()) {
michael@0 515 // If we fail to get a mount lock, abort this transaction
michael@0 516 // Directly sending disconnect-request is better than abort-request
michael@0 517 BT_WARNING("BluetoothOPPManager couldn't get a mount lock!");
michael@0 518 Disconnect(nullptr);
michael@0 519 }
michael@0 520 }
michael@0 521
michael@0 522 void
michael@0 523 BluetoothOppManager::AfterOppDisconnected()
michael@0 524 {
michael@0 525 MOZ_ASSERT(NS_IsMainThread());
michael@0 526
michael@0 527 mConnected = false;
michael@0 528 mLastCommand = 0;
michael@0 529 mPutPacketReceivedLength = 0;
michael@0 530 mDsFile = nullptr;
michael@0 531
michael@0 532 // We can't reset mSuccessFlag here since this function may be called
michael@0 533 // before we send system message of transfer complete
michael@0 534 // mSuccessFlag = false;
michael@0 535
michael@0 536 if (mInputStream) {
michael@0 537 mInputStream->Close();
michael@0 538 mInputStream = nullptr;
michael@0 539 }
michael@0 540
michael@0 541 if (mOutputStream) {
michael@0 542 mOutputStream->Close();
michael@0 543 mOutputStream = nullptr;
michael@0 544 }
michael@0 545
michael@0 546 if (mReadFileThread) {
michael@0 547 mReadFileThread->Shutdown();
michael@0 548 mReadFileThread = nullptr;
michael@0 549 }
michael@0 550 // Release the Mount lock if file transfer completed
michael@0 551 if (mMountLock) {
michael@0 552 // The mount lock will be implicitly unlocked
michael@0 553 mMountLock = nullptr;
michael@0 554 }
michael@0 555 }
michael@0 556
michael@0 557 void
michael@0 558 BluetoothOppManager::DeleteReceivedFile()
michael@0 559 {
michael@0 560 if (mOutputStream) {
michael@0 561 mOutputStream->Close();
michael@0 562 mOutputStream = nullptr;
michael@0 563 }
michael@0 564
michael@0 565 if (mDsFile && mDsFile->mFile) {
michael@0 566 mDsFile->mFile->Remove(false);
michael@0 567 mDsFile = nullptr;
michael@0 568 }
michael@0 569 }
michael@0 570
michael@0 571 bool
michael@0 572 BluetoothOppManager::CreateFile()
michael@0 573 {
michael@0 574 MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength);
michael@0 575
michael@0 576 nsString path;
michael@0 577 path.AssignLiteral(TARGET_SUBDIR);
michael@0 578 path.Append(mFileName);
michael@0 579
michael@0 580 mDsFile = DeviceStorageFile::CreateUnique(
michael@0 581 path, nsIFile::NORMAL_FILE_TYPE, 0644);
michael@0 582 NS_ENSURE_TRUE(mDsFile, false);
michael@0 583
michael@0 584 nsCOMPtr<nsIFile> f;
michael@0 585 mDsFile->mFile->Clone(getter_AddRefs(f));
michael@0 586
michael@0 587 /*
michael@0 588 * The function CreateUnique() may create a file with a different file
michael@0 589 * name from the original mFileName. Therefore we have to retrieve
michael@0 590 * the file name again.
michael@0 591 */
michael@0 592 f->GetLeafName(mFileName);
michael@0 593
michael@0 594 NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
michael@0 595 NS_ENSURE_TRUE(mOutputStream, false);
michael@0 596
michael@0 597 return true;
michael@0 598 }
michael@0 599
michael@0 600 bool
michael@0 601 BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
michael@0 602 {
michael@0 603 NS_ENSURE_TRUE(mOutputStream, false);
michael@0 604
michael@0 605 uint32_t wrote = 0;
michael@0 606 mOutputStream->Write((const char*)aData, aDataLength, &wrote);
michael@0 607 NS_ENSURE_TRUE(aDataLength == (int) wrote, false);
michael@0 608
michael@0 609 return true;
michael@0 610 }
michael@0 611
michael@0 612 // Virtual function of class SocketConsumer
michael@0 613 void
michael@0 614 BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
michael@0 615 {
michael@0 616 if (aHeader.Has(ObexHeaderId::Name)) {
michael@0 617 aHeader.GetName(mFileName);
michael@0 618 }
michael@0 619
michael@0 620 if (aHeader.Has(ObexHeaderId::Type)) {
michael@0 621 aHeader.GetContentType(mContentType);
michael@0 622 }
michael@0 623
michael@0 624 if (aHeader.Has(ObexHeaderId::Length)) {
michael@0 625 aHeader.GetLength(&mFileLength);
michael@0 626 }
michael@0 627
michael@0 628 if (aHeader.Has(ObexHeaderId::Body) ||
michael@0 629 aHeader.Has(ObexHeaderId::EndOfBody)) {
michael@0 630 uint8_t* bodyPtr;
michael@0 631 aHeader.GetBody(&bodyPtr);
michael@0 632 mBodySegment = bodyPtr;
michael@0 633
michael@0 634 aHeader.GetBodyLength(&mBodySegmentLength);
michael@0 635 }
michael@0 636 }
michael@0 637
michael@0 638 bool
michael@0 639 BluetoothOppManager::ExtractBlobHeaders()
michael@0 640 {
michael@0 641 RetrieveSentFileName();
michael@0 642
michael@0 643 nsresult rv = mBlob->GetType(mContentType);
michael@0 644 if (NS_FAILED(rv)) {
michael@0 645 BT_WARNING("Can't get content type");
michael@0 646 SendDisconnectRequest();
michael@0 647 return false;
michael@0 648 }
michael@0 649
michael@0 650 uint64_t fileLength;
michael@0 651 rv = mBlob->GetSize(&fileLength);
michael@0 652 if (NS_FAILED(rv)) {
michael@0 653 BT_WARNING("Can't get file size");
michael@0 654 SendDisconnectRequest();
michael@0 655 return false;
michael@0 656 }
michael@0 657
michael@0 658 // Currently we keep the size of files which were sent/received via
michael@0 659 // Bluetooth not exceed UINT32_MAX because the Length header in OBEX
michael@0 660 // is only 4-byte long. Although it is possible to transfer a file
michael@0 661 // larger than UINT32_MAX, it needs to parse another OBEX Header
michael@0 662 // and I would like to leave it as a feature.
michael@0 663 if (fileLength > (uint64_t)UINT32_MAX) {
michael@0 664 BT_WARNING("The file size is too large for now");
michael@0 665 SendDisconnectRequest();
michael@0 666 return false;
michael@0 667 }
michael@0 668
michael@0 669 mFileLength = fileLength;
michael@0 670 rv = NS_NewThread(getter_AddRefs(mReadFileThread));
michael@0 671 if (NS_FAILED(rv)) {
michael@0 672 BT_WARNING("Can't create thread");
michael@0 673 SendDisconnectRequest();
michael@0 674 return false;
michael@0 675 }
michael@0 676
michael@0 677 return true;
michael@0 678 }
michael@0 679
michael@0 680 void
michael@0 681 BluetoothOppManager::RetrieveSentFileName()
michael@0 682 {
michael@0 683 mFileName.Truncate();
michael@0 684
michael@0 685 nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
michael@0 686 if (file) {
michael@0 687 file->GetName(mFileName);
michael@0 688 }
michael@0 689
michael@0 690 /**
michael@0 691 * We try our best to get the file extention to avoid interoperability issues.
michael@0 692 * However, once we found that we are unable to get suitable extension or
michael@0 693 * information about the content type, sending a pre-defined file name without
michael@0 694 * extension would be fine.
michael@0 695 */
michael@0 696 if (mFileName.IsEmpty()) {
michael@0 697 mFileName.AssignLiteral("Unknown");
michael@0 698 }
michael@0 699
michael@0 700 int32_t offset = mFileName.RFindChar('/');
michael@0 701 if (offset != kNotFound) {
michael@0 702 mFileName = Substring(mFileName, offset + 1);
michael@0 703 }
michael@0 704
michael@0 705 offset = mFileName.RFindChar('.');
michael@0 706 if (offset == kNotFound) {
michael@0 707 nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
michael@0 708
michael@0 709 if (mimeSvc) {
michael@0 710 nsString mimeType;
michael@0 711 mBlob->GetType(mimeType);
michael@0 712
michael@0 713 nsCString extension;
michael@0 714 nsresult rv =
michael@0 715 mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
michael@0 716 EmptyCString(),
michael@0 717 extension);
michael@0 718 if (NS_SUCCEEDED(rv)) {
michael@0 719 mFileName.AppendLiteral(".");
michael@0 720 AppendUTF8toUTF16(extension, mFileName);
michael@0 721 }
michael@0 722 }
michael@0 723 }
michael@0 724 }
michael@0 725
michael@0 726 bool
michael@0 727 BluetoothOppManager::IsReservedChar(char16_t c)
michael@0 728 {
michael@0 729 return (c < 0x0020 ||
michael@0 730 c == char16_t('?') || c == char16_t('|') || c == char16_t('<') ||
michael@0 731 c == char16_t('>') || c == char16_t('"') || c == char16_t(':') ||
michael@0 732 c == char16_t('/') || c == char16_t('*') || c == char16_t('\\'));
michael@0 733 }
michael@0 734
michael@0 735 void
michael@0 736 BluetoothOppManager::ValidateFileName()
michael@0 737 {
michael@0 738 int length = mFileName.Length();
michael@0 739
michael@0 740 for (int i = 0; i < length; ++i) {
michael@0 741 // Replace reserved char of fat file system with '_'
michael@0 742 if (IsReservedChar(mFileName.CharAt(i))) {
michael@0 743 mFileName.Replace(i, 1, char16_t('_'));
michael@0 744 }
michael@0 745 }
michael@0 746 }
michael@0 747
michael@0 748 bool
michael@0 749 BluetoothOppManager::ComposePacket(uint8_t aOpCode, UnixSocketRawData* aMessage)
michael@0 750 {
michael@0 751 MOZ_ASSERT(NS_IsMainThread());
michael@0 752 MOZ_ASSERT(aMessage);
michael@0 753
michael@0 754 int frameHeaderLength = 0;
michael@0 755
michael@0 756 // See if this is the first part of each Put packet
michael@0 757 if (mPutPacketReceivedLength == 0) {
michael@0 758 // Section 3.3.3 "Put", IrOBEX 1.2
michael@0 759 // [opcode:1][length:2][Headers:var]
michael@0 760 frameHeaderLength = 3;
michael@0 761
michael@0 762 mPacketLength = ((((int)aMessage->mData[1]) << 8) | aMessage->mData[2]) -
michael@0 763 frameHeaderLength;
michael@0 764 /**
michael@0 765 * A PUT request from remote devices may be divided into multiple parts.
michael@0 766 * In other words, one request may need to be received multiple times,
michael@0 767 * so here we keep a variable mPutPacketReceivedLength to indicate if
michael@0 768 * current PUT request is done.
michael@0 769 */
michael@0 770 mReceivedDataBuffer = new uint8_t[mPacketLength];
michael@0 771 mPutFinalFlag = (aOpCode == ObexRequestCode::PutFinal);
michael@0 772 }
michael@0 773
michael@0 774 int dataLength = aMessage->mSize - frameHeaderLength;
michael@0 775
michael@0 776 // Check length before memcpy to prevent from memory pollution
michael@0 777 if (dataLength < 0 ||
michael@0 778 mPutPacketReceivedLength + dataLength > mPacketLength) {
michael@0 779 BT_LOGR("Received packet size is unreasonable");
michael@0 780
michael@0 781 ReplyToPut(mPutFinalFlag, false);
michael@0 782 DeleteReceivedFile();
michael@0 783 FileTransferComplete();
michael@0 784
michael@0 785 return false;
michael@0 786 }
michael@0 787
michael@0 788 memcpy(mReceivedDataBuffer.get() + mPutPacketReceivedLength,
michael@0 789 &aMessage->mData[frameHeaderLength], dataLength);
michael@0 790
michael@0 791 mPutPacketReceivedLength += dataLength;
michael@0 792
michael@0 793 return (mPutPacketReceivedLength == mPacketLength);
michael@0 794 }
michael@0 795
michael@0 796 void
michael@0 797 BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
michael@0 798 {
michael@0 799 MOZ_ASSERT(NS_IsMainThread());
michael@0 800
michael@0 801 uint8_t opCode;
michael@0 802 int receivedLength = aMessage->mSize;
michael@0 803
michael@0 804 if (mPutPacketReceivedLength > 0) {
michael@0 805 opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
michael@0 806 } else {
michael@0 807 opCode = aMessage->mData[0];
michael@0 808
michael@0 809 // When there's a Put packet right after a PutFinal packet,
michael@0 810 // which means it's the start point of a new file.
michael@0 811 if (mPutFinalFlag &&
michael@0 812 (opCode == ObexRequestCode::Put ||
michael@0 813 opCode == ObexRequestCode::PutFinal)) {
michael@0 814 mNewFileFlag = true;
michael@0 815 AfterFirstPut();
michael@0 816 }
michael@0 817 }
michael@0 818
michael@0 819 ObexHeaderSet pktHeaders(opCode);
michael@0 820 if (opCode == ObexRequestCode::Connect) {
michael@0 821 // Section 3.3.1 "Connect", IrOBEX 1.2
michael@0 822 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
michael@0 823 // [Headers:var]
michael@0 824 if (!ParseHeaders(&aMessage->mData[7], receivedLength - 7, &pktHeaders)) {
michael@0 825 ReplyError(ObexResponseCode::BadRequest);
michael@0 826 return;
michael@0 827 }
michael@0 828
michael@0 829 ReplyToConnect();
michael@0 830 AfterOppConnected();
michael@0 831 } else if (opCode == ObexRequestCode::Abort) {
michael@0 832 // Section 3.3.5 "Abort", IrOBEX 1.2
michael@0 833 // [opcode:1][length:2][Headers:var]
michael@0 834 if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
michael@0 835 ReplyError(ObexResponseCode::BadRequest);
michael@0 836 return;
michael@0 837 }
michael@0 838
michael@0 839 ReplyToDisconnectOrAbort();
michael@0 840 DeleteReceivedFile();
michael@0 841 } else if (opCode == ObexRequestCode::Disconnect) {
michael@0 842 // Section 3.3.2 "Disconnect", IrOBEX 1.2
michael@0 843 // [opcode:1][length:2][Headers:var]
michael@0 844 if (!ParseHeaders(&aMessage->mData[3], receivedLength - 3, &pktHeaders)) {
michael@0 845 ReplyError(ObexResponseCode::BadRequest);
michael@0 846 return;
michael@0 847 }
michael@0 848
michael@0 849 ReplyToDisconnectOrAbort();
michael@0 850 AfterOppDisconnected();
michael@0 851 FileTransferComplete();
michael@0 852 } else if (opCode == ObexRequestCode::Put ||
michael@0 853 opCode == ObexRequestCode::PutFinal) {
michael@0 854 if (!ComposePacket(opCode, aMessage)) {
michael@0 855 return;
michael@0 856 }
michael@0 857
michael@0 858 // A Put packet is received completely
michael@0 859 ParseHeaders(mReceivedDataBuffer.get(),
michael@0 860 mPutPacketReceivedLength, &pktHeaders);
michael@0 861 ExtractPacketHeaders(pktHeaders);
michael@0 862 ValidateFileName();
michael@0 863
michael@0 864 // When we cancel the transfer, delete the file and notify completion
michael@0 865 if (mAbortFlag) {
michael@0 866 ReplyToPut(mPutFinalFlag, false);
michael@0 867 mSentFileLength += mBodySegmentLength;
michael@0 868 DeleteReceivedFile();
michael@0 869 FileTransferComplete();
michael@0 870 return;
michael@0 871 }
michael@0 872
michael@0 873 // Wait until get confirmation from user, then create file and write to it
michael@0 874 if (mWaitingForConfirmationFlag) {
michael@0 875 ReceivingFileConfirmation();
michael@0 876 mSentFileLength += mBodySegmentLength;
michael@0 877 return;
michael@0 878 }
michael@0 879
michael@0 880 // Already get confirmation from user, create a new file if needed and
michael@0 881 // write to output stream
michael@0 882 if (mNewFileFlag) {
michael@0 883 StartFileTransfer();
michael@0 884 if (!CreateFile()) {
michael@0 885 ReplyToPut(mPutFinalFlag, false);
michael@0 886 return;
michael@0 887 }
michael@0 888 mNewFileFlag = false;
michael@0 889 }
michael@0 890
michael@0 891 if (!WriteToFile(mBodySegment.get(), mBodySegmentLength)) {
michael@0 892 ReplyToPut(mPutFinalFlag, false);
michael@0 893 return;
michael@0 894 }
michael@0 895
michael@0 896 ReplyToPut(mPutFinalFlag, true);
michael@0 897
michael@0 898 // Send progress update
michael@0 899 mSentFileLength += mBodySegmentLength;
michael@0 900 if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
michael@0 901 UpdateProgress();
michael@0 902 mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
michael@0 903 }
michael@0 904
michael@0 905 // Success to receive a file and notify completion
michael@0 906 if (mPutFinalFlag) {
michael@0 907 mSuccessFlag = true;
michael@0 908 FileTransferComplete();
michael@0 909 NotifyAboutFileChange();
michael@0 910 }
michael@0 911 } else if (opCode == ObexRequestCode::Get ||
michael@0 912 opCode == ObexRequestCode::GetFinal ||
michael@0 913 opCode == ObexRequestCode::SetPath) {
michael@0 914 ReplyError(ObexResponseCode::BadRequest);
michael@0 915 BT_WARNING("Unsupported ObexRequestCode");
michael@0 916 } else {
michael@0 917 ReplyError(ObexResponseCode::NotImplemented);
michael@0 918 BT_WARNING("Unrecognized ObexRequestCode");
michael@0 919 }
michael@0 920 }
michael@0 921
michael@0 922 void
michael@0 923 BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
michael@0 924 {
michael@0 925 MOZ_ASSERT(NS_IsMainThread());
michael@0 926
michael@0 927 uint8_t opCode = aMessage->mData[0];
michael@0 928
michael@0 929 // Check response code and send out system message as finished if the response
michael@0 930 // code is somehow incorrect.
michael@0 931 uint8_t expectedOpCode = ObexResponseCode::Success;
michael@0 932 if (mLastCommand == ObexRequestCode::Put) {
michael@0 933 expectedOpCode = ObexResponseCode::Continue;
michael@0 934 }
michael@0 935
michael@0 936 if (opCode != expectedOpCode) {
michael@0 937 if (mLastCommand == ObexRequestCode::Put ||
michael@0 938 mLastCommand == ObexRequestCode::Abort ||
michael@0 939 mLastCommand == ObexRequestCode::PutFinal) {
michael@0 940 SendDisconnectRequest();
michael@0 941 }
michael@0 942 nsAutoCString str;
michael@0 943 str += "[OPP] 0x";
michael@0 944 str.AppendInt(mLastCommand, 16);
michael@0 945 str += " failed";
michael@0 946 BT_WARNING(str.get());
michael@0 947 FileTransferComplete();
michael@0 948 return;
michael@0 949 }
michael@0 950
michael@0 951 if (mLastCommand == ObexRequestCode::PutFinal) {
michael@0 952 mSuccessFlag = true;
michael@0 953 FileTransferComplete();
michael@0 954
michael@0 955 if (mInputStream) {
michael@0 956 mInputStream->Close();
michael@0 957 mInputStream = nullptr;
michael@0 958 }
michael@0 959
michael@0 960 if (mCurrentBlobIndex + 1 == (int) mBatches[0].mBlobs.Length()) {
michael@0 961 SendDisconnectRequest();
michael@0 962 } else {
michael@0 963 StartSendingNextFile();
michael@0 964 }
michael@0 965 } else if (mLastCommand == ObexRequestCode::Abort) {
michael@0 966 SendDisconnectRequest();
michael@0 967 FileTransferComplete();
michael@0 968 } else if (mLastCommand == ObexRequestCode::Disconnect) {
michael@0 969 AfterOppDisconnected();
michael@0 970 // Most devices will directly terminate connection after receiving
michael@0 971 // Disconnect request, so we make a delay here. If the socket hasn't been
michael@0 972 // disconnected, we will close it.
michael@0 973 if (mSocket) {
michael@0 974 MessageLoop::current()->
michael@0 975 PostDelayedTask(FROM_HERE, new CloseSocketTask(mSocket), 1000);
michael@0 976 }
michael@0 977 } else if (mLastCommand == ObexRequestCode::Connect) {
michael@0 978 MOZ_ASSERT(!mFileName.IsEmpty());
michael@0 979 MOZ_ASSERT(mBlob);
michael@0 980
michael@0 981 AfterOppConnected();
michael@0 982
michael@0 983 // Keep remote information
michael@0 984 mRemoteObexVersion = aMessage->mData[3];
michael@0 985 mRemoteConnectionFlags = aMessage->mData[4];
michael@0 986 mRemoteMaxPacketLength =
michael@0 987 (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
michael@0 988
michael@0 989 // The length of file name exceeds maximum length.
michael@0 990 int fileNameByteLen = (mFileName.Length() + 1) * 2;
michael@0 991 int headerLen = kPutRequestHeaderSize + kPutRequestAppendHeaderSize;
michael@0 992 if (fileNameByteLen > mRemoteMaxPacketLength - headerLen) {
michael@0 993 BT_WARNING("The length of file name is aberrant.");
michael@0 994 SendDisconnectRequest();
michael@0 995 return;
michael@0 996 }
michael@0 997
michael@0 998 SendPutHeaderRequest(mFileName, mFileLength);
michael@0 999 } else if (mLastCommand == ObexRequestCode::Put) {
michael@0 1000 if (mWaitingToSendPutFinal) {
michael@0 1001 SendPutFinalRequest();
michael@0 1002 return;
michael@0 1003 }
michael@0 1004
michael@0 1005 if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) {
michael@0 1006 UpdateProgress();
michael@0 1007 mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
michael@0 1008 }
michael@0 1009
michael@0 1010 nsresult rv;
michael@0 1011 if (!mInputStream) {
michael@0 1012 rv = mBlob->GetInternalStream(getter_AddRefs(mInputStream));
michael@0 1013 if (NS_FAILED(rv)) {
michael@0 1014 BT_WARNING("Can't get internal stream of blob");
michael@0 1015 SendDisconnectRequest();
michael@0 1016 return;
michael@0 1017 }
michael@0 1018 }
michael@0 1019
michael@0 1020 nsRefPtr<ReadFileTask> task = new ReadFileTask(mInputStream,
michael@0 1021 mRemoteMaxPacketLength);
michael@0 1022 rv = mReadFileThread->Dispatch(task, NS_DISPATCH_NORMAL);
michael@0 1023 if (NS_FAILED(rv)) {
michael@0 1024 BT_WARNING("Cannot dispatch read file task!");
michael@0 1025 SendDisconnectRequest();
michael@0 1026 }
michael@0 1027 } else {
michael@0 1028 BT_WARNING("Unhandled ObexRequestCode");
michael@0 1029 }
michael@0 1030 }
michael@0 1031
michael@0 1032 // Virtual function of class SocketConsumer
michael@0 1033 void
michael@0 1034 BluetoothOppManager::ReceiveSocketData(BluetoothSocket* aSocket,
michael@0 1035 nsAutoPtr<UnixSocketRawData>& aMessage)
michael@0 1036 {
michael@0 1037 if (mIsServer) {
michael@0 1038 ServerDataHandler(aMessage);
michael@0 1039 } else {
michael@0 1040 ClientDataHandler(aMessage);
michael@0 1041 }
michael@0 1042 }
michael@0 1043
michael@0 1044 void
michael@0 1045 BluetoothOppManager::SendConnectRequest()
michael@0 1046 {
michael@0 1047 if (mConnected) return;
michael@0 1048
michael@0 1049 // Section 3.3.1 "Connect", IrOBEX 1.2
michael@0 1050 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
michael@0 1051 // [Headers:var]
michael@0 1052 uint8_t req[255];
michael@0 1053 int index = 7;
michael@0 1054
michael@0 1055 req[3] = 0x10; // version=1.0
michael@0 1056 req[4] = 0x00; // flag=0x00
michael@0 1057 req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
michael@0 1058 req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
michael@0 1059
michael@0 1060 SendObexData(req, ObexRequestCode::Connect, index);
michael@0 1061 }
michael@0 1062
michael@0 1063 void
michael@0 1064 BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
michael@0 1065 int aFileSize)
michael@0 1066 {
michael@0 1067 if (!mConnected) return;
michael@0 1068
michael@0 1069 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
michael@0 1070
michael@0 1071 int len = aFileName.Length();
michael@0 1072 uint8_t* fileName = new uint8_t[(len + 1) * 2];
michael@0 1073 const char16_t* fileNamePtr = aFileName.BeginReading();
michael@0 1074
michael@0 1075 for (int i = 0; i < len; i++) {
michael@0 1076 fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
michael@0 1077 fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i];
michael@0 1078 }
michael@0 1079
michael@0 1080 fileName[len * 2] = 0x00;
michael@0 1081 fileName[len * 2 + 1] = 0x00;
michael@0 1082
michael@0 1083 int index = 3;
michael@0 1084 index += AppendHeaderName(&req[index], mRemoteMaxPacketLength - index,
michael@0 1085 (char*)fileName, (len + 1) * 2);
michael@0 1086 index += AppendHeaderLength(&req[index], aFileSize);
michael@0 1087
michael@0 1088 SendObexData(req, ObexRequestCode::Put, index);
michael@0 1089
michael@0 1090 delete [] fileName;
michael@0 1091 delete [] req;
michael@0 1092 }
michael@0 1093
michael@0 1094 void
michael@0 1095 BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
michael@0 1096 int aFileBodyLength)
michael@0 1097 {
michael@0 1098 if (!mConnected) return;
michael@0 1099 int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
michael@0 1100 if (aFileBodyLength > packetLeftSpace) {
michael@0 1101 BT_WARNING("Not allowed such a small MaxPacketLength value");
michael@0 1102 return;
michael@0 1103 }
michael@0 1104
michael@0 1105 // Section 3.3.3 "Put", IrOBEX 1.2
michael@0 1106 // [opcode:1][length:2][Headers:var]
michael@0 1107 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
michael@0 1108
michael@0 1109 int index = 3;
michael@0 1110 index += AppendHeaderBody(&req[index], mRemoteMaxPacketLength - index,
michael@0 1111 aFileBody, aFileBodyLength);
michael@0 1112
michael@0 1113 SendObexData(req, ObexRequestCode::Put, index);
michael@0 1114 delete [] req;
michael@0 1115
michael@0 1116 mSentFileLength += aFileBodyLength;
michael@0 1117 }
michael@0 1118
michael@0 1119 void
michael@0 1120 BluetoothOppManager::SendPutFinalRequest()
michael@0 1121 {
michael@0 1122 if (!mConnected) return;
michael@0 1123
michael@0 1124 /**
michael@0 1125 * Section 2.2.9, "End-of-Body", IrObex 1.2
michael@0 1126 * End-of-Body is used to identify the last chunk of the object body.
michael@0 1127 * For most platforms, a PutFinal packet is sent with an zero length
michael@0 1128 * End-of-Body header.
michael@0 1129 */
michael@0 1130
michael@0 1131 // [opcode:1][length:2]
michael@0 1132 int index = 3;
michael@0 1133 uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
michael@0 1134 index += AppendHeaderEndOfBody(&req[index]);
michael@0 1135
michael@0 1136 SendObexData(req, ObexRequestCode::PutFinal, index);
michael@0 1137 delete [] req;
michael@0 1138
michael@0 1139 mWaitingToSendPutFinal = false;
michael@0 1140 }
michael@0 1141
michael@0 1142 void
michael@0 1143 BluetoothOppManager::SendDisconnectRequest()
michael@0 1144 {
michael@0 1145 if (!mConnected) return;
michael@0 1146
michael@0 1147 // Section 3.3.2 "Disconnect", IrOBEX 1.2
michael@0 1148 // [opcode:1][length:2][Headers:var]
michael@0 1149 uint8_t req[255];
michael@0 1150 int index = 3;
michael@0 1151
michael@0 1152 SendObexData(req, ObexRequestCode::Disconnect, index);
michael@0 1153 }
michael@0 1154
michael@0 1155 void
michael@0 1156 BluetoothOppManager::CheckPutFinal(uint32_t aNumRead)
michael@0 1157 {
michael@0 1158 if (mSentFileLength + aNumRead >= mFileLength) {
michael@0 1159 mWaitingToSendPutFinal = true;
michael@0 1160 }
michael@0 1161 }
michael@0 1162
michael@0 1163 bool
michael@0 1164 BluetoothOppManager::IsConnected()
michael@0 1165 {
michael@0 1166 return (mConnected && !mSendTransferCompleteFlag);
michael@0 1167 }
michael@0 1168
michael@0 1169 void
michael@0 1170 BluetoothOppManager::GetAddress(nsAString& aDeviceAddress)
michael@0 1171 {
michael@0 1172 return mSocket->GetAddress(aDeviceAddress);
michael@0 1173 }
michael@0 1174
michael@0 1175 void
michael@0 1176 BluetoothOppManager::ReplyToConnect()
michael@0 1177 {
michael@0 1178 if (mConnected) return;
michael@0 1179
michael@0 1180 // Section 3.3.1 "Connect", IrOBEX 1.2
michael@0 1181 // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
michael@0 1182 // [Headers:var]
michael@0 1183 uint8_t req[255];
michael@0 1184 int index = 7;
michael@0 1185
michael@0 1186 req[3] = 0x10; // version=1.0
michael@0 1187 req[4] = 0x00; // flag=0x00
michael@0 1188 req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
michael@0 1189 req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
michael@0 1190
michael@0 1191 SendObexData(req, ObexResponseCode::Success, index);
michael@0 1192 }
michael@0 1193
michael@0 1194 void
michael@0 1195 BluetoothOppManager::ReplyToDisconnectOrAbort()
michael@0 1196 {
michael@0 1197 if (!mConnected) return;
michael@0 1198
michael@0 1199 // Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
michael@0 1200 // The format of response packet of "Disconnect" and "Abort" are the same
michael@0 1201 // [opcode:1][length:2][Headers:var]
michael@0 1202 uint8_t req[255];
michael@0 1203 int index = 3;
michael@0 1204
michael@0 1205 SendObexData(req, ObexResponseCode::Success, index);
michael@0 1206 }
michael@0 1207
michael@0 1208 void
michael@0 1209 BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
michael@0 1210 {
michael@0 1211 if (!mConnected) return;
michael@0 1212
michael@0 1213 // The received length can be reset here because this is where we reply to a
michael@0 1214 // complete put packet.
michael@0 1215 mPutPacketReceivedLength = 0;
michael@0 1216
michael@0 1217 // Section 3.3.2 "Disconnect", IrOBEX 1.2
michael@0 1218 // [opcode:1][length:2][Headers:var]
michael@0 1219 uint8_t req[255];
michael@0 1220 int index = 3;
michael@0 1221 uint8_t opcode;
michael@0 1222
michael@0 1223 if (aContinue) {
michael@0 1224 opcode = (aFinal)? ObexResponseCode::Success :
michael@0 1225 ObexResponseCode::Continue;
michael@0 1226 } else {
michael@0 1227 opcode = (aFinal)? ObexResponseCode::Unauthorized :
michael@0 1228 ObexResponseCode::Unauthorized & (~FINAL_BIT);
michael@0 1229 }
michael@0 1230
michael@0 1231 SendObexData(req, opcode, index);
michael@0 1232 }
michael@0 1233
michael@0 1234 void
michael@0 1235 BluetoothOppManager::ReplyError(uint8_t aError)
michael@0 1236 {
michael@0 1237 BT_LOGR("error: %d", aError);
michael@0 1238
michael@0 1239 // Section 3.2 "Response Format", IrOBEX 1.2
michael@0 1240 // [opcode:1][length:2][Headers:var]
michael@0 1241 uint8_t req[255];
michael@0 1242 int index = 3;
michael@0 1243
michael@0 1244 SendObexData(req, aError, index);
michael@0 1245 }
michael@0 1246
michael@0 1247 void
michael@0 1248 BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize)
michael@0 1249 {
michael@0 1250 SetObexPacketInfo(aData, aOpcode, aSize);
michael@0 1251
michael@0 1252 if (!mIsServer) {
michael@0 1253 mLastCommand = aOpcode;
michael@0 1254 }
michael@0 1255
michael@0 1256 UnixSocketRawData* s = new UnixSocketRawData(aSize);
michael@0 1257 memcpy(s->mData, aData, s->mSize);
michael@0 1258 mSocket->SendDroidSocketData(s);
michael@0 1259 }
michael@0 1260
michael@0 1261 void
michael@0 1262 BluetoothOppManager::FileTransferComplete()
michael@0 1263 {
michael@0 1264 if (mSendTransferCompleteFlag) {
michael@0 1265 return;
michael@0 1266 }
michael@0 1267
michael@0 1268 NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-complete");
michael@0 1269 InfallibleTArray<BluetoothNamedValue> parameters;
michael@0 1270
michael@0 1271 BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
michael@0 1272 BT_APPEND_NAMED_VALUE(parameters, "success", mSuccessFlag);
michael@0 1273 BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
michael@0 1274 BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
michael@0 1275 BT_APPEND_NAMED_VALUE(parameters, "fileLength", mSentFileLength);
michael@0 1276 BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
michael@0 1277
michael@0 1278 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
michael@0 1279
michael@0 1280 mSendTransferCompleteFlag = true;
michael@0 1281 }
michael@0 1282
michael@0 1283 void
michael@0 1284 BluetoothOppManager::StartFileTransfer()
michael@0 1285 {
michael@0 1286 NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-start");
michael@0 1287 InfallibleTArray<BluetoothNamedValue> parameters;
michael@0 1288
michael@0 1289 BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
michael@0 1290 BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
michael@0 1291 BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
michael@0 1292 BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
michael@0 1293 BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
michael@0 1294
michael@0 1295 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
michael@0 1296
michael@0 1297 mSendTransferCompleteFlag = false;
michael@0 1298 }
michael@0 1299
michael@0 1300 void
michael@0 1301 BluetoothOppManager::UpdateProgress()
michael@0 1302 {
michael@0 1303 NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-update-progress");
michael@0 1304 InfallibleTArray<BluetoothNamedValue> parameters;
michael@0 1305
michael@0 1306 BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
michael@0 1307 BT_APPEND_NAMED_VALUE(parameters, "received", mIsServer);
michael@0 1308 BT_APPEND_NAMED_VALUE(parameters, "processedLength", mSentFileLength);
michael@0 1309 BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
michael@0 1310
michael@0 1311 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
michael@0 1312 }
michael@0 1313
michael@0 1314 void
michael@0 1315 BluetoothOppManager::ReceivingFileConfirmation()
michael@0 1316 {
michael@0 1317 NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-receiving-file-confirmation");
michael@0 1318 InfallibleTArray<BluetoothNamedValue> parameters;
michael@0 1319
michael@0 1320 BT_APPEND_NAMED_VALUE(parameters, "address", mDeviceAddress);
michael@0 1321 BT_APPEND_NAMED_VALUE(parameters, "fileName", mFileName);
michael@0 1322 BT_APPEND_NAMED_VALUE(parameters, "fileLength", mFileLength);
michael@0 1323 BT_APPEND_NAMED_VALUE(parameters, "contentType", mContentType);
michael@0 1324
michael@0 1325 BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
michael@0 1326 }
michael@0 1327
michael@0 1328 void
michael@0 1329 BluetoothOppManager::NotifyAboutFileChange()
michael@0 1330 {
michael@0 1331 NS_NAMED_LITERAL_STRING(data, "modified");
michael@0 1332
michael@0 1333 nsCOMPtr<nsIObserverService> obs =
michael@0 1334 mozilla::services::GetObserverService();
michael@0 1335 NS_ENSURE_TRUE_VOID(obs);
michael@0 1336
michael@0 1337 obs->NotifyObservers(mDsFile, "file-watcher-notify", data.get());
michael@0 1338 }
michael@0 1339
michael@0 1340 void
michael@0 1341 BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
michael@0 1342 {
michael@0 1343 BT_LOGR("[%s]", (mIsServer)? "server" : "client");
michael@0 1344 MOZ_ASSERT(aSocket);
michael@0 1345
michael@0 1346 /**
michael@0 1347 * If the created connection is an inbound connection, close server socket
michael@0 1348 * because currently only one file-transfer session is allowed. After that,
michael@0 1349 * we need to make sure that server socket would be nulled out.
michael@0 1350 * As for outbound connections, we just notify the controller that it's done.
michael@0 1351 */
michael@0 1352 if (aSocket == mServerSocket) {
michael@0 1353 MOZ_ASSERT(!mSocket);
michael@0 1354 mServerSocket.swap(mSocket);
michael@0 1355 }
michael@0 1356
michael@0 1357 // Cache device address since we can't get socket address when a remote
michael@0 1358 // device disconnect with us.
michael@0 1359 mSocket->GetAddress(mDeviceAddress);
michael@0 1360
michael@0 1361 // Start sending file if we connect as a client
michael@0 1362 if (!mIsServer) {
michael@0 1363 StartSendingNextFile();
michael@0 1364 }
michael@0 1365 }
michael@0 1366
michael@0 1367 void
michael@0 1368 BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
michael@0 1369 {
michael@0 1370 BT_LOGR("[%s]", (mIsServer)? "server" : "client");
michael@0 1371
michael@0 1372 mServerSocket = nullptr;
michael@0 1373 mSocket = nullptr;
michael@0 1374
michael@0 1375 if (!mIsServer) {
michael@0 1376 // Inform gaia of remaining blobs' sending failure
michael@0 1377 DiscardBlobsToSend();
michael@0 1378 }
michael@0 1379
michael@0 1380 // Listen as a server if there's no more batch to process
michael@0 1381 if (!ProcessNextBatch()) {
michael@0 1382 Listen();
michael@0 1383 }
michael@0 1384 }
michael@0 1385
michael@0 1386 void
michael@0 1387 BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
michael@0 1388 {
michael@0 1389 MOZ_ASSERT(aSocket);
michael@0 1390 if (aSocket != mSocket) {
michael@0 1391 // Do nothing when a listening server socket is closed.
michael@0 1392 return;
michael@0 1393 }
michael@0 1394 BT_LOGR("[%s]", (mIsServer) ? "server" : "client");
michael@0 1395
michael@0 1396 /**
michael@0 1397 * It is valid for a bluetooth device which is transfering file via OPP
michael@0 1398 * closing socket without sending OBEX disconnect request first. So we
michael@0 1399 * delete the broken file when we failed to receive a file from the remote,
michael@0 1400 * and notify the transfer has been completed (but failed). We also call
michael@0 1401 * AfterOppDisconnected here to ensure all variables will be cleaned.
michael@0 1402 */
michael@0 1403 if (!mSuccessFlag) {
michael@0 1404 if (mIsServer) {
michael@0 1405 DeleteReceivedFile();
michael@0 1406 }
michael@0 1407
michael@0 1408 FileTransferComplete();
michael@0 1409 if (!mIsServer) {
michael@0 1410 // Inform gaia of remaining blobs' sending failure
michael@0 1411 DiscardBlobsToSend();
michael@0 1412 }
michael@0 1413 }
michael@0 1414
michael@0 1415 AfterOppDisconnected();
michael@0 1416 mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
michael@0 1417 mSuccessFlag = false;
michael@0 1418
michael@0 1419 mSocket = nullptr;
michael@0 1420 // Listen as a server if there's no more batch to process
michael@0 1421 if (!ProcessNextBatch()) {
michael@0 1422 Listen();
michael@0 1423 }
michael@0 1424 }
michael@0 1425
michael@0 1426 void
michael@0 1427 BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
michael@0 1428 {
michael@0 1429 if (mSocket) {
michael@0 1430 mSocket->Disconnect();
michael@0 1431 } else {
michael@0 1432 BT_WARNING("%s: No ongoing file transfer to stop", __FUNCTION__);
michael@0 1433 }
michael@0 1434 }
michael@0 1435
michael@0 1436 NS_IMPL_ISUPPORTS(BluetoothOppManager, nsIObserver)
michael@0 1437
michael@0 1438 bool
michael@0 1439 BluetoothOppManager::AcquireSdcardMountLock()
michael@0 1440 {
michael@0 1441 nsCOMPtr<nsIVolumeService> volumeSrv =
michael@0 1442 do_GetService(NS_VOLUMESERVICE_CONTRACTID);
michael@0 1443 NS_ENSURE_TRUE(volumeSrv, false);
michael@0 1444 nsresult rv;
michael@0 1445 rv = volumeSrv->CreateMountLock(NS_LITERAL_STRING("sdcard"),
michael@0 1446 getter_AddRefs(mMountLock));
michael@0 1447 NS_ENSURE_SUCCESS(rv, false);
michael@0 1448 return true;
michael@0 1449 }
michael@0 1450
michael@0 1451 void
michael@0 1452 BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
michael@0 1453 const nsAString& aServiceUuid,
michael@0 1454 int aChannel)
michael@0 1455 {
michael@0 1456 MOZ_ASSERT(false);
michael@0 1457 }
michael@0 1458
michael@0 1459 void
michael@0 1460 BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
michael@0 1461 {
michael@0 1462 MOZ_ASSERT(false);
michael@0 1463 }
michael@0 1464
michael@0 1465 void
michael@0 1466 BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
michael@0 1467 BluetoothProfileController* aController)
michael@0 1468 {
michael@0 1469 MOZ_ASSERT(false);
michael@0 1470 }
michael@0 1471
michael@0 1472 void
michael@0 1473 BluetoothOppManager::OnConnect(const nsAString& aErrorStr)
michael@0 1474 {
michael@0 1475 MOZ_ASSERT(false);
michael@0 1476 }
michael@0 1477
michael@0 1478 void
michael@0 1479 BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
michael@0 1480 {
michael@0 1481 MOZ_ASSERT(false);
michael@0 1482 }
michael@0 1483
michael@0 1484 void
michael@0 1485 BluetoothOppManager::Reset()
michael@0 1486 {
michael@0 1487 MOZ_ASSERT(false);
michael@0 1488 }

mercurial