Wed, 31 Dec 2014 06:09:35 +0100
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 | } |