Bumping gaia.json for 4 gaia revision(s) a=gaia-bump
[gecko.git] / dom / bluetooth2 / BluetoothAdapter.cpp
blob16ffb395c6d4790fc0f0684231bb1fcd04c8d9ba
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "BluetoothReplyRunnable.h"
8 #include "BluetoothService.h"
9 #include "DOMRequest.h"
10 #include "nsIDocument.h"
11 #include "nsIPrincipal.h"
12 #include "nsTArrayHelpers.h"
14 #include "mozilla/dom/BluetoothAdapter2Binding.h"
15 #include "mozilla/dom/BluetoothAttributeEvent.h"
16 #include "mozilla/dom/BluetoothStatusChangedEvent.h"
17 #include "mozilla/dom/ContentChild.h"
18 #include "mozilla/dom/File.h"
20 #include "mozilla/dom/bluetooth/BluetoothAdapter.h"
21 #include "mozilla/dom/bluetooth/BluetoothClassOfDevice.h"
22 #include "mozilla/dom/bluetooth/BluetoothDevice.h"
23 #include "mozilla/dom/bluetooth/BluetoothDiscoveryHandle.h"
24 #include "mozilla/dom/bluetooth/BluetoothPairingListener.h"
25 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
27 using namespace mozilla;
28 using namespace mozilla::dom;
30 USING_BLUETOOTH_NAMESPACE
32 NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothAdapter, DOMEventTargetHelper,
33 mDevices, mDiscoveryHandleInUse,
34 mPairingReqs)
36 // QueryInterface implementation for BluetoothAdapter
37 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothAdapter)
38 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
40 NS_IMPL_ADDREF_INHERITED(BluetoothAdapter, DOMEventTargetHelper)
41 NS_IMPL_RELEASE_INHERITED(BluetoothAdapter, DOMEventTargetHelper)
43 class StartDiscoveryTask MOZ_FINAL : public BluetoothReplyRunnable
45 public:
46 StartDiscoveryTask(BluetoothAdapter* aAdapter, Promise* aPromise)
47 : BluetoothReplyRunnable(nullptr, aPromise,
48 NS_LITERAL_STRING("StartDiscovery"))
49 , mAdapter(aAdapter)
51 MOZ_ASSERT(aPromise);
52 MOZ_ASSERT(aAdapter);
55 bool
56 ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
58 aValue.setUndefined();
60 AutoJSAPI jsapi;
61 NS_ENSURE_TRUE(jsapi.Init(mAdapter->GetParentObject()), false);
62 JSContext* cx = jsapi.cx();
64 /**
65 * Create a new discovery handle and wrap it to return. Each
66 * discovery handle is one-time-use only.
68 nsRefPtr<BluetoothDiscoveryHandle> discoveryHandle =
69 BluetoothDiscoveryHandle::Create(mAdapter->GetParentObject());
70 if (!ToJSValue(cx, discoveryHandle, aValue)) {
71 JS_ClearPendingException(cx);
72 return false;
75 // Set the created discovery handle as the one in use.
76 mAdapter->SetDiscoveryHandleInUse(discoveryHandle);
77 return true;
80 virtual void
81 ReleaseMembers() MOZ_OVERRIDE
83 BluetoothReplyRunnable::ReleaseMembers();
84 mAdapter = nullptr;
87 private:
88 nsRefPtr<BluetoothAdapter> mAdapter;
91 class GetDevicesTask : public BluetoothReplyRunnable
93 public:
94 GetDevicesTask(BluetoothAdapter* aAdapterPtr, nsIDOMDOMRequest* aReq)
95 : BluetoothReplyRunnable(aReq)
96 , mAdapterPtr(aAdapterPtr)
98 MOZ_ASSERT(aReq && aAdapterPtr);
101 virtual bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
103 aValue.setUndefined();
105 const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
106 if (v.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
107 BT_WARNING("Not a BluetoothNamedValue array!");
108 SetError(NS_LITERAL_STRING("BluetoothReplyTypeError"));
109 return false;
112 const InfallibleTArray<BluetoothNamedValue>& values =
113 v.get_ArrayOfBluetoothNamedValue();
115 nsTArray<nsRefPtr<BluetoothDevice> > devices;
116 for (uint32_t i = 0; i < values.Length(); i++) {
117 const BluetoothValue properties = values[i].value();
118 if (properties.type() != BluetoothValue::TArrayOfBluetoothNamedValue) {
119 BT_WARNING("Not a BluetoothNamedValue array!");
120 SetError(NS_LITERAL_STRING("BluetoothReplyTypeError"));
121 return false;
123 nsRefPtr<BluetoothDevice> d =
124 BluetoothDevice::Create(mAdapterPtr->GetOwner(),
125 properties);
126 devices.AppendElement(d);
129 AutoJSAPI jsapi;
130 if (!jsapi.Init(mAdapterPtr->GetOwner())) {
131 BT_WARNING("Failed to initialise AutoJSAPI!");
132 SetError(NS_LITERAL_STRING("BluetoothAutoJSAPIInitError"));
133 return false;
135 JSContext* cx = jsapi.cx();
136 JS::Rooted<JSObject*> JsDevices(cx);
137 if (NS_FAILED(nsTArrayToJSArray(cx, devices, &JsDevices))) {
138 BT_WARNING("Cannot create JS array!");
139 SetError(NS_LITERAL_STRING("BluetoothError"));
140 return false;
143 aValue.setObject(*JsDevices);
144 return true;
147 void
148 ReleaseMembers()
150 BluetoothReplyRunnable::ReleaseMembers();
151 mAdapterPtr = nullptr;
154 private:
155 nsRefPtr<BluetoothAdapter> mAdapterPtr;
158 class GetScoConnectionStatusTask : public BluetoothReplyRunnable
160 public:
161 GetScoConnectionStatusTask(nsIDOMDOMRequest* aReq) :
162 BluetoothReplyRunnable(aReq)
164 MOZ_ASSERT(aReq);
167 virtual bool ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue)
169 aValue.setUndefined();
171 const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
172 if (v.type() != BluetoothValue::Tbool) {
173 BT_WARNING("Not a boolean!");
174 SetError(NS_LITERAL_STRING("BluetoothReplyTypeError"));
175 return false;
178 aValue.setBoolean(v.get_bool());
179 return true;
182 void
183 ReleaseMembers()
185 BluetoothReplyRunnable::ReleaseMembers();
189 static int kCreatePairedDeviceTimeout = 50000; // unit: msec
191 BluetoothAdapter::BluetoothAdapter(nsPIDOMWindow* aWindow,
192 const BluetoothValue& aValue)
193 : DOMEventTargetHelper(aWindow)
194 , mState(BluetoothAdapterState::Disabled)
195 , mDiscoverable(false)
196 , mDiscovering(false)
197 , mPairingReqs(nullptr)
198 , mDiscoveryHandleInUse(nullptr)
200 MOZ_ASSERT(aWindow);
202 // Only allow certified bluetooth application to receive pairing requests
203 if (IsBluetoothCertifiedApp()) {
204 mPairingReqs = BluetoothPairingListener::Create(aWindow);
207 const InfallibleTArray<BluetoothNamedValue>& values =
208 aValue.get_ArrayOfBluetoothNamedValue();
209 for (uint32_t i = 0; i < values.Length(); ++i) {
210 SetPropertyByValue(values[i]);
213 BluetoothService* bs = BluetoothService::Get();
214 NS_ENSURE_TRUE_VOID(bs);
215 bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
218 BluetoothAdapter::~BluetoothAdapter()
220 BluetoothService* bs = BluetoothService::Get();
221 // We can be null on shutdown, where this might happen
222 NS_ENSURE_TRUE_VOID(bs);
223 bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
226 void
227 BluetoothAdapter::DisconnectFromOwner()
229 DOMEventTargetHelper::DisconnectFromOwner();
231 BluetoothService* bs = BluetoothService::Get();
232 NS_ENSURE_TRUE_VOID(bs);
233 bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
236 void
237 BluetoothAdapter::GetPairedDeviceProperties(
238 const nsTArray<nsString>& aDeviceAddresses)
240 BluetoothService* bs = BluetoothService::Get();
241 NS_ENSURE_TRUE_VOID(bs);
243 nsRefPtr<BluetoothVoidReplyRunnable> results =
244 new BluetoothVoidReplyRunnable(nullptr);
246 nsresult rv =
247 bs->GetPairedDevicePropertiesInternal(aDeviceAddresses, results);
248 if (NS_FAILED(rv)) {
249 BT_WARNING("GetPairedDeviceProperties failed");
253 void
254 BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
256 const nsString& name = aValue.name();
257 const BluetoothValue& value = aValue.value();
258 if (name.EqualsLiteral("State")) {
259 mState = value.get_bool() ? BluetoothAdapterState::Enabled
260 : BluetoothAdapterState::Disabled;
262 // Clear saved devices when state changes to disabled
263 if (mState == BluetoothAdapterState::Disabled) {
264 mDevices.Clear();
266 } else if (name.EqualsLiteral("Name")) {
267 mName = value.get_nsString();
268 } else if (name.EqualsLiteral("Address")) {
269 mAddress = value.get_nsString();
270 } else if (name.EqualsLiteral("Discoverable")) {
271 mDiscoverable = value.get_bool();
272 } else if (name.EqualsLiteral("Discovering")) {
273 mDiscovering = value.get_bool();
274 if (!mDiscovering) {
275 // Reset discovery handle in use to nullptr
276 SetDiscoveryHandleInUse(nullptr);
278 } else if (name.EqualsLiteral("PairedDevices")) {
279 const InfallibleTArray<nsString>& pairedDeviceAddresses
280 = value.get_ArrayOfnsString();
282 for (uint32_t i = 0; i < pairedDeviceAddresses.Length(); i++) {
283 InfallibleTArray<BluetoothNamedValue> props;
284 BT_APPEND_NAMED_VALUE(props, "Address", pairedDeviceAddresses[i]);
285 BT_APPEND_NAMED_VALUE(props, "Paired", true);
287 // Create paired device with 'address' and 'paired' attributes
288 nsRefPtr<BluetoothDevice> pairedDevice =
289 BluetoothDevice::Create(GetOwner(), BluetoothValue(props));
291 // Append to adapter's device array if the device hasn't been created
292 if (!mDevices.Contains(pairedDevice)) {
293 mDevices.AppendElement(pairedDevice);
297 // Retrieve device properties, result will be handled by device objects.
298 GetPairedDeviceProperties(pairedDeviceAddresses);
299 } else {
300 BT_WARNING("Not handling adapter property: %s",
301 NS_ConvertUTF16toUTF8(name).get());
305 // static
306 already_AddRefed<BluetoothAdapter>
307 BluetoothAdapter::Create(nsPIDOMWindow* aWindow, const BluetoothValue& aValue)
309 MOZ_ASSERT(NS_IsMainThread());
310 MOZ_ASSERT(aWindow);
312 nsRefPtr<BluetoothAdapter> adapter = new BluetoothAdapter(aWindow, aValue);
313 return adapter.forget();
316 void
317 BluetoothAdapter::Notify(const BluetoothSignal& aData)
319 InfallibleTArray<BluetoothNamedValue> arr;
321 BT_LOGD("[A] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
323 BluetoothValue v = aData.value();
324 if (aData.name().EqualsLiteral("PropertyChanged")) {
325 HandlePropertyChanged(v);
326 } else if (aData.name().EqualsLiteral("DeviceFound")) {
328 * DeviceFound signal will be distributed to all existing adapters while
329 * doing discovery operations.
330 * The signal needs to be handled only if this adapter is holding a valid
331 * discovery handle, which means that the discovery operation is triggered
332 * by this adapter.
334 if (mDiscoveryHandleInUse) {
335 HandleDeviceFound(v);
337 } else if (aData.name().EqualsLiteral(DEVICE_PAIRED_ID)) {
338 HandleDevicePaired(aData.value());
339 } else if (aData.name().EqualsLiteral(DEVICE_UNPAIRED_ID)) {
340 HandleDeviceUnpaired(aData.value());
341 } else if (aData.name().EqualsLiteral(HFP_STATUS_CHANGED_ID) ||
342 aData.name().EqualsLiteral(SCO_STATUS_CHANGED_ID) ||
343 aData.name().EqualsLiteral(A2DP_STATUS_CHANGED_ID)) {
344 MOZ_ASSERT(v.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
345 const InfallibleTArray<BluetoothNamedValue>& arr =
346 v.get_ArrayOfBluetoothNamedValue();
348 MOZ_ASSERT(arr.Length() == 2 &&
349 arr[0].value().type() == BluetoothValue::TnsString &&
350 arr[1].value().type() == BluetoothValue::Tbool);
351 nsString address = arr[0].value().get_nsString();
352 bool status = arr[1].value().get_bool();
354 BluetoothStatusChangedEventInit init;
355 init.mBubbles = false;
356 init.mCancelable = false;
357 init.mAddress = address;
358 init.mStatus = status;
359 nsRefPtr<BluetoothStatusChangedEvent> event =
360 BluetoothStatusChangedEvent::Constructor(this, aData.name(), init);
361 DispatchTrustedEvent(event);
362 } else if (aData.name().EqualsLiteral(REQUEST_MEDIA_PLAYSTATUS_ID)) {
363 nsCOMPtr<nsIDOMEvent> event;
364 nsresult rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
365 NS_ENSURE_SUCCESS_VOID(rv);
367 rv = event->InitEvent(aData.name(), false, false);
368 NS_ENSURE_SUCCESS_VOID(rv);
370 DispatchTrustedEvent(event);
371 } else {
372 BT_WARNING("Not handling adapter signal: %s",
373 NS_ConvertUTF16toUTF8(aData.name()).get());
377 void
378 BluetoothAdapter::SetDiscoveryHandleInUse(
379 BluetoothDiscoveryHandle* aDiscoveryHandle)
381 // Stop discovery handle in use from listening to "DeviceFound" signal
382 if (mDiscoveryHandleInUse) {
383 mDiscoveryHandleInUse->DisconnectFromOwner();
386 mDiscoveryHandleInUse = aDiscoveryHandle;
389 already_AddRefed<Promise>
390 BluetoothAdapter::StartDiscovery(ErrorResult& aRv)
392 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
393 if (!global) {
394 aRv.Throw(NS_ERROR_FAILURE);
395 return nullptr;
398 nsRefPtr<Promise> promise = Promise::Create(global, aRv);
399 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
402 * Ensure
403 * - adapter is not discovering (note we reject here to ensure
404 each resolved promise returns a new BluetoothDiscoveryHandle),
405 * - adapter is already enabled, and
406 * - BluetoothService is available
408 BT_ENSURE_TRUE_REJECT(!mDiscovering, NS_ERROR_DOM_INVALID_STATE_ERR);
409 BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
410 NS_ERROR_DOM_INVALID_STATE_ERR);
411 BluetoothService* bs = BluetoothService::Get();
412 BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
414 BT_API2_LOGR();
416 // Clear unpaired devices before start discovery
417 for (int32_t i = mDevices.Length() - 1; i >= 0; i--) {
418 if (!mDevices[i]->Paired()) {
419 mDevices.RemoveElementAt(i);
423 // Return BluetoothDiscoveryHandle in StartDiscoveryTask
424 nsRefPtr<BluetoothReplyRunnable> result =
425 new StartDiscoveryTask(this, promise);
426 BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(bs->StartDiscoveryInternal(result)),
427 NS_ERROR_DOM_OPERATION_ERR);
429 return promise.forget();
432 already_AddRefed<Promise>
433 BluetoothAdapter::StopDiscovery(ErrorResult& aRv)
435 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
436 if (!global) {
437 aRv.Throw(NS_ERROR_FAILURE);
438 return nullptr;
441 nsRefPtr<Promise> promise = Promise::Create(global, aRv);
442 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
445 * Ensure
446 * - adapter is discovering,
447 * - adapter is already enabled, and
448 * - BluetoothService is available
450 BT_ENSURE_TRUE_RESOLVE(mDiscovering, JS::UndefinedHandleValue);
451 BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
452 NS_ERROR_DOM_INVALID_STATE_ERR);
453 BluetoothService* bs = BluetoothService::Get();
454 BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
456 BT_API2_LOGR();
458 nsRefPtr<BluetoothReplyRunnable> result =
459 new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
460 promise,
461 NS_LITERAL_STRING("StopDiscovery"));
462 BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(bs->StopDiscoveryInternal(result)),
463 NS_ERROR_DOM_OPERATION_ERR);
465 return promise.forget();
468 already_AddRefed<Promise>
469 BluetoothAdapter::SetName(const nsAString& aName, ErrorResult& aRv)
471 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
472 if (!global) {
473 aRv.Throw(NS_ERROR_FAILURE);
474 return nullptr;
477 nsRefPtr<Promise> promise = Promise::Create(global, aRv);
478 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
481 * Ensure
482 * - adapter's name does not equal to aName,
483 * - adapter is already enabled, and
484 * - BluetoothService is available
486 BT_ENSURE_TRUE_RESOLVE(!mName.Equals(aName), JS::UndefinedHandleValue);
487 BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
488 NS_ERROR_DOM_INVALID_STATE_ERR);
489 BluetoothService* bs = BluetoothService::Get();
490 BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
492 // Wrap property to set and runnable to handle result
493 nsString name(aName);
494 BluetoothNamedValue property(NS_LITERAL_STRING("Name"),
495 BluetoothValue(name));
496 nsRefPtr<BluetoothReplyRunnable> result =
497 new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
498 promise,
499 NS_LITERAL_STRING("SetName"));
500 BT_ENSURE_TRUE_REJECT(
501 NS_SUCCEEDED(bs->SetProperty(BluetoothObjectType::TYPE_ADAPTER,
502 property, result)),
503 NS_ERROR_DOM_OPERATION_ERR);
505 return promise.forget();
508 already_AddRefed<Promise>
509 BluetoothAdapter::SetDiscoverable(bool aDiscoverable, ErrorResult& aRv)
511 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
512 if (!global) {
513 aRv.Throw(NS_ERROR_FAILURE);
514 return nullptr;
517 nsRefPtr<Promise> promise = Promise::Create(global, aRv);
518 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
521 * Ensure
522 * - mDiscoverable does not equal to aDiscoverable,
523 * - adapter is already enabled, and
524 * - BluetoothService is available
526 BT_ENSURE_TRUE_RESOLVE(mDiscoverable != aDiscoverable,
527 JS::UndefinedHandleValue);
528 BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
529 NS_ERROR_DOM_INVALID_STATE_ERR);
530 BluetoothService* bs = BluetoothService::Get();
531 BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
533 // Wrap property to set and runnable to handle result
534 BluetoothNamedValue property(NS_LITERAL_STRING("Discoverable"),
535 BluetoothValue(aDiscoverable));
536 nsRefPtr<BluetoothReplyRunnable> result =
537 new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
538 promise,
539 NS_LITERAL_STRING("SetDiscoverable"));
540 BT_ENSURE_TRUE_REJECT(
541 NS_SUCCEEDED(bs->SetProperty(BluetoothObjectType::TYPE_ADAPTER,
542 property, result)),
543 NS_ERROR_DOM_OPERATION_ERR);
545 return promise.forget();
548 already_AddRefed<DOMRequest>
549 BluetoothAdapter::GetConnectedDevices(uint16_t aServiceUuid, ErrorResult& aRv)
551 MOZ_ASSERT(NS_IsMainThread());
553 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
554 if (!win) {
555 aRv.Throw(NS_ERROR_FAILURE);
556 return nullptr;
559 nsRefPtr<DOMRequest> request = new DOMRequest(win);
560 nsRefPtr<BluetoothReplyRunnable> results =
561 new GetDevicesTask(this, request);
563 BluetoothService* bs = BluetoothService::Get();
564 if (!bs) {
565 aRv.Throw(NS_ERROR_FAILURE);
566 return nullptr;
568 nsresult rv = bs->GetConnectedDevicePropertiesInternal(aServiceUuid, results);
569 if (NS_FAILED(rv)) {
570 aRv.Throw(rv);
571 return nullptr;
574 return request.forget();
577 void
578 BluetoothAdapter::GetPairedDevices(nsTArray<nsRefPtr<BluetoothDevice> >& aDevices)
580 for (uint32_t i = 0; i < mDevices.Length(); ++i) {
581 if (mDevices[i]->Paired()) {
582 aDevices.AppendElement(mDevices[i]);
587 already_AddRefed<Promise>
588 BluetoothAdapter::PairUnpair(bool aPair, const nsAString& aDeviceAddress,
589 ErrorResult& aRv)
591 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
592 if (!global) {
593 aRv.Throw(NS_ERROR_FAILURE);
594 return nullptr;
597 nsRefPtr<Promise> promise = Promise::Create(global, aRv);
598 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
601 * Ensure
602 * - device address is not empty,
603 * - adapter is already enabled, and
604 * - BluetoothService is available.
606 BT_ENSURE_TRUE_REJECT(!aDeviceAddress.IsEmpty(),
607 NS_ERROR_DOM_INVALID_STATE_ERR);
608 BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
609 NS_ERROR_DOM_INVALID_STATE_ERR);
610 BluetoothService* bs = BluetoothService::Get();
611 BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
613 nsresult rv;
614 if (aPair) {
615 nsRefPtr<BluetoothReplyRunnable> result =
616 new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
617 promise,
618 NS_LITERAL_STRING("Pair"));
619 rv = bs->CreatePairedDeviceInternal(aDeviceAddress,
620 kCreatePairedDeviceTimeout,
621 result);
622 } else {
623 nsRefPtr<BluetoothReplyRunnable> result =
624 new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
625 promise,
626 NS_LITERAL_STRING("Unpair"));
627 rv = bs->RemoveDeviceInternal(aDeviceAddress, result);
629 BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(rv), NS_ERROR_DOM_OPERATION_ERR);
631 return promise.forget();
634 already_AddRefed<Promise>
635 BluetoothAdapter::Pair(const nsAString& aDeviceAddress, ErrorResult& aRv)
637 return PairUnpair(true, aDeviceAddress, aRv);
640 already_AddRefed<Promise>
641 BluetoothAdapter::Unpair(const nsAString& aDeviceAddress, ErrorResult& aRv)
643 return PairUnpair(false, aDeviceAddress, aRv);
646 already_AddRefed<Promise>
647 BluetoothAdapter::Enable(ErrorResult& aRv)
649 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
650 if (!global) {
651 aRv.Throw(NS_ERROR_FAILURE);
652 return nullptr;
655 nsRefPtr<Promise> promise = Promise::Create(global, aRv);
656 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
659 * Ensure
660 * - adapter is disabled, and
661 * - BluetoothService is available.
663 BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Disabled,
664 NS_ERROR_DOM_INVALID_STATE_ERR);
665 BluetoothService* bs = BluetoothService::Get();
666 BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
668 // Set adapter state "Enabling"
669 SetAdapterState(BluetoothAdapterState::Enabling);
671 // Wrap runnable to handle result
672 nsRefPtr<BluetoothReplyRunnable> result =
673 new BluetoothVoidReplyRunnable(nullptr, /* DOMRequest */
674 promise,
675 NS_LITERAL_STRING("Enable"));
677 if (NS_FAILED(bs->EnableDisable(true, result))) {
678 // Restore adapter state and reject promise
679 SetAdapterState(BluetoothAdapterState::Disabled);
680 promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
683 return promise.forget();
686 already_AddRefed<Promise>
687 BluetoothAdapter::Disable(ErrorResult& aRv)
689 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
690 if (!global) {
691 aRv.Throw(NS_ERROR_FAILURE);
692 return nullptr;
695 nsRefPtr<Promise> promise = Promise::Create(global, aRv);
696 NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
699 * Ensure
700 * - adapter is enabled, and
701 * - BluetoothService is available.
703 BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
704 NS_ERROR_DOM_INVALID_STATE_ERR);
705 BluetoothService* bs = BluetoothService::Get();
706 BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
708 // Set adapter state "Disabling"
709 SetAdapterState(BluetoothAdapterState::Disabling);
711 // Wrap runnable to handle result
712 nsRefPtr<BluetoothReplyRunnable> result =
713 new BluetoothVoidReplyRunnable(nullptr, /* DOMRequest */
714 promise,
715 NS_LITERAL_STRING("Disable"));
717 if (NS_FAILED(bs->EnableDisable(false, result))) {
718 // Restore adapter state and reject promise
719 SetAdapterState(BluetoothAdapterState::Enabled);
720 promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
723 return promise.forget();
726 BluetoothAdapterAttribute
727 BluetoothAdapter::ConvertStringToAdapterAttribute(const nsAString& aString)
729 using namespace
730 mozilla::dom::BluetoothAdapterAttributeValues;
732 for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
733 if (aString.LowerCaseEqualsASCII(strings[index].value,
734 strings[index].length)) {
735 return static_cast<BluetoothAdapterAttribute>(index);
738 return BluetoothAdapterAttribute::Unknown;
741 bool
742 BluetoothAdapter::IsAdapterAttributeChanged(BluetoothAdapterAttribute aType,
743 const BluetoothValue& aValue)
745 switch(aType) {
746 case BluetoothAdapterAttribute::State:
747 MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
748 return aValue.get_bool() ? mState != BluetoothAdapterState::Enabled
749 : mState != BluetoothAdapterState::Disabled;
750 case BluetoothAdapterAttribute::Name:
751 MOZ_ASSERT(aValue.type() == BluetoothValue::TnsString);
752 return !mName.Equals(aValue.get_nsString());
753 case BluetoothAdapterAttribute::Address:
754 MOZ_ASSERT(aValue.type() == BluetoothValue::TnsString);
755 return !mAddress.Equals(aValue.get_nsString());
756 case BluetoothAdapterAttribute::Discoverable:
757 MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
758 return mDiscoverable != aValue.get_bool();
759 case BluetoothAdapterAttribute::Discovering:
760 MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
761 return mDiscovering != aValue.get_bool();
762 default:
763 BT_WARNING("Type %d is not handled", uint32_t(aType));
764 return false;
768 bool
769 BluetoothAdapter::IsBluetoothCertifiedApp()
771 // Retrieve the app status and origin for permission checking
772 nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
773 NS_ENSURE_TRUE(doc, false);
775 uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
776 nsAutoCString appOrigin;
778 doc->NodePrincipal()->GetAppStatus(&appStatus);
779 doc->NodePrincipal()->GetOrigin(getter_Copies(appOrigin));
781 return appStatus == nsIPrincipal::APP_STATUS_CERTIFIED &&
782 appOrigin.EqualsLiteral(BLUETOOTH_APP_ORIGIN);
785 void
786 BluetoothAdapter::SetAdapterState(BluetoothAdapterState aState)
788 if (mState == aState) {
789 return;
792 mState = aState;
794 // Fire BluetoothAttributeEvent for changed adapter state
795 nsTArray<nsString> types;
796 BT_APPEND_ENUM_STRING(types,
797 BluetoothAdapterAttribute,
798 BluetoothAdapterAttribute::State);
799 DispatchAttributeEvent(types);
802 void
803 BluetoothAdapter::HandlePropertyChanged(const BluetoothValue& aValue)
805 MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
807 const InfallibleTArray<BluetoothNamedValue>& arr =
808 aValue.get_ArrayOfBluetoothNamedValue();
810 nsTArray<nsString> types;
811 for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
812 BluetoothAdapterAttribute type =
813 ConvertStringToAdapterAttribute(arr[i].name());
815 // Non-BluetoothAdapterAttribute properties
816 if (type == BluetoothAdapterAttribute::Unknown) {
817 SetPropertyByValue(arr[i]);
818 continue;
821 // BluetoothAdapterAttribute properties
822 if (IsAdapterAttributeChanged(type, arr[i].value())) {
823 SetPropertyByValue(arr[i]);
824 BT_APPEND_ENUM_STRING(types, BluetoothAdapterAttribute, type);
828 DispatchAttributeEvent(types);
831 void
832 BluetoothAdapter::HandleDeviceFound(const BluetoothValue& aValue)
834 MOZ_ASSERT(mDiscoveryHandleInUse);
835 MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
837 // Create a temporary discovered BluetoothDevice to check existence
838 nsRefPtr<BluetoothDevice> discoveredDevice =
839 BluetoothDevice::Create(GetOwner(), aValue);
841 size_t index = mDevices.IndexOf(discoveredDevice);
842 if (index == mDevices.NoIndex) {
843 // New device, append it to adapter's device array
844 mDevices.AppendElement(discoveredDevice);
845 } else {
846 // Existing device, discard temporary discovered device
847 discoveredDevice = mDevices[index];
850 // Notify application of discovered device via discovery handle
851 mDiscoveryHandleInUse->DispatchDeviceEvent(discoveredDevice);
854 void
855 BluetoothAdapter::DispatchAttributeEvent(const nsTArray<nsString>& aTypes)
857 NS_ENSURE_TRUE_VOID(aTypes.Length());
859 AutoJSAPI jsapi;
860 NS_ENSURE_TRUE_VOID(jsapi.Init(GetOwner()));
861 JSContext* cx = jsapi.cx();
862 JS::Rooted<JS::Value> value(cx);
864 if (!ToJSValue(cx, aTypes, &value)) {
865 JS_ClearPendingException(cx);
866 return;
869 RootedDictionary<BluetoothAttributeEventInit> init(cx);
870 init.mAttrs = value;
871 nsRefPtr<BluetoothAttributeEvent> event =
872 BluetoothAttributeEvent::Constructor(this,
873 NS_LITERAL_STRING("attributechanged"),
874 init);
875 DispatchTrustedEvent(event);
878 void
879 BluetoothAdapter::HandleDevicePaired(const BluetoothValue& aValue)
881 MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
883 if (mState != BluetoothAdapterState::Enabled) {
884 BT_WARNING("HandleDevicePaired() is called when adapter isn't enabled.");
885 return;
888 // Create paired device with 'address' and 'paired' attributes
889 nsRefPtr<BluetoothDevice> pairedDevice =
890 BluetoothDevice::Create(GetOwner(), aValue);
892 size_t index = mDevices.IndexOf(pairedDevice);
893 if (index == mDevices.NoIndex) {
894 mDevices.AppendElement(pairedDevice);
895 } else {
896 pairedDevice = mDevices[index];
899 // Notify application of paired device
900 BluetoothDeviceEventInit init;
901 init.mDevice = pairedDevice;
902 DispatchDeviceEvent(NS_LITERAL_STRING("devicepaired"), init);
905 void
906 BluetoothAdapter::HandleDeviceUnpaired(const BluetoothValue& aValue)
908 MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
910 if (mState != BluetoothAdapterState::Enabled) {
911 BT_WARNING("HandleDeviceUnpaired() is called when adapter isn't enabled.");
912 return;
915 // Create unpaired device with 'address' and 'paired' attributes
916 nsRefPtr<BluetoothDevice> unpairedDevice =
917 BluetoothDevice::Create(GetOwner(), aValue);
919 size_t index = mDevices.IndexOf(unpairedDevice);
921 nsString deviceAddress;
922 if (index == mDevices.NoIndex) {
923 unpairedDevice->GetAddress(deviceAddress);
924 } else {
925 mDevices[index]->GetAddress(deviceAddress);
926 mDevices.RemoveElementAt(index);
929 // Notify application of unpaired device
930 BluetoothDeviceEventInit init;
931 init.mAddress = deviceAddress;
932 DispatchDeviceEvent(NS_LITERAL_STRING("deviceunpaired"), init);
935 void
936 BluetoothAdapter::DispatchDeviceEvent(const nsAString& aType,
937 const BluetoothDeviceEventInit& aInit)
939 BT_API2_LOGR("aType (%s)", NS_ConvertUTF16toUTF8(aType).get());
941 nsRefPtr<BluetoothDeviceEvent> event =
942 BluetoothDeviceEvent::Constructor(this, aType, aInit);
943 DispatchTrustedEvent(event);
946 already_AddRefed<DOMRequest>
947 BluetoothAdapter::Connect(BluetoothDevice& aDevice,
948 const Optional<short unsigned int>& aServiceUuid,
949 ErrorResult& aRv)
951 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
952 if (!win) {
953 aRv.Throw(NS_ERROR_FAILURE);
954 return nullptr;
957 nsRefPtr<DOMRequest> request = new DOMRequest(win);
958 nsRefPtr<BluetoothVoidReplyRunnable> results =
959 new BluetoothVoidReplyRunnable(request);
961 nsAutoString address;
962 aDevice.GetAddress(address);
963 uint32_t deviceClass = aDevice.Cod()->ToUint32();
964 uint16_t serviceUuid = 0;
965 if (aServiceUuid.WasPassed()) {
966 serviceUuid = aServiceUuid.Value();
969 BluetoothService* bs = BluetoothService::Get();
970 if (!bs) {
971 aRv.Throw(NS_ERROR_FAILURE);
972 return nullptr;
974 bs->Connect(address, deviceClass, serviceUuid, results);
976 return request.forget();
979 already_AddRefed<DOMRequest>
980 BluetoothAdapter::Disconnect(BluetoothDevice& aDevice,
981 const Optional<short unsigned int>& aServiceUuid,
982 ErrorResult& aRv)
984 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
985 if (!win) {
986 aRv.Throw(NS_ERROR_FAILURE);
987 return nullptr;
990 nsRefPtr<DOMRequest> request = new DOMRequest(win);
991 nsRefPtr<BluetoothVoidReplyRunnable> results =
992 new BluetoothVoidReplyRunnable(request);
994 nsAutoString address;
995 aDevice.GetAddress(address);
996 uint16_t serviceUuid = 0;
997 if (aServiceUuid.WasPassed()) {
998 serviceUuid = aServiceUuid.Value();
1001 BluetoothService* bs = BluetoothService::Get();
1002 if (!bs) {
1003 aRv.Throw(NS_ERROR_FAILURE);
1004 return nullptr;
1006 bs->Disconnect(address, serviceUuid, results);
1008 return request.forget();
1011 already_AddRefed<DOMRequest>
1012 BluetoothAdapter::SendFile(const nsAString& aDeviceAddress,
1013 File& aBlob, ErrorResult& aRv)
1015 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1016 if (!win) {
1017 aRv.Throw(NS_ERROR_FAILURE);
1018 return nullptr;
1021 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1022 nsRefPtr<BluetoothVoidReplyRunnable> results =
1023 new BluetoothVoidReplyRunnable(request);
1025 BluetoothService* bs = BluetoothService::Get();
1026 if (!bs) {
1027 aRv.Throw(NS_ERROR_FAILURE);
1028 return nullptr;
1031 if (XRE_GetProcessType() == GeckoProcessType_Default) {
1032 // In-process transfer
1033 bs->SendFile(aDeviceAddress, &aBlob, results);
1034 } else {
1035 ContentChild *cc = ContentChild::GetSingleton();
1036 if (!cc) {
1037 aRv.Throw(NS_ERROR_FAILURE);
1038 return nullptr;
1041 BlobChild* actor = cc->GetOrCreateActorForBlob(&aBlob);
1042 if (!actor) {
1043 aRv.Throw(NS_ERROR_FAILURE);
1044 return nullptr;
1047 bs->SendFile(aDeviceAddress, nullptr, actor, results);
1050 return request.forget();
1053 already_AddRefed<DOMRequest>
1054 BluetoothAdapter::StopSendingFile(const nsAString& aDeviceAddress, ErrorResult& aRv)
1056 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1057 if (!win) {
1058 aRv.Throw(NS_ERROR_FAILURE);
1059 return nullptr;
1062 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1063 nsRefPtr<BluetoothVoidReplyRunnable> results =
1064 new BluetoothVoidReplyRunnable(request);
1066 BluetoothService* bs = BluetoothService::Get();
1067 if (!bs) {
1068 aRv.Throw(NS_ERROR_FAILURE);
1069 return nullptr;
1071 bs->StopSendingFile(aDeviceAddress, results);
1073 return request.forget();
1076 already_AddRefed<DOMRequest>
1077 BluetoothAdapter::ConfirmReceivingFile(const nsAString& aDeviceAddress,
1078 bool aConfirmation, ErrorResult& aRv)
1080 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1081 if (!win) {
1082 aRv.Throw(NS_ERROR_FAILURE);
1083 return nullptr;
1086 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1087 nsRefPtr<BluetoothVoidReplyRunnable> results =
1088 new BluetoothVoidReplyRunnable(request);
1090 BluetoothService* bs = BluetoothService::Get();
1091 if (!bs) {
1092 aRv.Throw(NS_ERROR_FAILURE);
1093 return nullptr;
1095 bs->ConfirmReceivingFile(aDeviceAddress, aConfirmation, results);
1097 return request.forget();
1100 already_AddRefed<DOMRequest>
1101 BluetoothAdapter::ConnectSco(ErrorResult& aRv)
1103 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1104 if (!win) {
1105 aRv.Throw(NS_ERROR_FAILURE);
1106 return nullptr;
1109 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1110 nsRefPtr<BluetoothVoidReplyRunnable> results =
1111 new BluetoothVoidReplyRunnable(request);
1113 BluetoothService* bs = BluetoothService::Get();
1114 if (!bs) {
1115 aRv.Throw(NS_ERROR_FAILURE);
1116 return nullptr;
1118 bs->ConnectSco(results);
1120 return request.forget();
1123 already_AddRefed<DOMRequest>
1124 BluetoothAdapter::DisconnectSco(ErrorResult& aRv)
1126 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1127 if (!win) {
1128 aRv.Throw(NS_ERROR_FAILURE);
1129 return nullptr;
1132 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1133 nsRefPtr<BluetoothVoidReplyRunnable> results =
1134 new BluetoothVoidReplyRunnable(request);
1136 BluetoothService* bs = BluetoothService::Get();
1137 if (!bs) {
1138 aRv.Throw(NS_ERROR_FAILURE);
1139 return nullptr;
1141 bs->DisconnectSco(results);
1143 return request.forget();
1146 already_AddRefed<DOMRequest>
1147 BluetoothAdapter::IsScoConnected(ErrorResult& aRv)
1149 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1150 if (!win) {
1151 aRv.Throw(NS_ERROR_FAILURE);
1152 return nullptr;
1155 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1156 nsRefPtr<BluetoothReplyRunnable> results =
1157 new GetScoConnectionStatusTask(request);
1159 BluetoothService* bs = BluetoothService::Get();
1160 if (!bs) {
1161 aRv.Throw(NS_ERROR_FAILURE);
1162 return nullptr;
1164 bs->IsScoConnected(results);
1166 return request.forget();
1169 already_AddRefed<DOMRequest>
1170 BluetoothAdapter::AnswerWaitingCall(ErrorResult& aRv)
1172 #ifdef MOZ_B2G_RIL
1173 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1174 if (!win) {
1175 aRv.Throw(NS_ERROR_FAILURE);
1176 return nullptr;
1179 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1180 nsRefPtr<BluetoothVoidReplyRunnable> results =
1181 new BluetoothVoidReplyRunnable(request);
1183 BluetoothService* bs = BluetoothService::Get();
1184 if (!bs) {
1185 aRv.Throw(NS_ERROR_FAILURE);
1186 return nullptr;
1188 bs->AnswerWaitingCall(results);
1190 return request.forget();
1191 #else
1192 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
1193 return nullptr;
1194 #endif // MOZ_B2G_RIL
1197 already_AddRefed<DOMRequest>
1198 BluetoothAdapter::IgnoreWaitingCall(ErrorResult& aRv)
1200 #ifdef MOZ_B2G_RIL
1201 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1202 if (!win) {
1203 aRv.Throw(NS_ERROR_FAILURE);
1204 return nullptr;
1207 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1208 nsRefPtr<BluetoothVoidReplyRunnable> results =
1209 new BluetoothVoidReplyRunnable(request);
1211 BluetoothService* bs = BluetoothService::Get();
1212 if (!bs) {
1213 aRv.Throw(NS_ERROR_FAILURE);
1214 return nullptr;
1216 bs->IgnoreWaitingCall(results);
1218 return request.forget();
1219 #else
1220 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
1221 return nullptr;
1222 #endif // MOZ_B2G_RIL
1225 already_AddRefed<DOMRequest>
1226 BluetoothAdapter::ToggleCalls(ErrorResult& aRv)
1228 #ifdef MOZ_B2G_RIL
1229 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1230 if (!win) {
1231 aRv.Throw(NS_ERROR_FAILURE);
1232 return nullptr;
1235 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1236 nsRefPtr<BluetoothVoidReplyRunnable> results =
1237 new BluetoothVoidReplyRunnable(request);
1239 BluetoothService* bs = BluetoothService::Get();
1240 if (!bs) {
1241 aRv.Throw(NS_ERROR_FAILURE);
1242 return nullptr;
1244 bs->ToggleCalls(results);
1246 return request.forget();
1247 #else
1248 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
1249 return nullptr;
1250 #endif // MOZ_B2G_RIL
1253 already_AddRefed<DOMRequest>
1254 BluetoothAdapter::SendMediaMetaData(const MediaMetaData& aMediaMetaData, ErrorResult& aRv)
1256 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1257 if (!win) {
1258 aRv.Throw(NS_ERROR_FAILURE);
1259 return nullptr;
1262 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1263 nsRefPtr<BluetoothReplyRunnable> results =
1264 new BluetoothVoidReplyRunnable(request);
1266 BluetoothService* bs = BluetoothService::Get();
1267 if (!bs) {
1268 aRv.Throw(NS_ERROR_FAILURE);
1269 return nullptr;
1271 bs->SendMetaData(aMediaMetaData.mTitle,
1272 aMediaMetaData.mArtist,
1273 aMediaMetaData.mAlbum,
1274 aMediaMetaData.mMediaNumber,
1275 aMediaMetaData.mTotalMediaCount,
1276 aMediaMetaData.mDuration,
1277 results);
1279 return request.forget();
1282 already_AddRefed<DOMRequest>
1283 BluetoothAdapter::SendMediaPlayStatus(const MediaPlayStatus& aMediaPlayStatus, ErrorResult& aRv)
1285 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
1286 if (!win) {
1287 aRv.Throw(NS_ERROR_FAILURE);
1288 return nullptr;
1291 nsRefPtr<DOMRequest> request = new DOMRequest(win);
1292 nsRefPtr<BluetoothReplyRunnable> results =
1293 new BluetoothVoidReplyRunnable(request);
1295 BluetoothService* bs = BluetoothService::Get();
1296 if (!bs) {
1297 aRv.Throw(NS_ERROR_FAILURE);
1298 return nullptr;
1300 bs->SendPlayStatus(aMediaPlayStatus.mDuration,
1301 aMediaPlayStatus.mPosition,
1302 aMediaPlayStatus.mPlayStatus,
1303 results);
1305 return request.forget();
1308 JSObject*
1309 BluetoothAdapter::WrapObject(JSContext* aCx)
1311 return BluetoothAdapterBinding::Wrap(aCx, this);