0

So I have an iOS app that advertises as a BLE peripheral and sees other peripherals using CBPeripheralManager and CBCentralManager, and when I'm running those samples as iOS to iOS everything works fine. I also have another app on Android that does the same thing, and Android to Android can see each other fine as well. Where this gets messy is Android can see iOS devices advertising, but iOS can't see the advertising Android device. Anyone run into this situation before? My Android advertising code looks like this:

    private void advertise( String someString ) {

    if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && BluetoothAdapter.getDefaultAdapter().isMultipleAdvertisementSupported() ) {
        BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();

        AdvertiseSettings settings = new AdvertiseSettings.Builder()
                .setAdvertiseMode( AdvertiseSettings.ADVERTISE_MODE_BALANCED )
                .setTxPowerLevel( AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM )
                .setConnectable( false )
                .build();

        UUID uuid = UUID.fromString( mUUID );
        ParcelUuid pUuid = new ParcelUuid( uuid );

        AdvertiseData data = new AdvertiseData.Builder()
                .setIncludeDeviceName( true )
                .setIncludeTxPowerLevel( true )
                .addServiceUuid( pUuid )
                .addServiceData( pUuid, someString.getBytes( Charset.forName("UTF-8") ) )
                .build();

        //Using a wrapper class for the callback for backwards compatibility issues
        advertiser.startAdvertising(settings, data, AdvertisingCallbackCreator.getAdvertiseCallback());
    } else {
        if( Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ) {
            DLog.v( "Device requires Lollipop or higher to broadcast over bluetooth" );
        } else {
            DLog.v( "Device must support multiple advertising in order to broadcast over bluetooth. http://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#isMultipleAdvertisementSupported() ");
        }
    }
}

My iOS apps are fairly simple, one target is a scanner and the other is an advertiser. The scanner that's having problems viewing Android looks like this in the ViewController class:

#import "ViewController.h"

@interface ViewController ()

@property (strong, nonatomic) CBCentralManager* pMgr;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self initWithUUID:nil];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)centralManagerDidUpdateState:(CBCentralManager *)peripheral{
    if (peripheral.state == CBCentralManagerStatePoweredOn) {
        [self startDiscovery];
    }
}

-(void)initWithUUID:(CBUUID*) uuid{
    if( self ) {
        _serviceUUID = uuid;
        _pMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

    }
}

-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
    NSLog( @"%@", advertisementData );
}

-(void) startDiscovery {
    NSArray *array = @[[CBUUID UUIDWithString:@"6098FDEF-B82C-43F1-8BFB-18757743BA10"]];

    [_pMgr scanForPeripheralsWithServices:array options:nil];
}

@end

My advertiser that can be seen by both Android and iOS looks like this:

#import "ViewController.h"

@interface ViewController ()

@property (strong, nonatomic) CBPeripheralManager* pMgr;
@property (strong, nonatomic) CBMutableService* shareService;
@property (strong, nonatomic) CBMutableCharacteristic* notificationCharacteristic;
@property (strong, nonatomic) NSMutableArray *queuedWrites;
@property (strong, nonatomic) NSLock *queuedWritesLock;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self initWithUUID:[CBUUID UUIDWithString:@"6098FDEF-B82C-43F1-8BFB-18757743BA10"]];
    [self startAdvertising];
}

-(void) initWithUUID:(CBUUID*) uuid{
    if( self ) {
        _serviceUUID = uuid;
        _pMgr = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
        self.bkvsName = [[UIDevice currentDevice] name];
    }
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void) startAdvertising {
    if ([_pMgr state] == CBPeripheralManagerStatePoweredOn) {
        NSDictionary* adDictionary = @{ CBAdvertisementDataLocalNameKey :
                                            [NSString stringWithFormat:@"BKV %@", _bkvsName ],
                                        CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:@"6098FDEF-B82C-43F1-8BFB-18757743BA10"]]};
        [_pMgr startAdvertising:adDictionary];
    }
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
        _shareService = [[CBMutableService alloc] initWithType:_serviceUUID primary:YES];

        _notifyUUID = [CBUUID UUIDWithString:@"F098FDEF-B82C-43F1-8BFB-18757743BA10"];
        _notificationCharacteristic = [[CBMutableCharacteristic alloc] initWithType:_notifyUUID
                                                                         properties:CBCharacteristicPropertyNotify
                                                                              value:nil
                                                                        permissions:CBAttributePermissionsReadable];

        [_shareService setCharacteristics:@[_notificationCharacteristic]];

        [_pMgr addService:_shareService];

        [self startAdvertising];

    }
}

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


-(void) stopAdvertising{
    [_pMgr stopAdvertising];
}

@end

If anyone has some pointers on what may be wrong, that'd be awesome. I tried to keep the iOS code super simple since I'm just starting out learning it.

Edit:

Raw packet from the advertising iOS device:

2015-09-22 21:30:18.518 BLEScanner[724:42204] {
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = "BKV EV59'\U2019\U2018`s iphone ";
    kCBAdvDataServiceUUIDs =     (
        "6098FDEF-B82C-43F1-8BFB-18757743BA10"
    );
}

Also when nil is used instead of array in scanForPeripheralsWithServices, the Android device still isn't seen. The scanner does pick up another bluetooth device I have nearby that is in advertising mode (a bluetooth enabled toy, not a phone), however.

While I don't think this comes into play, the devices I'm testing with are a Nexus 9 on M Preview 3, Nexus 6 on 5.1.1, and two iPod Touches (recent models running iOS 8.3 and 8.4)

Paul Ruiz
  • 2,396
  • 3
  • 27
  • 47
  • 2
    Can the iOS device see the advertisement from Android if the UUID filter is removed? In other words, using `scanForPeripheralsWithServices:nil`? – devunwired Sep 23 '15 at 01:32
  • 1
    Also, what does the raw packet look like that the iOS device advertises? That might yield some clues as to what the iOS scanner is looking for. – devunwired Sep 23 '15 at 01:43
  • 1
    Can the iOS device pick up an Android advertisement that has no service information (just device name)? Using `nil` should have worked around that, unless iOS internally tries to validate service information. Also, what UUID is Android advertising? It can't be the 128-bit ID shown in the iOS code because it wouldn't fit as written. – devunwired Sep 23 '15 at 15:17
  • Android is advertising with the same UUID - private static final String mConnectedRobotsBroadcastUuid = "6098FDEF-B82C-43F1-8BFB-18757743BA10"; I didn't realize Android was expecting a 16 bit UUID since the filter works fine with 128. I'll give that a shot. – Paul Ruiz Sep 23 '15 at 15:20
  • Hmm, as is that packet only leaves 3 bytes to be shared between both the device name and the actual service data string. So unless both the device name and `someString` are almost empty…I would expect you to get a "data too large" error on advertise. – devunwired Sep 23 '15 at 15:50
  • Yeah the 'somedata' is only seven characters, but doesn't give me a problem when it's advertising and scanning android to android. I can't seem to use a 16 bit UUID with Android's UUID.fromString though - it's an illegalargumentexception. – Paul Ruiz Sep 23 '15 at 15:53
  • 16-bit UUIDs are short-hand, they all fit into the following base: "0000XXXX-0000-1000-8000-00805f9b34fb", where the X's are the 16-bit value. Android recognizes that pattern and uses the short version in the AD packet (iOS also). – devunwired Sep 23 '15 at 15:56
  • To clarify, Android can handle either UUID type. But a non-standard (128-bit) UUID can't be shortened, so it takes up 18 bytes out of the AD packet instead of 4. – devunwired Sep 23 '15 at 16:16
  • When I log out the UUID from the AdvertiseData object, it's the same UUID that I passed in as 128 bits. There's definitely something fishy going on with the IDs though, as I just commented out .addServiceUuid(puuid) and then I saw the Android device on iOS. – Paul Ruiz Sep 23 '15 at 16:48
  • Soon as I removed '.addServiceUuid(pUuid)' from Android, it works fine. I see my data coming through with the device name in Android. That's pretty weird. – Paul Ruiz Sep 23 '15 at 17:46
  • Agreed. I'd be curious if iOS will accept a SIG-approved service UUID instead. – devunwired Sep 23 '15 at 17:48
  • After changing my filter to NSArray *array = @[[CBUUID UUIDWithString:@"0010fdef-b82c-43f1-8bfb-18757743ba10"], [CBUUID UUIDWithString:@"fdef"]]; and removing that addServiceUuid line while keeping addServiceData, I can pull in all the data from Android in iOS. It's definitely only broadcasting with the 16 bit UUID, which may not work for what I'm doing, but at least it works. Thanks for the help getting this far! Now I'll see if I can pull off service data from iOS on Android. – Paul Ruiz Sep 23 '15 at 19:39

0 Answers0