/* -*- 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/BluetoothAdapter2Binding.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()); JS::Rooted JsDevices(cx); 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) // TODO: Change to Disabled after Bug 1006309 landed , mState(BluetoothAdapterState::Enabled) , 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("State")) { bool isEnabled = value.get_bool(); mState = isEnabled ? BluetoothAdapterState::Enabled : BluetoothAdapterState::Disabled; } else 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))) { 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))) { BT_WARNING("Cannot set JS Devices object!"); return; } mJsDeviceAddresses = deviceAddresses; Root(); } else { BT_WARNING("Not handling adapter property: %s", NS_ConvertUTF16toUTF8(name).get()); } } // 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 { BT_WARNING("Not handling adapter signal: %s", NS_ConvertUTF16toUTF8(aData.name()).get()); } } 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::EnableDisable(bool aEnable) { nsCOMPtr global = do_QueryInterface(GetOwner()); NS_ENSURE_TRUE(global, nullptr); nsRefPtr promise = new Promise(global); // Ensure BluetoothService is available before modifying adapter state BluetoothService* bs = BluetoothService::Get(); if (!bs) { promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); return promise.forget(); } nsString methodName; if (aEnable) { // Enable local adapter if (mState != BluetoothAdapterState::Disabled) { promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); return promise.forget(); } methodName.AssignLiteral("Enable"); mState = BluetoothAdapterState::Enabling; } else { // Disable local adapter if (mState != BluetoothAdapterState::Enabled) { promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); return promise.forget(); } methodName.AssignLiteral("Disable"); mState = BluetoothAdapterState::Disabling; } // TODO: Fire attr changed event for this state change nsRefPtr result = new BluetoothVoidReplyRunnable(nullptr, /* DOMRequest */ promise, methodName); if(NS_FAILED(bs->EnableDisable(aEnable, result))) { promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR); } return promise.forget(); } already_AddRefed BluetoothAdapter::Enable() { return EnableDisable(true); } already_AddRefed BluetoothAdapter::Disable() { return EnableDisable(false); } 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); }