diff -r 000000000000 -r 6474c204b198 dom/bluetooth/BluetoothAdapter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/bluetooth/BluetoothAdapter.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1042 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=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 "nsCxPusher.h" +#include "nsDOMClassInfo.h" +#include "nsTArrayHelpers.h" +#include "DOMRequest.h" +#include "nsThreadUtils.h" + +#include "mozilla/dom/bluetooth/BluetoothTypes.h" +#include "mozilla/dom/BluetoothAdapterBinding.h" +#include "mozilla/dom/BluetoothDeviceEvent.h" +#include "mozilla/dom/BluetoothStatusChangedEvent.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/LazyIdleThread.h" + +#include "BluetoothAdapter.h" +#include "BluetoothDevice.h" +#include "BluetoothReplyRunnable.h" +#include "BluetoothService.h" +#include "BluetoothUtils.h" + +using namespace mozilla; +using namespace mozilla::dom; + +USING_BLUETOOTH_NAMESPACE + +NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothAdapter) + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(BluetoothAdapter, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsUuids) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsDeviceAddresses) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothAdapter, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothAdapter, + DOMEventTargetHelper) + tmp->Unroot(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +// QueryInterface implementation for BluetoothAdapter +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothAdapter) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(BluetoothAdapter, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(BluetoothAdapter, DOMEventTargetHelper) + +class GetDevicesTask : public BluetoothReplyRunnable +{ +public: + GetDevicesTask(BluetoothAdapter* aAdapterPtr, + nsIDOMDOMRequest* aReq) : + BluetoothReplyRunnable(aReq), + mAdapterPtr(aAdapterPtr) + { + MOZ_ASSERT(aReq && aAdapterPtr); + } + + virtual bool ParseSuccessfulReply(JS::MutableHandle aValue) + { + aValue.setUndefined(); + + const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value(); + if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue) { + BT_WARNING("Not a BluetoothNamedValue array!"); + SetError(NS_LITERAL_STRING("BluetoothReplyTypeError")); + return false; + } + + const InfallibleTArray& values = + v.get_ArrayOfBluetoothNamedValue(); + + nsTArray > devices; + for (uint32_t i = 0; i < values.Length(); i++) { + const BluetoothValue properties = values[i].value(); + if (properties.type() != BluetoothValue::TArrayOfBluetoothNamedValue) { + BT_WARNING("Not a BluetoothNamedValue array!"); + SetError(NS_LITERAL_STRING("BluetoothReplyTypeError")); + return false; + } + nsRefPtr d = + BluetoothDevice::Create(mAdapterPtr->GetOwner(), + mAdapterPtr->GetPath(), + properties); + devices.AppendElement(d); + } + + nsresult rv; + nsIScriptContext* sc = mAdapterPtr->GetContextForEventHandlers(&rv); + if (!sc) { + BT_WARNING("Cannot create script context!"); + SetError(NS_LITERAL_STRING("BluetoothScriptContextError")); + return false; + } + + AutoPushJSContext cx(sc->GetNativeContext()); + JSObject* JsDevices = nullptr; + rv = nsTArrayToJSArray(cx, devices, &JsDevices); + if (!JsDevices) { + BT_WARNING("Cannot create JS array!"); + SetError(NS_LITERAL_STRING("BluetoothError")); + return false; + } + + aValue.setObject(*JsDevices); + return true; + } + + void + ReleaseMembers() + { + BluetoothReplyRunnable::ReleaseMembers(); + mAdapterPtr = nullptr; + } +private: + nsRefPtr mAdapterPtr; +}; + +class GetScoConnectionStatusTask : public BluetoothReplyRunnable +{ +public: + GetScoConnectionStatusTask(nsIDOMDOMRequest* aReq) : + BluetoothReplyRunnable(aReq) + { + MOZ_ASSERT(aReq); + } + + virtual bool ParseSuccessfulReply(JS::MutableHandle aValue) + { + aValue.setUndefined(); + + const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value(); + if (v.type() != BluetoothValue::Tbool) { + BT_WARNING("Not a boolean!"); + SetError(NS_LITERAL_STRING("BluetoothReplyTypeError")); + return false; + } + + aValue.setBoolean(v.get_bool()); + return true; + } + + void + ReleaseMembers() + { + BluetoothReplyRunnable::ReleaseMembers(); + } +}; + +static int kCreatePairedDeviceTimeout = 50000; // unit: msec + +BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow, + const BluetoothValue& aValue) + : DOMEventTargetHelper(aWindow) + , BluetoothPropertyContainer(BluetoothObjectType::TYPE_ADAPTER) + , mJsUuids(nullptr) + , mJsDeviceAddresses(nullptr) + , mDiscoverable(false) + , mDiscovering(false) + , mPairable(false) + , mPowered(false) + , mIsRooted(false) +{ + MOZ_ASSERT(aWindow); + MOZ_ASSERT(IsDOMBinding()); + + const InfallibleTArray& values = + aValue.get_ArrayOfBluetoothNamedValue(); + for (uint32_t i = 0; i < values.Length(); ++i) { + SetPropertyByValue(values[i]); + } + + BluetoothService* bs = BluetoothService::Get(); + NS_ENSURE_TRUE_VOID(bs); + bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this); +} + +BluetoothAdapter::~BluetoothAdapter() +{ + Unroot(); + BluetoothService* bs = BluetoothService::Get(); + // We can be null on shutdown, where this might happen + NS_ENSURE_TRUE_VOID(bs); + bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this); +} + +void +BluetoothAdapter::DisconnectFromOwner() +{ + DOMEventTargetHelper::DisconnectFromOwner(); + + BluetoothService* bs = BluetoothService::Get(); + NS_ENSURE_TRUE_VOID(bs); + bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this); +} + +void +BluetoothAdapter::Unroot() +{ + if (!mIsRooted) { + return; + } + mJsUuids = nullptr; + mJsDeviceAddresses = nullptr; + mozilla::DropJSObjects(this); + mIsRooted = false; +} + +void +BluetoothAdapter::Root() +{ + if (mIsRooted) { + return; + } + mozilla::HoldJSObjects(this); + mIsRooted = true; +} + +void +BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue) +{ + const nsString& name = aValue.name(); + const BluetoothValue& value = aValue.value(); + if (name.EqualsLiteral("Name")) { + mName = value.get_nsString(); + } else if (name.EqualsLiteral("Address")) { + mAddress = value.get_nsString(); + } else if (name.EqualsLiteral("Path")) { + mPath = value.get_nsString(); + } else if (name.EqualsLiteral("Discoverable")) { + mDiscoverable = value.get_bool(); + } else if (name.EqualsLiteral("Discovering")) { + mDiscovering = value.get_bool(); + } else if (name.EqualsLiteral("Pairable")) { + mPairable = value.get_bool(); + } else if (name.EqualsLiteral("Powered")) { + mPowered = value.get_bool(); + } else if (name.EqualsLiteral("PairableTimeout")) { + mPairableTimeout = value.get_uint32_t(); + } else if (name.EqualsLiteral("DiscoverableTimeout")) { + mDiscoverableTimeout = value.get_uint32_t(); + } else if (name.EqualsLiteral("Class")) { + mClass = value.get_uint32_t(); + } else if (name.EqualsLiteral("UUIDs")) { + mUuids = value.get_ArrayOfnsString(); + nsresult rv; + nsIScriptContext* sc = GetContextForEventHandlers(&rv); + NS_ENSURE_SUCCESS_VOID(rv); + NS_ENSURE_TRUE_VOID(sc); + + AutoPushJSContext cx(sc->GetNativeContext()); + JS::Rooted uuids(cx); + if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) { + BT_WARNING("Cannot set JS UUIDs object!"); + return; + } + mJsUuids = uuids; + Root(); + } else if (name.EqualsLiteral("Devices")) { + mDeviceAddresses = value.get_ArrayOfnsString(); + + nsresult rv; + nsIScriptContext* sc = GetContextForEventHandlers(&rv); + NS_ENSURE_SUCCESS_VOID(rv); + NS_ENSURE_TRUE_VOID(sc); + + AutoPushJSContext cx(sc->GetNativeContext()); + JS::Rooted deviceAddresses(cx); + if (NS_FAILED(nsTArrayToJSArray(cx, mDeviceAddresses, + deviceAddresses.address()))) { + BT_WARNING("Cannot set JS Devices object!"); + return; + } + mJsDeviceAddresses = deviceAddresses; + Root(); + } else { +#ifdef DEBUG + nsCString warningMsg; + warningMsg.AssignLiteral("Not handling adapter property: "); + warningMsg.Append(NS_ConvertUTF16toUTF8(name)); + BT_WARNING(warningMsg.get()); +#endif + } +} + +// static +already_AddRefed +BluetoothAdapter::Create(nsPIDOMWindow* aWindow, const BluetoothValue& aValue) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + + nsRefPtr adapter = new BluetoothAdapter(aWindow, aValue); + return adapter.forget(); +} + +void +BluetoothAdapter::Notify(const BluetoothSignal& aData) +{ + InfallibleTArray arr; + + BT_LOGD("[A] %s: %s", __FUNCTION__, NS_ConvertUTF16toUTF8(aData.name()).get()); + + BluetoothValue v = aData.value(); + if (aData.name().EqualsLiteral("DeviceFound")) { + nsRefPtr device = BluetoothDevice::Create(GetOwner(), mPath, aData.value()); + + BluetoothDeviceEventInit init; + init.mBubbles = false; + init.mCancelable = false; + init.mDevice = device; + nsRefPtr event = + BluetoothDeviceEvent::Constructor(this, NS_LITERAL_STRING("devicefound"), init); + DispatchTrustedEvent(event); + } else if (aData.name().EqualsLiteral("PropertyChanged")) { + MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue); + + const InfallibleTArray& arr = + v.get_ArrayOfBluetoothNamedValue(); + + for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) { + SetPropertyByValue(arr[i]); + } + } else if (aData.name().EqualsLiteral(PAIRED_STATUS_CHANGED_ID) || + aData.name().EqualsLiteral(HFP_STATUS_CHANGED_ID) || + aData.name().EqualsLiteral(SCO_STATUS_CHANGED_ID) || + aData.name().EqualsLiteral(A2DP_STATUS_CHANGED_ID)) { + MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue); + const InfallibleTArray& arr = + v.get_ArrayOfBluetoothNamedValue(); + + MOZ_ASSERT(arr.Length() == 2 && + arr[0].value().type() == BluetoothValue::TnsString && + arr[1].value().type() == BluetoothValue::Tbool); + nsString address = arr[0].value().get_nsString(); + bool status = arr[1].value().get_bool(); + + BluetoothStatusChangedEventInit init; + init.mBubbles = false; + init.mCancelable = false; + init.mAddress = address; + init.mStatus = status; + nsRefPtr event = + BluetoothStatusChangedEvent::Constructor(this, aData.name(), init); + DispatchTrustedEvent(event); + } else if (aData.name().EqualsLiteral(REQUEST_MEDIA_PLAYSTATUS_ID)) { + nsCOMPtr event; + nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr); + NS_ENSURE_SUCCESS_VOID(rv); + + rv = event->InitEvent(aData.name(), false, false); + NS_ENSURE_SUCCESS_VOID(rv); + + DispatchTrustedEvent(event); + } else { +#ifdef DEBUG + nsCString warningMsg; + warningMsg.AssignLiteral("Not handling adapter signal: "); + warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name())); + BT_WARNING(warningMsg.get()); +#endif + } +} + +already_AddRefed +BluetoothAdapter::StartStopDiscovery(bool aStart, ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + nsresult rv; + if (aStart) { + rv = bs->StartDiscoveryInternal(results); + } else { + rv = bs->StopDiscoveryInternal(results); + } + if (NS_FAILED(rv)) { + BT_WARNING("Start/Stop Discovery failed!"); + aRv.Throw(rv); + return nullptr; + } + + // mDiscovering is not set here, we'll get a Property update from our external + // protocol to tell us that it's been set. + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::StartDiscovery(ErrorResult& aRv) +{ + return StartStopDiscovery(true, aRv); +} + +already_AddRefed +BluetoothAdapter::StopDiscovery(ErrorResult& aRv) +{ + return StartStopDiscovery(false, aRv); +} + +void +BluetoothAdapter::GetDevices(JSContext* aContext, + JS::MutableHandle aDevices, + ErrorResult& aRv) +{ + if (!mJsDeviceAddresses) { + BT_WARNING("Devices not yet set!\n"); + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + JS::ExposeObjectToActiveJS(mJsDeviceAddresses); + aDevices.setObject(*mJsDeviceAddresses); +} + +void +BluetoothAdapter::GetUuids(JSContext* aContext, + JS::MutableHandle aUuids, + ErrorResult& aRv) +{ + if (!mJsUuids) { + BT_WARNING("UUIDs not yet set!\n"); + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + JS::ExposeObjectToActiveJS(mJsUuids); + aUuids.setObject(*mJsUuids); +} + +already_AddRefed +BluetoothAdapter::SetName(const nsAString& aName, ErrorResult& aRv) +{ + if (mName.Equals(aName)) { + return FirePropertyAlreadySet(GetOwner(), aRv); + } + nsString name(aName); + BluetoothValue value(name); + BluetoothNamedValue property(NS_LITERAL_STRING("Name"), value); + return SetProperty(GetOwner(), property, aRv); +} + +already_AddRefed +BluetoothAdapter::SetDiscoverable(bool aDiscoverable, ErrorResult& aRv) +{ + if (aDiscoverable == mDiscoverable) { + return FirePropertyAlreadySet(GetOwner(), aRv); + } + BluetoothValue value(aDiscoverable); + BluetoothNamedValue property(NS_LITERAL_STRING("Discoverable"), value); + return SetProperty(GetOwner(), property, aRv); +} + +already_AddRefed +BluetoothAdapter::SetDiscoverableTimeout(uint32_t aDiscoverableTimeout, ErrorResult& aRv) +{ + if (aDiscoverableTimeout == mDiscoverableTimeout) { + return FirePropertyAlreadySet(GetOwner(), aRv); + } + BluetoothValue value(aDiscoverableTimeout); + BluetoothNamedValue property(NS_LITERAL_STRING("DiscoverableTimeout"), value); + return SetProperty(GetOwner(), property, aRv); +} + +already_AddRefed +BluetoothAdapter::GetConnectedDevices(uint16_t aServiceUuid, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new GetDevicesTask(this, request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + nsresult rv = bs->GetConnectedDevicePropertiesInternal(aServiceUuid, results); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return nullptr; + } + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::GetPairedDevices(ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new GetDevicesTask(this, request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + nsresult rv = bs->GetPairedDevicePropertiesInternal(mDeviceAddresses, results); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return nullptr; + } + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::PairUnpair(bool aPair, const nsAString& aDeviceAddress, + ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + nsresult rv; + if (aPair) { + rv = bs->CreatePairedDeviceInternal(aDeviceAddress, + kCreatePairedDeviceTimeout, + results); + } else { + rv = bs->RemoveDeviceInternal(aDeviceAddress, results); + } + if (NS_FAILED(rv)) { + BT_WARNING("Pair/Unpair failed!"); + aRv.Throw(rv); + return nullptr; + } + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::Pair(const nsAString& aDeviceAddress, ErrorResult& aRv) +{ + return PairUnpair(true, aDeviceAddress, aRv); +} + +already_AddRefed +BluetoothAdapter::Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv) +{ + return PairUnpair(false, aDeviceAddress, aRv); +} + +already_AddRefed +BluetoothAdapter::SetPinCode(const nsAString& aDeviceAddress, + const nsAString& aPinCode, ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + if (!bs->SetPinCodeInternal(aDeviceAddress, aPinCode, results)) { + BT_WARNING("SetPinCode failed!"); + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::SetPasskey(const nsAString& aDeviceAddress, uint32_t aPasskey, + ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + if (bs->SetPasskeyInternal(aDeviceAddress, aPasskey, results)) { + BT_WARNING("SetPasskeyInternal failed!"); + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::SetPairingConfirmation(const nsAString& aDeviceAddress, + bool aConfirmation, ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + if (!bs->SetPairingConfirmationInternal(aDeviceAddress, + aConfirmation, + results)) { + BT_WARNING("SetPairingConfirmation failed!"); + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::Connect(BluetoothDevice& aDevice, + const Optional& aServiceUuid, + ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + nsAutoString address; + aDevice.GetAddress(address); + uint32_t deviceClass = aDevice.Class(); + uint16_t serviceUuid = 0; + if (aServiceUuid.WasPassed()) { + serviceUuid = aServiceUuid.Value(); + } + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->Connect(address, deviceClass, serviceUuid, results); + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::Disconnect(BluetoothDevice& aDevice, + const Optional& aServiceUuid, + ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + nsAutoString address; + aDevice.GetAddress(address); + uint16_t serviceUuid = 0; + if (aServiceUuid.WasPassed()) { + serviceUuid = aServiceUuid.Value(); + } + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->Disconnect(address, serviceUuid, results); + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::SendFile(const nsAString& aDeviceAddress, + nsIDOMBlob* aBlob, ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + if (XRE_GetProcessType() == GeckoProcessType_Default) { + // In-process transfer + bs->SendFile(aDeviceAddress, aBlob, results); + } else { + ContentChild *cc = ContentChild::GetSingleton(); + if (!cc) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + BlobChild* actor = cc->GetOrCreateActorForBlob(aBlob); + if (!actor) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + bs->SendFile(aDeviceAddress, nullptr, actor, results); + } + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::StopSendingFile(const nsAString& aDeviceAddress, ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->StopSendingFile(aDeviceAddress, results); + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::ConfirmReceivingFile(const nsAString& aDeviceAddress, + bool aConfirmation, ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->ConfirmReceivingFile(aDeviceAddress, aConfirmation, results); + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::ConnectSco(ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->ConnectSco(results); + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::DisconnectSco(ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->DisconnectSco(results); + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::IsScoConnected(ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new GetScoConnectionStatusTask(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->IsScoConnected(results); + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::AnswerWaitingCall(ErrorResult& aRv) +{ +#ifdef MOZ_B2G_RIL + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->AnswerWaitingCall(results); + + return request.forget(); +#else + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return nullptr; +#endif // MOZ_B2G_RIL +} + +already_AddRefed +BluetoothAdapter::IgnoreWaitingCall(ErrorResult& aRv) +{ +#ifdef MOZ_B2G_RIL + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->IgnoreWaitingCall(results); + + return request.forget(); +#else + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return nullptr; +#endif // MOZ_B2G_RIL +} + +already_AddRefed +BluetoothAdapter::ToggleCalls(ErrorResult& aRv) +{ +#ifdef MOZ_B2G_RIL + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->ToggleCalls(results); + + return request.forget(); +#else + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return nullptr; +#endif // MOZ_B2G_RIL +} + +already_AddRefed +BluetoothAdapter::SendMediaMetaData(const MediaMetaData& aMediaMetaData, ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->SendMetaData(aMediaMetaData.mTitle, + aMediaMetaData.mArtist, + aMediaMetaData.mAlbum, + aMediaMetaData.mMediaNumber, + aMediaMetaData.mTotalMediaCount, + aMediaMetaData.mDuration, + results); + + return request.forget(); +} + +already_AddRefed +BluetoothAdapter::SendMediaPlayStatus(const MediaPlayStatus& aMediaPlayStatus, ErrorResult& aRv) +{ + nsCOMPtr win = GetOwner(); + if (!win) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsRefPtr request = new DOMRequest(win); + nsRefPtr results = + new BluetoothVoidReplyRunnable(request); + + BluetoothService* bs = BluetoothService::Get(); + if (!bs) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + bs->SendPlayStatus(aMediaPlayStatus.mDuration, + aMediaPlayStatus.mPosition, + aMediaPlayStatus.mPlayStatus, + results); + + return request.forget(); +} + +JSObject* +BluetoothAdapter::WrapObject(JSContext* aCx) +{ + return BluetoothAdapterBinding::Wrap(aCx, this); +}