21

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);
                }
            }
        }   
    }
}
sudo
  • 548
  • 1
  • 4
  • 16
kdbdallas
  • 4,513
  • 10
  • 38
  • 53
  • you are aware you will only find devices if your location service is enabled? – Tim Nov 21 '17 at 16:01
  • Yes, and as I stated it works most of the time, but occasionally rerunning the app it will just find nothing. If location services were not enabled it wouldn't work most of the time. Killing the app and restarting it is enough to get it finding devices again, but this is not acceptable in a shipping app – kdbdallas Nov 21 '17 at 19:57
  • How about detecting whether the scan has found any devices, and if not, show a dialog allowing the user to perform another scan (e.g., by pressing a button in the dialog), or just automatically do another scan. In our app we actually always provide the user with an option to scan again if they don't see the device they expect listed in the detected devices. – hBrent Nov 22 '17 at 17:54
  • @kdbdallas I suggest you to try scanning with library https://github.com/Polidea/RxAndroidBle – Sundeep1501 Nov 23 '17 at 12:30
  • try HIGH_PERFORMANCE in the settings.. does that make a change? also, BLE will stop working in the background if you don't use something like an AlarmManager to keep the service alive – eiran Nov 27 '17 at 20:51
  • Somewhat related to what @eiran suggests; this may be a result from power saving/throttling. I'm not familiar with Android, but I know the Bluetooth's "Inquiry SCAN" substate has a relatively high energy impact, so perhaps it's usage is being limited somewhere, somehow. – MarkM Nov 28 '17 at 14:54
  • @eiran What is the HIGH_PERFORMANCE setting on? I am currently using SCAN_MODE_LOW_LATENCY when doing my scanning as Android specifies this as "Scan using highest duty cycle". – kdbdallas Nov 28 '17 at 17:43
  • my bad i'm using a lib called P2Pkit.. it's actually ok. but i think you should try MATCH_MODE_AGGRESSIVE taken from here: https://developer.android.com/reference/android/bluetooth/le/ScanSettings.html#MATCH_MODE_AGGRESSIVE – eiran Nov 29 '17 at 15:51
  • Restart your phone and try again, sometimes the bluetooth stack can crash. – Marcin D Nov 29 '17 at 21:24

4 Answers4

5

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

anand krish
  • 4,281
  • 4
  • 44
  • 47
  • 3
    Problem is that I am already setting the location permission and requesting it, as it does sometimes work, as stated. Problem is that even with requesting the permission it sometimes (randomly) will not work. If I wasn't requesting the permissions it would never work. – kdbdallas Nov 28 '17 at 17:36
2

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.

Emil
  • 16,784
  • 2
  • 41
  • 52
  • (when it works) I am able to be both a central and peripheral at the same time just fine. I unfortunately don't have another device (I am an iOS developer most of the time) but I will look into trying to borrow another device and I am trying to get coworkers to try it out on other devices. Thanks – kdbdallas Nov 29 '17 at 17:53
1

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.

Pedro Varela
  • 2,296
  • 1
  • 26
  • 32
  • 3
    I dont see how this could be the problem as I am running a device that uses newer API's and it sometimes works. Yes this would be needed for older devices but it doesn't explain why my current code works sometimes and doesn't other times on the same device – kdbdallas Nov 28 '17 at 17:37
  • so it might be the device itself. try your code in other phone and fabricant. – Pedro Varela Nov 28 '17 at 18:12
  • Just in case, do you have this in your manifest? – Pedro Varela Nov 28 '17 at 18:14
0

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

eiran
  • 1,378
  • 15
  • 16