I've been using StartLeScan for quite a while, the names of the returned devices after a scan are never populated. I could never figure out why, I just get the mac address back.
Today I ran a scan from the OS BT settings page and it showed only the MAC addresses until the scan completed, then it showed the names. Now when I run a scan programmatically the names are always populated.
Why does StartLeScan not return the device names?
Name is not broadcast with the MAC during scan, the name you see later is a local cache after you have required it from the device.
My memory might be betraying me, but I believe settings makes a separate request for the name after they scan. Settings also has a local separate cache for keeping track of devices and their current state.
Remember that the name is not really a good way of identifying a device as it may be changed by the user.
Related
I'm working on a project where a central device connects to peripherals advertising a specific GATT service. The central device exchanges some information with the phone after which the connection is no longer needed and the device disconnects.
The problem that I'm running into is that the phone seems to change its MAC address after every new connection. So basically, every time I connect to a device it looks like a new device appears. So my central node tries to connect to the new service again. This is very annoying since I was thinking of using the MAC address to know that I had already connected to the device and did not need to do the information exchange again. Note that I know that the MAC address rotates every 30 minutes anyways and that's something I'm willing to deal with, connecting once every 30 minutes is fine, but the rotation on connection causes my device to chain connect to the phone that always appear like a new device.
A few ideas I have thought of to work around this:
Obtain the new MAC address from the phone and share it with the central device. That has 2 issues:
MAC Address is not accessible with the Android APIs except using sketchy reflection.
It would only work if a single central device is in Range, since only the last node would know the new MAC address to avoid.
Advertise service Data with a random ID that I would manually rotate every 30 minutes. This data could be in the same advertisement packet. That allows me to ID the device without compromising privacy.
It however might be very hard to implement on iOS that has very restrictive background advertisement APIs. iOS doesn't actually appear to have the same behavior though so it might be possible to implement a completely different method for iOS.
Disabling this "rotate MAC on connection" feature doesn't seem possible.
How can I work around this issue?
If you Bluetooth pair the devices, then the Android device will send over its IRK (Identity Resolving Key). With this you will be able to derive if a given Bluetooth Device Address was generated by the particular IRK or not. That way you can identify an Android device. It works the same if you replace Android with iOS.
See your Bluetooth stack's documentation how to deal with IRKs.
I'm developing an android BLE app in which i try to establish a stable Connection between the app and a BLE device.
To achieve this i want my app to keep the BLE device data in shared preferences or somewhere.
After app close, i'd like my app to retrive this data and attemp a connection without scan for devices.
I prefer to avoid scanning every time becouse scan gives me a lot of problems and it takes time.
How can do it? Is there a way to save BLE data?
You need to store the Bluetooth Device Address (AB:CD:EF:01:02:03) of the device in for example shared preferences or in a sqlite db. Then use https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getRemoteDevice(java.lang.String) when you later restart the app to retrieve a BluetoothDevice object. Once you have the BluetoothDevice object you can use the connectGatt method as usual.
There are some undocumented things you need to keep in mind however. Due to some horrible design flaws in Android's BLE API there is no way to tell it if you mean the given address is a Public Address or a Random Address. (You can read more about different address types at https://devzone.nordicsemi.com/question/43670/how-to-distinguish-between-random-and-public-gap-addresses/). The getRemoteDevice method should take an additional parameter "random address/public address" but it doesn't. Without the correct address type, the Bluetooth controller cannot connect to the device.
Android's BLE stack has some internal heuristics to "guess" if the address is public or random, but unfortunately that differs between Android versions and also if you use autoConnect=true or false. However if you have bonded the device (https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond()) then it will store in its internal DB whether the given address is public or random. If the peripheral you want to connect to uses a random resolvable address it can handle that as well if you use bonding. Therefore I strongly suggest to use bonding.
If you don't use bonding you will need to scan before you connect to the device, since when you start a scan and a device is detected, Android's BLE stack will temporarily (until next restart of Bluetooth) remember the address type for an address.
I will continue on Emil's answer.
Thanks #Emil for the 'big picture'.
Google API is flawed and you can't use getRemoteDevice() because it only understand 48bit address and do not know about all BLE address types.
You can't use Bonding if the device is dumb (anyone would be able to bond to it).
So your only solution is a Bluetooth scan.
However, since Android6 (Marshmallow) the user has to give location permission to apps using Bluetooth scanning. If you don't permit, it won't work.
So if you buy a Bluetooth thermometer (or heart-rate monitor or whatever), you have to permit the app to access location of your phone (or tablet).
That's absurd!
#Emil call it a 'Google flaw'. You may also call it a 'Google machination'.
If I was a thermometer designer, I would add a push-button to accept bonding. It seems ridiculous because the button will only be used once.
[1]: https://www.polidea.com/blog/a-curious-relationship-android-ble-and-location/
Is there any simple way to check if an existing BluetoothDevice object is still reachable without performing a new scan?
I am writing an app that scans for devices and goes through connecting to each one at a time to populate a device list with some information about each device, which I acquire while connected to the device. Every now and again, I would like to go through my device list and remove any devices that are no longer active/in range. What is the best way to do this?
You could try to connect to the devices to find out if they are still around, but keep in mind, that devices will change their mac address from time to time.
I would suggest to use scanning to find out if the devices are still around. Also you may want to add an identifier into the advertisement packages, so you don't need to relay on the mac address.
I am doing an APP to configure/control battery powered devices using Bluetooth.
The devices are not "visible" to save energy.
I have a ready list of about 50 device. The list contains the Bluetooth MAC address and the required PIN to pair.
When the APP starts up, I prompt the user to select a device from the list.
So now I want to automatically bind with the device, if not already done yet, without the need for the user to enter the PIN again.
I googled a lot, but did not exactly find what I was looking for (at least what I understood).
But I have to admit that I am pretty new to android programming and might simple not get the hint-
I also assume I do not use the right terms to search.
I found some "hacks" that simply suppress the PIN entry.
Can someone help me out here?
I already had the APP up and running if I manually bind the device, so the SPP communication is not the problem, it's mainly how to provide the PIN for connection.
Thanks a lot.
You can create new bond with bluetooth device by calling BluetoothDevice setPin(byte[] pin) and createBond() methods. You need also set BLUETOOTH_ADMIN permission.
public boolean createBond ()
public boolean setPin (byte[] pin)
I was reading this
http://developer.android.com/guide/topics/wireless/bluetooth.html#QueryingPairedDevices
which is allot of help on how to pair,connect to a bluetooth device.
I have a situation where I have several BT devices that are in Non-Discover mode always. I know the MAC and the PIN of these devices. IS there a way in Android Dev to manually add devices to the PAIRED list so i can just use the connect as a client.
I understand this maual is written allot for V3. i think i will need to do this on 2.0 ; 2.1- has anybody done this before?
Basically these devices I want to connect to are power saving modules I used pre built BT modules to monitor daylight, another one humidity, etc.. every 3hrs or when interrupted and runs of a single battery for months. So turning off divcory on server saves immense power and prevents other people trying to connect and waste battery.
Not sure what you mean by "manually": Do you mean "manually" as in GUI/user interaction, or "manually" as "I do it in my own application code"?
Some suggestions though:
If you can make your BT devices discoverable at all, you could do it this way:
Make your BT device discoverable
Let Android search for and find the device and then initiate a connection
Android will ask for the PIN for pairing with the device; enter the PIN.
Once pairing was successful, Android stores the pairing information for future use, so that you can
Make your BT device invisible again.
From then on your app should be able to connect to the BT device at any time without further pairing operations.
If the said is not an option for you, maybe you want to go another way:
In current Android versions there are different API routines implemented which are neither documented nor exposed in the normal SDK. A hack kind of solution may be to use some of these "hidden" ("#hide"...) APIs, either via reflection or via modification of your SDK installation.
But be aware that this is always a hack and it may work on a specific device with a specific version of Android and is likely to break your app on another device and/or any other Android version.
Having said that, here comes some reference:
Example of how to access "hidden" bluetooth API.
Then, have a look at the source code for android.bluetooth.BluetoothDevice, e.g. here.
In there, public boolean createBond(){...} may do what you want.