stkvtflw
stkvtflw

Reputation: 13537

iOS/Objective-C: BLE in Peripheral mode throws `libsystem_kernel.dylib`__abort_with_payload`

I'm trying to launch BLE in both Central and Peripheral modes. With hardcoded variables for now for sake of simplicity.
I think i've implemented everything according to the docs. I'm unable to check if Central mode works, but when the code contained only Central-mode-related code, the app was asking to enable BT, and wasn't crashing so i assume it was scanning properly :) I'll check this later.

After i've added Peripheral mode in there, app started freezing once i've initiating start method.

Xcode shows very informative log:

libsystem_kernel.dylib`__abort_with_payload:
    0x18acc5d6c <+0>:  movz   x16, #0x209
    0x18acc5d70 <+4>:  svc    #0x80
->  0x18acc5d74 <+8>:  b.lo   0x18acc5d8c               ; <+32>
    0x18acc5d78 <+12>: stp    x29, x30, [sp, #-16]!
    0x18acc5d7c <+16>: mov    x29, sp
    0x18acc5d80 <+20>: bl     0x18acaa7d0               ; cerror_nocancel
    0x18acc5d84 <+24>: mov    sp, x29
    0x18acc5d88 <+28>: ldp    x29, x30, [sp], #16
    0x18acc5d8c <+32>: ret    

Nothing else, no errors, no warnings etc. This is not related to Central mode, because if i'm removing it, nothing changes.
So, what does it mean? I've googled a bit and found misc assumptions about permission descriptions, iOS 7 etc.

I'm barely familiar with objective-c, so sorry, if i'm asking very simple question :)

sendEventWithName in the code below works like logs atm. The only log i'm receiving is started (at the end of start method`)

Here is .h:

#import <RCTBridgeModule.h>
#import <RCTEventEmitter.h>
@import CoreBluetooth;
@import QuartzCore;

@interface BTManager : RCTEventEmitter <RCTBridgeModule, CBCentralManagerDelegate, CBPeripheralManagerDelegate, CBPeripheralDelegate>

@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) CBMutableCharacteristic *transferCharacteristic;

@end

And .m:

#import "BTManager.h"

@implementation BTManager

RCT_EXPORT_MODULE();

- (NSArray<NSString *> *)supportedEvents
{
  return @[@"BTManagerDeviceFound", @"BTManagerStatus"];
}

- (void)viewDidLoad
{
  CBCentralManager *centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
  self.centralManager = centralManager;
  CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
  self.peripheralManager = peripheralManager;
}

RCT_EXPORT_METHOD(start:(NSDictionary *)options)
{

  [self.centralManager scanForPeripheralsWithServices:nil options:nil];

  self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:@"EB6727C4-F184-497A-A656-76B0CDAC633A"] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];

  CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:@"EB6727C4-F184-497A-A656-76B0CDAC633A"] primary:YES];

  transferService.characteristics = @[self.transferCharacteristic];

  [self.peripheralManager addService:transferService];

  [self.peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"FB694B90-F49E-4597-8306-171BBA78F846"]] }];

  [self sendEventWithName:@"BTManagerStatus" body:@"started"];
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
  // log peripheralManager state
  NSLog(@"peripheralManagerDidUpdateState peripheral %@", peripheral);
}

- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
{
  // log centralManager state
  NSLog(@"peripheralManager didAddService peripheral %@", peripheral);
  NSLog(@"peripheralManager didAddService service %@", service);
  NSLog(@"peripheralManager didAddService error %@", error);
}

- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
{
  NSLog(@"peripheralManagerDidStartAdvertising peripheral %@", peripheral);
  NSLog(@"peripheralManagerDidStartAdvertising error %@", error);
}

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
  [self sendEventWithName:@"BTManagerDeviceFound" body:advertisementData];
}

- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
  NSLog(@"centralManagerDidUpdateState central %@", central);
}

RCT_EXPORT_METHOD(stop:(NSDictionary *)options)
{
  // remove all related processes, send event to js
  [self sendEventWithName:@"BTManagerStatus" body:@"details here"];
  //  [self.myCentralManager stopScan];
}

@end

Upvotes: 0

Views: 349

Answers (1)

davidgyoung
davidgyoung

Reputation: 64941

A few thoughts:

  1. The code declares CBCentralManager *centralManager and CBPeripheralManager *peripheralManager as local variables inside the start method. These variables will go out of scope when the method ends and cease to exist, preventing bluetooth operations from continuing. You need to declare these as class-level variables, then initialize them in the the start method. On a related subject, make sure that whatever code creates and executes your BTManager allows the object to live for long enough for the bluetooth actions to be performed. If it goes out of scope or gets garbage collected, you'll have the same problem.

  2. Code should not perform actions on either the central or the peripheral until after receiving a powered on callback to: peripheralManagerDidUpdateState or centralManagerDidUpdateState, respectively. I would move the code that scans or sets up advertising to new initialization methods for peripheral/central, and call them when you get a callback to those methods with a value of CBPeripheralManagerStatePoweredOn or CBCentalManagerStatePoweredOn, respectively. Even if the interface is powered on when you code starts, CoreBluetooth won't let you do anything successfully until it has fully initialized and sent the powered on callback, so you must always wait for this.

  3. Make sure you trust your custom logging implementation. When in doubt, NSLog(@"XXXX") is a handy tool for a sanity check.

Upvotes: 1

Related Questions