Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
965e69a
Started working on ANCS
cyberneel Dec 19, 2024
646c209
Started working on he client, which is the correct half that needs to…
cyberneel Dec 20, 2024
315f69b
Formatting
cyberneel Dec 21, 2024
fcdecbb
Added RTT logging
cyberneel Dec 21, 2024
b9cc3c3
Added debugging through watch notifications
cyberneel Dec 24, 2024
72df4fc
Removed encryption of battery and added required pairing on connect G…
cyberneel Dec 29, 2024
0a26a59
ANCS Partially works
cyberneel Dec 29, 2024
3cc4ece
Started working on using Control Point and Data Source:
cyberneel Dec 30, 2024
dfa13a9
More debugging
cyberneel Dec 31, 2024
8916920
Got Datsource to somewhat work and notifications now show title.
cyberneel Dec 31, 2024
7f0e066
Fixed check to only let new notifications pass (maybe)
cyberneel Dec 31, 2024
fce4a60
Proper Notification works.
cyberneel Jan 1, 2025
9fbc594
Fixed the event flag detection. Only shows a notification when it's new.
cyberneel Jan 2, 2025
1bed731
Fixed first letter in Subtitle being cut off
cyberneel Jan 2, 2025
670c379
Improve message display
liamcharger Jan 2, 2025
5bf8406
Updated notification, Title and Subtitle are in the orange test and m…
cyberneel Jan 2, 2025
288d167
Implemented Incoming Calls to ANCS
cyberneel Jan 3, 2025
f370d63
Turned off non important debugs
cyberneel Jan 3, 2025
0ba425a
Formatted Code
cyberneel Jan 3, 2025
2fe3a6b
Remove RTT settings
liamcharger Jan 3, 2025
1e4989f
Removed more debug notifications
cyberneel Jan 4, 2025
8ab8296
Added check for special characters and show unknown symbol.
cyberneel Jan 5, 2025
bb48b5f
removed all debug notifications
cyberneel Jan 5, 2025
c2b392b
Ignore silent notifications
cyberneel Jan 5, 2025
f888483
Make trunacated messages end in "..." to indicate that message has mo…
cyberneel Jan 5, 2025
65ead6e
Fix typos and remove unnecessary commented code
liamcharger Jan 16, 2025
b8da191
Merge branch 'main' into ancs
liamcharger Jan 16, 2025
c73f016
Merge pull request #26 from InfiniTimeOrg/main
tituscmd Jan 21, 2025
de63dce
Merge branch 'main' into ancs
liamcharger Feb 4, 2025
ec4b3a7
Merge branch 'InfiniTimeOrg:main' into main
tituscmd Apr 10, 2025
d653c17
Merge branch 'InfiniTimeOrg:main' into main
tituscmd May 13, 2025
5e7a77f
Merge branch 'main' into ancs
liamcharger May 14, 2025
3669665
Made codebase changes based on PR Comments
cyberneel May 19, 2025
92673eb
Merge branch 'main' into ancs
cyberneel May 19, 2025
fede286
Merge branch 'InfiniTimeOrg:main' into main
tituscmd May 20, 2025
5e35637
Merge branch 'InfiniTimeOrg:main' into main
tituscmd Jun 10, 2025
ce15d50
Merge branch 'InfiniTimeOrg:main' into ancs
cyberneel Jul 12, 2025
f47c921
Added security initiate back for ancs to work.
cyberneel Jul 12, 2025
8cb0ec4
Updated checks to prevent duplicate notification triggers, NEEDS test…
cyberneel Jul 12, 2025
1937e7e
Check for silent before ringing notification
cyberneel Jul 13, 2025
1d17899
Fix typo in ping check
cyberneel Jul 13, 2025
3450956
ignore notifications not in list
cyberneel Jul 14, 2025
9c919d3
Added a session uuid cache to try to help with duplicate pings
cyberneel Jul 14, 2025
e549e89
clear session uids on reset
cyberneel Jul 14, 2025
a693cff
removed extra array
cyberneel Jul 18, 2025
94b88a0
Merge branch 'InfiniTimeOrg:main' into main
tituscmd Aug 10, 2025
211e85e
Merge branch 'ancs' of https://github.com/cyberneel/InfiniTime into a…
tituscmd Sep 27, 2025
6c89b79
add new helper method
tituscmd Sep 27, 2025
d745049
add another condition for connecting
tituscmd Sep 27, 2025
34a6850
add in header as well
tituscmd Sep 27, 2025
791a483
Started working on ANCS
cyberneel Dec 19, 2024
dd9865b
Started working on he client, which is the correct half that needs to…
cyberneel Dec 20, 2024
dd08767
Formatting
cyberneel Dec 21, 2024
07d1667
Added RTT logging
cyberneel Dec 21, 2024
7ea3de3
Added debugging through watch notifications
cyberneel Dec 24, 2024
ad47ee0
Removed encryption of battery and added required pairing on connect G…
cyberneel Dec 29, 2024
f636067
ANCS Partially works
cyberneel Dec 29, 2024
c1997d5
Started working on using Control Point and Data Source:
cyberneel Dec 30, 2024
cf10a8b
More debugging
cyberneel Dec 31, 2024
4500493
Got Datsource to somewhat work and notifications now show title.
cyberneel Dec 31, 2024
202a0bd
Fixed check to only let new notifications pass (maybe)
cyberneel Dec 31, 2024
08882ad
Proper Notification works.
cyberneel Jan 1, 2025
6347ee3
Fixed the event flag detection. Only shows a notification when it's new.
cyberneel Jan 2, 2025
6d3d668
Fixed first letter in Subtitle being cut off
cyberneel Jan 2, 2025
d6c5bac
Improve message display
liamcharger Jan 2, 2025
f148fea
Updated notification, Title and Subtitle are in the orange test and m…
cyberneel Jan 2, 2025
756201e
Implemented Incoming Calls to ANCS
cyberneel Jan 3, 2025
f3aff14
Turned off non important debugs
cyberneel Jan 3, 2025
54908fb
Formatted Code
cyberneel Jan 3, 2025
23242e8
Remove RTT settings
liamcharger Jan 3, 2025
72c3b04
Removed more debug notifications
cyberneel Jan 4, 2025
7a7f1b0
Added check for special characters and show unknown symbol.
cyberneel Jan 5, 2025
f0850c0
removed all debug notifications
cyberneel Jan 5, 2025
624cef1
Ignore silent notifications
cyberneel Jan 5, 2025
4c4d21e
Make trunacated messages end in "..." to indicate that message has mo…
cyberneel Jan 5, 2025
77f5ce2
Fix typos and remove unnecessary commented code
liamcharger Jan 16, 2025
3ae4561
Made codebase changes based on PR Comments
cyberneel May 19, 2025
f8b55d9
Added security initiate back for ancs to work.
cyberneel Jul 12, 2025
b29d4fb
Updated checks to prevent duplicate notification triggers, NEEDS test…
cyberneel Jul 12, 2025
da887a4
Check for silent before ringing notification
cyberneel Jul 13, 2025
9244a3d
Fix typo in ping check
cyberneel Jul 13, 2025
32e0cbe
ignore notifications not in list
cyberneel Jul 14, 2025
59a6e8d
Added a session uuid cache to try to help with duplicate pings
cyberneel Jul 14, 2025
733935f
clear session uids on reset
cyberneel Jul 14, 2025
3a63df3
removed extra array
cyberneel Jul 18, 2025
07f6b72
Fix formatting
liamcharger Nov 5, 2025
1599edb
Merge branch 'ancs' into ancs-fix-crashes
tituscmd Nov 8, 2025
2345e12
Merge pull request #19 from InfiniBros/ancs-fix-crashes
cyberneel Nov 8, 2025
459701d
Merge branch 'main' into ancs
liamcharger Dec 16, 2025
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 @@ -461,6 +461,7 @@ list(APPEND SOURCE_FILES
components/ble/SimpleWeatherService.cpp
components/ble/NavigationService.cpp
components/ble/BatteryInformationService.cpp
components/ble/AppleNotificationCenterClient.cpp
components/ble/FSService.cpp
components/ble/ImmediateAlertService.cpp
components/ble/ServiceDiscovery.cpp
Expand Down Expand Up @@ -531,6 +532,7 @@ list(APPEND RECOVERY_SOURCE_FILES
components/ble/MusicService.cpp
components/ble/SimpleWeatherService.cpp
components/ble/BatteryInformationService.cpp
components/ble/AppleNotificationCenterClient.cpp
components/ble/FSService.cpp
components/ble/ImmediateAlertService.cpp
components/ble/ServiceDiscovery.cpp
Expand Down Expand Up @@ -648,6 +650,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
562 changes: 562 additions & 0 deletions src/components/ble/AppleNotificationCenterClient.cpp

Large diffs are not rendered by default.

140 changes: 140 additions & 0 deletions src/components/ble/AppleNotificationCenterClient.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#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"
#include <unordered_map>
#include <string>

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);
int OnControlPointWrite(uint16_t connectionHandle, const ble_gatt_error* error, ble_gatt_attr* attribute);
void MaybeFinishDiscovery(uint16_t connectionHandle);
void OnNotification(ble_gap_event* event);
void Reset();
void Discover(uint16_t connectionHandle, std::function<void(uint16_t)> lambda) override;
void DebugNotification(const char* msg) const;

void AcceptIncomingCall(uint32_t notificationUid);
void RejectIncomingCall(uint32_t notificationUid);

static constexpr uint8_t maxTitleSize {20};
static constexpr uint8_t maxSubtitleSize {15};
static constexpr uint8_t maxMessageSize {120};

// The Apple Notification Center Service UUID are from
// https://developer.apple.com/library/archive/documentation/CoreBluetooth/Reference/AppleNotificationCenterServiceSpecification/Specification/Specification.html

// 7905F431-B5CE-4E99-A40F-4B1E122D00D0
static constexpr ble_uuid128_t ancsUuid {
.u {.type = BLE_UUID_TYPE_128},
.value = {0xd0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4, 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79}};

private:
// 9FBF120D-6301-42D9-8C58-25E699A21DBD
static constexpr ble_uuid128_t notificationSourceChar {
.u {.type = BLE_UUID_TYPE_128},
.value = {0xBD, 0x1D, 0xA2, 0x99, 0xE6, 0x25, 0x58, 0x8C, 0xD9, 0x42, 0x01, 0x63, 0x0D, 0x12, 0xBF, 0x9F}};
// 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9
static constexpr ble_uuid128_t controlPointChar {
.u {.type = BLE_UUID_TYPE_128},
.value = {0xD9, 0xD9, 0xAA, 0xFD, 0xBD, 0x9B, 0x21, 0x98, 0xA8, 0x49, 0xE1, 0x45, 0xF3, 0xD8, 0xD1, 0x69}};
// 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB
static constexpr ble_uuid128_t dataSourceChar {
.u {.type = BLE_UUID_TYPE_128},
.value = {0xFB, 0x7B, 0x7C, 0xCE, 0x6A, 0xB3, 0x44, 0xBE, 0xB5, 0x4B, 0xD6, 0x24, 0xE9, 0xC6, 0xEA, 0x22}};

static constexpr ble_uuid16_t gattServiceUuid = {BLE_UUID_TYPE_16, 0x1801};
static constexpr ble_uuid16_t serviceChangedCharUuid = {BLE_UUID_TYPE_16, 0x2A05};

enum class Categories : uint8_t {
Copy link
Member

Choose a reason for hiding this comment

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

For all of these constants above and below, is there a reference document somewhere? It would be great to have the URL linked in a comment

Copy link
Author

Choose a reason for hiding this comment

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

If you are talking about the UUIDs, they are from the Apple ANCS spec sheet. I'll add a comment linking it and explaining this.

Here it is for quick reference: Apple ANCS

Other = 0,
IncomingCall = 1,
MissedCall = 2,
Voicemail = 3,
Social = 4,
Schedule = 5,
Email = 6,
News = 7,
HealthAndFitness = 8,
BuissnessAndFinance = 9,
Location = 10,
Entertainment = 11
};

enum class EventIds : uint8_t { Added = 0, Modified = 1, Removed = 2 };

enum class EventFlags : uint8_t {
Silent = (1 << 0),
Important = (1 << 1),
PreExisting = (1 << 2),
PositiveAction = (1 << 3),
NegativeAction = (1 << 4)
};

struct AncsNotification {
uint8_t eventId {0};
uint8_t eventFlags {0};
uint8_t category {0};
uint32_t uuid {0};
bool isProcessed {false};
};

std::unordered_map<uint32_t, AncsNotification> notifications;

std::string DecodeUtf8String(os_mbuf* om, uint16_t size, uint16_t offset);

bool subscriptionsDone = false;
uint16_t ancsStartHandle {0};
uint16_t ancsEndHandle {0};
uint16_t notificationSourceHandle {0};
uint16_t controlPointHandle {0};
uint16_t dataSourceHandle {0};
uint16_t notificationSourceDescriptorHandle {0};
uint16_t controlPointDescriptorHandle {0};
uint16_t dataSourceDescriptorHandle {0};

uint16_t gattStartHandle {0};
uint16_t gattEndHandle {0};
uint16_t serviceChangedHandle {0};
uint16_t serviceChangedDescriptorHandle {0};
bool isGattDiscovered {false};
bool isGattCharacteristicDiscovered {false};
bool isGattDescriptorFound {false};
bool isDiscovered {false};
bool isCharacteristicDiscovered {false};
bool isDescriptorFound {false};
bool isControlCharacteristicDiscovered {false};
bool isControlDescriptorFound {false};
bool isDataCharacteristicDiscovered {false};
bool isDataDescriptorFound {false};
Comment on lines +125 to +134
Copy link
Member

Choose a reason for hiding this comment

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

Massive networks of booleans like this scare me a bit. There are theoretically 2^10 = 1024 states here. Could this be refactored into a state enum somehow?

Copy link
Author

Choose a reason for hiding this comment

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

So what happened was, I used the existing BLE code for the Android notification system, and It was keeping track of if various characteristics were found of not. They are not all used together so I don't think there are 1024 states.

At some point I got lost and started keeping track of each service and made some of my code conditionally run based on these booleans and it started working.

I think we could maybe cut these down if we analyze where the BLE stack actually needs these confirmations.

Pinetime::System::SystemTask& systemTask;
Pinetime::Controllers::NotificationManager& notificationManager;
std::function<void(uint16_t)> onServiceDiscovered;
};
}
}
2 changes: 1 addition & 1 deletion src/components/ble/BatteryInformationService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ BatteryInformationService::BatteryInformationService(Controllers::Battery& batte
characteristicDefinition {{.uuid = &batteryLevelUuid.u,
.access_cb = BatteryInformationServiceCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC | BLE_GATT_CHR_F_READ_AUTHEN | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &batteryLevelHandle},
{0}},
serviceDefinition {
Expand Down
12 changes: 9 additions & 3 deletions src/components/ble/NimbleController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
heartRateService {*this, heartRateController},
motionService {*this, motionController},
fsService {systemTask, fs},
serviceDiscovery({&currentTimeClient, &alertNotificationClient}) {
ancsClient {systemTask, notificationManager},
serviceDiscovery({&currentTimeClient, &alertNotificationClient, &ancsClient}) {
}

void nimble_on_reset(int reason) {
Expand Down Expand Up @@ -161,8 +162,9 @@ void NimbleController::StartAdvertising() {
fields.uuids16 = &HeartRateService::heartRateServiceUuid;
fields.num_uuids16 = 1;
fields.uuids16_is_complete = 1;
fields.uuids128 = &DfuService::serviceUuid;
fields.num_uuids128 = 1;
const ble_uuid128_t uuids128[2] = {DfuService::serviceUuid, AppleNotificationCenterClient::ancsUuid};
Copy link
Member

Choose a reason for hiding this comment

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

Is all OK size wise? I have vague memories of there not being space in the advertisement packet for more 128bit UUIDS. Could be totally wrong on this

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure...

I read somewhere ANCS needs to be advertised from the watch and added this, but I forgot if it actually advertises or what.

Something to look into.

fields.uuids128 = uuids128;
fields.num_uuids128 = 2;
fields.uuids128_is_complete = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;

Expand Down Expand Up @@ -200,6 +202,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
/* Connection failed; resume advertising. */
currentTimeClient.Reset();
alertNotificationClient.Reset();
ancsClient.Reset();
connectionHandle = BLE_HS_CONN_HANDLE_NONE;
bleController.Disconnect();
fastAdvCount = 0;
Expand All @@ -209,6 +212,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
bleController.Connect();
systemTask.PushMessage(Pinetime::System::Messages::BleConnected);
// Service discovery is deferred via systemtask
ble_gap_security_initiate(event->connect.conn_handle);
}
break;

Expand All @@ -223,6 +227,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {

currentTimeClient.Reset();
alertNotificationClient.Reset();
ancsClient.Reset();
connectionHandle = BLE_HS_CONN_HANDLE_NONE;
if (bleController.IsConnected()) {
bleController.Disconnect();
Expand Down Expand Up @@ -370,6 +375,7 @@ int NimbleController::OnGAPEvent(ble_gap_event* event) {
notifSize);

alertNotificationClient.OnNotification(event);
ancsClient.OnNotification(event);
} break;

case BLE_GAP_EVENT_NOTIFY_TX:
Expand Down
6 changes: 6 additions & 0 deletions src/components/ble/NimbleController.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "components/ble/MotionService.h"
#include "components/ble/SimpleWeatherService.h"
#include "components/fs/FS.h"
#include "components/ble/AppleNotificationCenterClient.h"

namespace Pinetime {
namespace Drivers {
Expand Down Expand Up @@ -67,6 +68,10 @@ namespace Pinetime {
return anService;
};

Pinetime::Controllers::AppleNotificationCenterClient& ancs() {
return ancsClient;
};

Pinetime::Controllers::SimpleWeatherService& weather() {
return weatherService;
};
Expand Down Expand Up @@ -106,6 +111,7 @@ namespace Pinetime {
HeartRateService heartRateService;
MotionService motionService;
FSService fsService;
AppleNotificationCenterClient ancsClient;
ServiceDiscovery serviceDiscovery;

uint8_t addrType;
Expand Down
2 changes: 2 additions & 0 deletions src/components/ble/NotificationManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ namespace Pinetime {
Id id = 0;
bool valid = false;

uint32_t ancsUid = 0;

const char* Message() const;
const char* Title() const;
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/ble/ServiceDiscovery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using namespace Pinetime::Controllers;

ServiceDiscovery::ServiceDiscovery(std::array<BleClient*, 2>&& clients) : clients {clients} {
ServiceDiscovery::ServiceDiscovery(std::array<BleClient*, 3>&& clients) : clients {clients} {
}

void ServiceDiscovery::StartDiscovery(uint16_t connectionHandle) {
Expand Down
4 changes: 2 additions & 2 deletions src/components/ble/ServiceDiscovery.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ namespace Pinetime {

class ServiceDiscovery {
public:
ServiceDiscovery(std::array<BleClient*, 2>&& bleClients);
ServiceDiscovery(std::array<BleClient*, 3>&& bleClients);

void StartDiscovery(uint16_t connectionHandle);

private:
BleClient** clientIterator;
std::array<BleClient*, 2> clients;
std::array<BleClient*, 3> clients;
void OnServiceDiscovered(uint16_t connectionHandle);
void DiscoverNextService(uint16_t connectionHandle);
};
Expand Down
2 changes: 2 additions & 0 deletions src/displayapp/DisplayApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
currentScreen = std::make_unique<Screens::Notifications>(this,
notificationManager,
systemTask->nimble().alertService(),
systemTask->nimble().ancs(),
motorController,
*systemTask,
Screens::Notifications::Modes::Normal);
Expand All @@ -574,6 +575,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
currentScreen = std::make_unique<Screens::Notifications>(this,
notificationManager,
systemTask->nimble().alertService(),
systemTask->nimble().ancs(),
motorController,
*systemTask,
Screens::Notifications::Modes::Preview);
Expand Down
2 changes: 1 addition & 1 deletion src/displayapp/fonts/fonts.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"sources": [
{
"file": "JetBrainsMono-Bold.ttf",
"range": "0x20-0x7e, 0x410-0x44f, 0xB0"
"range": "0x20-0x7e, 0x410-0x44f, 0xB0, 0xFFFD"
},
{
"file": "FontAwesome5-Solid+Brands+Regular.woff",
Expand Down
Loading
Loading