Search code examples
androidandroid-permissionsflutter-plugin

java.lang.SecurityException: Permission Denial: starting Intent when trying to ask for bluetooth permission


I'm working in a Flutter android plugin and I'm not well familiar with Android development environment. I'm facing an issue that seems to be "popular", but all the solutions I tried didn't work, and I can't understand the origin of the issue.

The issue is:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.bluetooth_example/com.example.bluetooth.BluetoothPermissionActivity}: java.lang.SecurityException: Permission Denial: starting Intent { act=android.bluetooth.adapter.action.REQUEST_ENABLE cmp=com.android.settings/.bluetooth.RequestPermissionActivity } from ProcessRecord{e30ff3f 13434:com.example.bluetooth_example/u0a349} (pid=13434, uid=10349) requires android.permission.BLUETOOTH_CONNECT

As far as I could understand, the issue is related with a missing android.permission.BLUETOOTH_CONNECT permission, which should be placed in the AndroidManifest.xml. The problem is that I already have all the bluetooth permissions there. Here's my AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.bluetooth">
    <!-- Request legacy Bluetooth permissions on older devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />

    <!-- Needed only if your app looks for Bluetooth devices.
         If your app doesn't use Bluetooth scan results to derive physical
         location information, you can strongly assert that your app
         doesn't derive physical location. -->
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

    <!-- Needed only if your app makes the device discoverable to Bluetooth
         devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

    <!-- Needed only if your app communicates with already-paired Bluetooth
         devices. -->
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

    <!-- Needed only if your app uses Bluetooth scan results to derive physical location. -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <application>
        <activity android:name="BluetoothPermissionActivity" android:exported="true" android:theme="@style/Theme.Transparent" />
    </application>
</manifest>

I have all the necessary permission there I think... The activity BluetoothPermissionActivity is the one I created ask for bluetooth enable permission to the user, just as android docs suggests. Here's the implementation:

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.bluetooth.BluetoothAdapter
import androidx.activity.result.contract.ActivityResultContracts

internal const val ACTION_PERMISSIONS_GRANTED = "BluetoothPermission.permissions_granted"
internal const val ACTION_PERMISSIONS_DENIED = "BluetoothPermission.permissions_denied"

class BluetoothPermissionActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        val registerForResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == Activity.RESULT_OK) {
                sendBroadcast(Intent(ACTION_PERMISSIONS_GRANTED))
            } else {
                sendBroadcast(Intent(ACTION_PERMISSIONS_DENIED))
            }
        }

        registerForResult.launch(enableBtIntent)
    }
}

But as soon as I try to launch the enableBtIntent intent, the error above is thrown. I tried to add the exported=true to the activity in the manifest but it didn't work as well. I'm kind of lost about what could be the cause of this issue, since I have all the permissions in the manifest and the activity is exported (which seems to be 99% of the suggestions for this issue).

UPDATE: It seems to work on Android devices with version <12. It seems something related with the newer Android versions.


Solution

  • I'm not confident with the Android environment yet, but I think I managed to fix the issue above. The line registerForActivityResult(ActivityResultContracts.StartActivityForResult()) is not requesting for permission, it's trying to start the activity. We need to ask the user permission first. I changed the BluetoothPermissionActivity to be:

    internal const val ACTION_PERMISSIONS_GRANTED = "BluetoothPermission.permissions_granted"
    internal const val ACTION_PERMISSIONS_DENIED = "BluetoothPermission.permissions_denied"
    
    class BluetoothPermissionActivity: AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            when {
                checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED -> {
                    sendBroadcast(Intent(ACTION_PERMISSIONS_GRANTED))
                    finish()
                }
                else -> {
                    requestPermissions(arrayOf(Manifest.permission.BLUETOOTH_CONNECT), 1)
                }
            }
        }
    
        override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<out String>,
            grantResults: IntArray
        ) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                sendBroadcast(Intent(ACTION_PERMISSIONS_GRANTED))
                finish()
            } else {
                sendBroadcast(Intent(ACTION_PERMISSIONS_DENIED))
                finish()
            }
        }
    }
    

    And it worked as expected. I hope this helps someone facing a similar issue.