• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

Bluetooth scanning and connecting android studio

Tizfaver

Lurker
Hi,
I'm new in this forum so I don't know if i'm in the correct section...
I created this class named BLEConnection becuase i'm tring to connect to a esp32 via BLE but i'm having some problem in the connection part... the class:
Code:
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
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.Context;
import android.os.ParcelUuid;
import android.util.Log;

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

public class BLEConnection {
    private BluetoothAdapter mBluetoothAdapter;
    private BluetoothLeScanner mBluetoothLeScanner;
    private String mTargetDeviceAddress;
    private boolean mScanning;
    private BLEConnectionListener mListener;
    private BluetoothGatt mBluetoothGatt;
    private Context mContext;

    private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            // This callback is invoked when the connection state changes, e.g. from disconnected to connected.
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.e("bleee", "connected ");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.e("bleee", "disconected");
            }
        }
    };

    private ScanCallback mScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) { // This callback is invoked when a BLE advertisement is found.
            Log.e("bleee", "scan result");
            BluetoothDevice device = result.getDevice();
            if (device.getAddress().equals(mTargetDeviceAddress)) {
                Log.e("bleee", "device found 1");
                mBluetoothLeScanner.stopScan(mScanCallback);
                mScanning = false;
                mListener.onDeviceFound(device);
            }
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) { // This callback is invoked when multiple BLE advertisements are found at once.
            Log.e("bleee", "scan result multiple");
            for (ScanResult result : results) {
                BluetoothDevice device = result.getDevice();
                if (device.getAddress().equals(mTargetDeviceAddress)) {
                    Log.e("bleee", "batch scan multiple");
                    mBluetoothLeScanner.stopScan(mScanCallback);
                    mScanning = false;
                    mListener.onDeviceFound(device);
                    break;
                }
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            Log.e("bleee", "scan failed 1");
            mScanning = false;
            mListener.onScanFailed(errorCode);
        }
    };

    public BLEConnection(Context context, String targetDeviceAddress, BLEConnectionListener listener) {
        mTargetDeviceAddress = targetDeviceAddress;
        mListener = listener;

        BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
        mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
    }

    public void startScan() {
        Log.e("bleee", "starting scan..................");
        if (mScanning) {
            return;
        }

        List<ScanFilter> filters = new ArrayList<>();
        ScanFilter filter = new ScanFilter.Builder()
                .setDeviceAddress(mTargetDeviceAddress)
                .build();
        filters.add(filter);

        ScanSettings settings = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
                .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
                .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
                .setReportDelay(0L)
                .build();

        mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
        mScanning = true;
        Log.e("bleee", "finished?");
    }
    public void stopScan() {
        Log.e("bleee", "something stopped the scan");
        if (mScanning) {
            mBluetoothLeScanner.stopScan(mScanCallback);
            mScanning = false;
        }
    }

    public void writeMessage(byte[] message) {
        Log.e("bleee", "writing message...............");
        if (mBluetoothGatt == null) {
            return;
        }

        BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString("4fafc201-1fb5-459e-8fcc-c5c9c331914b"));
        if (service == null) {
            return;
        }

        BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString("beb5483e-36e1-4688-b7f5-ea07361b26a8"));
        if (characteristic == null) {
            return;
        }

        characteristic.setValue(message);
        mBluetoothGatt.writeCharacteristic(characteristic);
    }


    public interface BLEConnectionListener {
        void onDeviceFound(BluetoothDevice device);
        void onScanFailed(int errorCode);
    }

    public void connectToDevice(BluetoothDevice device) {
        Log.e("bleee", "Trying connecting");
        mBluetoothGatt = device.connectGatt(mContext, false, mGattCallback);
    }

    public void disconnectFromDevice() {
        if (mBluetoothGatt != null) {
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
    }

}

please ignore that android studio thinks that some code is an error (because could not be safe and could crash if Bluetooth is not enable, but i'll skip for now this).

So now, I have a service that is this (eliminated useless method for my problem):
Code:
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.util.Log;
//import com.welie.blessed.*;

public class SendNotifyService extends Service implements BLEConnection.BLEConnectionListener {
    private volatile boolean shouldStopThread;
    private volatile boolean oksend = false;
    private String ESP32_MAC_ADDRESS = "84:F7:03:68:06:52";
    private BLEConnection mBLEConnection;

    public SendNotifyService() {}

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        shouldStopThread = false;

        mBLEConnection = new BLEConnection(this, ESP32_MAC_ADDRESS, this);
        // Start scanning for the device
        mBLEConnection.startScan();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!shouldStopThread) {
                //-----------------------------------------------------------------------------------------------------
                    if(oksend == true){
                        byte[] message = "Hello, device!".getBytes();
                        mBLEConnection.writeMessage(message);
                    }

                    Log.e("Service Ard_OS", "Service is running...");
                    Log.e("Service Ard_OS", mac);
                    pause(4000);

                //-----------------------------------------------------------------------------------------------------
            }}
            public void pause(int millis){ try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } }
        }).start();

        return START_STICKY;
    }

    @Override
    public void onDeviceFound(BluetoothDevice device) {
        Log.e("bleee", "device found 2");
        oksend = true;
        mBLEConnection.connectToDevice(device);
    }

    @Override
    public void onScanFailed(int errorCode) {
        Log.e("bleee", "scan failed, i think...");
    }
}

Now it comes the problem:
Obviously I'm using a real phone (samsung galaxy a20e) that runs API 30.
When I start the Service in the LOGCAT using the filter "bleee" i can only see theese two debug message:
------------------ PROCESS STARTED (9353) for package com.example.ard_os ------------------
2023-01-03 22:52:02.993 9353-9353 bleee com.example.ard_os E starting scan..................
2023-01-03 22:52:03.012 9353-9353 bleee com.example.ard_os E finished?
So the problem is that i don't get nothing else, not in the BluetoothGattCallback and not even in the ScanCallback. It doesn't connect to the device... If I was not clear about something I will answer!

Thanks to everyone.
 
you could try is to make sure that the ESP32 is advertising itself correctly. The Android device will not be able to connect to the ESP32 if it is not advertising. You can check the ESP32 documentation to see how to correctly advertise the device.

You may also want to make sure that you have the necessary permissions to use BLE on the Android device. You can do this by adding the following to your app's manifest file:
Code:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

Additionally, you may want to add the following to your manifest file to ensure that your app is only installed on devices that support BLE:


Code:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
 
you could try is to make sure that the ESP32 is advertising itself correctly. The Android device will not be able to connect to the ESP32 if it is not advertising. You can check the ESP32 documentation to see how to correctly advertise the device.

You may also want to make sure that you have the necessary permissions to use BLE on the Android device. You can do this by adding the following to your app's manifest file:
Code:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

Additionally, you may want to add the following to your manifest file to ensure that your app is only installed on devices that support BLE:


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

I have all theese permissions in tehe manifest:

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

    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

But i noticed that when I do:

Code:
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
            Log.e("bleee", "no permissions");
        } else {
            Log.e("bleee", "permissions granted");
            mBLEConnection.startScan();
        }

It gives me in the logcat:
no permissions

So the problem maybe: "why my android app in not aving the permissions?"
 
I fixed, while this functions were called in a Service I needed the ACCESS_BACKGROUND_LOCATION to scan... So my manifest is now:

Code:
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" tools:node="remove" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION" tools:node="remove" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:maxSdkVersion="30" />

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

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

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

rember that some of these permissions needs the user approvment, so you need the Run Time Permission.

table with needed permissions to scan:
18 to 22 (No runtime permissions needed)
23 to 28 One of below:
- android.permission.ACCESS_COARSE_LOCATION
- android.permission.ACCESS_FINE_LOCATION
29 to 30 - android.permission.ACCESS_FINE_LOCATION
- android.permission.ACCESS_BACKGROUND_LOCATION*
31 to current - android.permission.BLUETOOTH_SCAN
- android.permission.ACCESS_FINE_LOCATION**

* Needed if scan is performed in background
** Only needed if you want to obtain user's location with BLE scanning (BLUETOOTH_SCAN is not using neverForLocation attribute in your App)
 
Back
Top Bottom