Bug 1140951 - Implement start/stopNotifications for GATT client API. r=shuang, r=mrbkap

This commit is contained in:
Jocelyn Liu 2015-04-08 13:49:28 +08:00
parent 7613fecb87
commit 8338cc1176
16 changed files with 445 additions and 1 deletions

View File

@ -4,6 +4,7 @@
* 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 "BluetoothReplyRunnable.h"
#include "BluetoothService.h"
#include "BluetoothUtils.h"
#include "mozilla/dom/BluetoothGattCharacteristicBinding.h"
@ -12,6 +13,7 @@
#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
#include "mozilla/dom/bluetooth/BluetoothGattService.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/dom/Promise.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -58,6 +60,64 @@ BluetoothGattCharacteristic::~BluetoothGattCharacteristic()
bs->UnregisterBluetoothSignalHandler(path, this);
}
already_AddRefed<Promise>
BluetoothGattCharacteristic::StartNotifications(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
BluetoothService* bs = BluetoothService::Get();
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
BT_ENSURE_TRUE_REJECT(mService, NS_ERROR_NOT_AVAILABLE);
nsRefPtr<BluetoothReplyRunnable> result =
new BluetoothVoidReplyRunnable(
nullptr /* DOMRequest */,
promise,
NS_LITERAL_STRING("GattClientStartNotifications"));
bs->GattClientStartNotificationsInternal(mService->GetAppUuid(),
mService->GetServiceId(),
mCharId,
result);
return promise.forget();
}
already_AddRefed<Promise>
BluetoothGattCharacteristic::StopNotifications(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(global, aRv);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
BluetoothService* bs = BluetoothService::Get();
BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
BT_ENSURE_TRUE_REJECT(mService, NS_ERROR_NOT_AVAILABLE);
nsRefPtr<BluetoothReplyRunnable> result =
new BluetoothVoidReplyRunnable(
nullptr /* DOMRequest */,
promise,
NS_LITERAL_STRING("GattClientStopNotifications"));
bs->GattClientStopNotificationsInternal(mService->GetAppUuid(),
mService->GetServiceId(),
mCharId,
result);
return promise.forget();
}
void
BluetoothGattCharacteristic::HandleDescriptorsDiscovered(
const BluetoothValue& aValue)

View File

@ -15,6 +15,12 @@
#include "nsWrapperCache.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
class Promise;
}
}
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothGattService;
@ -53,6 +59,12 @@ public:
return mCharId.mInstanceId;
}
/****************************************************************************
* Methods (Web API Implementation)
***************************************************************************/
already_AddRefed<Promise> StartNotifications(ErrorResult& aRv);
already_AddRefed<Promise> StopNotifications(ErrorResult& aRv);
/****************************************************************************
* Others
***************************************************************************/

View File

@ -372,6 +372,26 @@ public:
DiscoverGattServicesInternal(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable) = 0;
/**
* Enable notifications of a given GATT characteristic.
* (platform specific implementation)
*/
virtual void
GattClientStartNotificationsInternal(const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable) = 0;
/**
* Disable notifications of a given GATT characteristic.
* (platform specific implementation)
*/
virtual void
GattClientStopNotificationsInternal(const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable) = 0;
/**
* Unregister a GATT client. (platform specific implementation)
*/

View File

@ -58,6 +58,8 @@ public:
mDiscoverRunnable = nullptr;
mUnregisterClientRunnable = nullptr;
mReadRemoteRssiRunnable = nullptr;
mRegisterNotificationsRunnable = nullptr;
mDeregisterNotificationsRunnable = nullptr;
}
void NotifyDiscoverCompleted(bool aSuccess)
@ -98,6 +100,8 @@ public:
nsRefPtr<BluetoothReplyRunnable> mDisconnectRunnable;
nsRefPtr<BluetoothReplyRunnable> mDiscoverRunnable;
nsRefPtr<BluetoothReplyRunnable> mReadRemoteRssiRunnable;
nsRefPtr<BluetoothReplyRunnable> mRegisterNotificationsRunnable;
nsRefPtr<BluetoothReplyRunnable> mDeregisterNotificationsRunnable;
nsRefPtr<BluetoothReplyRunnable> mUnregisterClientRunnable;
/**
@ -636,6 +640,151 @@ BluetoothGattManager::ReadRemoteRssi(int aClientIf,
new ReadRemoteRssiResultHandler(client));
}
class BluetoothGattManager::RegisterNotificationsResultHandler final
: public BluetoothGattClientResultHandler
{
public:
RegisterNotificationsResultHandler(BluetoothGattClient* aClient)
: mClient(aClient)
{
MOZ_ASSERT(mClient);
}
void RegisterNotification() override
{
MOZ_ASSERT(mClient->mRegisterNotificationsRunnable);
/**
* Resolve the promise directly if we successfully issued this request to
* stack.
*
* We resolve the promise here since bluedroid stack always returns
* incorrect connId in |RegisterNotificationNotification| and we cannot map
* back to the target client because of it.
* Please see Bug 1149043 for more information.
*/
DispatchReplySuccess(mClient->mRegisterNotificationsRunnable);
mClient->mRegisterNotificationsRunnable = nullptr;
}
void OnError(BluetoothStatus aStatus) override
{
BT_WARNING(
"BluetoothGattClientInterface::RegisterNotifications failed: %d",
(int)aStatus);
MOZ_ASSERT(mClient->mRegisterNotificationsRunnable);
DispatchReplyError(mClient->mRegisterNotificationsRunnable,
NS_LITERAL_STRING("RegisterNotifications failed"));
mClient->mRegisterNotificationsRunnable = nullptr;
}
private:
nsRefPtr<BluetoothGattClient> mClient;
};
void
BluetoothGattManager::RegisterNotifications(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRunnable);
ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
// Reject the request if there is an ongoing request or client is already
// disconnected
if (client->mRegisterNotificationsRunnable || client->mConnId <= 0) {
DispatchReplyError(aRunnable,
NS_LITERAL_STRING("RegisterNotifications failed"));
return;
}
client->mRegisterNotificationsRunnable = aRunnable;
sBluetoothGattClientInterface->RegisterNotification(
client->mClientIf, client->mDeviceAddr, aServId, aCharId,
new RegisterNotificationsResultHandler(client));
}
class BluetoothGattManager::DeregisterNotificationsResultHandler final
: public BluetoothGattClientResultHandler
{
public:
DeregisterNotificationsResultHandler(BluetoothGattClient* aClient)
: mClient(aClient)
{
MOZ_ASSERT(mClient);
}
void DeregisterNotification() override
{
MOZ_ASSERT(mClient->mDeregisterNotificationsRunnable);
/**
* Resolve the promise directly if we successfully issued this request to
* stack.
*
* We resolve the promise here since bluedroid stack always returns
* incorrect connId in |RegisterNotificationNotification| and we cannot map
* back to the target client because of it.
* Please see Bug 1149043 for more information.
*/
DispatchReplySuccess(mClient->mDeregisterNotificationsRunnable);
mClient->mDeregisterNotificationsRunnable = nullptr;
}
void OnError(BluetoothStatus aStatus) override
{
BT_WARNING(
"BluetoothGattClientInterface::DeregisterNotifications failed: %d",
(int)aStatus);
MOZ_ASSERT(mClient->mDeregisterNotificationsRunnable);
DispatchReplyError(mClient->mDeregisterNotificationsRunnable,
NS_LITERAL_STRING("DeregisterNotifications failed"));
mClient->mDeregisterNotificationsRunnable = nullptr;
}
private:
nsRefPtr<BluetoothGattClient> mClient;
};
void
BluetoothGattManager::DeregisterNotifications(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRunnable);
ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
MOZ_ASSERT(index != sClients->NoIndex);
nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
// Reject the request if there is an ongoing request
if (client->mDeregisterNotificationsRunnable) {
DispatchReplyError(aRunnable,
NS_LITERAL_STRING("DeregisterNotifications failed"));
return;
}
client->mDeregisterNotificationsRunnable = aRunnable;
sBluetoothGattClientInterface->DeregisterNotification(
client->mClientIf, client->mDeviceAddr, aServId, aCharId,
new DeregisterNotificationsResultHandler(client));
}
//
// Notification Handlers
//
@ -999,7 +1148,25 @@ BluetoothGattManager::RegisterNotificationNotification(
int aConnId, int aIsRegister, BluetoothGattStatus aStatus,
const BluetoothGattServiceId& aServiceId,
const BluetoothGattId& aCharId)
{ }
{
MOZ_ASSERT(NS_IsMainThread());
BT_LOGD("aStatus = %d, aConnId = %d, aIsRegister = %d",
aStatus, aConnId, aIsRegister);
/**
* FIXME: Bug 1149043
*
* aConnId reported by bluedroid stack is wrong, with these limited
* information we have, we currently cannot map back to the client from this
* callback. Therefore, we resolve/reject the Promise for registering or
* deregistering notifications in their result handlers instead of this
* callback.
* We should resolve/reject the Promise for registering or deregistering
* notifications here if this bluedroid stack bug is fixed.
*
* Please see Bug 1149043 for more information.
*/
}
void
BluetoothGattManager::NotifyNotification(

View File

@ -46,6 +46,16 @@ public:
const nsAString& aDeviceAddr,
BluetoothReplyRunnable* aRunnable);
void RegisterNotifications(const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable);
void DeregisterNotifications(const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable);
private:
class CleanupResultHandler;
class CleanupResultHandlerRunnable;
@ -56,6 +66,8 @@ private:
class DisconnectResultHandler;
class DiscoverResultHandler;
class ReadRemoteRssiResultHandler;
class RegisterNotificationsResultHandler;
class DeregisterNotificationsResultHandler;
BluetoothGattManager();

View File

@ -1148,6 +1148,36 @@ BluetoothServiceBluedroid::DiscoverGattServicesInternal(
gatt->Discover(aAppUuid, aRunnable);
}
void
BluetoothServiceBluedroid::GattClientStartNotificationsInternal(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->RegisterNotifications(aAppUuid, aServId, aCharId, aRunnable);
}
void
BluetoothServiceBluedroid::GattClientStopNotificationsInternal(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
BluetoothGattManager* gatt = BluetoothGattManager::Get();
ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
gatt->DeregisterNotifications(aAppUuid, aServId, aCharId, aRunnable);
}
void
BluetoothServiceBluedroid::UnregisterGattClientInternal(
int aClientIf, BluetoothReplyRunnable* aRunnable)

View File

@ -203,6 +203,20 @@ public:
DiscoverGattServicesInternal(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable) override;
virtual void
GattClientStartNotificationsInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable) override;
virtual void
GattClientStopNotificationsInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable) override;
virtual void
UnregisterGattClientInternal(int aClientIf,
BluetoothReplyRunnable* aRunnable) override;

View File

@ -4301,6 +4301,20 @@ BluetoothDBusService::DiscoverGattServicesInternal(
{
}
void
BluetoothDBusService::GattClientStartNotificationsInternal(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
}
void
BluetoothDBusService::GattClientStopNotificationsInternal(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
}
void
BluetoothDBusService::UnregisterGattClientInternal(
int aClientIf, BluetoothReplyRunnable* aRunnable)

View File

@ -213,6 +213,20 @@ public:
DiscoverGattServicesInternal(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable) override;
virtual void
GattClientStartNotificationsInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable) override;
virtual void
GattClientStopNotificationsInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable) override;
virtual void
UnregisterGattClientInternal(int aClientIf,
BluetoothReplyRunnable* aRunnable) override;

View File

@ -260,6 +260,12 @@ BluetoothParent::RecvPBluetoothRequestConstructor(
return actor->DoRequest(aRequest.get_DisconnectGattClientRequest());
case Request::TDiscoverGattServicesRequest:
return actor->DoRequest(aRequest.get_DiscoverGattServicesRequest());
case Request::TGattClientStartNotificationsRequest:
return actor->DoRequest(
aRequest.get_GattClientStartNotificationsRequest());
case Request::TGattClientStopNotificationsRequest:
return actor->DoRequest(
aRequest.get_GattClientStopNotificationsRequest());
case Request::TUnregisterGattClientRequest:
return actor->DoRequest(aRequest.get_UnregisterGattClientRequest());
case Request::TGattClientReadRemoteRssiRequest:
@ -765,6 +771,36 @@ BluetoothRequestParent::DoRequest(const DiscoverGattServicesRequest& aRequest)
return true;
}
bool
BluetoothRequestParent::DoRequest(
const GattClientStartNotificationsRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TGattClientStartNotificationsRequest);
mService->GattClientStartNotificationsInternal(aRequest.appUuid(),
aRequest.servId(),
aRequest.charId(),
mReplyRunnable.get());
return true;
}
bool
BluetoothRequestParent::DoRequest(
const GattClientStopNotificationsRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TGattClientStopNotificationsRequest);
mService->GattClientStopNotificationsInternal(aRequest.appUuid(),
aRequest.servId(),
aRequest.charId(),
mReplyRunnable.get());
return true;
}
bool
BluetoothRequestParent::DoRequest(const UnregisterGattClientRequest& aRequest)
{

View File

@ -232,6 +232,12 @@ protected:
bool
DoRequest(const DiscoverGattServicesRequest& aRequest);
bool
DoRequest(const GattClientStartNotificationsRequest& aRequest);
bool
DoRequest(const GattClientStopNotificationsRequest& aRequest);
bool
DoRequest(const UnregisterGattClientRequest& aRequest);

View File

@ -427,6 +427,24 @@ BluetoothServiceChildProcess::DiscoverGattServicesInternal(
DiscoverGattServicesRequest(nsString(aAppUuid)));
}
void
BluetoothServiceChildProcess::GattClientStartNotificationsInternal(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable,
GattClientStartNotificationsRequest(nsString(aAppUuid), aServId, aCharId));
}
void
BluetoothServiceChildProcess::GattClientStopNotificationsInternal(
const nsAString& aAppUuid, const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId, BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable,
GattClientStopNotificationsRequest(nsString(aAppUuid), aServId, aCharId));
}
void
BluetoothServiceChildProcess::UnregisterGattClientInternal(
int aClientIf, BluetoothReplyRunnable* aRunnable)

View File

@ -218,6 +218,20 @@ public:
DiscoverGattServicesInternal(const nsAString& aAppUuid,
BluetoothReplyRunnable* aRunnable) override;
virtual void
GattClientStartNotificationsInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable) override;
virtual void
GattClientStopNotificationsInternal(
const nsAString& aAppUuid,
const BluetoothGattServiceId& aServId,
const BluetoothGattId& aCharId,
BluetoothReplyRunnable* aRunnable) override;
virtual void
UnregisterGattClientInternal(int aClientIf,
BluetoothReplyRunnable* aRunnable) override;

View File

@ -30,7 +30,9 @@ union BluetoothValue
nsString[];
uint8_t[];
BluetoothNamedValue[];
BluetoothGattId;
BluetoothGattId[];
BluetoothGattServiceId;
BluetoothGattServiceId[];
};

View File

@ -207,6 +207,20 @@ struct DiscoverGattServicesRequest
nsString appUuid;
};
struct GattClientStartNotificationsRequest
{
nsString appUuid;
BluetoothGattServiceId servId;
BluetoothGattId charId;
};
struct GattClientStopNotificationsRequest
{
nsString appUuid;
BluetoothGattServiceId servId;
BluetoothGattId charId;
};
struct UnregisterGattClientRequest
{
int clientIf;
@ -255,6 +269,8 @@ union Request
ConnectGattClientRequest;
DisconnectGattClientRequest;
DiscoverGattServicesRequest;
GattClientStartNotificationsRequest;
GattClientStopNotificationsRequest;
UnregisterGattClientRequest;
GattClientReadRemoteRssiRequest;
};

View File

@ -13,4 +13,13 @@ interface BluetoothGattCharacteristic
readonly attribute DOMString uuid;
readonly attribute unsigned short instanceId;
/**
* Start or stop subscribing notifications of this characteristic from the
* remote GATT server.
*/
[NewObject]
Promise<void> startNotifications();
[NewObject]
Promise<void> stopNotifications();
};