Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bluetooth startDiscovery() is not working on Android 10

I´ve got stuck on a problem. I´ve tried some hints from Stack Overflow and other websites.

I would like to program an app, which searches for all Bluetooth devices around and if the matching device has been found (MAC-Address as reference), the app will start a connection.

Therefore I have programmed a test application to test the discovery-function. BUT there is unfortunately a big problem with starting the discovering process on my Android 10 device. I´ve got an older Samsung S3 Mini with Android 4.1.2 (SDK 16) on it, where my code is working fine. On the Android 10 device startDiscovery() returns false, different to the Android 4 device, which returns true. On the android developer page they say, that false is the returning value, if an error has occurred. The BroadcastReceiver should work fine, because the app on the Android 9 mobile phone detects that a Bluetooth search has been started in the settings. It is only the startDiscovery() function, which the problem is all about (in my opinion).

I am checking all permissions and the Bluetooth state before starting the discovering-process. But i think, it can´t be the written code, because it works perfectly on the older device. Maybe I have something missing for newer devices.

Edit

As Thomas Morris explains below, in Android 10 you need the location turned on by the user. In Android 9 or lower, Thomas Morris answer is correct, because in all SDKs below 29, only the permission is needed and not the enabled location service.

Is there a solution to avoid asking the user to turn on the location themselves?

This is my MainActivity:

public class MainActivity extends AppCompatActivity {
final String TAG = "MainActivity";
BluetoothAdapter bluetoothAdapter;
int status = 0;     //0 = start discovering, 1 = cancel discovering

public static final int REQUEST_ACCESS_COARSE_LOCATION = 1;
public static final int REQUEST_ENABLE_BLUETOOTH = 11;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
    registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED));
    registerReceiver(receiver, new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED));

    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    checkBluetoothState();

    final Button test = findViewById(R.id.testbutton);
    test.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(status == 0) {
                if(bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
                    if (checkCoarseLocationPermission()) {
                        Boolean result = bluetoothAdapter.startDiscovery(); //start discovering and show result of function
                        Toast.makeText(getApplicationContext(), "Start discovery result: " + result, Toast.LENGTH_SHORT).show();
                        Log.d(TAG, "Start discovery: " + result);
                        test.setText("Stop");
                        status = 1;
                    }
                }else{
                    checkBluetoothState();
                }
            }else{
                Log.d(TAG,"Stop");
                status = 0;
                bluetoothAdapter.cancelDiscovery();
                test.setText("Start");
            }
        }
    });

    checkCoarseLocationPermission();
}

private boolean checkCoarseLocationPermission() {
    //checks all needed permissions
    if(ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){
        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_ACCESS_COARSE_LOCATION);
        return false;
    }else{
        return true;
    }

}

private void checkBluetoothState() {
    //checks if bluetooth is available and if it´s enabled or not
    if(bluetoothAdapter == null){
        Toast.makeText(getApplicationContext(), "Bluetooth not available", Toast.LENGTH_SHORT).show();
    }else{
        if(bluetoothAdapter.isEnabled()){
            if(bluetoothAdapter.isDiscovering()){
                Toast.makeText(getApplicationContext(), "Device is discovering...", Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(getApplicationContext(), "Bluetooth is enabled", Toast.LENGTH_SHORT).show();
            }
        }else{
            Toast.makeText(getApplicationContext(), "You need to enabled bluetooth", Toast.LENGTH_SHORT).show();
            Intent enabledIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enabledIntent, REQUEST_ENABLE_BLUETOOTH);
        }
    }
}

// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Discovery has found a device. Get the BluetoothDevice
            // object and its info from the Intent.
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            String deviceName = device.getName();
            String deviceHardwareAddress = device.getAddress(); // MAC address
            Log.d(TAG,"Device found: " + deviceName + "|" + deviceHardwareAddress);
            Toast.makeText(getApplicationContext(), "FOUND: " + deviceName + "|" + deviceHardwareAddress, Toast.LENGTH_SHORT).show();
        }

        if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
            //report user
            Log.d(TAG,"Started");
            Toast.makeText(getApplicationContext(), "STARTED", Toast.LENGTH_SHORT).show();
        }

        if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            //change button back to "Start"
            status = 0;
            final Button test = findViewById(R.id.testbutton);
            test.setText("Start");
            //report user
            Log.d(TAG,"Finished");
            Toast.makeText(getApplicationContext(), "FINISHED", Toast.LENGTH_SHORT).show();
        }

        if(BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)){
            final int extra = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,-1);
            if(extra == (BluetoothAdapter.STATE_ON)) {
                if (bluetoothAdapter.isDiscovering()) {
                    bluetoothAdapter.cancelDiscovery();
                }
                Boolean b = bluetoothAdapter.startDiscovery();
                Toast.makeText(getApplicationContext(), "Start discovery" + b, Toast.LENGTH_SHORT).show();
            }
        }
    }
};


@Override
protected void onDestroy() {
    super.onDestroy();
    if (bluetoothAdapter.isDiscovering()){
        bluetoothAdapter.cancelDiscovery();
    }

    unregisterReceiver(receiver);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
    super.onActivityResult(requestCode,resultCode,data);

    if(requestCode == REQUEST_ENABLE_BLUETOOTH){
        checkBluetoothState();
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
    super.onRequestPermissionsResult(requestCode,permissions,grantResults);

    switch (requestCode){
        case REQUEST_ACCESS_COARSE_LOCATION:
            if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                Toast.makeText(getApplicationContext(),"Permission granted",Toast.LENGTH_SHORT).show();
            }else{
                Toast.makeText(getApplicationContext(),"Permission denied",Toast.LENGTH_SHORT).show();
            }
    }
}



}

This is my Manifest:

<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/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

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

like image 534
robert_113 Avatar asked May 14 '20 08:05

robert_113


3 Answers

Enable location permission on the application. To do this go to:

  • Android phone settings
  • App & notifications
  • See all app
  • Locate your application and select it
  • Permissions
  • Allow location slide it on

Then

  • Turn bluetooth on the device on
  • Turn location on the device on

Or some code to do it automatically via a popup (call in oncreate method)

public void checkPermission() {
        if (Build.VERSION.SDK_INT >= 23) {
            if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

            } else {
                ActivityCompat.requestPermissions(this, new String[]{
                        Manifest.permission.ACCESS_FINE_LOCATION,
                        Manifest.permission.ACCESS_COARSE_LOCATION,}, 1);
            }
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
        } else {
            checkPermission();
        }
    }
like image 193
Thomas Morris Avatar answered Sep 26 '22 22:09

Thomas Morris


According to the official Android Documentation you need to have both ACCESS_FINE_LOCATION and ACCESS_BACKGROUND_LOCATION permissions to start discovering Bluetooth devices.

 /**
 * From Android 10 onwards it needs Access Location to search Bluetooth Devices
 */

private void checkForLocationPermission(){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            discoverDevices();
        } else {
            ActivityCompat.requestPermissions(this, new String[]{
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_BACKGROUND_LOCATION,}, 1);
        }
    }

}

/**
 * Request Access Location while using the App, because bluetooth need location to start discovering devices
 * @param requestCode
 * @param permissions
 * @param grantResults
 */

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
        discoverDevices();
    } else {
        checkForLocationPermission();
    }
}

The above code snippet will help you to request the above permissions from the user.

P.S: Also, these need to specified on the Android Manifest as well.

like image 40
Achintha Isuru Avatar answered Sep 26 '22 22:09

Achintha Isuru


Starting Android 10 the Location service must be enabled otherwise no devices will be found.

I tested BluetoothAdapter.startDiscovery() on Huawei P30 with Android 10 and this method alway return false but really the discovery is started (with Location permission granted and location service enabled). So i don't check the result of startDiscovery() method.

like image 38
Giuseppe Laera Avatar answered Sep 26 '22 22:09

Giuseppe Laera