Problem
I currently have only one 5.0 phone to test this on (Moto E2 3G on 5.0 Lollipop), but I am not able to advertise Custom UUID's on Android 5.0. When I say a custom UUID I mean one that isn't predefined by the Bluetooth LE spec.
On my Nexus 6 Android 5.1, everything works fine.
Code
AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
// Advertising this spec assigned UUID works
//ParcelUuid temp = new ParcelUuid(UUID.fromString("0000180D-0000-1000-8000-00805f9b34fb"));
// Advertising this custom UUID doesn't work!!
ParcelUuid temp = new ParcelUuid(UUID.fromString("02FD04F4-CFFF-4573-B478-F7470A7CF2F2"));
dataBuilder.addServiceUuid(temp);
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED); settingsBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
settingsBuilder.setConnectable(true);
AdvertiseData ad = dataBuilder.build();
bluetoothLeAdvertiser.startAdvertising(settingsBuilder.build(), ad, null, advertiseCallback);
Observed Behaviour
When viewing the Advertising packet of the Moto E2 with a custom UUID, here's what I can see - notice there is no information about the Service UUID:
If I advertise an assigned service UUID on the Moto E2 (0000180D-0000-1000-8000-00805f9b34fb), I can see the 16-bit representation of the UUID is available:
If I advertise a custom UUID on the Nexus 6, I see the UUID data as expected:
Is anyone able to confirm this behaviour on another 5.0 device? Do I need to make the minimum supported version 5.1? The only other related reference I've been able to find on the internet about this, is this empty ticket on the Android bug tracker.
I can confirm that this is a bug on the Moto G 2014 running 5.0.2 (and possibly other devices and/or versions).
My workaround is to just insert your custom UUID into the manufacturer data instead of the service UUID field. This will only work if you have access to the central that is scanning for it and can explicitly check the manufacturer data to see if the UUID is present in making your decision to connect.
With my implementation I had control over what the central checks to decide whether to connect, so I just adopted this scheme for all Android devices that operate in the peripheral mode, instead of using the normal 'addServiceUuid" method. Tested on Galaxy S6 (5.0.2) and Nexus 5X (6.0) and it's working.
private AdvertiseData buildAdvertiseData()
{
int manufacturerID = 0x0001;
byte[] uuidBytes = new byte[]{ (byte)0x00, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07, (byte)0x08, (byte)0x09, (byte)0x0A, (byte)0x0B, (byte)0x0C, (byte)0x0D, (byte)0x0E, (byte)0x0F };
return new AdvertiseData.Builder()
.addManufacturerData(manufacturerID, uuidBytes)
.setIncludeDeviceName(false)
.build();
}
Make sure you exclude the device name, some devices will put the advertising packet over the 31 bytes otherwise and it will fail to start advertising.
Only a handful of devices supports BLE advertising although most device support BLE scanning, as per as I know 5.0 devices are not supported.
To check if your device is supported by using this code in you android code.You can use it anywhere in it.
BluetoothAdapter.getDefaultAdapter().isMultipleAdvertisementSupported();
It is the best method to find whether your device is supported or not. If it's not supported you can't do anything programmatically, expect to buy the one which supports.
Related
We have devices that beacon data with «Service Data - 128-bit UUID» AD type : 0x21.
For Android scan, we are filtering BLE device by MAC addresses.
It works fine.
Because we need more flexibility, we want to filter by UUID.
My code works fine only with Xiaomi MI9 (Android 9).
It doesn't work on Xiaomi MI A1 (Android 9) and Huawei P10 (Android 8).
(But it works when I launch a BLE scanner with another app, like nRF Connect)
Anyone knows how to solve this or does some phones simply not support certain types of filtering?
static final UUID SERVICE_DATA_UUID = UUID.fromString("00010001-0000-1000-8000-112233445566");
byte[] setServiceData = new byte[] {
0x11
};
filters = new ArrayList<>();
ScanFilter.Builder builder = new ScanFilter.Builder()
.setServiceData(new ParcelUuid(SERVICE_DATA_UUID), setServiceData);
//.setDeviceAddress(Globals.MAC[1]);
filters.add(builder.build());
mBluetoothLeScanner.startScan(filters, settings, mLeScanCallback);
Logcat when it works (MI9 or when I activate nRF in parallel)
D/Constraints: ScanRecord [mAdvertiseFlags=2, mServiceUuids=null, mServiceSolicitationUuids=null, mManufacturerSpecificData={}, mServiceData={00020001-0000-1000-8000-112233445566=[17]}, mTxPowerLevel=-2147483648, mDeviceName=null]
The mask should not be all zero -- if so it won't match anything. It should generally be set to something like below so that it matches all bytes of the Service UUID:
String serviceUuidMaskString = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
ParcelUuid parcelUuidMask = ParcelUuid.fromString(serviceUuidMaskString);
builder.setServiceUuid(new ParcelUuid(SERVICE_DATA_UUID), parcelUuidMask);
I suspect that the all zero mask you are using works on some devices and not other devices because the behavior of an all zero mask is indeterminate. It might vary from one BLE chipset to the next.
Most off the shelf mobile apps do not use scan filters, so they are not a good judge of whether filters work on a specific device.
Context:
I am working on a FIDO-U2F bluetooth authenticator with a nRF52 BLE SoC. and want it to test with google example.
So far I have implemented the FIDO Bluetooth specification and I have a device that advertises as a FIDO-compatible device.
Thanks to nRF Connect I have ensured that all the services and characteristics are correctly exposed and that I can interact with it only when my device is securely paired (with LTK):
Problem:
When the app scans for eligible FIDO device, it does not find mine.
I am stuck on the screen that ask to press a button for 5s, which I don't need to since my device responds to pairing request without user interaction and is already paired with my SAMSUNG A8.
I/BluetoothDevicePreference: onDeviceAttributesChanged :: Device = (N) D8BE86, isBonded = 12 , mIsOnProgressAddVI = false
I/Fido: [BleScanner] startScan()
E/Fido: [BluetoothPairingStateProvider] getUuids() returns null for device: D8:BE:86:4A:E5:65
I/Fido: [PreferredTransportProvider] BLE enabled but no device is paired
I/Fido: [AuthenticateBaseChimeraActivity] User selected transport ble
I/Fido: [ViewController] Accepting proposed view {"viewName":"ble_instructions","anyU2fDevicesPaired":false}: outranks current (2 > 0)
I/Fido: [ViewPresenter] viewSelected(...) ble_instructions
I/Fido: [U2fRequestController] onResultReceived(null, ErrorResponseData{errorCode=5})
I/Fido: [BleScanner] stopScan()
I tried to remove pairing data and all I have is:
I/BluetoothDevicePreference: onDeviceAttributesChanged :: Device = (N) D8BE86, isBonded = 10 , mIsOnProgressAddVI = false
The advertising flags are currently set to "BR/EDR not supported", but I also tried "LE Limited Discoverable Mode" and "LE General Discoverable Mode" without success.
I looked into android-fido sources but the BLE scan seems imported from elsewhere, I cannot debug it in this project.
Any pointer is welcome
Have you got to this screen?
We may need to add Service Data field (0x16) into advertising packet. This is mentioned in FIDO specification here
Android code snippet for advertising with Service Data field added:
AdvertiseData data = new AdvertiseData.Builder()
.addServiceUuid(new ParcelUuid(fido2GattService.getUuid()))
.setIncludeDeviceName(true)
.addServiceData(new ParcelUuid(fido2GattService.getUuid()), new byte[] {(byte)192, (byte)192, (byte)192})
.build();
If you want to capture bluetooth packets to see what is your advertising packet, you can use PacketLogger (for MacOS)
Here is the screenshot of the advertising packet
The red box is Service Data
I am using pixel with latest android 8.1.0 update.
I am facing issue related to BLE advertisement scanning. Whenever I turned off the screen(i.e power button press) my scanning will stop.
it will restart immediately after turn on the screen.
I have checked latest code for BLE. google newly introduce this feature (Reference Link).
Is there any way to skip this part, I mean scan should not stop regardless of the screen on or off.
As of Android 8.1, unfiltered bluetooth scans are blocked when the screen is turned off. While it is surprising for such a dramatic change to be made in a minor release of Android, this is certainly an intended change based on the comments in the commit:
Stop unfiltered BLE scans when the screen goes off.
The workaround is to use a ScanFilter with all scans. The new 8.1 operating system code simply verifies that any scans active when the screen is off have at least one scan filter. If those conditions are met the scan results are delivered as in Android 8.0.x and earlier.
In order to set up such a scan, you must use the APIs introduced in Android 5.0 and create a ScanFilter with each scan. Below is a filter that will find manufacturer advertisements for any device from Apple with manufacturer ID 0x004c (this will include iBeacons):
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setManufacturerData(0x004c, new byte[] {});
ScanFilter filter = builder.build();
Similarly, if you are interested in GATT Service advertisements (like the kind used with Eddystone beacons) you can search for a GATT Service UUID with a filter like this:
ScanFilter.Builder builder = new ScanFilter.Builder();
String serviceUuidString = "0000feaa-0000-1000-8000-00805f9b34fb";
String serviceUuidMaskString = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF";
ParcelUuid parcelUuid = ParcelUuid.fromString(serviceUuidString);
ParcelUuid parcelUuidMask = ParcelUuid.fromString(serviceUuidMaskString);
builder.setServiceUuid(parcelUuid, parcelUuidMask);
ScanFilter filter = builder.build();
If needed, you can add multiple filters to a single scan, and any that match will return results. The only real limitation here is that you must know all of the manufacturer codes or all of the GATT Service UUIDs that you might match up front, at least when scanning with the screen off.
You start your scan with code like this:
bluetoothAdapter.getBluetoothLeScanner().startScan(filters, settings, scanCallback);
EDIT: It is also possible to do this with an empty ScanFilter that looks like this:
ScanFilter.Builder builder = new ScanFilter.Builder();
ScanFilter filter = builder.build();
If you use such a scan filter, it will match any advertising packet, and still allow detections with the screen off on Android 8.1, effectively giving you the same behavior on Android 8.0.x and earlier.
EDIT 2: On the Galaxy Note 9 with Android 8.1 and perhaps other Samsung devices with 8.1, scans are blocked with the screen off even with an empty scan filter. Scans are allowed with the screen off with a non-empty scan filter as described above.
I faced the same issue. I had Scan filters in order to scan BLE devices even if the screen were locked. But on Samsung devices it didn't work, so I search on Samsung forum and I discovered Knox SDK (https://seap.samsung.com/sdk/knox-android).
And it was the solution of my problem. All you have to do is add it to your app, create a license and activate it and finally use this method addPackageToBatteryOptimizationWhiteList to unlock the scan when the Samsung device screen is lock.
Obviously not, unless they missed something. But it will still work in the background if you have scan filters, which you should have anyway. So is it really an issue?
in android 11
scanFilter can't being null
you need to set something then will working
like:
List<ScanFilter> filterList = new ArrayList<>();
filterList.add(new ScanFilter.Builder().setDeviceAddress(address).build());
BluetoothAdapter.getBluetoothLeScanner().startScan(filterList, scanSettings, scanCallback);
I implemented a GATT Server and Client App on Android. The connection is working and I forced pairing by adding PERMISSION_READ/WRITE_ENCRYPTED_MITM to all of the GattCharacteristics.
But the pairing behavior differs on different clients:
1) Pin is shown on the client/central (Android 5 on Samsung Galaxy S3) and should be insert on the server/peripheral (Android 7 on Nexus 5).
2) Passkey is shown on both devices client/central (Android 5 on Samsung Galaxy S3) and server/peripheral (Android 6 on Nexus 7)
3) Pairing with Windows or iOS fails with server/peripheral expecing a pin for input.
What I expected and want to happen is:
Pin is shown on the server/peripheral and has to be insert on client/central
Is there any way to configure that behavior?
Thanks in advance!
EDIT
Here is my setup:
BluetoothGattService gattService = new BluetoothGattService(
serviceUUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
gattService.addCharacteristic(new BluetoothGattCharacteristic(
charReadUUID,
BluetoothGattCharacteristic.PROPERTY_READ,
BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED_MITM
));
gattService.addCharacteristic(new BluetoothGattCharacteristic(
charWriteUUID,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED_MITM
));
gattServer.addService(gattService);
...
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.setConnectable(true)
.build();
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeTxPowerLevel(false)
.addServiceUuid(serviceUUID)
.build();
BluetoothLeAdvertiser advertiser = adaper.getBluetoothLeAdvertiser()
advertiser.startAdvertising(settings, data, callback);
Summary: Set the I/O Capabilites of your client to "Keyboard Only".
Explanation:
I am not fully sure what happens "under the hood" of your system. But I can tell you what should be happening according to the BLE CoreSpec. First see CoreSpec V4.2, Vol. 3, Part H, chap. 2.3.5.1, table 2.7 & 2.8. There it is defined which pairing is used, depending on the authentication requirements and the I/O capabilities of the devices.
What you want is described as "Passkey Entry: responder displays, initiator inputs". This is the case if legacy pairing (pairing according to Bluetooth V4.0) is used, and if:
the server (responder) has a display AND
the client (initiator) has a keyboard AND
server and client do NOT both have display AND keyboard.
(And if OOB data is not used and MITM is enforced, but I assume this as given.) Note that if both client and server have display and keyboard, the default case is that the client displays and the server inputs. It would seem that if your protocol automatically handles pairing, it will also automatically chose the pairing method as defined in the CoreSpec.
So what you see is corresponding to different I/O capabilities of different servers. It seems that your client has display and keyboard, so if you use a server with display and keyboard, the client will display the passkey and the responder will wait for input (which fits to your case 1). For case 2, we have Numeric Comparison; this is only possible if LE Secure Connections (pairing according to Bluetooth V4.2) is supported by both client and server.
For case 3, I don't know what is going on, but it may be a problem between an Android system and an iOS system not operating well together (but I have no idea why).
Since pairing seems to be fully automized here, the only possibility to change things is to change the I/O capabilities. There should be a function to change these capabilities, check your manual. If you do not want to use a display on the client, set its I/O capabilities to "Keyboard Only" and it will exhibit the behavior you expect.(*)
(*) This holds only if you use legacy pairing. If both devices support LE Secure Connections, it is recommended that you use this newer pairing protocol, since it removes security issues with the old protocol. (I would however assume that in this case, the newer protocol is used automatically anyway.)
I am working on using the BT 4.0 API that Motorola has provided with the RAZR. In one of their documents it states to use the Android API to pair before connecting and using their framework. Per their instructions I have been pairing with OS Bluetooth settings application, but it never prompts me for a key. It will pair but doesn't appear to bond, and this is critical for me.
My question is, when they say "using the Android API" is this referring to simply using the OS Bluetooth utility to pair before hand (like I have been doing), or is there some way to do it with code in my application. They reference the "createBond()" function which, to my knowledge, is not an accessible function (at least not without some squirrely libraries or reflection).
Any advice is greatly appreciated, especially anyone who has used the API successfully, if they could give an account of their process. I'm just looking for some clarity at this point :)
Lloyd,
You are correct, follow the instructions in the link you posted.
Outside of coding, when they say use the standard android api for "non-le" operations, they mean go ahead and pair the ble device the same way you would any bluetooth classic devices inside android settings -> wireless & network -> bluetooth -> scan for devices.
If the device you are using is a motorola le compatible device the ble device will be paired but not connected.
Now, in the code, you can detect this paired device through the same method of
BluetoothAdapter.getDefaultAdapter().getBondedDevices()
To double check if your Android Phone is LE compatible, run this code:
public static boolean checkBLESupport() {
boolean deviceSupportsLE;
try {
#SuppressWarnings({ "unused", "rawtypes" })
Class object = Class.forName("android.server.BluetoothGattService");
deviceSupportsLE = true;
} catch (Exception e) {
deviceSupportsLE = false;
}
return deviceSupportsLE;
}
And to double check if the bluetooth device you paired is LE, when you are looping through the bonded devices.
Check the device with this code.
if (device.getBluetoothClass() == null) {
Log.i(TAG, "This device is BLE compatible");
b = true;
} else {
Log.i(TAG, "This device is not BLE");
b = false;
}
Now for establishing connection from your LE compatible phone to your LE compatible bluetooth device, follow the Gatt service instructions under the link you posted. http://developer.motorola.com/docs/bluetooth-low-energy-api/
Take note that under this example it is connecting to a bluetooth low energy heart rate monitor.
If you are not trying to connect to the heart rate monitor with LE heart rate profile, here is a link to another Motorola document that details creating your own LE Profile to use with the GATT framework. http://developer.motorola.com/docs/bluetooth-low-energy-gatt-framework-api/
If the instructions are not clear enough at any point in either of these documents, motorola offers sample android applications using the frameworks in those documents.
I guess motorola stack has BLE support. But what i feel is that it does not pair with the devices that require bonding though It does work some sensors. I have tried with a proximity sensor that require bonding. It never gets paired though the devices is discovered with Razr which even does not with S3.
There's a helpful video here.
Late to the game, but can confirm -
If your BLE Peripheral requires bonding, Moto X - and some other older Motorola devices - MUST be paired via Bluetooth Settings prior to programmatic connection via the Android GATT interface.
If you bond via the createBond method, or reading of an encrypted characteristic, your connection will be dropped typically in under 60 seconds, despite DDMS logs that show a good bond may be established.