Reputation: 1581
Im trying to wake my application when the device enters a region of beacons with a specific UUID.
So far I tried using the native BluetoothAdapter.getBluetoothLeScanner, providing a PendingIntent to launch a Broadcast reciever.
I also tried RxBleClient which is an amazing library which does work when doing a regular discovery.
I tried AltBeacon lib which is also a very thorough and brilliant lib.
I also tried play services Nearby Apis.
But all failed. They can all scan for beacons regularly but when trying to basically, register and let the device scan while in the bg. The broadcast never gets triggered.
I also tried all 4 while using a foreground service. That did not help either. Ill also mention this is all done on Android 8. I went over the code in both libs and they both eventually use the same function call from the native bluetoothadapter. So Im assuming if that fails, everything else will fail too. Ill also mention that when supplying the startScan function with a CallBack instead of a PendingIntent, the callback is constantly triggered for all scanned BT devices. If I add a ScanFilter to filter for my UUID only, it fails. What am I missing?
Btw, when I implemented this in Ios, it worked flawlessly. The app is awoken as if I used a Geofence enter/exit trigger.
Why wont this work in Android?? Any thoughts?
EDIT: This is how I used the code from the AltBeacon library. It is basically a copy paste from the DOCS tutorial. Only difference is I encapsulated it in a seperate class:
public void init(AppCompatActivity context) {
if (isRegistered) {
return;
}
BluetoothManager bluetoothManager = (BluetoothManager) context.getApplicationContext().getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
bluetoothAdapter = bluetoothManager.getAdapter();
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
context.startActivityForResult(enableBtIntent, 1002);
} else {
register(context);
}
}
}
public void register(AppCompatActivity context) {
if (isRegistered) {
return;
}
executorService.execute(new Runnable() {
@Override
public void run() {
Region region = new Region("com.sample.WakeOnDiscovery", Identifier.parse("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"), Identifier.fromInt(11180), null);
bootstrapRegion = new RegionBootstrap(new BootstrapNotifier() {
@Override
public Context getApplicationContext() {
return Application.getContextFromApplicationClass();
}
@Override
public void didEnterRegion(Region region) {
Logger.log(TAG, "didEnterRegion: " + region.toString());
}
@Override
public void didExitRegion(Region region) {
Logger.log(TAG, "didExitRegion: " + region.toString());
}
@Override
public void didDetermineStateForRegion(int i, Region region) {
Logger.log(TAG, "didDetermineStateForRegion: " + region.toString() + "|" + i);
}
}, region);
}
});
isRegistered = true;
}
This is when I try to scan for this specific UUID and major int (and without the major int). Only the didDetermineStateForRegion is triggered once I think. With int i = 0 or 1.
Regarding the UUID I tried 2 different UUIDs. The first is one that I generate in a sample app I wrote in IOS. The ios app advertises itself as a beacon. I can see it in a different app that uses a regular discovery function. But the onEnterRegion does not trigger.
I also tried a UUID from an iBeacon device that I use regularly for years now. Still no dice.
When I try using the native android ble scanner I use this:
ScanSettings settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).build();
List<ScanFilter> filters = new ArrayList<>(); // Make a scan filter matching the beacons I care about
filters.add(new ScanFilter.Builder()/*.setServiceUuid(ParcelUuid.fromString("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0"))*/.build());
bluetoothAdapter.getBluetoothLeScanner().startScan(filters, settings, getPendingIntent(context));
and:
private PendingIntent getPendingIntent(Context context) {
return PendingIntent.getBroadcast(context, 0, new Intent(context, BTDiscoveryBroadcast.class), PendingIntent.FLAG_UPDATE_CURRENT);
}
I tried passing null as filters, I tried an empty array list and I tried an actual filter with the same UUID.
Still nothing works.
what I did see by chance in logcat is this error message:
E/NearbyDirect: Could not start scan. [CONTEXT service_id=49 ]
java.lang.IllegalStateException: Receiver com.google.location.nearby.direct.bluetooth.state.FastPairScanner$4@535d6ea registered with differing handler (was Handler (adxv) {8fab16a} now Handler (android.app.ActivityThread$H) {e4e0078})
I think this occurs when using the AltBeacon lib as well. I can only assume this is related to my problem.
EDIT 2: I tried this:
ScanSettings settings = (new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).build();
List<ScanFilter> filters = new ArrayList<>(); // Make a scan filter matching the beacons I care about
byte[] manufacturerData = new byte[] {
// iBeacon identifier prefix
0x02, 0x15,
// Your Proximity UUID
(byte) 0xE2, (byte) 0xC5, 0x6D, (byte) 0xB5, (byte) 0xDF, (byte) 0xFB, 0x48, (byte) 0xD2, (byte) 0xB0, 0x60, (byte) 0xD0, (byte) 0xF5, (byte) 0xA7, 0x10, (byte) 0x96, (byte) 0xE0,
0x3, 0x20, 0x3, 0x20, (byte) 0xC5
};
int manufacturerId = 0x004c; // Apple
filters.add(new ScanFilter.Builder().setManufacturerData(manufacturerId, manufacturerData).build());
bluetoothAdapter.getBluetoothLeScanner().startScan(filters, settings, getPendingIntent(context));
The bytes data I retrieved after doing a regular .startDiscovery scan, locating my bt device and extracting from ScanResult the data. Just to make sure its precisely the same.
Still does not trigger broadcast :(
Upvotes: 1
Views: 974
Reputation: 64916
If you want to set up a scan filter to look for a beacon UUID, code like this won't work:
filters.add(new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")).build());
The above code will set a filter to look for a BLE device advertising a GATT Service UUID matching E2C56DB5-DFFB-48D2-B060-D0F5A71096E0. A GATT Service UUID has absolutely nothing to do with a Bluetooth Beacon Proximity UUID, even though both UUIDs may look superficially the same. In other words, you are telling the filter to look for a connectable bluetooth device that advertises a custom service that just happens to be the same as your beacon identifier. This will never find anything, because such a device is almost guaranteed not to exist.
What you want to do instead is more complicated -- you want to set up a scan filter that looks for a bluetooth LE manufacturer advertisement (which iBeacon and AltBeacon advertisements are), that have a specific manufacturer code and start with a certain sequence of bytes.
Getting the exact byte sequence for the filter is tricky because it depends on both the manufacturer ID the beacon layout (iBeacon or AltBeacon). This is one of the many complexities the Android Beacon Library handles for you.
If you really need to do it yourself you would do something like this for iBeacon (WARNING: untested code):
byte[] manufacturerData = new byte[] {
// iBeacon identifier prefix
0x02, 0x15,
// Your Proximity UUID
0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2, 0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0
};
int manufacturerId = 0x004c; // Apple
ScanFilter filter =
filters.add(new ScanFilter.Buider().setManufacturerData(manufacturerId, manufacturerData).build());
Upvotes: 1