identify BLE devices on Android / Kotlin with bluetooth off - android

I am developing a solution to check BLE devices and I used the native API that comes with Android to check BluetoothLeScanner.
Wanted to understand a little better operation, I take the location permission and bluetooth.
After the scan starts, I turn Bluetooth on my phone to off, on Moto G2 Android 6.0 Scan still keeps giving me the expected result when I test on a Samsung S8 Android 9 and Sony Xperia T2 Ultra Android 5.1 in the log I get which was bluetooth disabled and the scan was stopped.
I can only perform the test when I purchase it as follows
bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
bluetoothScanner = bluetoothAdapter.bluetoothLeScanner
}
#TargetApi(Build.VERSION_CODES.M)
class BleScanCallback(resultMap: MutableMap) : ScanCallback() {
var resultOfScan = resultMap
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
#TargetApi(Build.VERSION_CODES.M)
override fun onScanResult(callbackType: Int, result: ScanResult?) {
addScanResult(result)
Log.v("Main Activity", "I found a ble device ${result}")
Log.v("Main Activity", "I found a ble device ${result?.device?.address}")
}
override fun onBatchScanResults(results: MutableList<ScanResult>?) {
results?.forEach { result -> addScanResult(result) }
}
override fun onScanFailed(errorCode: Int) {
Log.v("Main Activity","Bluetooth LE scan failed. Error code: $errorCode")
}
fun addScanResult(scanResult: ScanResult?) {
val bleDevice = scanResult?.device
val deviceAddress = bleDevice?.address
resultOfScan.put(deviceAddress, bleDevice)
}
scanResult is bringing the necessary information when bluetooth is online, I already set it up as the image below
https://i.stack.imgur.com/o9jGRm.png
I see that this makes scanning even off

On some Android devices including Pixel phones, Android One devices, and unmodified AOSP builds, turning off bluetooth in the quick settings panel doesn't really turn off bluetooth. Instead, it merely blocks bluetooth connections and pairing in software, yet allows Bluetooth LE scans to continue unaffected. As #Jorgesys correctly notes, it is impossible to detect BLE devices if the Bluetooth radio is really turned off, so let me say again: despite what the quick settings panel says, bluetooth is not necessarily powered off.
On supported devices, this happens only if two things are true:
Bluetooth is turned on in the full settings menu (On Android 9: Settings -> Connected Devices -> Connection preferences -> Bluetooth ON)
The user has selected to "Allow apps and services to scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services." (Settings -> Security & Location -> Location -> Advanced -> Scanning -> Bluetooth scanning ON)

There is no way to detect BLE devices with bluetooth off
Bluetooth must be enabled
Set up BLE
Before your application can communicate over BLE, you need
to verify that BLE is supported on the device, and if so, ensure that
it is enabled.

Related

Difference BLE throughput between PC and mobile(android)

I'm trying to receive data in mobile(android) devices from a BLE device based on nRF52840 and custom firmware.
Here is my setting
sampling rate : 250Hz
mobile devices : Galaxy flip z 3, Galaxy S22
Used library : RxBleAndroid
To check whether the BLE device sends data correctly or not, I tested it using nRF Connect for Desktop. There were about 250 samples per second. (nRF52840 dongle was equipped with the Desktop)
However, in mobile devices, there were about 20~30 samples per second. I already checked these mobile devices could receive 250 samples per second using a commercial device.
So I think it is not a problem of mobile devices, but code.
Here is my code
fun connectDevice(){
rxBleDevice = rxBleClient.getBleDevice(lxDeviceAddress)
connectSubscription = rxBleDevice.establishConnection(false)
.subscribe(
{ rxBleConnection ->
this.rxBleConnection = rxBleConnection
Log.v(TAG, "success to connect")
}
) { throwable ->
throwable.printStackTrace()
}
}
fun bleNotification() = rxBleConnection
.setupNotification(lxDeviceUUID)
?.doOnNext { notificationObservable->
}
fun readDataFromDevice(){
scanSubscription.dispose()
bleNotification()
?.observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread())
?.flatMap { notificationObservable -> notificationObservable }
?.subscribe({ bytes ->
Log.v(TAG, byteArrayToHex(bytes))
}, { throwable ->
throwable.printStackTrace()
})
}
I called connectDevice() to connect the BLE device, and called readDataFromDevice() to read data.
Could you give me some solutions?
It all depends on the Bluetooth chip in the device (how good its radio scheduler is) and the circumstances such as connection interval, number of concurrent connections, if BLE antenna is shared with WiFi antenna and so on. So, not that much depends on your code. You just seem to set up a regular notification listener.
the BLE connection performance mainly depends on three connection parameters:
connection interval, latency, and timeout. These are negotiated between peripheral and central after connecting. The peripheral can request for parameters by giving their range, but it is the central that decides the final parameters in our case the mobile and PC applications.
So, there will be a change in the values of these parameters between mobile and pc apps, also between different OS's. Hence the change in throughput.
As #Emil covered it depends on hardware as well.

How to check Internet available on app running on wear OS 3 (Samsung Galaxy Watch 4)

I am developing a standalone app for Samsung Galaxy watch 4. I am using internet to connect with webservice APIs. Samsung Galaxy watch 4 supports Wi-Fi connection but it gets switched to phone internet when watch is paired with a phone. So to check internet availability on watch, I have written below code
fun isNetworkConnected(context: Context): Boolean {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val n = cm.activeNetwork
if (n != null) {
val nc = cm.getNetworkCapabilities(n)
//It will check for both WiFi and Bluetooth transport
return (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
nc.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH))
}
Log.d(TAG,"No Active Network Available")
return false
}
Issue with the above code is that when watch is paired with phone, and phone's mobile data & Wi-Fi is switch off and watch Wi-Fi is also switched off still nc.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) returns true and my app crashes because actually there is no internet available. Can anyone has any idea to check internet availability in better way on app running on wear OS 3.0 ?
Your code is ok, but is intended for other purposes, not to check for internet connection only. I suggest you take a look to this documentation https://developer.android.com/training/wearables/data/network-access#requesting-wifi-connectivity
It may require a bit of rework on your end since it will be an async callback, but it is for your use case I think is the best approach.
val callback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
// The Wi-Fi network has been acquired, bind it to use this network by default
connectivityManager.bindProcessToNetwork(network)
}
override fun onLost(network: Network) {
super.onLost(network)
// The Wi-Fi network has been disconnected
}
}
connectivityManager.requestNetwork(
NetworkRequest.Builder()
.addTransportType(
NetworkCapabilities.TRANSPORT_WIFI
).build(),
callback
)

Bluetooth LE Scanning Sometimes Doesn't Find Devices

I am scanning for Bluetooth LE devices and running as a Peripheral (running Android 6.0 on a Moto G 2nd Gen)
The problem I am having is that sometimes (randomly it seems but often) it will not find any of my other peripheral devices, the other times it works fine.
I have a companion iOS device running similar code (both scanning for peripherals and acting as a peripheral), and when the Android scanning can't find the iOS device, my iOS finds the Android device acting as a peripheral just fine. So it seems only to be a problem with the scanning side of things.
It's not only just not finding my companion iOS device, but doesn't find any Bluetooth devices. When it works, it finds my companion iOS device as well as a bunch of other devices.
I have tried it with and without ScanFilters, and get the same issue.
I am building against SDK 26 with a minimum SDK of 23.
I am setting the permissions that are needed, as it sometimes works.
Relevant code below:
private void startScanning() {
mHandler = new Handler(mContext.getMainLooper());
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0)
.build();
mBluetoothLeScanner.startScan(null, settings, mScanCallback);
}
}, 1000);
}
private ScanCallback mScanCallback = new ScanCallback() {
#Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if( result == null || result.getDevice() == null )
return;
Log.e("myTest", "Found Device");
BluetoothDevice device = result.getDevice();
final String deviceAddress = device.getAddress();
List<ParcelUuid> parcel = result.getScanRecord().getServiceUuids();
if (parcel != null) {
String parcelUUID = parcel.toString().substring(1,37);
if (parcelUUID.equalsIgnoreCase(mContext.getString(R.string.service_uuid))) {
final BluetoothDevice bleDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(deviceAddress);
if (!seenPeripherals.contains(deviceAddress)) {
stopScanning();
mHandler = new Handler(mContext.getMainLooper());
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Log.e("AppToApp", "Trying to connect to device " + deviceAddress);
mGatt = bleDevice.connectGatt(mContext, false, mGattCallback);
}
}, 1000);
}
}
}
}
}
I face the same issue. This is because Google policy has been changed for Marshmallow API 23 and higher version, to use BLE user need to turn ON GPS. For Google Docs check this Permission # Runtime link. To fix it you have to enable "Location" in the settings of the phone as well as request location permission in the app at Runtime. Both need to be done for scanning to work properly.
To request the location permission put the following in a dialog or the likes:
myActivity.requestPermissions(new String[]{Manifest.permission.ACCESS_COURSE_LOCATION}, yourPermissionRequestCode);
and implement:
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] Results){
if(requestCode == yourPermissionRequestCode)
{
... //Do something based on Results
}
}
in myActivity handle whatever the user selects. You also need to do the following to turn on your device's location services:
Intent enableLocationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
myActivity.startActivityForResult(enableLocationIntent, yourServiceRequestCode);
You can check if the user turned on the location services by implementing:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if(requestCode == yourServiceRequestCode)
{
...//Do whatever you need to
}
}
in myActivity. You can also manually turn on location services by doing:
Enter phone settings -> Select "Apps" -> Select your app -> Select the "Permissions" option -> Switch the "Location" permission on.
Once the user has enabled the permission and started location services then you could start scanning for peripherals. I've noticed that if you are already scanning while you enable the permission/turn on the location service it will still not put anything in your onScanResults
This allow companies to see your location and direct you to where they want.
while SCANNING for device, Once you are connected to the BLE device you can Turn Off the location service on your phone and you will still stay connected to your peripheral device. However, once you connected to a firmware (peripheral) you cannot then connect to any new peripheral until you disconnect the connection to the paired device, and all other scanning devices(mobile) cannot see the peripheral device name in their scanned list (when user search for near by peripheral device) when the peripheral is already connected to any other mobile device, because a peripheral can be connect to only 1 device at a time.
For BLE basic sample you can check this Truiton Ble tutorial. This snippet will fix your Issue I think. Happy coding :)
It might just simply be the case that the Android phone has a crappy Bluetooth chip. Have you looked at the hci log? Or logcat?
Bluetooth 4.0 chips (which is in your moto g) have strict limitations that you can't be a central and a peripheral at the same time, even though scanning should be allowed all the time. Therefore I wouldn't make a product that depends on the peripheral feature in Android until BT 4.0 is phased out.
You should test with a newer phone that has at least a Bluetooth 4.1 chip.
This question is related to something I posted before. Check these links
https://stackoverflow.com/a/39084810/3997720, https://stackoverflow.com/a/39597845/3997720
You need two methods: one for older api and one for newest api.
As for SDK 23 and up, You have to request run time permissions. Ask for location permission.
If you're using the app for non-commercial purposes (meaning expecting less than 5K users), you can use P2Pkit.
It uses P2P wifi connection in addition to BLE and non-BLE bluetooth, on older versions of android, and actually gets great results on discovery of nearby phones.
Upsides are it works on IOS too, and it has a fairly simple interface.
Downside is over 5K users you have to pay..

Android Bluetooth Scan for classic AND BTLE Devices

The android documentation states:
Note: You can only scan for Bluetooth LE devices or scan for Classic Bluetooth devices, as described in Bluetooth. You cannot scan for both Bluetooth LE and classic devices at the same time.
However I am noticing that calling mBtAdapter.startDiscovery(); is returning both classic and btle devices. Does anybody know what is correct here?
From my understanding what the documentation means is that you cannot have a startLeScan() and a startDiscovery() running at the same time. The reason might be that there is only one BluetoothAdapter object (the object that represents the local Bluetooth hardware) therefor it cannot do two different operations that use the BluetoothAdapter at the same time.(If someone knows anything different as to how it works in the background, let us know)
startLeScan() -> scans only for BLE devices
startDiscovery() -> discovers all Bluetooth devices, also it only scans for 12 seconds and this cannot be changed (have a read through the method description)
Note: After doing a startDiscovery() inquiry scan when a BT device is found you can get the device type to identify what each device is, For example:
int deviceType = device.getType();
if (deviceType == BluetoothDevice.DEVICE_TYPE_CLASSIC) {
} else if (deviceType == BluetoothDevice.DEVICE_TYPE_LE) {
} else if (deviceType == BluetoothDevice.DEVICE_TYPE_DUAL) {
} else if (deviceType == BluetoothDevice.DEVICE_TYPE_UNKNOWN) {
}
device: BluetoothDevice
when (device.type) {
1 -> edt.text = "DEVICE_TYPE_CLASSIC"
2 -> edt.text = "DEVICE_TYPE_LE"
3 -> edt.text = "DEVICE_TYPE_DUAL"
else -> edt.text = "DEVICE_TYPE_UNKNOWN"
}

Android Bluetooth Low Energy Motorola API pairing

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.

Categories

Resources