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 :)
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.
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.
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..