Bluetooth device is disconnected when app is closed

Viewed 297

I am new to BLE implementaion within an app but I am able to connect to a BLE device in the app.

When the app is closed the connection is broken but I need to be able to be connected even when app is closed because I will be receiving data from a microcontroller.

Can anyone give me advice or help me to fix the issue?

Is it due to only being connected because it is waiting for data to be sent and when app is closed I have an issue?

package com.example.Pillwoah;

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

import android.app.Activity;
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.BluetoothSocket;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import static android.bluetooth.BluetoothDevice.ACTION_BOND_STATE_CHANGED;
import static android.bluetooth.BluetoothDevice.BOND_BONDED;
import static android.bluetooth.BluetoothDevice.BOND_BONDING;
import static android.bluetooth.BluetoothDevice.BOND_NONE;
import static android.bluetooth.BluetoothDevice.EXTRA_BOND_STATE;

public class MainActivity4 extends AppCompatActivity {

    protected static final String TAG = "TAG";

    TextView phoneName, deviceList;

    private Handler mHandler;
    private BluetoothAdapter bluetoothAdapter;
    private Set<BluetoothDevice> pairedDevices;
    private ArrayAdapter<String> BAarray, BANewArray;
    private ListView DeviceList, NewDevice;
    private boolean mScanning;
    private BluetoothGatt mGatt;
    private BluetoothLeScanner bluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();

    private BluetoothSocket BTSocket = null;
    private BluetoothDevice device = null;

    private final static UUID BTMODULEUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private final static int REQUEST_ENABLE_BT = 1;
    private final static int REQUEST_CONNECT_DEVICE = 2;
    private final static int REQUEST_COARSE_LOCATION = 1;
    private static final long SCAN_PERIOD = 10000;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main4);

        Button mEnable = (Button) findViewById(R.id.enable);
        Button mOff = (Button) findViewById(R.id.off);
        Button mPaired = (Button) findViewById(R.id.paired);
        Button mDiscover = (Button) findViewById(R.id.discover);

        deviceList = findViewById(R.id.deviceTitle);
        deviceList.setText(null);
        phoneName = findViewById(R.id.name);
        phoneName.setText(getLocalBtName());

        mHandler = new Handler();

        //BA = BluetoothAdapter.getDefaultAdapter();
        // Initializes Bluetooth adapter.
        final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();

        // Ensures Bluetooth is available on the device and it is enabled. If not,
        // displays a dialog requesting user permission to enable Bluetooth.
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

        // Build ScanSetting
        ScanSettings.Builder scanSetting = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
                .setReportDelay(5000);

        scanSetting.build();

        BAarray = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
        BANewArray = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);

        DeviceList = (ListView)findViewById(R.id.deviceListView);
        DeviceList.setAdapter(BAarray);
        DeviceList.setOnItemClickListener(DeviceClickListener);

        NewDevice = (ListView)findViewById(R.id.newDeviceView);
        NewDevice.setAdapter(BANewArray);
        NewDevice.setOnItemClickListener(DeviceClickListener);

        if (bluetoothAdapter == null) {
            Toast.makeText(this, "Bluetooth not supported", Toast.LENGTH_SHORT).show();
            finish();
        } else {
            mEnable.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    checkLocationPermission();
                    bluetoothOn(v);
                }

            });

            mOff.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    bluetoothOff(v);
                }
            });

            mPaired.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    checkLocationPermission();
                    listPairedDevices(v);
                }
            });

            mDiscover.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    checkLocationPermission();
                    discover(v);
                }
            });
        }
    };

    protected void checkLocationPermission() {
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_COARSE_LOCATION);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_COARSE_LOCATION: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(getApplicationContext(), "Permission on", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(getApplicationContext(), "Permission off", Toast.LENGTH_SHORT).show();
                }
                break;
            }
        }
    }

    public String getLocalBtName(){
        if(bluetoothAdapter == null){
            bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        String name = bluetoothAdapter.getName();
        if(name == null){
            name = bluetoothAdapter.getAddress();
        }

        return name;
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Ensures Bluetooth is enabled on the device.  If Bluetooth is not currently enabled,
        // fire an intent to display a dialog asking the user to grant permission to enable it.
        if (!bluetoothAdapter.isEnabled()) {
            if (!bluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            }
        }else {
            if (Build.VERSION.SDK_INT >= 21) {
                bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
                ScanSettings scanSetting = new ScanSettings.Builder()
                        .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
                        .build();

            }
            //scanLeDevice(true);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // User chose not to enable Bluetooth.
        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
            finish();
            return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }
    @Override
    protected void onPause() {
        super.onPause();
        scanLeDevice(false);
        BANewArray.clear();
        BAarray.clear();
    }

    @Override
    public void onDestroy() {
        if (mGatt == null) {
            return;
        }
        Log.i("destroy ", "ble destroy");
        BANewArray.clear();
        BAarray.clear();
        mGatt.close();
        mGatt = null;
        super.onDestroy();
        System.out.println("BLE// destroy");
    }

    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    bluetoothLeScanner.stopScan(leScanCallback);
                    System.out.println("BLE// bluetoothLeScanner.stopScan(leScanCallback)");
                }
            }, SCAN_PERIOD);

            mScanning = true;
            bluetoothLeScanner.startScan(leScanCallback);
            System.out.println("BLE// bluetoothLeScanner.startScan(leScanCallback)");
        } else {
            mScanning = false;
            bluetoothLeScanner.stopScan(leScanCallback);
            System.out.println("BLE// bluetoothLeScanner.stopScan(leScanCallback)");
        }
    }

    // Device scan callback.
    private ScanCallback leScanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);

            Log.i("callbackType", String.valueOf(callbackType));
            Log.i("result", result.toString());
            //Log.i("Device Name: ", result.getDevice().getName());
            System.out.println("Signal: " + result.getRssi());

            BluetoothDevice btDevice = result.getDevice();
            System.out.println("name: " + btDevice);
            BANewArray.add(btDevice.getName() + "\n" + btDevice.getAddress());
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            System.out.println("BLE// onBatchScanResults");
            for (ScanResult sr : results) {
                Log.i("ScanResult - Results", sr.toString());
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            System.out.println("BLE// onScanFailed");
            Log.e("Scan Failed", "Error Code: " + errorCode);
        }
    };

    private void bluetoothOn(View view){
        if(!bluetoothAdapter.isEnabled()){
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
            Toast.makeText(getApplicationContext(), "Bluetooth turned on", Toast.LENGTH_SHORT).show();
        }
        else{
            Toast.makeText(getApplicationContext(), "Bluetooth is already on", Toast.LENGTH_SHORT).show();
        }
    }

    private void bluetoothOff(View view){
        bluetoothAdapter.disable();
        BAarray.clear();
        BANewArray.clear();
        Toast.makeText(getApplicationContext(), "Bluetooth turned off", Toast.LENGTH_SHORT).show();
    }

    public void connectToDevice(BluetoothDevice device) {
        Toast.makeText(getApplicationContext(), "BLE// connectToDevice()", Toast.LENGTH_SHORT).show();
        //System.out.println("BLE// connectToDevice()");
        bluetoothAdapter.cancelDiscovery();
        if (mGatt == null) {
            mGatt = device.connectGatt(this, true, gattCallback); //Connect to a GATT Server
            scanLeDevice(false);// will stop after first device detection
        }
        else{
            //mGatt.discoverServices();
            Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_SHORT).show();
        }
    }

    private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            System.out.println("BLE// BluetoothGattCallback");
            Log.i("onConnectionStateChange", "Status: " + status);

            switch (newState) {
                case BluetoothProfile.STATE_CONNECTED:
                    Log.i("gattCallback", "STATE_CONNECTED");
                    gatt.discoverServices();
                    break;
                case BluetoothProfile.STATE_CONNECTING:
                    Log.i("gattCallback", "STATE_CONNECTING");
                    break;
                case BluetoothProfile.STATE_DISCONNECTED:
                    Log.e("gattCallback", "STATE_DISCONNECTED");
                    //gatt.close();
                    break;
                default:
                    Log.e("gattCallback", "STATE_OTHER");
            }
        }


        @Override
        //New services discovered
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            List<BluetoothGattService> services = gatt.getServices();
            Log.i("onServicesDiscovered", services.toString());
            gatt.readCharacteristic(services.get(1).getCharacteristics().get(0));
        }

        @Override
        //Result of a characteristic read operation
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.i("onCharacteristicRead", characteristic.toString());
            gatt.disconnect();
        }
    };

    private void listPairedDevices(View view){
        BANewArray.clear();
        BAarray.clear();
        deviceList.setText("Previously Connected Devices");
        pairedDevices = bluetoothAdapter.getBondedDevices();
        if(bluetoothAdapter.isEnabled()){
            BANewArray.clear();
            BAarray.clear();
            for(BluetoothDevice device : pairedDevices)
                BAarray.add(device.getName() + "\n" + device.getAddress());

            Toast.makeText(getApplicationContext(), "Show paired devices", Toast.LENGTH_SHORT).show();
        }
        else{
            Toast.makeText(getApplicationContext(), "Bluetooth not on", Toast.LENGTH_SHORT).show();
        }
    }

    private void discover(View view) {
        deviceList.setText("Available Devices");
        BANewArray.clear();
        BAarray.clear();
        scanLeDevice(true);
    }

    private AdapterView.OnItemClickListener DeviceClickListener = new AdapterView.OnItemClickListener() {
        public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {

            //BTSocket = null;
            String info = ((TextView) v).getText().toString();
            final String address = info.substring(info.length() - 17);
            final String name = info.substring(0, info.length() - 17);

            device = bluetoothAdapter.getRemoteDevice(address);

            Log.d(TAG, "You clicked");
            Log.d(TAG, "Device name" + name);
            Log.d(TAG, "Address" + address);

            Toast.makeText(getApplicationContext(), address, Toast.LENGTH_SHORT).show();
            connectToDevice(device);
        }
    };
}

1 Answers

You can use FroegroundService for working in the background, after your app is closed, so there will be non-closable notification and your app will continue work. This is the best solution in new Android versions, because simple services will be killed by Android. You can find more info about working with FroegroundService in this article.

Related