Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / controller / python / chip / ble / darwin / Scanning.mm
1 #include <ble/BleUUID.h>
2 #include <ble/CHIPBleServiceData.h>
3 #include <platform/Darwin/UUIDHelper.h>
4 #include <support/CHIPMem.h>
5 #include <support/logging/CHIPLogging.h>
6
7 #import <CoreBluetooth/CoreBluetooth.h>
8
9 namespace {
10 struct PyObject;
11 using DeviceScannedCallback
12     = void (*)(PyObject * context, const char * address, uint16_t discriminator, uint16_t vendorId, uint16_t productId);
13 using ScanCompleteCallback = void (*)(PyObject * context);
14 }
15
16 @interface ChipDeviceBleScanner : NSObject <CBCentralManagerDelegate>
17
18 @property (strong, nonatomic) dispatch_queue_t workQueue;
19 @property (nonatomic, readonly, nullable) dispatch_source_t timer;
20 @property (strong, nonatomic) CBCentralManager * centralManager;
21 @property (strong, nonatomic) CBUUID * shortServiceUUID;
22
23 @property (assign, nonatomic) PyObject * context;
24 @property (assign, nonatomic) DeviceScannedCallback scanCallback;
25 @property (assign, nonatomic) ScanCompleteCallback completeCallback;
26
27 - (id)initWithContext:(PyObject *)context
28          scanCallback:(DeviceScannedCallback)scanCallback
29      completeCallback:(ScanCompleteCallback)completeCallback
30             timeoutMs:(uint32_t)timeout;
31
32 - (void)stopTimeoutReached;
33
34 @end
35
36 @implementation ChipDeviceBleScanner
37
38 - (id)initWithContext:(PyObject *)context
39          scanCallback:(DeviceScannedCallback)scanCallback
40      completeCallback:(ScanCompleteCallback)completeCallback
41             timeoutMs:(uint32_t)timeout
42 {
43     self = [super init];
44     if (self) {
45         self.shortServiceUUID = [UUIDHelper GetShortestServiceUUID:&chip::Ble::CHIP_BLE_SVC_ID];
46
47         _workQueue = dispatch_queue_create("com.chip.python.ble.work_queue", DISPATCH_QUEUE_SERIAL);
48         _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _workQueue);
49         _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:_workQueue];
50         _context = context;
51         _scanCallback = scanCallback;
52         _completeCallback = completeCallback;
53
54         dispatch_source_set_event_handler(_timer, ^{
55             [self stopTimeoutReached];
56         });
57         dispatch_source_set_timer(
58             _timer, dispatch_walltime(NULL, timeout * NSEC_PER_MSEC), DISPATCH_TIME_FOREVER, 50 * NSEC_PER_MSEC);
59         dispatch_resume(_timer);
60     }
61     return self;
62 }
63
64 - (void)centralManager:(CBCentralManager *)central
65     didDiscoverPeripheral:(CBPeripheral *)peripheral
66         advertisementData:(NSDictionary *)advertisementData
67                      RSSI:(NSNumber *)RSSI
68 {
69     NSNumber * isConnectable = [advertisementData objectForKey:CBAdvertisementDataIsConnectable];
70
71     if (![isConnectable boolValue]) {
72         return;
73     }
74
75     NSDictionary * servicesData = [advertisementData objectForKey:CBAdvertisementDataServiceDataKey];
76     for (CBUUID * serviceUUID in servicesData) {
77         if (![serviceUUID.data isEqualToData:_shortServiceUUID.data]) {
78             continue;
79         }
80         NSData * serviceData = [servicesData objectForKey:serviceUUID];
81
82         NSUInteger length = [serviceData length];
83         if (length != sizeof(chip::Ble::ChipBLEDeviceIdentificationInfo)) {
84             ChipLogError(Ble, "Device has invalid advertisement data length.");
85             break;
86         }
87
88         chip::Ble::ChipBLEDeviceIdentificationInfo data;
89         memcpy(&data, [serviceData bytes], sizeof(data));
90
91         _scanCallback(_context, [peripheral.identifier.UUIDString UTF8String], data.GetDeviceDiscriminator(), data.GetVendorId(),
92             data.GetProductId());
93
94         break;
95     }
96 }
97
98 - (void)stopTimeoutReached
99 {
100     ChipLogProgress(Ble, "Scan timeout reached.");
101
102     _completeCallback(_context);
103
104     dispatch_source_cancel(_timer);
105     [self.centralManager stopScan];
106     self.centralManager = nil;
107 }
108
109 - (void)centralManagerDidUpdateState:(CBCentralManager *)central
110 {
111     switch (central.state) {
112     case CBManagerStatePoweredOn:
113         ChipLogProgress(Ble, "Central BLE Manager is on. Starting to scan.");
114         [central scanForPeripheralsWithServices:@[ _shortServiceUUID ] options:nil];
115         break;
116     default:
117         ChipLogError(Ble, "CBManagerState is NOT ON. Unable to scan.");
118         break;
119     }
120 }
121
122 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
123 {
124 }
125
126 @end
127
128 extern "C" void * pychip_ble_start_scanning(
129     PyObject * context, void * adapter, uint32_t timeout, DeviceScannedCallback scanCallback, ScanCompleteCallback completeCallback)
130 {
131     // NOTE: adapter is ignored as it does not apply to mac
132
133     ChipDeviceBleScanner * scanner = [[ChipDeviceBleScanner alloc] initWithContext:context
134                                                                       scanCallback:scanCallback
135                                                                   completeCallback:completeCallback
136                                                                          timeoutMs:timeout];
137
138     return static_cast<void *>(scanner);
139 }