Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Apple Notification Center Service Implementation #1

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ list(APPEND SOURCE_FILES
components/ble/DfuService.cpp
components/ble/CurrentTimeService.cpp
components/ble/AlertNotificationService.cpp
components/ble/AppleNotificationCenterClient.cpp
components/ble/MusicService.cpp
components/ble/weather/WeatherService.cpp
components/ble/NavigationService.cpp
Expand Down Expand Up @@ -556,6 +557,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/ble/DfuService.cpp
components/ble/CurrentTimeService.cpp
components/ble/AlertNotificationService.cpp
components/ble/AppleNotificationCenterClient.cpp
components/ble/MusicService.cpp
components/ble/weather/WeatherService.cpp
components/ble/BatteryInformationService.cpp
Expand Down Expand Up @@ -670,6 +672,7 @@ set(INCLUDE_FILES
components/ble/DeviceInformationService.h
components/ble/CurrentTimeClient.h
components/ble/AlertNotificationClient.h
components/ble/AppleNotificationCenterClient.h
components/ble/DfuService.h
components/firmwarevalidator/FirmwareValidator.h
components/ble/BatteryInformationService.h
Expand Down
140 changes: 140 additions & 0 deletions src/components/ble/AppleNotificationCenterClient.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include "components/ble/AppleNotificationCenterClient.h"
#include <algorithm>
#include "components/ble/NotificationManager.h"
#include "systemtask/SystemTask.h"

using namespace Pinetime::Controllers;


int OnDiscoveryEventCallback(uint16_t conn_handle, const struct ble_gatt_error* error, const struct ble_gatt_svc* service, void* arg) {
auto client = static_cast<AppleNotificationCenterClient*>(arg);
return client->OnDiscoveryEvent(conn_handle, error, service);
}

int OnANCSCharacteristicDiscoveredCallback(uint16_t conn_handle,
const struct ble_gatt_error* error,
const struct ble_gatt_chr* chr,
void* arg) {
auto client = static_cast<AppleNotificationCenterClient*>(arg);
return client->OnCharacteristicsDiscoveryEvent(conn_handle, error, chr);
}

int OnANCSDescriptorDiscoveryEventCallback(
uint16_t conn_handle, const struct ble_gatt_error* error, uint16_t chr_val_handle, const struct ble_gatt_dsc* dsc, void* arg) {
auto client = static_cast<AppleNotificationCenterClient*>(arg);
return client->OnDescriptorDiscoveryEventCallback(conn_handle, error, chr_val_handle, dsc);
}

int NewAlertSubcribeCallback(uint16_t conn_handle, const struct ble_gatt_error* error, struct ble_gatt_attr* attr, void* arg) {
auto client = static_cast<AppleNotificationCenterClient*>(arg);
return client->OnNewAlertSubcribe(conn_handle, error, attr);
}

AppleNotificationCenterClient::AppleNotificationCenterClient(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager)
: systemTask {systemTask}, notificationManager {notificationManager} {
}

bool AppleNotificationCenterClient::OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service) {
if (service == nullptr && error->status == BLE_HS_EDONE) {
if (isDiscovered) {
NRF_LOG_INFO("ANCS Discovery found, starting characteristics discovery");

ble_gattc_disc_all_chrs(connectionHandle, ancsStartHandle, ancsEndHandle, OnANCSCharacteristicDiscoveredCallback, this);
} else {
NRF_LOG_INFO("ANCS not found");
onServiceDiscovered(connectionHandle);
}
return true;
}

if (service != nullptr && ble_uuid_cmp(&ancsUuid.u, &service->uuid.u) == 0) {
NRF_LOG_INFO("ANCS discovered : 0x%x - 0x%x", service->start_handle, service->end_handle);
ancsStartHandle = service->start_handle;
ancsEndHandle = service->end_handle;
isDiscovered = true;
}
return false;
}

int AppleNotificationCenterClient::OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic) {
if (error->status != 0 && error->status != BLE_HS_EDONE) {
NRF_LOG_INFO("ANCS Characteristic discovery ERROR");
onServiceDiscovered(connectionHandle);
return 0;
}

if (characteristic == nullptr && error->status == BLE_HS_EDONE) {
NRF_LOG_INFO("ANCS Characteristic discovery complete");
if (isCharacteristicDiscovered) {
ble_gattc_disc_all_dscs(connectionHandle, notificationSourceHandle, ancsEndHandle, OnANCSDescriptorDiscoveryEventCallback, this);
} else {
onServiceDiscovered(connectionHandle);
}
} else {
if (characteristic != nullptr && ble_uuid_cmp(&notificationSourceChar.u, &characteristic->uuid.u) == 0) {
NRF_LOG_INFO("ANCS Characteristic discovered: Notification Source");
notificationSourceHandle = characteristic->val_handle;
// notificationSourceDefHandle = characteristic->def_handle;
isCharacteristicDiscovered = true;
}
}
return 0;
}

int AppleNotificationCenterClient::OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error* error, uint16_t characteristicValueHandle, const ble_gatt_dsc* descriptor) {
if (error->status == 0) {
if (characteristicValueHandle == notificationSourceHandle && ble_uuid_cmp(&notificationSourceChar.u, &descriptor->uuid.u)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the last comparison needed?
This callback is called only for notificationSourceChar's descriptors, no?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to recall this. This handles the notification subscription, right? Then we also want this for the dataSourceChar as well, but this code as it is just tries to detect an incoming message and then to show a dummy "Hello World" Notification. So this should stay in, because later on, there will be another case.

if (notificationSourceDescriptorHandle == 0) {
NRF_LOG_INFO("ANCS Descriptor discovered : %d", descriptor->handle);
notificationSourceDescriptorHandle = descriptor->handle;
isDescriptorFound = true;
uint8_t value[2] {1, 0};
ble_gattc_write_flat(connectionHandle, notificationSourceDescriptorHandle, value, sizeof(value), NewAlertSubcribeCallback, this);
}
}
} else {
if (!isDescriptorFound)
onServiceDiscovered(connectionHandle);
}
return 0;
}

int AppleNotificationCenterClient::OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute) {
if (error->status == 0) {
NRF_LOG_INFO("ANCS New alert subscribe OK");
} else {
NRF_LOG_INFO("ANCS New alert subscribe ERROR");
}
onServiceDiscovered(connectionHandle);

return 0;
}

void AppleNotificationCenterClient::OnNotification(ble_gap_event* event) {
if (event->notify_rx.attr_handle == notificationSourceHandle) {
NotificationManager::Notification notif;
notif.message = std::array<char, 101> {"Hello\0World"};
notif.size = 11;
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
notificationManager.Push(std::move(notif));

systemTask.PushMessage(Pinetime::System::Messages::OnNewNotification);
}
}

void AppleNotificationCenterClient::Reset() {
ancsStartHandle = 0;
ancsEndHandle = 0;
notificationSourceHandle = 0;
notificationSourceDescriptorHandle = 0;
isDiscovered = false;
isCharacteristicDiscovered = false;
isDescriptorFound = false;
}

void AppleNotificationCenterClient::Discover(uint16_t connectionHandle, std::function<void(uint16_t)> onServiceDiscovered) {
NRF_LOG_INFO("[ANCS] Starting discovery");
this->onServiceDiscovered = onServiceDiscovered;
ble_gattc_disc_svc_by_uuid(connectionHandle, &ancsUuid.u, OnDiscoveryEventCallback, this);
}
71 changes: 71 additions & 0 deletions src/components/ble/AppleNotificationCenterClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#pragma once

#include <cstdint>
#include <functional>
#define min // workaround: nimble's min/max macros conflict with libstdc++
#define max
#include <host/ble_gap.h>
#undef max
#undef min
#include "components/ble/BleClient.h"

namespace Pinetime {

namespace System {
class SystemTask;
}

namespace Controllers {
class NotificationManager;

class AppleNotificationCenterClient : public BleClient {
public:
explicit AppleNotificationCenterClient(Pinetime::System::SystemTask& systemTask,
Pinetime::Controllers::NotificationManager& notificationManager);

bool OnDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_svc* service);
int OnCharacteristicsDiscoveryEvent(uint16_t connectionHandle, const ble_gatt_error* error, const ble_gatt_chr* characteristic);
int OnNewAlertSubcribe(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
int OnDescriptorDiscoveryEventCallback(uint16_t connectionHandle, const ble_gatt_error* error, uint16_t characteristicValueHandle, const ble_gatt_dsc* descriptor);
void OnNotification(ble_gap_event* event);
void Reset();
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;

private:
// 7905F431-B5CE-4E99-A40F-4B1E122D00D0
const ble_uuid128_t ancsUuid {
{BLE_UUID_TYPE_128},
{0xd0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0x24, 0x99, 0x0E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79}
};

// 9FBF120D-6301-42D9-8C58-25E699A21DBD
const ble_uuid128_t notificationSourceChar {
{BLE_UUID_TYPE_128},
{0xBD, 0x1D, 0xA2, 0x99, 0xE6, 0x25, 0x58, 0X0C, 0xD9, 0x02, 0x01, 0x63, 0x0D, 0x12, 0xBF, 0x9F}
};
// 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9
const ble_uuid128_t controlPointChar {
{BLE_UUID_TYPE_128},
{0xD9, 0xD9, 0xAA, 0xFD, 0xBD, 0x9B, 0x21, 0X18,0xA8, 0x09,0xE1, 0x45, 0xF3, 0xD8, 0xD1, 0x69 }
};
// 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB
const ble_uuid128_t dataSourceChar {
{BLE_UUID_TYPE_128},
{0xFB, 0x7B, 0x7C, 0xCE, 0x6A, 0xB3, 0x44, 0X3E,0xB5, 0x0B,0xD6, 0x24, 0xE9, 0xC6, 0xEA, 0x22 }
};

uint16_t ancsStartHandle {0};
uint16_t ancsEndHandle {0};
uint16_t notificationSourceHandle {0};
//uint16_t controlPointHandle {0};
//uint16_t dataSourceHandle {0};
uint16_t notificationSourceDescriptorHandle {0};
bool isDiscovered {false};
bool isCharacteristicDiscovered {false};
bool isDescriptorFound {false};
Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::NotificationManager& notificationManager;
std::function<void(uint16_t)> onServiceDiscovered;
};
}
}
122 changes: 122 additions & 0 deletions src/components/ble/AppleNotificationCenterService.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include "components/ble/AppleNotificationCenterService.h"
#include <hal/nrf_rtc.h>
#include "components/ble/NotificationManager.h"
#include "systemtask/SystemTask.h"

using namespace Pinetime::Controllers;

constexpr ble_uuid128_t AppleNotificationCenterService::ancsSvc;
constexpr ble_uuid128_t AppleNotificationCenterService::ancsChar;
constexpr ble_uuid128_t AppleNotificationCenterService::dataSourceChar;
constexpr ble_uuid128_t AppleNotificationCenterService::controlPointChar;

int AppleNotificationCenterAlertCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto ancService = static_cast<AppleNotificationCenterService*>(arg);
return ancService->OnAlert(conn_handle, attr_handle, ctxt);
}

int AppleNotificationCenterDataCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt, void* arg) {
auto ancService = static_cast<AppleNotificationCenterService*>(arg);
return ancService->OnData(conn_handle, attr_handle, ctxt);
}

void AppleNotificationCenterService::Init() {
int res;
res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0);

res = ble_gatts_add_svcs(serviceDefinition);
ASSERT(res == 0);
}

AppleNotificationCenterService::AppleNotificationCenterService(System::SystemTask& systemTask, NotificationManager& notificationManager)
: characteristicDefinition {
{
.uuid = &ancsChar.u,
.access_cb = AppleNotificationCenterAlertCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_WRITE_ENC | BLE_GATT_CHR_F_WRITE_AUTHEN
},
{
.uuid = &dataSourceChar.u,
.access_cb = AppleNotificationCenterDataCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_NOTIFY | BLE_GATT_CHR_F_WRITE_ENC | BLE_GATT_CHR_F_WRITE_AUTHEN
},
{0}
},
serviceDefinition {
{/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &ancsSvc.u,
.characteristics = characteristicDefinition
},
{0},
},
systemTask {systemTask},
notificationManager {notificationManager} {
}

// Handle notification
int AppleNotificationCenterService::OnAlert(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
Event event;
EventFlag eventFlag;
Category category;
size_t categoryCount;
uint32_t notificationUUID;

NotificationManager::Notification notif;
os_mbuf_copydata(ctxt->om, 0, 1, &event);
os_mbuf_copydata(ctxt->om, 1, 1, &eventFlag);
os_mbuf_copydata(ctxt->om, 2, 1, &category);
os_mbuf_copydata(ctxt->om, 3, 1, &categoryCount);
os_mbuf_copydata(ctxt->om, 4, 4, &notificationUUID);

switch (category) {
case Category::Email:
notif.category = Pinetime::Controllers::NotificationManager::Categories::Email;
break;
case Category::IncomingCall:
notif.category = Pinetime::Controllers::NotificationManager::Categories::IncomingCall;
break;
case Category::MissedCall:
notif.category = Pinetime::Controllers::NotificationManager::Categories::MissedCall;
break;
case Category::News:
notif.category = Pinetime::Controllers::NotificationManager::Categories::News;
break;
case Category::Schedule:
notif.category = Pinetime::Controllers::NotificationManager::Categories::Sms;
break;
case Category::Voicemail:
notif.category = Pinetime::Controllers::NotificationManager::Categories::VoiceMail;
break;
default:
notif.category = Pinetime::Controllers::NotificationManager::Categories::SimpleAlert;
break;
}

notif.message = std::array<char, 101> {"Hello\0World"};
notif.size = 11;

auto notifEvent = Pinetime::System::Messages::OnNewNotification;
notificationManager.Push(std::move(notif));
systemTask.PushMessage(notifEvent);

}
return 0;
}

// Handle data from dataSourceChar
int AppleNotificationCenterService::OnData(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt* ctxt) {
return 0;
}

// Send a Get Notification Attributes command to Control Point
void AppleNotificationCenterService::GetNotificationAttribute() {}
// Send a Get App Attributes command to Control Point
void AppleNotificationCenterService::GetAppAttributes() {}
// Send a Perform Notification Action command to Control Point
void AppleNotificationCenterService::PerformNotificationAction() {}

Loading