dom/bluetooth/bluez/BluetoothOppManager.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial