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
,
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
46 StartDiscoveryTask(BluetoothAdapter
* aAdapter
, Promise
* aPromise
)
47 : BluetoothReplyRunnable(nullptr, aPromise
,
48 NS_LITERAL_STRING("StartDiscovery"))
56 ParseSuccessfulReply(JS::MutableHandle
<JS::Value
> aValue
)
58 aValue
.setUndefined();
61 NS_ENSURE_TRUE(jsapi
.Init(mAdapter
->GetParentObject()), false);
62 JSContext
* cx
= jsapi
.cx();
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
);
75 // Set the created discovery handle as the one in use.
76 mAdapter
->SetDiscoveryHandleInUse(discoveryHandle
);
81 ReleaseMembers() MOZ_OVERRIDE
83 BluetoothReplyRunnable::ReleaseMembers();
88 nsRefPtr
<BluetoothAdapter
> mAdapter
;
91 class GetDevicesTask
: public BluetoothReplyRunnable
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"));
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"));
123 nsRefPtr
<BluetoothDevice
> d
=
124 BluetoothDevice::Create(mAdapterPtr
->GetOwner(),
126 devices
.AppendElement(d
);
130 if (!jsapi
.Init(mAdapterPtr
->GetOwner())) {
131 BT_WARNING("Failed to initialise AutoJSAPI!");
132 SetError(NS_LITERAL_STRING("BluetoothAutoJSAPIInitError"));
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"));
143 aValue
.setObject(*JsDevices
);
150 BluetoothReplyRunnable::ReleaseMembers();
151 mAdapterPtr
= nullptr;
155 nsRefPtr
<BluetoothAdapter
> mAdapterPtr
;
158 class GetScoConnectionStatusTask
: public BluetoothReplyRunnable
161 GetScoConnectionStatusTask(nsIDOMDOMRequest
* aReq
) :
162 BluetoothReplyRunnable(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"));
178 aValue
.setBoolean(v
.get_bool());
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)
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);
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);
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);
247 bs
->GetPairedDevicePropertiesInternal(aDeviceAddresses
, results
);
249 BT_WARNING("GetPairedDeviceProperties failed");
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
) {
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();
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
);
300 BT_WARNING("Not handling adapter property: %s",
301 NS_ConvertUTF16toUTF8(name
).get());
306 already_AddRefed
<BluetoothAdapter
>
307 BluetoothAdapter::Create(nsPIDOMWindow
* aWindow
, const BluetoothValue
& aValue
)
309 MOZ_ASSERT(NS_IsMainThread());
312 nsRefPtr
<BluetoothAdapter
> adapter
= new BluetoothAdapter(aWindow
, aValue
);
313 return adapter
.forget();
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
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
);
372 BT_WARNING("Not handling adapter signal: %s",
373 NS_ConvertUTF16toUTF8(aData
.name()).get());
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());
394 aRv
.Throw(NS_ERROR_FAILURE
);
398 nsRefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
399 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
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
);
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());
437 aRv
.Throw(NS_ERROR_FAILURE
);
441 nsRefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
442 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
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
);
458 nsRefPtr
<BluetoothReplyRunnable
> result
=
459 new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
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());
473 aRv
.Throw(NS_ERROR_FAILURE
);
477 nsRefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
478 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
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 */,
499 NS_LITERAL_STRING("SetName"));
500 BT_ENSURE_TRUE_REJECT(
501 NS_SUCCEEDED(bs
->SetProperty(BluetoothObjectType::TYPE_ADAPTER
,
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());
513 aRv
.Throw(NS_ERROR_FAILURE
);
517 nsRefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
518 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
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 */,
539 NS_LITERAL_STRING("SetDiscoverable"));
540 BT_ENSURE_TRUE_REJECT(
541 NS_SUCCEEDED(bs
->SetProperty(BluetoothObjectType::TYPE_ADAPTER
,
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();
555 aRv
.Throw(NS_ERROR_FAILURE
);
559 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
560 nsRefPtr
<BluetoothReplyRunnable
> results
=
561 new GetDevicesTask(this, request
);
563 BluetoothService
* bs
= BluetoothService::Get();
565 aRv
.Throw(NS_ERROR_FAILURE
);
568 nsresult rv
= bs
->GetConnectedDevicePropertiesInternal(aServiceUuid
, results
);
574 return request
.forget();
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
,
591 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(GetOwner());
593 aRv
.Throw(NS_ERROR_FAILURE
);
597 nsRefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
598 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
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
);
615 nsRefPtr
<BluetoothReplyRunnable
> result
=
616 new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
618 NS_LITERAL_STRING("Pair"));
619 rv
= bs
->CreatePairedDeviceInternal(aDeviceAddress
,
620 kCreatePairedDeviceTimeout
,
623 nsRefPtr
<BluetoothReplyRunnable
> result
=
624 new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
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());
651 aRv
.Throw(NS_ERROR_FAILURE
);
655 nsRefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
656 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
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 */
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());
691 aRv
.Throw(NS_ERROR_FAILURE
);
695 nsRefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
696 NS_ENSURE_TRUE(!aRv
.Failed(), nullptr);
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 */
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
)
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
;
742 BluetoothAdapter::IsAdapterAttributeChanged(BluetoothAdapterAttribute aType
,
743 const BluetoothValue
& aValue
)
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();
763 BT_WARNING("Type %d is not handled", uint32_t(aType
));
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
);
786 BluetoothAdapter::SetAdapterState(BluetoothAdapterState aState
)
788 if (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
);
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
]);
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
);
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
);
846 // Existing device, discard temporary discovered device
847 discoveredDevice
= mDevices
[index
];
850 // Notify application of discovered device via discovery handle
851 mDiscoveryHandleInUse
->DispatchDeviceEvent(discoveredDevice
);
855 BluetoothAdapter::DispatchAttributeEvent(const nsTArray
<nsString
>& aTypes
)
857 NS_ENSURE_TRUE_VOID(aTypes
.Length());
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
);
869 RootedDictionary
<BluetoothAttributeEventInit
> init(cx
);
871 nsRefPtr
<BluetoothAttributeEvent
> event
=
872 BluetoothAttributeEvent::Constructor(this,
873 NS_LITERAL_STRING("attributechanged"),
875 DispatchTrustedEvent(event
);
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.");
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
);
896 pairedDevice
= mDevices
[index
];
899 // Notify application of paired device
900 BluetoothDeviceEventInit init
;
901 init
.mDevice
= pairedDevice
;
902 DispatchDeviceEvent(NS_LITERAL_STRING("devicepaired"), init
);
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.");
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
);
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
);
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
,
951 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
953 aRv
.Throw(NS_ERROR_FAILURE
);
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();
971 aRv
.Throw(NS_ERROR_FAILURE
);
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
,
984 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
986 aRv
.Throw(NS_ERROR_FAILURE
);
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();
1003 aRv
.Throw(NS_ERROR_FAILURE
);
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();
1017 aRv
.Throw(NS_ERROR_FAILURE
);
1021 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1022 nsRefPtr
<BluetoothVoidReplyRunnable
> results
=
1023 new BluetoothVoidReplyRunnable(request
);
1025 BluetoothService
* bs
= BluetoothService::Get();
1027 aRv
.Throw(NS_ERROR_FAILURE
);
1031 if (XRE_GetProcessType() == GeckoProcessType_Default
) {
1032 // In-process transfer
1033 bs
->SendFile(aDeviceAddress
, &aBlob
, results
);
1035 ContentChild
*cc
= ContentChild::GetSingleton();
1037 aRv
.Throw(NS_ERROR_FAILURE
);
1041 BlobChild
* actor
= cc
->GetOrCreateActorForBlob(&aBlob
);
1043 aRv
.Throw(NS_ERROR_FAILURE
);
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();
1058 aRv
.Throw(NS_ERROR_FAILURE
);
1062 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1063 nsRefPtr
<BluetoothVoidReplyRunnable
> results
=
1064 new BluetoothVoidReplyRunnable(request
);
1066 BluetoothService
* bs
= BluetoothService::Get();
1068 aRv
.Throw(NS_ERROR_FAILURE
);
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();
1082 aRv
.Throw(NS_ERROR_FAILURE
);
1086 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1087 nsRefPtr
<BluetoothVoidReplyRunnable
> results
=
1088 new BluetoothVoidReplyRunnable(request
);
1090 BluetoothService
* bs
= BluetoothService::Get();
1092 aRv
.Throw(NS_ERROR_FAILURE
);
1095 bs
->ConfirmReceivingFile(aDeviceAddress
, aConfirmation
, results
);
1097 return request
.forget();
1100 already_AddRefed
<DOMRequest
>
1101 BluetoothAdapter::ConnectSco(ErrorResult
& aRv
)
1103 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
1105 aRv
.Throw(NS_ERROR_FAILURE
);
1109 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1110 nsRefPtr
<BluetoothVoidReplyRunnable
> results
=
1111 new BluetoothVoidReplyRunnable(request
);
1113 BluetoothService
* bs
= BluetoothService::Get();
1115 aRv
.Throw(NS_ERROR_FAILURE
);
1118 bs
->ConnectSco(results
);
1120 return request
.forget();
1123 already_AddRefed
<DOMRequest
>
1124 BluetoothAdapter::DisconnectSco(ErrorResult
& aRv
)
1126 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
1128 aRv
.Throw(NS_ERROR_FAILURE
);
1132 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1133 nsRefPtr
<BluetoothVoidReplyRunnable
> results
=
1134 new BluetoothVoidReplyRunnable(request
);
1136 BluetoothService
* bs
= BluetoothService::Get();
1138 aRv
.Throw(NS_ERROR_FAILURE
);
1141 bs
->DisconnectSco(results
);
1143 return request
.forget();
1146 already_AddRefed
<DOMRequest
>
1147 BluetoothAdapter::IsScoConnected(ErrorResult
& aRv
)
1149 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
1151 aRv
.Throw(NS_ERROR_FAILURE
);
1155 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1156 nsRefPtr
<BluetoothReplyRunnable
> results
=
1157 new GetScoConnectionStatusTask(request
);
1159 BluetoothService
* bs
= BluetoothService::Get();
1161 aRv
.Throw(NS_ERROR_FAILURE
);
1164 bs
->IsScoConnected(results
);
1166 return request
.forget();
1169 already_AddRefed
<DOMRequest
>
1170 BluetoothAdapter::AnswerWaitingCall(ErrorResult
& aRv
)
1173 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
1175 aRv
.Throw(NS_ERROR_FAILURE
);
1179 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1180 nsRefPtr
<BluetoothVoidReplyRunnable
> results
=
1181 new BluetoothVoidReplyRunnable(request
);
1183 BluetoothService
* bs
= BluetoothService::Get();
1185 aRv
.Throw(NS_ERROR_FAILURE
);
1188 bs
->AnswerWaitingCall(results
);
1190 return request
.forget();
1192 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
1194 #endif // MOZ_B2G_RIL
1197 already_AddRefed
<DOMRequest
>
1198 BluetoothAdapter::IgnoreWaitingCall(ErrorResult
& aRv
)
1201 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
1203 aRv
.Throw(NS_ERROR_FAILURE
);
1207 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1208 nsRefPtr
<BluetoothVoidReplyRunnable
> results
=
1209 new BluetoothVoidReplyRunnable(request
);
1211 BluetoothService
* bs
= BluetoothService::Get();
1213 aRv
.Throw(NS_ERROR_FAILURE
);
1216 bs
->IgnoreWaitingCall(results
);
1218 return request
.forget();
1220 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
1222 #endif // MOZ_B2G_RIL
1225 already_AddRefed
<DOMRequest
>
1226 BluetoothAdapter::ToggleCalls(ErrorResult
& aRv
)
1229 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
1231 aRv
.Throw(NS_ERROR_FAILURE
);
1235 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1236 nsRefPtr
<BluetoothVoidReplyRunnable
> results
=
1237 new BluetoothVoidReplyRunnable(request
);
1239 BluetoothService
* bs
= BluetoothService::Get();
1241 aRv
.Throw(NS_ERROR_FAILURE
);
1244 bs
->ToggleCalls(results
);
1246 return request
.forget();
1248 aRv
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
1250 #endif // MOZ_B2G_RIL
1253 already_AddRefed
<DOMRequest
>
1254 BluetoothAdapter::SendMediaMetaData(const MediaMetaData
& aMediaMetaData
, ErrorResult
& aRv
)
1256 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
1258 aRv
.Throw(NS_ERROR_FAILURE
);
1262 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1263 nsRefPtr
<BluetoothReplyRunnable
> results
=
1264 new BluetoothVoidReplyRunnable(request
);
1266 BluetoothService
* bs
= BluetoothService::Get();
1268 aRv
.Throw(NS_ERROR_FAILURE
);
1271 bs
->SendMetaData(aMediaMetaData
.mTitle
,
1272 aMediaMetaData
.mArtist
,
1273 aMediaMetaData
.mAlbum
,
1274 aMediaMetaData
.mMediaNumber
,
1275 aMediaMetaData
.mTotalMediaCount
,
1276 aMediaMetaData
.mDuration
,
1279 return request
.forget();
1282 already_AddRefed
<DOMRequest
>
1283 BluetoothAdapter::SendMediaPlayStatus(const MediaPlayStatus
& aMediaPlayStatus
, ErrorResult
& aRv
)
1285 nsCOMPtr
<nsPIDOMWindow
> win
= GetOwner();
1287 aRv
.Throw(NS_ERROR_FAILURE
);
1291 nsRefPtr
<DOMRequest
> request
= new DOMRequest(win
);
1292 nsRefPtr
<BluetoothReplyRunnable
> results
=
1293 new BluetoothVoidReplyRunnable(request
);
1295 BluetoothService
* bs
= BluetoothService::Get();
1297 aRv
.Throw(NS_ERROR_FAILURE
);
1300 bs
->SendPlayStatus(aMediaPlayStatus
.mDuration
,
1301 aMediaPlayStatus
.mPosition
,
1302 aMediaPlayStatus
.mPlayStatus
,
1305 return request
.forget();
1309 BluetoothAdapter::WrapObject(JSContext
* aCx
)
1311 return BluetoothAdapterBinding::Wrap(aCx
, this);