Altbeacon and bootstrap always defaults to default beacon format - altbeacon

I have used the Alt-Beacon reference application as a starting point on how to implement bootstrap regions. The application restarts if killed but the beacon format is always being set to Alt-Beacon and not iBeacon.
Logcat on startup:
D/BeaconParser: Parsing beacon layout: m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25
Bootstrap code:
beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().clear();
beaconManager
.getBeaconParsers()
.add(new BeaconParser()
.setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24"));
beaconManager.setBackgroundBetweenScanPeriod(5000l);
beaconManager.setBackgroundScanPeriod(1000l);
beaconManager.setRegionStatePersistenceEnabled(false);
Region region = new Region("backgroundRegion",null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
BackgroundPowerSaver backgroundPowerSaver = new BackgroundPowerSaver(this);
I would expect that when the app was restarted I should see:
m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24
I am using 2.15.2 of AltBeacon in Android Studio 3.1.3 with test device of Samsung S5 Mini running Android 6.0.1
Further information: When the phone is rebooted and the USB connected I would expect to see the app start and listen for iBeacon packets. It appears to restart but is killed by the OS.
Logcat:
12-07 17:32:08.132 8359-8359/net.simplesdktest I/InstantRun: starting instant run server: is main process
12-07 17:32:08.212 8359-8359/net.simplesdktest I/BeaconManager: BeaconManager started up on pid 8359 named 'net.simplesdktest' for application package 'net.simplesdktest'. isMainProcess=true
12-07 17:32:08.357 4889-4889/? I/TaskManager:PackageInfo:
getRunningAppProcesses() - proc:net.simplesdktest, pkg:net.simplesdktest, uid:10346, pid:8359
12-07 17:32:12.822 2390-3458/? I/ActivityManager: Killing 8359:net.simplesdktest/u0a346 (adj 15): DHA:empty #21

setBeaconLayout("m:0-3=4c000215,i:4-19,i:20-21,i:22-23,p:24-24"));
This one detects ibeacon format BLE beacons doesn't it?
Source

It appears that when the beacon manager is first created it adds the AltBeacon format by default. I have managed to clear this out and set iBeacon which is now being detected.

Related

Altbeacon - Samsung Galaxy s9 not ranging beacons as expected when locked and power cable unplugged

I'm using the Altbeacon library and implementing the foreground service to allow for faster ranging. My app is designed to send a notification to the user whenever a matching beacon is ranged. The goal is that this happens no matter what, phone locked, unlocked, app open, app closed, and app cleared from task manager.
The app works as expected with a Samsung Galaxy s7 running Android 8.0. The issue I'm experiencing with the Samsung Galaxy s9 running Android 8.0, is when the power button is pressed to lock the phone. The phone no longer receives the notifications as often as it should, as if the foreground beacon scanning is being blocked.
The device sending the beacons can transmit a new beacon continuously at a 25ms rate for 12 seconds, for testing purposes, at the end of the 12 seconds it is set to repeat that process. The user should receive a new notification every 12 seconds, however the s9 may only receive 1 notification and then 5 could be missed before is sees another. The s7 will receive every single notification as expected.
The background scan rate is set to scan for 1.1 secs and wait for 10 seconds. I wish I could provide logs for the issue but when the phone is plugged in the issue doesn't occur, so I can't really pin point why the s9 would be acting this way when the s7 doesn't. I assume it has to be something with doze mode or sleep mode since when the power cord is plugged in, the s9 receives all the notifications as expected. Those are the only two phones I have been able to test so I don't know if it is also an issue with the s8.
What could be causing the s9 to not range the same as the s7 when locked by the power button? If the phone just goes to sleep on its own, it acts the same as if the power button were pressed to put it to sleep. Code snippet below.
Thanks!
mBeaconManager = BeaconManager.getInstanceForApplication(this.getApplicationContext());
set_forground_notification(true);
logManager.setLogger(Loggers.verboseLogger());
logManager.setVerboseLoggingEnabled(true);
mBeaconManager.setAndroidLScanningDisabled(false); // Setting to false allows low latency scanning
mBeaconManager.setRegionStatePersistenceEnabled(false);
mBeaconManager.setBackgroundScanPeriod(1100);
mBeaconManager.setForegroundScanPeriod(1100);
mBeaconManager.setForegroundBetweenScanPeriod(2000);
mBeaconManager.setBackgroundBetweenScanPeriod(10000);
mBeaconManager.setBackgroundMode(true); // this is being set to true here so that on app termination
// or reboot when the app starts back up because of the service, the scanning period
// is set to background and not foreground
private void set_forground_notification(boolean post)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (post) {
String CHANNEL_ID = "Scanning_for_device";
CharSequence name = "Scanning_for_device";
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, android.app.NotificationManager.IMPORTANCE_HIGH);
mChannel.setShowBadge(false);
mChannel.setSound(null,null);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_foreground_logo)
.setContentTitle("Scanning for Device")
.setSound(null)
.setOnlyAlertOnce(true);
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(pendingIntent);
android.app.NotificationManager mNotificationManager =
(android.app.NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.createNotificationChannel(mChannel);
// sends foreground notification and starts foreground scanning?
mBeaconManager.enableForegroundServiceScanning(mBuilder.build(), 457);
} else {
// removes foreground notification and stops foreground scanning?
mBeaconManager.disableForegroundServiceScanning();
}
}
}

iOS location regions firing events incorrectly since app has been built with Xcode 8

So I have an iOS application that was built with Xcode 7 up until recently. It uses iOS location regions for determining if users enter/exit regions. I simply have code in the DidDetermineState method of the CLLocationManagerDelegate that sends API calls to my backend based on the state (entered/left). It was working very well with very minimal issues up until I upgraded my build machine to Xcode 8 and made a new build. Suddenly, some users are reporting "flip flopping" where they'll be sleeping and their phone will "enter home" then "left home" all while the phone is sitting on their night stand. This never happened before upgrading to Xcode 8. Were there some location changes in Xcode 8 that I missed that I'm not accounting for?
What's strange is I cannot reproduce it. And they claim that it's working normally, but misfiring a lot. I'm looking at their logs and seeing exactly what they're describing. It'll be 3am and DidDetermineState will fire with "left home" only to "enter home" seconds or minutes later. This seems to be ~10+% of users (total rough estimation).
public override void DidDetermineState(CLLocationManager manager, CLRegionState state, CLRegion region)
{
this.HandleRegionEnterExitDebugStuff("Did Determine State = " + state, manager, region);
//Console.WriteLine("region.id = " + region.Identifier);
// This is all code that hasn't changed in a long time and worked fine when built with Xcode 7. But this event is misfiring (false positives) with Xcode 8....
string message = null;
switch (state)
{
case CLRegionState.Inside:
RegionLogic (region, manager, "Region Entered", LocationManager.Location_WebHook_Enter);
break;
case CLRegionState.Outside:
RegionLogic (region, manager, "Region Exit", LocationManager.Location_WebHook_Exit);
break;
case CLRegionState.Unknown:
message = String.Format("DidDetermineState state = CLRegionState.Unknown for region.Identifier = " + region.Identifier);
LogManager.LogMessage(
LogManager.LogType.Location,
message
);
LogManager.LogMessage(
LogManager.LogType.CriticalInfo,
message
);
break;
default:
message = String.Format("DidDetermineState state = invalid for region.Identifier = " + region.Identifier);
LogManager.LogMessage(
LogManager.LogType.Location,
message
);
LogManager.LogMessage(
LogManager.LogType.CriticalInfo,
message
);
break;
}
}
Additional information:
So far i've dug into 3 users and confirmed they're seeing "false positives" via their logs and they have the following information:
User 1: iOS 10.3.1 iPhone 6s. Location USA. Language English.
User 2: iOS 10.3.1 iPhone 6s. Location USA. Language English.
User 3: iOS 10.3.2 (beta version) iPhone 7. Location USA. Language English.
Me (cannot reproduce issue): iOS 10.3.1 iPhone 6. Location USA. Language English.
Application is built with Xamarin.iOS
Target Deployment is 9.0

NearbyAPI iOS doesn't detect beacon already in range

I've set up Google Nearby API for my objective-c project to scan for beacons.
The app detects the beacons fine when moving into the range of a beacon but it does't work if I start the app when I'm already in range. I have to walk away from the beacon and return.
I am not using background scanning. The lib version I use is: 0.10.0
My code is:
[GNSMessageManager setDebugLoggingEnabled:YES];
_messageManager = [[GNSMessageManager alloc] initWithAPIKey:#"..."];
_beaconSubscription = [_messageManager subscriptionWithMessageFoundHandler:^(GNSMessage *message) {
NSLog(#"beacon found: %#",message);
...
} messageLostHandler:^(GNSMessage *message) {
NSLog(#"beacon lost: %#",message);
...
} paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
params.beaconStrategy = [GNSBeaconStrategy strategyWithParamsBlock:^(GNSBeaconStrategyParams *params) {
params.includeIBeacons = YES;
}];
}];
I know about the Core Location Framework didEnterRegion / didExitRegion methods that are called only when crossing the boundaries of a beacon region and that I can use didDetermineState method but how does the NearbyAPI work on the inside with these and how can I make the app detect the beacons already in range at startup using it?
This is indeed a bug in the way Nearby Messages monitors iBeacon regions. It uses didEnterRegion/didExitRegion, and as you stated, if you're already in a region when scanning starts, didEnterRegion isn't called.
I've experimented with using didDetermineState, and with a bit of work I'm now able to handle this case. We will include this in the next bug fix release.
In the meantime, here's a trick you can use to avoid the problem while testing your app: Put your beacon into a metal enclosure (a faraday cage), and remove it from the enclosure after your app starts scanning for beacons. This simulates movement into the beacon region. I use a small cocktail shaker for my faraday cage, but a small amount of aluminum foil also works.

Problems with connecting to IBeacon

I am using the example from here to connect IBeacon. I believe that my UUID is correct. But the Event RegionEntered is never called and e.Beacons.Length in DidRangeBeacons event is always 0.
locationMgr.DidRangeBeacons += (object sender, CLRegionBeaconsRangedEventArgs e) => {
var a = e.Region;
if (e.Beacons.Length > 0) {
//make notification
}
}
The difference from above mentioned sample is that I use the IBeacon instead of IPad.
Check to be sure you know the ProcimityUUID of your beacon by using the Locate app for iOS. You will need to configure the app with your ProximityUUID.
If the app will not detect your beacon, the beacon may be misconfigured or you may not have the proper UUID.
EDIT: I have added instructions for how to scan for your ProximityUUID here.

OSX Bluetooth LE Peripheral transfer rates are slow

Background Info:
I've implemented a Bluetooth LE Peripheral for OSX which exposes two characteristics (using CoreBluetooth). One is readable, and one is writable (both with indications on). I've implemented a Bluetooth LE Central on iOS which will read from the readable characteristic and write to the writable characteristic. I've set it up so that every time the characteristic value is read, the value is updated (in a way similar to this example). The transfer rates I get with this set up are pathetically slow (topping out at a measured sustained speed of roughly 340 bytes / second). This speed is the actual data, and not a measure including the packet details, ACKs and so on.
Problem:
This sustained speed is too slow. I've considered two solutions:
There's some parameter in CoreBluetooth that I've missed that will help me increase the speed.
I'll need to implement a custom Bluetooth LE service using the IOBluetooth classes instead of CoreBluetooth.
I believe, I've exhausted option 1. I don't see any other parameters I can tweak. I'm limited to sending 20 bytes per message. Anything else and I get cryptic errors on the iOS device concerning Unknown Errors, Unlikely Errors, or the value being "Not Long". Since the demo project also indicates a 20 byte MTU, I'll accept that this likely isn't possible.
So I'm left with option 2. I'm trying to somehow modify the connection parameters for Bluetooth LE on OSX to hopefully allow me to increase the transfer speed (by setting the min and max conn intervals to be 20ms and 40ms respectively - as well as sending multiple BT packets per connection interval). It looks like providing my own SDP Service on IOBluetooth is the only way to achieve this on OSX. The problem with this is the documentation for how to do this is negligible to non-existent.
This tells me how to implement my own service (albeit using deprecate API), however, it doesn't explain the required parameters for registering an SDP service. So I'm left wondering:
Where can I find the required parameters for this dictionary?
How do I define these parameters in a way to offer a Bluetooth LE service?
Is there any alternative to providing a Bluetooth LE Peripheral on OSX via another framework (Python library? Linux in a VM with access to the Bluetooth stack? I'd like to avoid this altogether.)
I decided my best course of action was to attempt to use Linux in a VM as there is more documentation available and access to the source code would hopefully guarantee that I could find a solution. For anyone who is also facing this problem, here's how you can issue a Connection Parameter Update Request on OS X (sort of).
Step 1
Install a Linux VM. I used Virtual Box with Linux Mint 15 (64-bit Cinnamon).
Step 2
Allow usage of the OS X Bluetooth device in your VM. Attempting to forward the Bluetooth USB Controller to your VM will give an error message. To allow this, you need to stop everything that is using the controller. On my machine, that included issuing the following commands from the command line:
sudo launchctl unload /System/Library/LaunchDaemons/com.apple.blued.plist
This will kill the OS X Bluetooth daemon. Attempting to kill blued from the Activity Monitor will just cause it to be automatically relaunched.
sudo kextunload -b com.apple.iokit.BroadcomBluetoothHostControllerUSBTransport
On my MacBook, I've got a Broadcom controller and this is the kernel module that OS X uses for it. Don't worry about issuing these commands. To undo the changes, you can power down and reboot your machine (note, in some cases when playing with the BT controller and it got into a bad state, I had to actually leave the machine powered down for ~10 seconds before rebooting to clear volatile memory).
If after running these two commands you still can't mount the BT controller, you can run kextstat | grep Bluetooth and see other Bluetooth related kernel modules and then try to unload them as well. I've got ones named IOBluetoothFamily and IOBluetoothSerialManager that don't need to be unloaded.
Step 3
Launch your VM and get your Linux BT stack. I checked out the bluez Git repo from here. I specifically grabbed the 5.14 release tag using git checkout tags/5.14 just to be sure it was at least a tagged version and less likely to be broken. 5.14 is the newest tag as of writing this answer.
Step 4
Build bluez. This was done using bootstrap, then configure, then make and make install. I used the --prefix=/opt/bluez flag on configure to prevent overwriting the install bluetooth stack. Also, I used the --enable-maintainer-mode configure flag for the reason stated in the next step. You also might need to use --disable-systemd to get it to configure. Bluez has a bunch of tools and utilities you can use for various things. In order to use the built Bluetooth daemon, you need to stop the system daemon using sudo service bluetooth stop. You can then launch the built one using sudo /opt/bluez/libexec/bluetooth/bluetoothd -n -d (this launches in non-daemon mode with debug output).
Step 5
Get your LE service running via bluez. You can view the bluez/plugins/gatt-example.c for how to do this. I directly modified this by removing the unnecessary code and using the battery service code as a template for my own service and characteristics. You need to recompile bluez to have this code added to the bluetooth daemon. One thing to note (that caused my a day or two of trouble getting this working) was that iOS caches the GATT service listing and this is not read/refreshed on each connection. If you add a service or characteristic or change a UUID, you'll need to disable Bluetooth on your iOS device and then re-enable it. This is undocumented in Apples docs and there is no programmatic way to do it.
Step 6
Unfortunately, this is where things get tricky. Bluez doesn't have support built-in for issuing the Connection Parameters Update Request using any of its utilities. I had to write it myself. I'm currently seeing if they want my code to be included in the bluez stack. I can't post the code currently as I'd need to first see if the bluez devs are interested in the code and then get approval from my workplace to give the code. However, I can currently explain what I did to enable support.
Step 7
Prime yourself on the Bluetooth Standard. Any version 4.0 or greater will have the details you need. Read the following sections.
See Vol. 2, Part E, 4.1 for Host to Controller HCI flow.
See Vol. 2, Part E, 5.4.2 for HCI ACL Data Packet format.
See Vol. 3, Part A, 4 for Signalling Packet format.
See Vol. 3, Part A, 4.20 for Connection Parameter Update Request format.
You're basically going to need to write the code to format the packets and then write them to the hci device. The HCI ACL Data Packet header will contain 4 bytes. This is followed by 4 bytes for the Signalling command's length and channel id. This is then followed by your signal payload which in my case was 12 bytes (for the Connection Parameter Update Request).
You can then write them to the device similar to hci_send_cmd in bluez/lib/hci.c. I did each packet header as it's own struct and wrote them each as iovecs to the device. I put my new function in the hci.c file and exposed it with a function prototype in bluez/lib/hci_lib.h. I then modified bluez/tools/hcitool.c to allow me to call this method from the command line. In my case, I made it so that the command was nearly identical to the lecup command as it requires the same parameters (lecup can't be used as it's meant to be called on the master side, not the slave).
Recompiled all of this and then, voila, I can use my new command on hcitool to send the parameters to the bluetooth controller. After sending my command, it then re-negotiates with the iOS device as expected.
Comments
This process is not for the faint of heart. Hopefully, either this, or some other method of setting the connection parameters is added to bluez to simplify this process. Ideally, Apple will allow the ability to do so via CoreBluetooth or IOBluetooth at some point as well (it could be possible, but undocumentated / difficult to do so, I gave up with the Apple libraries). I've journeyed down the rabbit hole and learned much more about the Bluetooth Spec then I thought I'd have to to simply change the connection parameters between a MacBook and an iPhone. Hopefully this will be helpful to somebody at some point (even if it's me checking back on how I did this).
I know I've left out a lot of details in this in order to keep it somewhat brief (i.e. usage on the bluez tools). Please comment if something isn't clear.
If you are implementing your Peripheral using CoreBluetooth, you can request somewhat customized connection parameters by calling -[CBPeripheralManager setDesiredConnectionLatency:forCentral:] to Low, Medium, or High (where Low latency means higher bandwidth). The documentation does not specify what this means, so we have to test it ourselves.
On an OSX Peripheral, when you set the desired latency to Low, the interval is still 22.5ms which is far from the minimum of 7.5ms.
On OSX Yosemite 10.10.4, this is what the CBPeripheralManagerConnectionLatency values mean:
Low: Min Interval: 18 (22.5ms), Max Interval: 18 (22.5ms), Slave Latency: 4 events, Timeout: 200 (2s).
Medium: Min Interval: 32 (40ms), Max Interval: 32 (40ms), Slave Latency: 6 events, Timeout: 200 (2s)
High: Min Interval: 160 (200ms), Max Interval: 160 (200ms), Slave Latency: 2 events, Timeout: 300 (3s)
Here is the code that I used to run a CBPeripheralManager on OSX. I used an Android device as central using BLE Explorer and dumped the Bluetooth traffic to a Btsnoop file.
// clang main.m -framework Foundation -framework IOBluetooth
#import <Foundation/Foundation.h>
#import <IOBluetooth/IOBluetooth.h>
#interface MyPeripheralManagerDelegate: NSObject<CBPeripheralManagerDelegate>
#property (nonatomic, assign) CBPeripheralManager* peripheralManager;
#property (nonatomic) CBPeripheralManagerConnectionLatency nextLatency;
#end
#implementation MyPeripheralManagerDelegate
+ (NSString*)stringFromCBPeripheralManagerState:(CBPeripheralManagerState)state {
switch (state) {
case CBPeripheralManagerStatePoweredOff: return #"PoweredOff";
case CBPeripheralManagerStatePoweredOn: return #"PoweredOn";
case CBPeripheralManagerStateResetting: return #"Resetting";
case CBPeripheralManagerStateUnauthorized: return #"Unauthorized";
case CBPeripheralManagerStateUnknown: return #"Unknown";
case CBPeripheralManagerStateUnsupported: return #"Unsupported";
}
}
+ (CBUUID*)LatencyCharacteristicUuid {
return [CBUUID UUIDWithString:#"B81672D5-396B-4803-82C2-029D34319015"];
}
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
NSLog(#"CBPeripheralManager entered state %#", [MyPeripheralManagerDelegate stringFromCBPeripheralManagerState:peripheral.state]);
if (peripheral.state == CBPeripheralManagerStatePoweredOn) {
NSDictionary* dict = #{CBAdvertisementDataLocalNameKey: #"ConnLatencyTest"};
// Generated with uuidgen
CBUUID *serviceUuid = [CBUUID UUIDWithString:#"7AE48DEE-2597-4B4D-904E-A3E8C7735738"];
CBMutableService* service = [[CBMutableService alloc] initWithType:serviceUuid primary:TRUE];
// value:nil makes it a dynamic-valued characteristic
CBMutableCharacteristic* latencyCharacteristic = [[CBMutableCharacteristic alloc] initWithType:MyPeripheralManagerDelegate.LatencyCharacteristicUuid properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
service.characteristics = #[latencyCharacteristic];
[self.peripheralManager addService:service];
[self.peripheralManager startAdvertising:dict];
NSLog(#"startAdvertising. isAdvertising: %d", self.peripheralManager.isAdvertising);
}
}
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral
error:(NSError *)error {
if (error) {
NSLog(#"Error advertising: %#", [error localizedDescription]);
}
NSLog(#"peripheralManagerDidStartAdvertising %d", self.peripheralManager.isAdvertising);
}
+ (CBPeripheralManagerConnectionLatency) nextLatencyAfter:(CBPeripheralManagerConnectionLatency)latency {
switch (latency) {
case CBPeripheralManagerConnectionLatencyLow: return CBPeripheralManagerConnectionLatencyMedium;
case CBPeripheralManagerConnectionLatencyMedium: return CBPeripheralManagerConnectionLatencyHigh;
case CBPeripheralManagerConnectionLatencyHigh: return CBPeripheralManagerConnectionLatencyLow;
}
}
+ (NSString*)describeLatency:(CBPeripheralManagerConnectionLatency)latency {
switch (latency) {
case CBPeripheralManagerConnectionLatencyLow: return #"Low";
case CBPeripheralManagerConnectionLatencyMedium: return #"Medium";
case CBPeripheralManagerConnectionLatencyHigh: return #"High";
}
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqualTo:MyPeripheralManagerDelegate.LatencyCharacteristicUuid]) {
[self.peripheralManager setDesiredConnectionLatency:self.nextLatency forCentral:request.central];
NSString* description = [MyPeripheralManagerDelegate describeLatency: self.nextLatency];
request.value = [description dataUsingEncoding:NSUTF8StringEncoding];
[self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
NSLog(#"didReceiveReadRequest:latencyCharacteristic. Responding with %#", description);
self.nextLatency = [MyPeripheralManagerDelegate nextLatencyAfter:self.nextLatency];
} else {
NSLog(#"didReceiveReadRequest: (unknown) %#", request);
}
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyPeripheralManagerDelegate *peripheralManagerDelegate = [[MyPeripheralManagerDelegate alloc] init];
CBPeripheralManager* peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:peripheralManagerDelegate queue:nil];
peripheralManagerDelegate.peripheralManager = peripheralManager;
[[NSRunLoop currentRunLoop] run];
}
return 0;
}

Resources