Skip to content
Advertisement

Android BLE Scan Callback is not called after scanning for BLE devices

I’m creating an Android app that scans all nearby BLE (Bluetooth Low Energy) devices using Android Studio.

I think I have implemented all these documentations:

  1. https://developer.android.com/guide/topics/connectivity/bluetooth/ble-overview
  2. https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#java
  3. https://developer.android.com/guide/topics/connectivity/bluetooth/setup#java
  4. https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices

But as you can see in the log below, (the problem) there are no BLE devices scanned by the app.

Here are the logs in the logcat:

Log

I think it could happen because the BLE scan callback is not called (no BLE scan callback in the log).

So my question is what is the solution for my code? Did I miss coding something?

Here is my code

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example">

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Example">
        <activity android:name=".ui.authentication.signin.SignInActivity" />
        <activity android:name=".ui.authentication.signup.SignUpActivity" />
        <activity android:name=".ui.home.HomeActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

BluetoothHelper.java:

package com.example.util;

import android.Manifest;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.util.Log;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public class BluetoothHelper {
    static int REQUEST_ENABLE_BT = 1;
    static int PERMISSION_CODE = 1;
    static String TAG = "Bluetooth";
    static boolean isScanning = false;

    // SOURCE FOR BLUETOOTH PERMISSION: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#java

    // CHECK FOR BLUETOOTH PERMISSION FOR ANDROID 10
    public static void android10BluetoothPermission (Context context, Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            if (ContextCompat.checkSelfPermission(context,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION)
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(
                        activity,
                        new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                        PERMISSION_CODE
                );
            }
        }
    }

    // SOURCE FOR SET UP BLUETOOTH: https://developer.android.com/guide/topics/connectivity/bluetooth/setup#java

    // GET BLUETOOTH ADAPTER
    public static void setupBluetooth (BluetoothAdapter bluetoothAdapter) {
        if (bluetoothAdapter == null) {
            Log.d(TAG, "Bluetooth: " + "bluetooth adapter is null");
        }
        else {
            Log.d(TAG, "Bluetooth: " + "bluetooth is adapter not null");
        }
    }

    // ENABLE BLUETOOTH
    public static void enableBluetooth (Activity activity, BluetoothAdapter bluetoothAdapter) {
        Log.d(TAG, "Bluetooth: " + "bluetooth adapter is enabled: " + bluetoothAdapter.isEnabled());
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            Log.d(TAG, "Bluetooth: " + "bluetooth is enabled");
        }
        else {
            Log.d(TAG, "Bluetooth: " + "bluetooth is already enabled");
        }
    }

    // SOURCE FOR FIND BLUETOOTH DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices

    // QUERY PAIRED DEVICES
    public static Set<BluetoothDevice> queryPairedDevices (BluetoothAdapter bluetoothAdapter) {
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
        Log.d(TAG, "PairedDevices: " + pairedDevices.toString());
        return pairedDevices;
    }

    // GET ALL NAMES AND MAC ADDRESSES
    public static ArrayList<HashMap<String, String>> getNamesAndMacAddresses(Set<BluetoothDevice> bluetoothDevices) {
        ArrayList<HashMap<String, String>> arrayList = new ArrayList<>();

        if (bluetoothDevices.size() > 0) {
            for (BluetoothDevice device : bluetoothDevices) {
                String deviceName = device.getName();
                String deviceMacAddress = device.getAddress();

                HashMap<String, String> hashMap = new HashMap<>();
                hashMap.put("deviceName", deviceName);
                hashMap.put("deviceMacAddress", deviceMacAddress);
                arrayList.add(hashMap);
            }
        }

        Log.d(TAG, "NamesAndMacAddress: " + arrayList);
        return arrayList;
    }

    // SOURCE FOR FIND BLE DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices#java

    // SCAN BLE DEVICES
    public static void scanBLEDevices (BluetoothLeScanner bluetoothLeScanner) {
        int SCAN_DURATION = 10000;

        if(!isScanning) {
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    isScanning = false;
                    bluetoothLeScanner.stopScan(bleScanCallback);
                    Log.d(TAG, "ScanBLEDevices: " + "stop scanning");
                }
            }, SCAN_DURATION);

            isScanning = true;
            bluetoothLeScanner.startScan(bleScanCallback);
            Log.d(TAG, "ScanBLEDevices: " + "start scanning");
        }
        else {
            isScanning = false;
            bluetoothLeScanner.stopScan(bleScanCallback);
            Log.d(TAG, "ScanBLEDevices: " + "stop scanning");
        }
    }

    // BLE SCAN CALLBACK
    private static ScanCallback bleScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            Log.d(TAG, "ScanCallback: " + result.getDevice());
        }
    };
}

HomeActivity.java:

package com.example.ui.home;

import androidx.appcompat.app.AppCompatActivity;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.os.Bundle;
import android.view.View;

import com.example.R;
import com.example.databinding.ActivityHomeBinding;
import com.example.util.AuthenticationHelper;
import com.example.util.BluetoothHelper;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public class HomeActivity extends AppCompatActivity implements View.OnClickListener {

    ActivityHomeBinding binding;
    Set<BluetoothDevice> pairedDevices;
    ArrayList<HashMap<String, String>> arrayList;
    BluetoothLeScanner bluetoothLeScanner;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityHomeBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        setUpBluetooth();

        binding.buttonKeluar.setOnClickListener(this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        AuthenticationHelper.checkHasTheUserLoggedIn(this);
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.buttonKeluar) {
            AuthenticationHelper.signOutUser(this);
        }
    }

    private void setUpBluetooth() {
        BluetoothHelper.android10BluetoothPermission(this, HomeActivity.this);
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        BluetoothHelper.setupBluetooth(bluetoothAdapter);
        BluetoothHelper.enableBluetooth(this, bluetoothAdapter);

        pairedDevices = BluetoothHelper.queryPairedDevices(bluetoothAdapter);
        arrayList = BluetoothHelper.getNamesAndMacAddresses(pairedDevices);

        bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
        BluetoothHelper.scanBLEDevices(bluetoothLeScanner);
    }
}

UPDATE

I added this code into android10BluetoothPermission() function in BluetoothHelper.java class but still didn’t work.

if (ContextCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    activity,
                    new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSION_CODE
            );
        }
        if (ContextCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    activity,
                    new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                    PERMISSION_CODE
            );
        }

Advertisement

Answer

Finally, I have modified my code so the code now is working. Here it is:

ScanAndPairActivity.java (I’m creating a new activity for scanning and pairing BLE devices):

package com.example.ui.scanandpair;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.example.R;
import com.example.databinding.ActivityScanAndPairBinding;
import com.example.entity.BLEDeviceEntity;

import java.util.ArrayList;
import java.util.List;

public class ScanAndPairActivity extends AppCompatActivity {

    // GENERAL
    String TAG = getClass().getSimpleName();
    ActivityScanAndPairBinding binding;

    // BLUETOOTH
    int REQUEST_ENABLE_BT = 1;
    boolean isScanning = false;
    BluetoothAdapter bluetoothAdapter;
    BluetoothLeScanner bluetoothLeScanner;
    ScanAndPairAdapter scanAndPairAdapter;
    List<BLEDeviceEntity> bleDeviceEntityList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityScanAndPairBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        android10BluetoothPermission();

        scanAndPairAdapter = new ScanAndPairAdapter(this);
        bleDeviceEntityList = new ArrayList<>();
    }

    // SOURCE FOR BLUETOOTH PERMISSION: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#java

    // CHECK FOR BLUETOOTH PERMISSION FOR ANDROID 10
    private void android10BluetoothPermission () {
        boolean isLocationPermissionRequired = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
        boolean isBGLocationAccessNotGranted = false;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
            isBGLocationAccessNotGranted = ContextCompat.checkSelfPermission(
                    this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED;
        }
        boolean isLocationAccessNotGranted = ContextCompat.checkSelfPermission(
                this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED;

        if (isLocationPermissionRequired && isBGLocationAccessNotGranted && isLocationAccessNotGranted) {
            requestLocationPermission();
        }
        else {
            setUpBluetooth();
        }
    }

    private void requestLocationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
                alertDialogBuilder
                        .setTitle(getString(R.string.izinkan_lokasi))
                        .setMessage(getString(R.string.mohon_izinkan_aplikasi_mengakses_lokasi))
                        .setPositiveButton(getString(R.string.ya), new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                makeLocationRequest();
                            }
                        });
                alertDialogBuilder.show();
            } else {
                makeLocationRequest();
            }
        }
    }

    private void makeLocationRequest() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(new String[]{"android.permission.ACCESS_FINE_LOCATION"}, 101);
        }
    }

    // ON REQUEST PERMISSION RESULT
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch(requestCode) {
            case 101:
                if (grantResults.length != 0 && grantResults[0] == 0) {
                    setUpBluetooth();
                }
                break;
            default:
                Toast.makeText(this, getString(R.string.maaf_izin_lokasi_harus_disetujui), Toast.LENGTH_LONG).show();
        }
    }

    private void setUpBluetooth() {
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        checkBluetooth(bluetoothAdapter);
        enableBluetooth(this, bluetoothAdapter);

        bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
        scanBLEDevices(bluetoothLeScanner);
    }

    // SOURCE FOR SET UP BLUETOOTH: https://developer.android.com/guide/topics/connectivity/bluetooth/setup#java

    // GET BLUETOOTH ADAPTER
    private void checkBluetooth (BluetoothAdapter bluetoothAdapter) {
        if (bluetoothAdapter == null) {
            Log.d(TAG, "Bluetooth: " + "bluetooth adapter is null");
        }
        else {
            Log.d(TAG, "Bluetooth: " + "bluetooth is adapter not null");
        }
    }

    // ENABLE BLUETOOTH
    private void enableBluetooth (Activity activity, BluetoothAdapter bluetoothAdapter) {
        Log.d(TAG, "Bluetooth: " + "bluetooth adapter is enabled: " + bluetoothAdapter.isEnabled());
        if (!bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            activity.startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            Log.d(TAG, "Bluetooth: " + "bluetooth is enabled");
        }
        else {
            Log.d(TAG, "Bluetooth: " + "bluetooth is already enabled");
        }
    }

    // SOURCE FOR FIND BLE DEVICES: https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices#java

    // SCAN BLE DEVICES
    private void scanBLEDevices (BluetoothLeScanner bluetoothLeScanner) {
        showProgressBar();

        int SCAN_DURATION = 5000;

        if(!isScanning) {
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    isScanning = false;
                    bluetoothLeScanner.stopScan(bleScanCallback);
                    Log.d(TAG, "ScanBLEDevices: " + "stop scanning");
                    populateDataToRecyclerView();
                }
            }, SCAN_DURATION);

            isScanning = true;
            showProgressBar();
            bluetoothLeScanner.startScan(buildScanFilters(), buildScanSettings(), bleScanCallback);
            Log.d(TAG, "ScanBLEDevices: " + "start scanning");
        }
        else {
            isScanning = false;
            bluetoothLeScanner.stopScan(bleScanCallback);
            Log.d(TAG, "ScanBLEDevices: " + "stop scanning");
            populateDataToRecyclerView();
        }
    }

    private void showProgressBar() {
        binding.recylerViewFilm.setVisibility(View.GONE);
        binding.progressBar.setVisibility(View.VISIBLE);
    }

    private void hideProgressBar() {
        binding.recylerViewFilm.setVisibility(View.VISIBLE);
        binding.progressBar.setVisibility(View.GONE);
    }

    private void populateDataToRecyclerView() {
        hideProgressBar();
        scanAndPairAdapter.setBleDeviceEntityList(bleDeviceEntityList);
        scanAndPairAdapter.notifyDataSetChanged();

        binding.recylerViewFilm.setLayoutManager(new LinearLayoutManager(ScanAndPairActivity.this));
        binding.recylerViewFilm.setHasFixedSize(true);
        binding.recylerViewFilm.setAdapter(scanAndPairAdapter);

        scanAndPairAdapter.setOnItemClickCallback(new ScanAndPairAdapter.OnItemClickCallback() {
            @Override
            public void onItemClicked(BLEDeviceEntity bleDeviceEntity) {
                Toast.makeText(
                        ScanAndPairActivity.this, "try to connect to " + bleDeviceEntity.getName(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    // BLE SCAN CALLBACK
    private ScanCallback bleScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            String deviceAddress = result.getDevice().getAddress();
            String deviceName = result.getDevice().getName();

            Log.d(TAG, "ScanCallback, deviceAddress: " + deviceAddress + ", deviceName: " + deviceName);
            if(bleDeviceEntityList.size() == 0) {
                bleDeviceEntityList.add(new BLEDeviceEntity(deviceAddress, deviceName));
            }
            else {
                for(BLEDeviceEntity bleDeviceEntity : bleDeviceEntityList){
                    if(!bleDeviceEntity.getAddress().equals(deviceAddress)) {
                        bleDeviceEntityList.add(new BLEDeviceEntity(deviceAddress, deviceName));
                    }
                }
            }

            Log.d(TAG, "bleDeviceEntityList: " + bleDeviceEntityList);
        }
    };

    // SOURCE FOR LATEST ANDROID BLE APPLICATION SAMPLE: https://github.com/android/connectivity-samples/tree/main/BluetoothAdvertisementsKotlin

    private static List<ScanFilter> buildScanFilters() {
        List<ScanFilter> scanFilters = new ArrayList<>();

        ScanFilter.Builder builder = new ScanFilter.Builder();
        // Comment out the below line to see all BLE devices around you
        // builder.setServiceUuid(Constants.Service_UUID);
        scanFilters.add(builder.build());
        return scanFilters;
    }

    private static ScanSettings buildScanSettings() {
        ScanSettings.Builder builder = new ScanSettings.Builder();
        builder.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
        return builder.build();
    }
}

Thank you everyone for your help and special thanks to Isaidamier for showing me the example app here.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement