mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
76fdf7909d
- Bug 1222098 - Devicemotion event timestamp should return values from Android sensor API and not Gecko. r:smaug (0fb05341d5)
- Bug 1266701 - some header missing in dom/events, r=jwatt (59a8a30af3)
- namespace (c5796648b6)
- Bug 1243555: Remove unnecessary nsDocShell static_cast in EventListenerManager::HandleEventInternal(). r=smaug (21c559122c)
- Bug 1254629 - Let query events fail when content root is wrong; r=masayuki (64454249aa)
- Bug 1224233 - fix crashy usage of IMENotificationSender::Run using on-screen keyboard on Windows, r=smaug (ddcdf13ad0)
- Bug 1252058 IMEContentObserver::IMENotificationSender shouldn't post notifications when IMEContentObserver which is the owner of it stopped observing contents r=smaug (9f4a14f13a)
- Bug 1259671 part.1 Rename InternalScrollPortEvent::orientType to InternalScrollPortEvent::OrientType r=smaug (eaefa4472f)
- Bug 1259671 part.2 Rename InternalScrollPortEvent::vertical to InternalScrollPortEvent::eVertical r=smaug (91bcdcd1df)
- Bug 1259671 part.3 Rename InternalScrollPortEvent::horizontal to InternalScrollPortEvent::eHorizontal r=smaug (f3c206dc4c)
- Bug 1259671 part.4 Rename InternalScrollPortEvent::both to InternalScrollPortEvent::eBoth r=smaug (ac34dc95c1)
- Bug 1259671 part.5 Rename InternalScrollPortEvent::orient to InternalScrollPortEvent::mOrient r=smaug (6736862c75)
- Bug 1262324 (part 1) - Remove "locale.all" prefix from Unix charsets. r=emk. (b337137c3d)
- Bug 1255655 - Const-ify dom encodings and similar arrays. r=baku. (93d79e84a7)
- Bug 1262324 (part 2) - Introduce nsUConvProp. r=emk. (bad497dee6)
- Bug 1257877 - Remove UTF-16 support from TextEncoder. r=hsivonen (ad647b12a3)
- Bug 1120813 - Add support for the MS932 label of Shift_JIS. r=emk, r=Ms2ger (8a35fd93cf)
- Bug 1250930 - Use correct global when creating a key in GenerateAsymmetricKeyTask r=bz (ba65e0ae34)
- Bug 842818 - Allow calling WebCryptoTask::DispatchWithPromise() from workers r=keeler (e99301ba3e)
- Bug 1251390. Make timer queries available at the appropriate time. r=jgilbert (f1a0dcf7d3)
- Bug 1259696 - Check read buffer mode when doing CopyTexImage. r=jgilbert (fe51211b56)
- Bug 1241042 - Get correct frag varying from angle validator. r=jmuizelaar (29a4298a7e)
- Bug 1244611 - "Using named uniform buffer objects in the fragment shader fails". r=jgilbert (2e05e31d09)
- Bug 1263018 - Only update active prog info if linking active prog. - r=jrmuizel (1603ebdd8c)
- Bug 1257593 - Handle webgl FramebufferTexture2D() with an unbound texture. r=jgilbert (4d75981cb7)
- Bug 1257593 followup, fix bustage from believing a reviewer and then sending a different patch to try (7766b0cdb4)
- Bug 1258061 - Clarify FramebufferTexture2D handling for tex2D and cubeMap; r=jgilbert (0635d9412a)
- Bug 1259449 - Require GLFeature::sync for WebGL's disjoint_timer_query. - r=jrmuizel (b6017134a3)
- Bug 1247804 - Enable seamless cubemaps where available. - r=jrmuizel (e7fd3ec5ed)
- Bug 1247977: More information when we hit the OpenGL error in FakeBlackTexture. r=jgilbert (f6f2c82e33)
- Bug 1255655 - Const-ify sExtensionNames. r=mattwoodrow. (c0c60b34c7)
- Bug 1262757 - Use StaticRefPtr for the global context in GLContextProviderWGL, r=jrmuizel (0cc29a3385)
- Bug 1259811 - Require FBO support for GLContexts. - r=jrmuizel (ec3c1a6045)
- Bug 1234441 - Allow malformed ESSL version string. - r=jrmuizel (8fda1569ab)
- Bug 1199923 - Work around Intel Linux driver lying about max texture size - r=jgilbert (546b7dfe6a)
- Bug 1262265 - Cleanup GLContext symbol init. - r=jrmuizel (8da8ce647a)
- Bug 1232334 - [1.2] Only set context on successful attach. r=snorp (ca29c322bf)
- Bug 1261320 - Check DataSurface is vaild before using, r=milan (be9aebba47)
- Bug 1245868 - repalce pass by value with pass by pointer in IsHeadRequest. r=jst (f9d7f6185f)
- fix tests (3ccf9d1715)
- Bug 1232941 - Register Observer and listen to NS_XPCOM_SHUTDOWN_OBSERVER_ID for GATT, r=shuang (aa367807fa)
- Bug 1239979: Init and uninit all Bluetooth profile managers, r=shuang (78c1ebbd13)
- Bug 1239979: Uninitialized Bluetooth profile managers explictly to release refs, r=shuang (83b5389539)
- Bug 1262630. Replace workers::GetGlobalObjectForGlobal with xpc::NativeGlobal. r=khuey (9f639580be)
- Bug 1255817 part 6. Fix up some comments in CallbackObject that refer to things that no longer exist. r=bholley (575bf90c98)
- Bug 1259545. Remove the JS_SaveFrameChain bits in CallbackObject error reporting, since they are no longer needed. r=bholley (16181ddb38)
- Bug 1260511 part 2. Change XPConnect's handling of exceptions thrown from JS components so that if an nsresult integer is thrown we convert it into an actual exception object before handing it out to content instead of propagating out the numeric value. r=khuey (dc02854e3f)
- Bug 1260511 part 3. In dom::Throw, ignore the pending xpconnect exception if we were given a non-default message string (because we don't want to lose that string). Also, make sure to always clear the pending xpconnect exception there. r=khuey (978a1e9132)
- Bug 1250106 - Correctly set charging status and remaining time when battery level is rounded to 1.0 r=bz (662e18648c)
- Bug 1253641 - DOMException's CC participant should traverse mData. r=khuey. (0a48e3f8e7)
- Bug 1261115 - when Console is running in the main thread the existence of mWindow should always be ensured, r=smaug (caa1efd087)
- Bug 1257208 - Use the nsTextNode concrete type in several places in DirectionalityUtils.cpp instead of nsINode and nsIContent; r=peterv (69529fd8d4)
- Bug 1260982 - BlobFileImpl::GetType() should work also in workers, r=smaug (4b01d269bc)
- Bug 1262104 - Remove a non-used CTOR for BlobImplFile, r=ehsan (37ee0ec6d4)
- Bug 1259477 - Port test_document_register.html to mochitest-plain so that it can be turned on in e10s mode; r=mrbkap (411c220cda)
- Bug 1222128 - Turn test_bug1011748.html into a browser mochitest to make it run properly in e10s mode r=bzbarsky (45403d3d15)
- Bug 1259588 - new File("") throws TypeError exception, r=baku (05f6e7292d)
- Bug 1264710 - Catch IDB exceptions in IndexedDBHelper. r=fabrice (0d3c860a89)
- Bug 1263553 - Move MultipartBlobImpl into mozilla::dom namespace, r=smaug (e286c6cfe1)
- Bug 1263551 - Remove unused method in MultipartBlobImpl, r=smaug (54c2da9a12)
- Bug 1252687 - get rid of static nsStrings in PerformanceObserver.cpp r=bz (319f2697d4)
- Bug 1148535 - Check if the density descriptor in srcset consists of a valid floating-point number. r=jdm (79ac8d8dea)
- Bug 1257742 - Part 1: Follow the update-source-set rule to append default source into source set; r=jdm (853c69cc8c)
- Bug 1257742 - Part 2: Allow both width and static density candidates showing in same selector; r=jdm (4f1e00225d)
- Bug 1257742 - Part 3: Support using floating point in sizes descriptor; r=jdm (c1a7e36bb3)
- Bug 1257742 - Part 4: Update web-platform test expectation; r=jdm (e41044b88e)
- Bug 1262942 - Remove unnecessary warning message in ResponsiveImageSelector; r=jdm (ffb757204d)
- Bug 1158412 - Remove assertion for document prescontex and add crash test; r=jdm (947ccdfbfc)
- Bug 1237633 - Part 1: Percentages are not allowed in a <source-size-value>. r=jdm (614b560097)
- Bug 1237633 - Part 2: Avoid fatal assertion when a responsive image's size specifier is invalid. r=johns, r=jdm (4e90829d97)
- Bug 495546 - Add crashtest. (c2765ecbf4)
- fix some tests (d9b393b168)
- Bug 1256419. Null-check our nsDOMWindowList before trying to get its length. r=smaug (6c14430e5d)
- Bug 1162775: Make contentAreaDropListener use dataTransfer.files to get the files dropped. r=smaug (d2850f2008)
- Bug 1220679 - replace AutoSafeJSContext with AutoJSAPI. r=bz. (64538bdd44)
- reinstantiate assert, present in up to esr68 (221cec538a)
- Bug 1209329 - Improve comments about about: URIs in nsContentUtils::InternalStorageAllowedForPrincipal, r=bholley (71152e5639)
- Bug 1246250 - Deal with failure to create a blob actor. r=khuey (102686ac28)
- Bug 1265902 - part 1 - be more efficient when using nsContentUtils::GetSurfaceData(); r=mccr8 (ba2a52abd3)
- Bug 1258857 - Add empty items to an IPC transferable object for every flavor of the source object that did not have any data associated to it r=enndeakin (0a02b61566)
- Bug 1265902 - part 2 - don't construct unnecessary string temporaries in TransferablesToIPCTransferables; r=mccr8 (aec10c8fc6)
- remove unknown blob handling, not found in Tycho-dev repo, nor esr60 or TFF (eb6a24720a)
- Bug 1155486 - Convert nsDOMAttributeMap::mLocalName to void* to ensure that we can never dereference it; r=baku (34e2864340)
- Bug 1250926 - Remove unused SCRIPTABLE_FLAGS defines from nsDOMClassInfo; r=peterv (76917fb76b)
- No bug. Helper tool to partially autogenerate portions of the release notes. (3818e5534b)
- Bug 1203423 - Move call to AddClone outside nsMutationReceiver constructor; r=smaug (35c94ad785)
- Bug 1254096 - Update CaretPositionFromPoint() for type=number, r=ehsan (df31edca8f)
- Bug 1265771 P1 Only store active documents in the global observer list. r=bz (01502e91e5)
- Bug 1265771 P2 Expand navigate-window.https.html wpt test to cover uncontrolled windows. r=bz (3333906720)
- Bug 1265771 P3 Expand browser_force_refresh.js to verify Clients.matchAll() behavior on refresh. r=bz (01394ec8f1)
- stop hiding things for _LIBCPP_VERSION (5de86e8bbf)
1728 lines
45 KiB
C++
1728 lines
45 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "base/basictypes.h"
|
|
#include "BluetoothOppManager.h"
|
|
|
|
#include "BluetoothService.h"
|
|
#include "BluetoothSocket.h"
|
|
#include "BluetoothUtils.h"
|
|
#include "BluetoothUuidHelper.h"
|
|
#include "ObexBase.h"
|
|
|
|
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
|
#include "mozilla/dom/ipc/BlobParent.h"
|
|
#include "mozilla/dom/File.h"
|
|
#include "mozilla/RefPtr.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "nsCExternalHandlerService.h"
|
|
#include "nsIObserver.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIFile.h"
|
|
#include "nsIInputStream.h"
|
|
#include "nsIMIMEService.h"
|
|
#include "nsIOutputStream.h"
|
|
#include "nsIVolumeService.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
#define TARGET_SUBDIR "Download/Bluetooth/"
|
|
|
|
USING_BLUETOOTH_NAMESPACE
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::ipc;
|
|
|
|
namespace {
|
|
// Sending system message "bluetooth-opp-update-progress" every 50kb
|
|
static const uint32_t kUpdateProgressBase = 50 * 1024;
|
|
|
|
/*
|
|
* The format of the header of an PUT request is
|
|
* [opcode:1][packet length:2][headerId:1][header length:2]
|
|
*/
|
|
static const uint32_t kPutRequestHeaderSize = 6;
|
|
|
|
/*
|
|
* The format of the appended header of an PUT request is
|
|
* [headerId:1][header length:4]
|
|
* P.S. Length of name header is 4 since unicode is 2 bytes per char.
|
|
*/
|
|
static const uint32_t kPutRequestAppendHeaderSize = 5;
|
|
|
|
// The default timeout we permit to wait for SDP updating if we can't get
|
|
// service channel.
|
|
static const double kSdpUpdatingTimeoutMs = 3000.0;
|
|
|
|
// UUID of OBEX Object Push
|
|
static const BluetoothUuid kObexObjectPush(OBJECT_PUSH);
|
|
|
|
StaticRefPtr<BluetoothOppManager> sBluetoothOppManager;
|
|
static bool sInShutdown = false;
|
|
}
|
|
|
|
BEGIN_BLUETOOTH_NAMESPACE
|
|
|
|
class BluetoothOppManager::SendFileBatch final
|
|
{
|
|
public:
|
|
SendFileBatch(const BluetoothAddress& aDeviceAddress, Blob* aBlob)
|
|
: mDeviceAddress(aDeviceAddress)
|
|
{
|
|
mBlobs.AppendElement(aBlob);
|
|
}
|
|
|
|
BluetoothAddress mDeviceAddress;
|
|
nsTArray<RefPtr<Blob>> mBlobs;
|
|
};
|
|
|
|
NS_IMETHODIMP
|
|
BluetoothOppManager::Observe(nsISupports* aSubject,
|
|
const char* aTopic,
|
|
const char16_t* aData)
|
|
{
|
|
MOZ_ASSERT(sBluetoothOppManager);
|
|
|
|
if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
|
|
HandleVolumeStateChanged(aSubject);
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
|
HandleShutdown();
|
|
return NS_OK;
|
|
}
|
|
|
|
MOZ_ASSERT(false, "BluetoothOppManager got unexpected topic!");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
class BluetoothOppManager::SendSocketDataTask final : public nsRunnable
|
|
{
|
|
public:
|
|
SendSocketDataTask(UniquePtr<uint8_t[]> aStream, uint32_t aSize)
|
|
: mStream(Move(aStream))
|
|
, mSize(aSize)
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
sBluetoothOppManager->SendPutRequest(mStream.get(), mSize);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
UniquePtr<uint8_t[]> mStream;
|
|
uint32_t mSize;
|
|
};
|
|
|
|
class BluetoothOppManager::ReadFileTask final : public nsRunnable
|
|
{
|
|
public:
|
|
ReadFileTask(nsIInputStream* aInputStream,
|
|
uint32_t aRemoteMaxPacketSize) : mInputStream(aInputStream)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mAvailablePacketSize = aRemoteMaxPacketSize - kPutRequestHeaderSize;
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
MOZ_ASSERT(!NS_IsMainThread());
|
|
|
|
uint32_t numRead;
|
|
auto buf = MakeUnique<uint8_t[]>(mAvailablePacketSize);
|
|
|
|
// function inputstream->Read() only works on non-main thread
|
|
nsresult rv = mInputStream->Read((char*)buf.get(), mAvailablePacketSize,
|
|
&numRead);
|
|
if (NS_FAILED(rv)) {
|
|
// Needs error handling here
|
|
BT_WARNING("Failed to read from input stream");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (numRead > 0) {
|
|
sBluetoothOppManager->CheckPutFinal(numRead);
|
|
|
|
RefPtr<SendSocketDataTask> task =
|
|
new SendSocketDataTask(Move(buf), numRead);
|
|
if (NS_FAILED(NS_DispatchToMainThread(task))) {
|
|
BT_WARNING("Failed to dispatch to main thread!");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
};
|
|
|
|
private:
|
|
nsCOMPtr<nsIInputStream> mInputStream;
|
|
uint32_t mAvailablePacketSize;
|
|
};
|
|
|
|
class BluetoothOppManager::CloseSocketTask final : public Task
|
|
{
|
|
public:
|
|
CloseSocketTask(BluetoothSocket* aSocket) : mSocket(aSocket)
|
|
{
|
|
MOZ_ASSERT(aSocket);
|
|
}
|
|
|
|
void Run() override
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
mSocket->Close();
|
|
}
|
|
|
|
private:
|
|
RefPtr<BluetoothSocket> mSocket;
|
|
};
|
|
|
|
BluetoothOppManager::BluetoothOppManager() : mConnected(false)
|
|
, mRemoteObexVersion(0)
|
|
, mRemoteConnectionFlags(0)
|
|
, mRemoteMaxPacketLength(0)
|
|
, mLastCommand(0)
|
|
, mPacketLength(0)
|
|
, mPutPacketReceivedLength(0)
|
|
, mBodySegmentLength(0)
|
|
, mNeedsUpdatingSdpRecords(false)
|
|
, mAbortFlag(false)
|
|
, mNewFileFlag(false)
|
|
, mPutFinalFlag(false)
|
|
, mSendTransferCompleteFlag(false)
|
|
, mSuccessFlag(false)
|
|
, mIsServer(true)
|
|
, mWaitingForConfirmationFlag(false)
|
|
, mFileLength(0)
|
|
, mSentFileLength(0)
|
|
, mWaitingToSendPutFinal(false)
|
|
, mCurrentBlobIndex(-1)
|
|
{ }
|
|
|
|
BluetoothOppManager::~BluetoothOppManager()
|
|
{ }
|
|
|
|
nsresult
|
|
BluetoothOppManager::Init()
|
|
{
|
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
NS_ENSURE_TRUE(obs, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
auto rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
|
if (NS_FAILED(rv)) {
|
|
BT_WARNING("Failed to add shutdown observer!");
|
|
return rv;
|
|
}
|
|
|
|
rv = obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
|
|
if (NS_FAILED(rv)) {
|
|
BT_WARNING("Failed to add ns volume observer!");
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* We don't start listening here as BluetoothServiceBluedroid calls Listen()
|
|
* immediately when BT stops.
|
|
*
|
|
* If we start listening here, the listening fails when device boots up since
|
|
* Listen() is called again and restarts server socket. The restart causes
|
|
* absence of read events when device boots up.
|
|
*/
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::Uninit()
|
|
{
|
|
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
|
NS_ENSURE_TRUE_VOID(obs);
|
|
|
|
if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
|
|
BT_WARNING("Failed to remove shutdown observer!");
|
|
}
|
|
|
|
if (NS_FAILED(obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED))) {
|
|
BT_WARNING("Failed to remove volume observer!");
|
|
}
|
|
}
|
|
|
|
// static
|
|
void
|
|
BluetoothOppManager::InitOppInterface(BluetoothProfileResultHandler* aRes)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (aRes) {
|
|
aRes->Init();
|
|
}
|
|
}
|
|
|
|
// static
|
|
void
|
|
BluetoothOppManager::DeinitOppInterface(BluetoothProfileResultHandler* aRes)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (sBluetoothOppManager) {
|
|
sBluetoothOppManager->Uninit();
|
|
sBluetoothOppManager = nullptr;
|
|
}
|
|
|
|
if (aRes) {
|
|
aRes->Deinit();
|
|
}
|
|
}
|
|
|
|
//static
|
|
BluetoothOppManager*
|
|
BluetoothOppManager::Get()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// If sBluetoothOppManager already exists, exit early
|
|
if (sBluetoothOppManager) {
|
|
return sBluetoothOppManager;
|
|
}
|
|
|
|
// If we're in shutdown, don't create a new instance
|
|
NS_ENSURE_FALSE(sInShutdown, nullptr);
|
|
|
|
// Create a new instance, register, and return
|
|
RefPtr<BluetoothOppManager> manager = new BluetoothOppManager();
|
|
NS_ENSURE_SUCCESS(manager->Init(), nullptr);
|
|
|
|
sBluetoothOppManager = manager;
|
|
|
|
return sBluetoothOppManager;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ConnectInternal(const BluetoothAddress& aDeviceAddress)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Stop listening because currently we only support one connection at a time.
|
|
if (mServerSocket) {
|
|
mServerSocket->Close();
|
|
mServerSocket = nullptr;
|
|
}
|
|
|
|
mIsServer = false;
|
|
|
|
BluetoothService* bs = BluetoothService::Get();
|
|
if (!bs || sInShutdown || mSocket) {
|
|
OnSocketConnectError(mSocket);
|
|
return;
|
|
}
|
|
|
|
mNeedsUpdatingSdpRecords = true;
|
|
|
|
if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, kObexObjectPush, this))) {
|
|
OnSocketConnectError(mSocket);
|
|
return;
|
|
}
|
|
|
|
mSocket = new BluetoothSocket(this);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::HandleShutdown()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
sInShutdown = true;
|
|
Disconnect(nullptr);
|
|
Uninit();
|
|
|
|
sBluetoothOppManager = nullptr;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::HandleVolumeStateChanged(nsISupports* aSubject)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (!mConnected) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Disconnect ongoing OPP connection when a volume becomes non-mounted, in
|
|
* case files of transfer are located on that volume. |OnSocketDisconnect|
|
|
* will handle incomplete file transfers.
|
|
*/
|
|
|
|
nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
|
|
if (!vol) {
|
|
return;
|
|
}
|
|
|
|
int32_t state;
|
|
vol->GetState(&state);
|
|
if (nsIVolume::STATE_MOUNTED != state) {
|
|
BT_LOGR("Volume becomes non-mounted. Abort ongoing OPP connection");
|
|
Disconnect(nullptr);
|
|
}
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::Listen()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (mSocket) {
|
|
BT_WARNING("mSocket exists. Failed to listen.");
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Restart server socket since its underlying fd becomes invalid when
|
|
* BT stops; otherwise no more read events would be received even if
|
|
* BT restarts.
|
|
*/
|
|
if (mServerSocket) {
|
|
mServerSocket->Close();
|
|
mServerSocket = nullptr;
|
|
}
|
|
|
|
mServerSocket = new BluetoothSocket(this);
|
|
|
|
nsresult rv = mServerSocket->Listen(NS_LITERAL_STRING("OBEX Object Push"),
|
|
kObexObjectPush,
|
|
BluetoothSocketType::RFCOMM,
|
|
BluetoothReservedChannels::CHANNEL_OPUSH,
|
|
false, true);
|
|
if (NS_FAILED(rv)) {
|
|
BT_WARNING("[OPP] Can't listen on RFCOMM socket!");
|
|
mServerSocket = nullptr;
|
|
return false;
|
|
}
|
|
|
|
mIsServer = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::StartSendingNextFile()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(!mBatches.IsEmpty());
|
|
MOZ_ASSERT((int)mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1);
|
|
|
|
mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
|
|
|
|
// Before sending content, we have to send a header including
|
|
// information such as file name, file length and content type.
|
|
ExtractBlobHeaders();
|
|
StartFileTransfer();
|
|
|
|
if (mCurrentBlobIndex == 0) {
|
|
// We may have more than one file waiting for transferring, but only one
|
|
// CONNECT request would be sent. Therefore check if this is the very first
|
|
// file at the head of queue.
|
|
SendConnectRequest();
|
|
} else {
|
|
SendPutHeaderRequest(mFileName, mFileLength);
|
|
AfterFirstPut();
|
|
}
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::SendFile(const BluetoothAddress& aDeviceAddress,
|
|
BlobParent* aActor)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
RefPtr<BlobImpl> impl = aActor->GetBlobImpl();
|
|
RefPtr<Blob> blob = Blob::Create(nullptr, impl);
|
|
|
|
return SendFile(aDeviceAddress, blob.get());
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::SendFile(const BluetoothAddress& aDeviceAddress,
|
|
Blob* aBlob)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
AppendBlobToSend(aDeviceAddress, aBlob);
|
|
if (!mSocket) {
|
|
ProcessNextBatch();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::AppendBlobToSend(const BluetoothAddress& aDeviceAddress,
|
|
Blob* aBlob)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
int indexTail = mBatches.Length() - 1;
|
|
|
|
/**
|
|
* Create a new batch if
|
|
* - mBatches is empty, or
|
|
* - aDeviceAddress differs from mDeviceAddress of the last batch
|
|
*/
|
|
if (mBatches.IsEmpty() ||
|
|
aDeviceAddress != mBatches[indexTail].mDeviceAddress) {
|
|
SendFileBatch batch(aDeviceAddress, aBlob);
|
|
mBatches.AppendElement(batch);
|
|
} else {
|
|
mBatches[indexTail].mBlobs.AppendElement(aBlob);
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::DiscardBlobsToSend()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(!mBatches.IsEmpty());
|
|
MOZ_ASSERT(!mIsServer);
|
|
|
|
int length = (int) mBatches[0].mBlobs.Length();
|
|
while (length > mCurrentBlobIndex + 1) {
|
|
mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
|
|
|
|
BT_LOGR("idx %d", mCurrentBlobIndex);
|
|
ExtractBlobHeaders();
|
|
StartFileTransfer();
|
|
FileTransferComplete();
|
|
}
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::ProcessNextBatch()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!IsConnected());
|
|
|
|
// Remove the processed batch.
|
|
// A batch is processed if we've incremented mCurrentBlobIndex for it.
|
|
if (mCurrentBlobIndex >= 0) {
|
|
ClearQueue();
|
|
mBatches.RemoveElementAt(0);
|
|
BT_LOGR("REMOVE. %d remaining", mBatches.Length());
|
|
}
|
|
|
|
// Process the next batch
|
|
if (!mBatches.IsEmpty()) {
|
|
ConnectInternal(mBatches[0].mDeviceAddress);
|
|
return true;
|
|
}
|
|
|
|
// No more batch to process
|
|
return false;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ClearQueue()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
MOZ_ASSERT(!mIsServer);
|
|
MOZ_ASSERT(!mBatches.IsEmpty());
|
|
MOZ_ASSERT(!mBatches[0].mBlobs.IsEmpty());
|
|
|
|
mCurrentBlobIndex = -1;
|
|
mBlob = nullptr;
|
|
mBatches[0].mBlobs.Clear();
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::StopSendingFile()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (mIsServer) {
|
|
mAbortFlag = true;
|
|
} else {
|
|
Disconnect(nullptr);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::ConfirmReceivingFile(bool aConfirm)
|
|
{
|
|
NS_ENSURE_TRUE(mConnected, false);
|
|
NS_ENSURE_TRUE(mWaitingForConfirmationFlag, false);
|
|
|
|
mWaitingForConfirmationFlag = false;
|
|
|
|
// For the first packet of first file
|
|
bool success = false;
|
|
if (aConfirm) {
|
|
StartFileTransfer();
|
|
if (CreateFile()) {
|
|
success = WriteToFile(mBodySegment.get(), mBodySegmentLength);
|
|
}
|
|
}
|
|
|
|
if (success && mPutFinalFlag) {
|
|
mSuccessFlag = true;
|
|
RestoreReceivedFileAndNotify();
|
|
FileTransferComplete();
|
|
}
|
|
|
|
ReplyToPut(mPutFinalFlag, success);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::AfterFirstPut()
|
|
{
|
|
mUpdateProgressCounter = 1;
|
|
mPutFinalFlag = false;
|
|
mPutPacketReceivedLength = 0;
|
|
mSentFileLength = 0;
|
|
mWaitingToSendPutFinal = false;
|
|
mSuccessFlag = false;
|
|
mBodySegmentLength = 0;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::AfterOppConnected()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mConnected = true;
|
|
mAbortFlag = false;
|
|
mWaitingForConfirmationFlag = true;
|
|
AfterFirstPut();
|
|
// Get a mount lock to prevent the sdcard from being shared with
|
|
// the PC while we're doing a OPP file transfer. After OPP transaction
|
|
// were done, the mount lock will be freed.
|
|
if (!AcquireSdcardMountLock()) {
|
|
// If we fail to get a mount lock, abort this transaction
|
|
// Directly sending disconnect-request is better than abort-request
|
|
BT_WARNING("BluetoothOPPManager couldn't get a mount lock!");
|
|
Disconnect(nullptr);
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::AfterOppDisconnected()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
mConnected = false;
|
|
mLastCommand = 0;
|
|
mPutPacketReceivedLength = 0;
|
|
mDsFile = nullptr;
|
|
mDummyDsFile = nullptr;
|
|
|
|
// We can't reset mSuccessFlag here since this function may be called
|
|
// before we send system message of transfer complete
|
|
// mSuccessFlag = false;
|
|
|
|
if (mInputStream) {
|
|
mInputStream->Close();
|
|
mInputStream = nullptr;
|
|
}
|
|
|
|
if (mOutputStream) {
|
|
mOutputStream->Close();
|
|
mOutputStream = nullptr;
|
|
}
|
|
|
|
// Store local pointer of |mReadFileThread| to avoid shutdown reentry crash
|
|
// See bug 1191715 comment 19 for more details.
|
|
nsCOMPtr<nsIThread> thread = mReadFileThread.forget();
|
|
if (thread) {
|
|
thread->Shutdown();
|
|
}
|
|
|
|
// Release the mount lock if file transfer completed
|
|
if (mMountLock) {
|
|
// The mount lock will be implicitly unlocked
|
|
mMountLock = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::RestoreReceivedFileAndNotify()
|
|
{
|
|
// Remove the empty dummy file
|
|
if (mDummyDsFile && mDummyDsFile->mFile) {
|
|
mDummyDsFile->mFile->Remove(false);
|
|
mDummyDsFile = nullptr;
|
|
}
|
|
|
|
// Remove the trailing ".part" file name from mDsFile by two steps
|
|
// 1. mDsFile->SetPath() so that the notification sent to Gaia will carry
|
|
// correct information of the file.
|
|
// 2. mDsFile->mFile->RenameTo() so that the file name would actually be
|
|
// changed in file system.
|
|
if (mDsFile && mDsFile->mFile) {
|
|
nsString path;
|
|
path.AssignLiteral(TARGET_SUBDIR);
|
|
path.Append(mFileName);
|
|
|
|
mDsFile->SetPath(path);
|
|
mDsFile->mFile->RenameTo(nullptr, mFileName);
|
|
}
|
|
|
|
// Notify about change of received file
|
|
NotifyAboutFileChange();
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::DeleteReceivedFile()
|
|
{
|
|
if (mOutputStream) {
|
|
mOutputStream->Close();
|
|
mOutputStream = nullptr;
|
|
}
|
|
|
|
if (mDsFile && mDsFile->mFile) {
|
|
mDsFile->mFile->Remove(false);
|
|
mDsFile = nullptr;
|
|
}
|
|
|
|
// Remove the empty dummy file
|
|
if (mDummyDsFile && mDummyDsFile->mFile) {
|
|
mDummyDsFile->mFile->Remove(false);
|
|
mDummyDsFile = nullptr;
|
|
}
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::CreateFile()
|
|
{
|
|
MOZ_ASSERT(mPutPacketReceivedLength == mPacketLength);
|
|
|
|
// Create one dummy file to be a placeholder for the target file name, and
|
|
// create another file with a meaningless file extension to write the received
|
|
// data. By doing this, we can prevent applications from parsing incomplete
|
|
// data in the middle of the receiving process.
|
|
nsString path;
|
|
path.AssignLiteral(TARGET_SUBDIR);
|
|
path.Append(mFileName);
|
|
|
|
// Use an empty dummy file object to occupy the file name, so that after the
|
|
// whole file has been received successfully by using mDsFile, we could just
|
|
// remove mDummyDsFile and rename mDsFile to the file name of mDummyDsFile.
|
|
mDummyDsFile =
|
|
DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
|
|
NS_ENSURE_TRUE(mDummyDsFile, false);
|
|
|
|
// The function CreateUnique() may create a file with a different file
|
|
// name from the original mFileName. Therefore we have to retrieve
|
|
// the file name again.
|
|
mDummyDsFile->mFile->GetLeafName(mFileName);
|
|
|
|
BT_LOGR("mFileName: %s", NS_ConvertUTF16toUTF8(mFileName).get());
|
|
|
|
// Prepare the entire file path for the .part file
|
|
path.Truncate();
|
|
path.AssignLiteral(TARGET_SUBDIR);
|
|
path.Append(mFileName);
|
|
path.AppendLiteral(".part");
|
|
|
|
mDsFile =
|
|
DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644);
|
|
NS_ENSURE_TRUE(mDsFile, false);
|
|
|
|
NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mDsFile->mFile);
|
|
NS_ENSURE_TRUE(mOutputStream, false);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
|
|
{
|
|
NS_ENSURE_TRUE(mOutputStream, false);
|
|
|
|
uint32_t wrote = 0;
|
|
mOutputStream->Write((const char*)aData, aDataLength, &wrote);
|
|
NS_ENSURE_TRUE(aDataLength == (int) wrote, false);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Virtual function of class SocketConsumer
|
|
void
|
|
BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
|
|
{
|
|
if (aHeader.Has(ObexHeaderId::Name)) {
|
|
aHeader.GetName(mFileName);
|
|
}
|
|
|
|
if (aHeader.Has(ObexHeaderId::Type)) {
|
|
aHeader.GetContentType(mContentType);
|
|
}
|
|
|
|
if (aHeader.Has(ObexHeaderId::Length)) {
|
|
aHeader.GetLength(&mFileLength);
|
|
}
|
|
|
|
if (aHeader.Has(ObexHeaderId::Body) ||
|
|
aHeader.Has(ObexHeaderId::EndOfBody)) {
|
|
uint8_t* bodyPtr;
|
|
aHeader.GetBody(&bodyPtr, &mBodySegmentLength);
|
|
mBodySegment.reset(bodyPtr);
|
|
}
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::ExtractBlobHeaders()
|
|
{
|
|
RetrieveSentFileName();
|
|
mBlob->GetType(mContentType);
|
|
|
|
ErrorResult rv;
|
|
uint64_t fileLength = mBlob->GetSize(rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
SendDisconnectRequest();
|
|
rv.SuppressException();
|
|
return false;
|
|
}
|
|
|
|
// Currently we keep the size of files which were sent/received via
|
|
// Bluetooth not exceed UINT32_MAX because the Length header in OBEX
|
|
// is only 4-byte long. Although it is possible to transfer a file
|
|
// larger than UINT32_MAX, it needs to parse another OBEX Header
|
|
// and I would like to leave it as a feature.
|
|
if (fileLength > (uint64_t)UINT32_MAX) {
|
|
BT_WARNING("The file size is too large for now");
|
|
SendDisconnectRequest();
|
|
return false;
|
|
}
|
|
|
|
mFileLength = fileLength;
|
|
rv = NS_NewThread(getter_AddRefs(mReadFileThread));
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
SendDisconnectRequest();
|
|
rv.SuppressException();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::RetrieveSentFileName()
|
|
{
|
|
mFileName.Truncate();
|
|
|
|
RefPtr<File> file = static_cast<Blob*>(mBlob.get())->ToFile();
|
|
if (file) {
|
|
file->GetName(mFileName);
|
|
}
|
|
|
|
/**
|
|
* We try our best to get the file extension to avoid interoperability issues.
|
|
* However, once we found that we are unable to get suitable extension or
|
|
* information about the content type, sending a pre-defined file name without
|
|
* extension would be fine.
|
|
*/
|
|
if (mFileName.IsEmpty()) {
|
|
mFileName.AssignLiteral("Unknown");
|
|
}
|
|
|
|
int32_t offset = mFileName.RFindChar('/');
|
|
if (offset != kNotFound) {
|
|
mFileName = Substring(mFileName, offset + 1);
|
|
}
|
|
|
|
offset = mFileName.RFindChar('.');
|
|
if (offset == kNotFound) {
|
|
nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
|
|
|
|
if (mimeSvc) {
|
|
nsString mimeType;
|
|
mBlob->GetType(mimeType);
|
|
|
|
nsCString extension;
|
|
nsresult rv =
|
|
mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
|
|
EmptyCString(),
|
|
extension);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
mFileName.Append('.');
|
|
AppendUTF8toUTF16(extension, mFileName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::IsReservedChar(char16_t c)
|
|
{
|
|
return (c < 0x0020 ||
|
|
c == char16_t('?') || c == char16_t('|') || c == char16_t('<') ||
|
|
c == char16_t('>') || c == char16_t('"') || c == char16_t(':') ||
|
|
c == char16_t('/') || c == char16_t('*') || c == char16_t('\\'));
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ValidateFileName()
|
|
{
|
|
int length = mFileName.Length();
|
|
|
|
for (int i = 0; i < length; ++i) {
|
|
// Replace reserved char of fat file system with '_'
|
|
if (IsReservedChar(mFileName.CharAt(i))) {
|
|
mFileName.Replace(i, 1, char16_t('_'));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::ComposePacket(uint8_t aOpCode, UnixSocketBuffer* aMessage)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aMessage);
|
|
|
|
const uint8_t* data = aMessage->GetData();
|
|
int frameHeaderLength = 0;
|
|
|
|
// See if this is the first part of each Put packet
|
|
if (mPutPacketReceivedLength == 0) {
|
|
// Section 3.3.3 "Put", IrOBEX 1.2
|
|
// [opcode:1][length:2][Headers:var]
|
|
frameHeaderLength = 3;
|
|
|
|
mPacketLength = BigEndian::readUint16(&data[1]) - frameHeaderLength;
|
|
|
|
/**
|
|
* A PUT request from remote devices may be divided into multiple parts.
|
|
* In other words, one request may need to be received multiple times,
|
|
* so here we keep a variable mPutPacketReceivedLength to indicate if
|
|
* current PUT request is done.
|
|
*/
|
|
mReceivedDataBuffer.reset(new uint8_t[mPacketLength]);
|
|
mPutFinalFlag = (aOpCode == ObexRequestCode::PutFinal);
|
|
}
|
|
|
|
int dataLength = aMessage->GetSize() - frameHeaderLength;
|
|
|
|
// Check length before memcpy to prevent from memory pollution
|
|
if (dataLength < 0 ||
|
|
mPutPacketReceivedLength + dataLength > mPacketLength) {
|
|
BT_LOGR("Received packet size is unreasonable");
|
|
|
|
ReplyToPut(mPutFinalFlag, false);
|
|
DeleteReceivedFile();
|
|
FileTransferComplete();
|
|
|
|
return false;
|
|
}
|
|
|
|
memcpy(mReceivedDataBuffer.get() + mPutPacketReceivedLength,
|
|
&data[frameHeaderLength], dataLength);
|
|
|
|
mPutPacketReceivedLength += dataLength;
|
|
|
|
return (mPutPacketReceivedLength == mPacketLength);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ServerDataHandler(UnixSocketBuffer* aMessage)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
/**
|
|
* Ensure
|
|
* - valid access to data[0], i.e., opCode
|
|
* - received packet length smaller than max packet length
|
|
*/
|
|
int receivedLength = aMessage->GetSize();
|
|
if (receivedLength < 1 || receivedLength > MAX_PACKET_LENGTH) {
|
|
ReplyError(ObexResponseCode::BadRequest);
|
|
return;
|
|
}
|
|
|
|
uint8_t opCode;
|
|
const uint8_t* data = aMessage->GetData();
|
|
if (mPutPacketReceivedLength > 0) {
|
|
opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
|
|
} else {
|
|
opCode = data[0];
|
|
|
|
// When there's a Put packet right after a PutFinal packet,
|
|
// which means it's the start point of a new file.
|
|
if (mPutFinalFlag &&
|
|
(opCode == ObexRequestCode::Put ||
|
|
opCode == ObexRequestCode::PutFinal)) {
|
|
mNewFileFlag = true;
|
|
AfterFirstPut();
|
|
}
|
|
}
|
|
|
|
ObexHeaderSet pktHeaders;
|
|
if (opCode == ObexRequestCode::Connect) {
|
|
// Section 3.3.1 "Connect", IrOBEX 1.2
|
|
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
|
// [Headers:var]
|
|
if (receivedLength < 7 ||
|
|
!ParseHeaders(&data[7], receivedLength - 7, &pktHeaders)) {
|
|
ReplyError(ObexResponseCode::BadRequest);
|
|
return;
|
|
}
|
|
|
|
ReplyToConnect();
|
|
AfterOppConnected();
|
|
} else if (opCode == ObexRequestCode::Abort) {
|
|
// Section 3.3.5 "Abort", IrOBEX 1.2
|
|
// [opcode:1][length:2][Headers:var]
|
|
if (receivedLength < 3 ||
|
|
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
|
ReplyError(ObexResponseCode::BadRequest);
|
|
return;
|
|
}
|
|
|
|
ReplyToDisconnectOrAbort();
|
|
DeleteReceivedFile();
|
|
} else if (opCode == ObexRequestCode::Disconnect) {
|
|
// Section 3.3.2 "Disconnect", IrOBEX 1.2
|
|
// [opcode:1][length:2][Headers:var]
|
|
if (receivedLength < 3 ||
|
|
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
|
|
ReplyError(ObexResponseCode::BadRequest);
|
|
return;
|
|
}
|
|
|
|
ReplyToDisconnectOrAbort();
|
|
AfterOppDisconnected();
|
|
FileTransferComplete();
|
|
} else if (opCode == ObexRequestCode::Put ||
|
|
opCode == ObexRequestCode::PutFinal) {
|
|
if (!ComposePacket(opCode, aMessage)) {
|
|
return;
|
|
}
|
|
|
|
// A Put packet is received completely
|
|
ParseHeaders(mReceivedDataBuffer.get(),
|
|
mPutPacketReceivedLength, &pktHeaders);
|
|
ExtractPacketHeaders(pktHeaders);
|
|
ValidateFileName();
|
|
|
|
// When we cancel the transfer, delete the file and notify completion
|
|
if (mAbortFlag) {
|
|
ReplyToPut(mPutFinalFlag, false);
|
|
mSentFileLength += mBodySegmentLength;
|
|
DeleteReceivedFile();
|
|
FileTransferComplete();
|
|
return;
|
|
}
|
|
|
|
// Wait until get confirmation from user, then create file and write to it
|
|
if (mWaitingForConfirmationFlag) {
|
|
ReceivingFileConfirmation();
|
|
mSentFileLength += mBodySegmentLength;
|
|
return;
|
|
}
|
|
|
|
// Already get confirmation from user, create a new file if needed and
|
|
// write to output stream
|
|
if (mNewFileFlag) {
|
|
StartFileTransfer();
|
|
if (!CreateFile()) {
|
|
ReplyToPut(mPutFinalFlag, false);
|
|
return;
|
|
}
|
|
mNewFileFlag = false;
|
|
}
|
|
|
|
if (!WriteToFile(mBodySegment.get(), mBodySegmentLength)) {
|
|
ReplyToPut(mPutFinalFlag, false);
|
|
return;
|
|
}
|
|
|
|
ReplyToPut(mPutFinalFlag, true);
|
|
|
|
// Send progress update
|
|
mSentFileLength += mBodySegmentLength;
|
|
if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) {
|
|
UpdateProgress();
|
|
mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
|
|
}
|
|
|
|
// Success to receive a file and notify completion
|
|
if (mPutFinalFlag) {
|
|
mSuccessFlag = true;
|
|
RestoreReceivedFileAndNotify();
|
|
FileTransferComplete();
|
|
}
|
|
} else if (opCode == ObexRequestCode::Get ||
|
|
opCode == ObexRequestCode::GetFinal ||
|
|
opCode == ObexRequestCode::SetPath) {
|
|
ReplyError(ObexResponseCode::BadRequest);
|
|
BT_WARNING("Unsupported ObexRequestCode");
|
|
} else {
|
|
ReplyError(ObexResponseCode::NotImplemented);
|
|
BT_WARNING("Unrecognized ObexRequestCode");
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ClientDataHandler(UnixSocketBuffer* aMessage)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
// Ensure valid access to data[0], i.e., opCode
|
|
int receivedLength = aMessage->GetSize();
|
|
if (receivedLength < 1) {
|
|
BT_LOGR("Receive empty response packet");
|
|
return;
|
|
}
|
|
|
|
const uint8_t* data = aMessage->GetData();
|
|
uint8_t opCode = data[0];
|
|
|
|
// Check response code and send out system message as finished if the response
|
|
// code is somehow incorrect.
|
|
uint8_t expectedOpCode = ObexResponseCode::Success;
|
|
if (mLastCommand == ObexRequestCode::Put) {
|
|
expectedOpCode = ObexResponseCode::Continue;
|
|
}
|
|
|
|
if (opCode != expectedOpCode) {
|
|
if (mLastCommand == ObexRequestCode::Put ||
|
|
mLastCommand == ObexRequestCode::Abort ||
|
|
mLastCommand == ObexRequestCode::PutFinal) {
|
|
SendDisconnectRequest();
|
|
}
|
|
nsAutoCString str;
|
|
str += "[OPP] 0x";
|
|
str.AppendInt(mLastCommand, 16);
|
|
str += " failed";
|
|
BT_WARNING(str.get());
|
|
FileTransferComplete();
|
|
return;
|
|
}
|
|
|
|
if (mLastCommand == ObexRequestCode::PutFinal) {
|
|
mSuccessFlag = true;
|
|
FileTransferComplete();
|
|
|
|
if (mInputStream) {
|
|
mInputStream->Close();
|
|
mInputStream = nullptr;
|
|
}
|
|
|
|
if (mCurrentBlobIndex + 1 == (int) mBatches[0].mBlobs.Length()) {
|
|
SendDisconnectRequest();
|
|
} else {
|
|
StartSendingNextFile();
|
|
}
|
|
} else if (mLastCommand == ObexRequestCode::Abort) {
|
|
SendDisconnectRequest();
|
|
FileTransferComplete();
|
|
} else if (mLastCommand == ObexRequestCode::Disconnect) {
|
|
AfterOppDisconnected();
|
|
// Most devices will directly terminate connection after receiving
|
|
// Disconnect request, so we make a delay here. If the socket hasn't been
|
|
// disconnected, we will close it.
|
|
if (mSocket) {
|
|
MessageLoop::current()->
|
|
PostDelayedTask(FROM_HERE, new CloseSocketTask(mSocket), 1000);
|
|
}
|
|
} else if (mLastCommand == ObexRequestCode::Connect) {
|
|
MOZ_ASSERT(!mFileName.IsEmpty());
|
|
MOZ_ASSERT(mBlob);
|
|
|
|
AfterOppConnected();
|
|
|
|
// Ensure valid access to remote information
|
|
if (receivedLength < 7) {
|
|
BT_LOGR("The length of connect response packet is invalid");
|
|
SendDisconnectRequest();
|
|
return;
|
|
}
|
|
|
|
// Keep remote information
|
|
mRemoteObexVersion = data[3];
|
|
mRemoteConnectionFlags = data[4];
|
|
mRemoteMaxPacketLength = BigEndian::readUint16(&data[5]);
|
|
|
|
// The length of file name exceeds maximum length.
|
|
int fileNameByteLen = (mFileName.Length() + 1) * 2;
|
|
int headerLen = kPutRequestHeaderSize + kPutRequestAppendHeaderSize;
|
|
if (fileNameByteLen > mRemoteMaxPacketLength - headerLen) {
|
|
BT_WARNING("The length of file name is aberrant.");
|
|
SendDisconnectRequest();
|
|
return;
|
|
}
|
|
|
|
SendPutHeaderRequest(mFileName, mFileLength);
|
|
} else if (mLastCommand == ObexRequestCode::Put) {
|
|
if (mWaitingToSendPutFinal) {
|
|
SendPutFinalRequest();
|
|
return;
|
|
}
|
|
|
|
if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) {
|
|
UpdateProgress();
|
|
mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1;
|
|
}
|
|
|
|
ErrorResult rv;
|
|
if (!mInputStream) {
|
|
mBlob->GetInternalStream(getter_AddRefs(mInputStream), rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
SendDisconnectRequest();
|
|
rv.SuppressException();
|
|
return;
|
|
}
|
|
}
|
|
|
|
RefPtr<ReadFileTask> task = new ReadFileTask(mInputStream,
|
|
mRemoteMaxPacketLength);
|
|
rv = mReadFileThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
SendDisconnectRequest();
|
|
rv.SuppressException();
|
|
return;
|
|
}
|
|
} else {
|
|
BT_WARNING("Unhandled ObexRequestCode");
|
|
}
|
|
}
|
|
|
|
// Virtual function of class SocketConsumer
|
|
void
|
|
BluetoothOppManager::ReceiveSocketData(BluetoothSocket* aSocket,
|
|
UniquePtr<UnixSocketBuffer>& aBuffer)
|
|
{
|
|
if (mIsServer) {
|
|
ServerDataHandler(aBuffer.get());
|
|
} else {
|
|
ClientDataHandler(aBuffer.get());
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::SendConnectRequest()
|
|
{
|
|
if (mConnected) {
|
|
return;
|
|
}
|
|
|
|
// Section 3.3.1 "Connect", IrOBEX 1.2
|
|
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
|
// [Headers:var]
|
|
uint8_t req[255];
|
|
int index = 7;
|
|
|
|
req[3] = 0x10; // version=1.0
|
|
req[4] = 0x00; // flag=0x00
|
|
req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
|
|
req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
|
|
|
|
SendObexData(req, ObexRequestCode::Connect, index);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
|
|
int aFileSize)
|
|
{
|
|
if (!mConnected) {
|
|
return;
|
|
}
|
|
|
|
int len = aFileName.Length();
|
|
auto fileName = MakeUnique<uint8_t[]>((len + 1) * 2);
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
BigEndian::writeUint16(&fileName[i * 2], aFileName[i]);
|
|
}
|
|
BigEndian::writeUint16(&fileName[len * 2], 0);
|
|
|
|
// Prepare packet
|
|
auto req = MakeUnique<uint8_t[]>(mRemoteMaxPacketLength);
|
|
int index = 3;
|
|
index += AppendHeaderName(&req[index], mRemoteMaxPacketLength - index,
|
|
fileName.get(), (len + 1) * 2);
|
|
index += AppendHeaderLength(&req[index], aFileSize);
|
|
|
|
// This is final put packet if file size equals to 0
|
|
uint8_t opcode = (aFileSize > 0) ? ObexRequestCode::Put
|
|
: ObexRequestCode::PutFinal;
|
|
SendObexData(Move(req), opcode, index);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
|
|
int aFileBodyLength)
|
|
{
|
|
if (!mConnected) {
|
|
return;
|
|
}
|
|
|
|
int packetLeftSpace = mRemoteMaxPacketLength - kPutRequestHeaderSize;
|
|
if (aFileBodyLength > packetLeftSpace) {
|
|
BT_WARNING("Not allowed such a small MaxPacketLength value");
|
|
return;
|
|
}
|
|
|
|
// Section 3.3.3 "Put", IrOBEX 1.2
|
|
// [opcode:1][length:2][Headers:var]
|
|
auto req = MakeUnique<uint8_t[]>(mRemoteMaxPacketLength);
|
|
|
|
int index = 3;
|
|
index += AppendHeaderBody(&req[index], mRemoteMaxPacketLength - index,
|
|
aFileBody, aFileBodyLength);
|
|
|
|
SendObexData(Move(req), ObexRequestCode::Put, index);
|
|
|
|
mSentFileLength += aFileBodyLength;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::SendPutFinalRequest()
|
|
{
|
|
if (!mConnected) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Section 2.2.9, "End-of-Body", IrObex 1.2
|
|
* End-of-Body is used to identify the last chunk of the object body.
|
|
* For most platforms, a PutFinal packet is sent with an zero length
|
|
* End-of-Body header.
|
|
*/
|
|
|
|
// [opcode:1][length:2]
|
|
int index = 3;
|
|
auto req = MakeUnique<uint8_t[]>(mRemoteMaxPacketLength);
|
|
index += AppendHeaderEndOfBody(&req[index]);
|
|
|
|
SendObexData(Move(req), ObexRequestCode::PutFinal, index);
|
|
|
|
mWaitingToSendPutFinal = false;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::SendDisconnectRequest()
|
|
{
|
|
if (!mConnected) {
|
|
return;
|
|
}
|
|
|
|
// Section 3.3.2 "Disconnect", IrOBEX 1.2
|
|
// [opcode:1][length:2][Headers:var]
|
|
uint8_t req[255];
|
|
int index = 3;
|
|
|
|
SendObexData(req, ObexRequestCode::Disconnect, index);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::CheckPutFinal(uint32_t aNumRead)
|
|
{
|
|
if (mSentFileLength + aNumRead >= mFileLength) {
|
|
mWaitingToSendPutFinal = true;
|
|
}
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::IsConnected()
|
|
{
|
|
return mConnected;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::GetAddress(BluetoothAddress& aDeviceAddress)
|
|
{
|
|
return mSocket->GetAddress(aDeviceAddress);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ReplyToConnect()
|
|
{
|
|
if (mConnected) {
|
|
return;
|
|
}
|
|
|
|
// Section 3.3.1 "Connect", IrOBEX 1.2
|
|
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
|
|
// [Headers:var]
|
|
uint8_t req[255];
|
|
int index = 7;
|
|
|
|
req[3] = 0x10; // version=1.0
|
|
req[4] = 0x00; // flag=0x00
|
|
req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
|
|
req[6] = (uint8_t)BluetoothOppManager::MAX_PACKET_LENGTH;
|
|
|
|
SendObexData(req, ObexResponseCode::Success, index);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ReplyToDisconnectOrAbort()
|
|
{
|
|
if (!mConnected) {
|
|
return;
|
|
}
|
|
|
|
// Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
|
|
// The format of response packet of "Disconnect" and "Abort" are the same
|
|
// [opcode:1][length:2][Headers:var]
|
|
uint8_t req[255];
|
|
int index = 3;
|
|
|
|
SendObexData(req, ObexResponseCode::Success, index);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue)
|
|
{
|
|
if (!mConnected) {
|
|
return;
|
|
}
|
|
|
|
// The received length can be reset here because this is where we reply to a
|
|
// complete put packet.
|
|
mPutPacketReceivedLength = 0;
|
|
|
|
// Section 3.3.2 "Disconnect", IrOBEX 1.2
|
|
// [opcode:1][length:2][Headers:var]
|
|
uint8_t req[255];
|
|
int index = 3;
|
|
uint8_t opcode;
|
|
|
|
if (aContinue) {
|
|
opcode = (aFinal)? ObexResponseCode::Success :
|
|
ObexResponseCode::Continue;
|
|
} else {
|
|
opcode = (aFinal)? ObexResponseCode::Unauthorized :
|
|
ObexResponseCode::Unauthorized & (~FINAL_BIT);
|
|
}
|
|
|
|
SendObexData(req, opcode, index);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ReplyError(uint8_t aError)
|
|
{
|
|
BT_LOGR("error: %d", aError);
|
|
|
|
// Section 3.2 "Response Format", IrOBEX 1.2
|
|
// [opcode:1][length:2][Headers:var]
|
|
uint8_t req[255];
|
|
int index = 3;
|
|
|
|
SendObexData(req, aError, index);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize)
|
|
{
|
|
if (!mIsServer) {
|
|
mLastCommand = aOpcode;
|
|
}
|
|
|
|
SetObexPacketInfo(aData, aOpcode, aSize);
|
|
mSocket->SendSocketData(new UnixSocketRawData(aData, aSize));
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::SendObexData(UniquePtr<uint8_t[]> aData, uint8_t aOpcode,
|
|
int aSize)
|
|
{
|
|
if (!mIsServer) {
|
|
mLastCommand = aOpcode;
|
|
}
|
|
|
|
SetObexPacketInfo(aData.get(), aOpcode, aSize);
|
|
mSocket->SendSocketData(new UnixSocketRawData(Move(aData), aSize));
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::FileTransferComplete()
|
|
{
|
|
if (mSendTransferCompleteFlag) {
|
|
return;
|
|
}
|
|
|
|
NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-complete");
|
|
InfallibleTArray<BluetoothNamedValue> parameters;
|
|
|
|
AppendNamedValue(parameters, "address", mDeviceAddress);
|
|
AppendNamedValue(parameters, "success", mSuccessFlag);
|
|
AppendNamedValue(parameters, "received", mIsServer);
|
|
AppendNamedValue(parameters, "fileName", mFileName);
|
|
AppendNamedValue(parameters, "fileLength", mSentFileLength);
|
|
AppendNamedValue(parameters, "contentType", mContentType);
|
|
|
|
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
|
|
|
|
mSendTransferCompleteFlag = true;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::StartFileTransfer()
|
|
{
|
|
NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-transfer-start");
|
|
InfallibleTArray<BluetoothNamedValue> parameters;
|
|
|
|
AppendNamedValue(parameters, "address", mDeviceAddress);
|
|
AppendNamedValue(parameters, "received", mIsServer);
|
|
AppendNamedValue(parameters, "fileName", mFileName);
|
|
AppendNamedValue(parameters, "fileLength", mFileLength);
|
|
AppendNamedValue(parameters, "contentType", mContentType);
|
|
|
|
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
|
|
|
|
mSendTransferCompleteFlag = false;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::UpdateProgress()
|
|
{
|
|
NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-update-progress");
|
|
InfallibleTArray<BluetoothNamedValue> parameters;
|
|
|
|
AppendNamedValue(parameters, "address", mDeviceAddress);
|
|
AppendNamedValue(parameters, "received", mIsServer);
|
|
AppendNamedValue(parameters, "processedLength", mSentFileLength);
|
|
AppendNamedValue(parameters, "fileLength", mFileLength);
|
|
|
|
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::ReceivingFileConfirmation()
|
|
{
|
|
NS_NAMED_LITERAL_STRING(type, "bluetooth-opp-receiving-file-confirmation");
|
|
InfallibleTArray<BluetoothNamedValue> parameters;
|
|
|
|
AppendNamedValue(parameters, "address", mDeviceAddress);
|
|
AppendNamedValue(parameters, "fileName", mFileName);
|
|
AppendNamedValue(parameters, "fileLength", mFileLength);
|
|
AppendNamedValue(parameters, "contentType", mContentType);
|
|
|
|
BT_ENSURE_TRUE_VOID_BROADCAST_SYSMSG(type, parameters);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::NotifyAboutFileChange()
|
|
{
|
|
NS_NAMED_LITERAL_STRING(data, "modified");
|
|
|
|
nsCOMPtr<nsIObserverService> obs =
|
|
mozilla::services::GetObserverService();
|
|
NS_ENSURE_TRUE_VOID(obs);
|
|
|
|
obs->NotifyObservers(mDsFile, "file-watcher-notify", data.get());
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
|
|
{
|
|
BT_LOGR("[%s]", (mIsServer)? "server" : "client");
|
|
MOZ_ASSERT(aSocket);
|
|
|
|
/**
|
|
* If the created connection is an inbound connection, close server socket
|
|
* because currently only one file-transfer session is allowed. After that,
|
|
* we need to make sure that server socket would be nulled out.
|
|
* As for outbound connections, we just notify the controller that it's done.
|
|
*/
|
|
if (aSocket == mServerSocket) {
|
|
MOZ_ASSERT(!mSocket);
|
|
mServerSocket.swap(mSocket);
|
|
}
|
|
|
|
// Cache device address since we can't get socket address when a remote
|
|
// device disconnect with us.
|
|
mSocket->GetAddress(mDeviceAddress);
|
|
|
|
// Start sending file if we connect as a client
|
|
if (!mIsServer) {
|
|
StartSendingNextFile();
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
|
|
{
|
|
BT_LOGR("[%s]", (mIsServer)? "server" : "client");
|
|
|
|
mServerSocket = nullptr;
|
|
mSocket = nullptr;
|
|
|
|
if (!mIsServer) {
|
|
// Inform gaia of remaining blobs' sending failure
|
|
DiscardBlobsToSend();
|
|
}
|
|
|
|
// Listen as a server if there's no more batch to process
|
|
if (!ProcessNextBatch()) {
|
|
Listen();
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
|
|
{
|
|
MOZ_ASSERT(aSocket);
|
|
if (aSocket != mSocket) {
|
|
// Do nothing when a listening server socket is closed.
|
|
return;
|
|
}
|
|
BT_LOGR("[%s]", (mIsServer) ? "server" : "client");
|
|
|
|
/**
|
|
* It is valid for a bluetooth device which is transfering file via OPP
|
|
* closing socket without sending OBEX disconnect request first. So we
|
|
* delete the broken file when we failed to receive a file from the remote,
|
|
* and notify the transfer has been completed (but failed). We also call
|
|
* AfterOppDisconnected here to ensure all variables will be cleaned.
|
|
*/
|
|
if (!mSuccessFlag) {
|
|
if (mIsServer) {
|
|
// Remove received file and inform gaia of receiving failure
|
|
DeleteReceivedFile();
|
|
FileTransferComplete();
|
|
} else {
|
|
// Inform gaia of current blob transfer failure
|
|
if (mCurrentBlobIndex >= 0) {
|
|
FileTransferComplete();
|
|
}
|
|
// Inform gaia of remaining blobs' sending failure
|
|
DiscardBlobsToSend();
|
|
}
|
|
}
|
|
|
|
AfterOppDisconnected();
|
|
mDeviceAddress.Clear();
|
|
mSuccessFlag = false;
|
|
|
|
mSocket = nullptr;
|
|
// Listen as a server if there's no more batch to process
|
|
if (!ProcessNextBatch()) {
|
|
Listen();
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
|
|
{
|
|
if (mSocket) {
|
|
mSocket->Close();
|
|
} else {
|
|
BT_WARNING("%s: No ongoing file transfer to stop", __FUNCTION__);
|
|
}
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(BluetoothOppManager, nsIObserver)
|
|
|
|
bool
|
|
BluetoothOppManager::AcquireSdcardMountLock()
|
|
{
|
|
nsCOMPtr<nsIVolumeService> volumeSrv =
|
|
do_GetService(NS_VOLUMESERVICE_CONTRACTID);
|
|
NS_ENSURE_TRUE(volumeSrv, false);
|
|
|
|
NS_ENSURE_SUCCESS(
|
|
volumeSrv->CreateMountLock(NS_LITERAL_STRING("sdcard"),
|
|
getter_AddRefs(mMountLock)),
|
|
false);
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::OnGetServiceChannel(
|
|
const BluetoothAddress& aDeviceAddress,
|
|
const BluetoothUuid& aServiceUuid,
|
|
int aChannel)
|
|
{
|
|
if (aChannel < 0) {
|
|
BluetoothService* bs = BluetoothService::Get();
|
|
if (!bs || sInShutdown) {
|
|
OnSocketConnectError(mSocket);
|
|
return;
|
|
}
|
|
|
|
if (mNeedsUpdatingSdpRecords) {
|
|
mNeedsUpdatingSdpRecords = false;
|
|
mLastServiceChannelCheck = TimeStamp::Now();
|
|
} else {
|
|
// Refresh SDP records until it gets valid service channel
|
|
// unless timeout is hit.
|
|
TimeDuration duration = TimeStamp::Now() - mLastServiceChannelCheck;
|
|
if (duration.ToMilliseconds() >= kSdpUpdatingTimeoutMs) {
|
|
OnSocketConnectError(mSocket);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!bs->UpdateSdpRecords(aDeviceAddress, this)) {
|
|
OnSocketConnectError(mSocket);
|
|
return;
|
|
}
|
|
}
|
|
|
|
mSocket->Connect(aDeviceAddress, aServiceUuid,
|
|
BluetoothSocketType::RFCOMM, aChannel,
|
|
false, true);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::OnUpdateSdpRecords(const BluetoothAddress& aDeviceAddress)
|
|
{
|
|
BluetoothService* bs = BluetoothService::Get();
|
|
if (!bs || sInShutdown) {
|
|
OnSocketConnectError(mSocket);
|
|
return;
|
|
}
|
|
|
|
if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, kObexObjectPush, this))) {
|
|
OnSocketConnectError(mSocket);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::Connect(const BluetoothAddress& aDeviceAddress,
|
|
BluetoothProfileController* aController)
|
|
{
|
|
MOZ_ASSERT(false);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::OnConnect(const nsAString& aErrorStr)
|
|
{
|
|
MOZ_ASSERT(false);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
|
|
{
|
|
MOZ_ASSERT(false);
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::Reset()
|
|
{
|
|
MOZ_ASSERT(false);
|
|
}
|
|
|
|
END_BLUETOOTH_NAMESPACE
|