1// Copyright 2015 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "device/bluetooth/test/mock_bluetooth_cbperipheral_mac.h" 6 7#include "base/mac/foundation_util.h" 8#include "base/mac/scoped_nsobject.h" 9#include "device/bluetooth/test/bluetooth_test_mac.h" 10#include "device/bluetooth/test/mock_bluetooth_cbcharacteristic_mac.h" 11#include "device/bluetooth/test/mock_bluetooth_cbdescriptor_mac.h" 12#include "device/bluetooth/test/mock_bluetooth_cbservice_mac.h" 13 14using base::mac::ObjCCast; 15using base::scoped_nsobject; 16 17@interface MockCBPeripheral () { 18 scoped_nsobject<NSUUID> _identifier; 19 scoped_nsobject<NSString> _name; 20 id<CBPeripheralDelegate> _delegate; 21 scoped_nsobject<NSMutableArray> _services; 22} 23 24@end 25 26@implementation MockCBPeripheral 27 28@synthesize state = _state; 29@synthesize delegate = _delegate; 30@synthesize bluetoothTestMac = _bluetoothTestMac; 31 32- (instancetype)init { 33 [self doesNotRecognizeSelector:_cmd]; 34 return self; 35} 36 37- (instancetype)initWithUTF8StringIdentifier:(const char*)utf8Identifier { 38 return [self initWithUTF8StringIdentifier:utf8Identifier name:nil]; 39} 40 41- (instancetype)initWithUTF8StringIdentifier:(const char*)utf8Identifier 42 name:(NSString*)name { 43 scoped_nsobject<NSUUID> identifier( 44 [[NSUUID alloc] initWithUUIDString:@(utf8Identifier)]); 45 return [self initWithIdentifier:identifier name:name]; 46} 47 48- (instancetype)initWithIdentifier:(NSUUID*)identifier name:(NSString*)name { 49 self = [super init]; 50 if (self) { 51 _identifier.reset([identifier retain]); 52 if (name) { 53 _name.reset([name retain]); 54 } 55 _state = CBPeripheralStateDisconnected; 56 } 57 return self; 58} 59 60- (BOOL)isKindOfClass:(Class)aClass { 61 if (aClass == [CBPeripheral class] || 62 [aClass isSubclassOfClass:[CBPeripheral class]]) { 63 return YES; 64 } 65 return [super isKindOfClass:aClass]; 66} 67 68- (BOOL)isMemberOfClass:(Class)aClass { 69 if (aClass == [CBPeripheral class] || 70 [aClass isSubclassOfClass:[CBPeripheral class]]) { 71 return YES; 72 } 73 return [super isKindOfClass:aClass]; 74} 75 76- (void)setState:(CBPeripheralState)state { 77 _state = state; 78 if (_state == CBPeripheralStateDisconnected) { 79 _services.reset(); 80 } 81} 82 83- (void)discoverServices:(NSArray*)serviceUUIDs { 84 if (_bluetoothTestMac) { 85 _bluetoothTestMac->OnFakeBluetoothServiceDiscovery(); 86 } 87} 88 89- (void)discoverCharacteristics:(NSArray*)characteristics 90 forService:(CBService*)service { 91 if (_bluetoothTestMac) { 92 _bluetoothTestMac->OnFakeBluetoothCharacteristicDiscovery(); 93 } 94} 95 96- (void)discoverDescriptorsForCharacteristic:(CBCharacteristic*)characteristic { 97} 98 99- (void)readValueForCharacteristic:(CBCharacteristic*)characteristic { 100 DCHECK(_bluetoothTestMac); 101 _bluetoothTestMac->OnFakeBluetoothCharacteristicReadValue(); 102} 103 104- (void)writeValue:(NSData*)data 105 forCharacteristic:(CBCharacteristic*)characteristic 106 type:(CBCharacteristicWriteType)type { 107 DCHECK(_bluetoothTestMac); 108 const uint8_t* buffer = static_cast<const uint8_t*>(data.bytes); 109 std::vector<uint8_t> value(buffer, buffer + data.length); 110 _bluetoothTestMac->OnFakeBluetoothCharacteristicWriteValue(value); 111} 112 113- (void)readValueForDescriptor:(CBDescriptor*)descriptor { 114 DCHECK(_bluetoothTestMac); 115 _bluetoothTestMac->OnFakeBluetoothDescriptorReadValue(); 116} 117 118- (void)writeValue:(NSData*)data forDescriptor:(CBDescriptor*)descriptor { 119 DCHECK(_bluetoothTestMac); 120 const uint8_t* buffer = static_cast<const uint8_t*>(data.bytes); 121 std::vector<uint8_t> value(buffer, buffer + data.length); 122 _bluetoothTestMac->OnFakeBluetoothDescriptorWriteValue(value); 123} 124 125- (void)removeAllServices { 126 [_services removeAllObjects]; 127} 128 129- (void)addServices:(NSArray*)services { 130 if (!_services) { 131 _services.reset([[NSMutableArray alloc] init]); 132 } 133 for (CBUUID* uuid in services) { 134 base::scoped_nsobject<MockCBService> service([[MockCBService alloc] 135 initWithPeripheral:self.peripheral 136 CBUUID:uuid 137 primary:YES]); 138 [_services addObject:[service service]]; 139 } 140} 141 142- (void)mockDidDiscoverServicesWithError:(NSError*)error { 143 [_delegate peripheral:self.peripheral didDiscoverServices:error]; 144} 145 146- (void)removeService:(CBService*)service { 147 base::scoped_nsobject<CBService> serviceToRemove(service, 148 base::scoped_policy::RETAIN); 149 DCHECK(serviceToRemove); 150 [_services removeObject:serviceToRemove]; 151 [self didModifyServices:@[ serviceToRemove ]]; 152} 153 154- (void)mockDidDiscoverServices { 155 [_delegate peripheral:self.peripheral didDiscoverServices:nil]; 156} 157 158- (void)mockDidDiscoverCharacteristicsForService:(CBService*)service { 159 [_delegate peripheral:self.peripheral 160 didDiscoverCharacteristicsForService:service 161 error:nil]; 162} 163 164- (void)mockDidDiscoverCharacteristicsForService:(CBService*)service 165 WithError:(NSError*)error { 166 [_delegate peripheral:self.peripheral 167 didDiscoverCharacteristicsForService:service 168 error:error]; 169} 170 171- (void)mockDidDiscoverDescriptorsForCharacteristic: 172 (CBCharacteristic*)characteristic { 173 [_delegate peripheral:self.peripheral 174 didDiscoverDescriptorsForCharacteristic:characteristic 175 error:nil]; 176} 177 178- (void)mockDidDiscoverDescriptorsForCharacteristic: 179 (CBCharacteristic*)characteristic 180 WithError:(NSError*)error { 181 [_delegate peripheral:self.peripheral 182 didDiscoverDescriptorsForCharacteristic:characteristic 183 error:error]; 184} 185 186- (void)mockDidDiscoverEvents { 187 [self mockDidDiscoverServices]; 188 // BluetoothLowEnergyDeviceMac is expected to call 189 // -[CBPeripheral discoverCharacteristics:forService:] for each services, 190 // so -[<CBPeripheralDelegate peripheral:didDiscoverCharacteristicsForService: 191 // error:] needs to be called for all services. 192 for (CBService* service in _services.get()) { 193 [self mockDidDiscoverCharacteristicsForService:service]; 194 for (CBCharacteristic* characteristic in service.characteristics) { 195 // After discovering services, BluetoothLowEnergyDeviceMac is expected to 196 // discover characteristics for all services. 197 [_delegate peripheral:self.peripheral 198 didDiscoverDescriptorsForCharacteristic:characteristic 199 error:nil]; 200 } 201 } 202} 203 204- (void)didModifyServices:(NSArray*)invalidatedServices { 205 DCHECK( 206 [_delegate respondsToSelector:@selector(peripheral:didModifyServices:)]); 207 [_delegate peripheral:self.peripheral didModifyServices:invalidatedServices]; 208} 209 210- (void)didDiscoverDescriptorsWithCharacteristic: 211 (MockCBCharacteristic*)characteristic_mock { 212 [_delegate peripheral:self.peripheral 213 didDiscoverDescriptorsForCharacteristic:characteristic_mock.characteristic 214 error:nil]; 215} 216 217- (NSUUID*)identifier { 218 return _identifier; 219} 220 221- (NSString*)name { 222 return _name; 223} 224 225- (NSArray*)services { 226 return _services; 227} 228 229- (CBPeripheral*)peripheral { 230 return ObjCCast<CBPeripheral>(self); 231} 232 233- (void)setNotifyValue:(BOOL)notification 234 forCharacteristic:(CBCharacteristic*)characteristic { 235 _bluetoothTestMac->OnFakeBluetoothGattSetCharacteristicNotification( 236 notification == YES); 237} 238 239@end 240