android的蓝牙低功耗权限错误

Bluetooth low energy permission error for android

提问人:UncannyHarmony 提问时间:10/27/2023 最后编辑:David WasserUncannyHarmony 更新时间:10/27/2023 访问量:40

问:

我创建了一个应用程序来扫描和连接 BLE 设备,但不幸的是,每当启动 MainActivity(BLE 扫描)时,该应用程序都会不断崩溃,请帮助我找到代码中的错误,因为我已经实现了所有必要的 BLE 权限和运行时权限。

主要活动代码

public class BluetoothScanning extends AppCompatActivity {

    private GifImageView imageView;
    private boolean isScanning = false;
    private BluetoothGatt bluetoothGatt;
    private BluetoothGattCallback gattCallback;

    private ActivityResultLauncher<Intent> enableBtLauncher;
    private BluetoothAdapter bluetoothAdapter;
    private ArrayList<String> deviceList;
    private ArrayAdapter<String> adapter;

    private Handler handler = new Handler();
    private static final long SCAN_PERIOD = 60000; // 1 minute


    private static final int REQUEST_ENABLE_BT = 1;
    private static final int REQUEST_FINE_LOCATION = 2;

    private ListView listView;

    private Button scan;



    private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                String deviceName = device.getName();
                String deviceAddress = device.getAddress();
                String deviceInfo = deviceName + "\n" + deviceAddress;
                if (!deviceList.contains(deviceInfo)) {
                    deviceList.add(deviceInfo);
                    adapter.notifyDataSetChanged();
                }
            }
        }
    };

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

        deviceList = new ArrayList<>();
        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, deviceList);

        imageView = findViewById(R.id.gif);
        scan = findViewById(R.id.scanbutton);
        listView = findViewById(R.id.devicelist);
        listView.setAdapter(adapter);
        scan.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startBluetoothScan();
            }
        });

        enableBtLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
                result -> {
                    if (result.getResultCode() == Activity.RESULT_OK) {
                        startBluetoothScan();
                    } else {
                        Toast.makeText(this, "Bluetooth is required for this app to function", Toast.LENGTH_SHORT).show();
                    }
                });



        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        if (bluetoothAdapter == null) {
            // Device doesn't support Bluetooth
            Toast.makeText(this, "Bluetooth not supported", Toast.LENGTH_SHORT).show();
        } else {
            // Request Bluetooth and Location permissions at runtime
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED
                    && ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED
                    && ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED
                    && ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN,Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_FINE_LOCATION);
            } else {
                // Permissions are already granted, start scanning
                scan.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        startBluetoothScan();
                    }
                });

                if (!bluetoothAdapter.isEnabled()) {
                    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                    enableBtLauncher.launch(enableBtIntent);
                } else {
                    // Bluetooth is already enabled, start scanning
                    startBluetoothScan();
                }

                listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                    @Override
                    public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                        String deviceInfo = adapter.getItem(i);
                        String[] parts = deviceInfo.split("\n");
                        String deviceAddress = parts[1]; // Assuming the device address is the second part of the split string

                        // Call the method to connect to the selected Bluetooth device
                        connectToBluetoothDevice(deviceAddress);
                    }
                });





                }
            }


        }

    private void connectToBluetoothDevice(String deviceAddress) {
        BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        bluetoothAdapter = bluetoothManager.getAdapter();
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(deviceAddress);

        gattCallback = new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    // Device connected. Discover services here if necessary.
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    // Device disconnected. Handle disconnection here.
                }
            }

            // Other callback methods for BLE operations can be implemented here
        };

        // Connect to the BLE device
        bluetoothGatt = device.connectGatt(this, false, gattCallback);
    }

    // Your existing methods



    @Override
    protected void onStart() {
        super.onStart();

        registerReceiver(bluetoothStateReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));
    }


    @Override
    protected void onResume() {
        super.onResume();
    }


    @Override
    protected void onStop() {
        super.onStop();
        unregisterReceiver(bluetoothStateReceiver);
        stopScan();
    }




    private void startBluetoothScan() {
        if (bluetoothAdapter.isDiscovering()) {
            bluetoothAdapter.cancelDiscovery();
        }
        bluetoothAdapter.startDiscovery();
        // Register for broadcasts when a device is discovered
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(broadcastReceiver, filter);

        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                stopScan();
            }
        }, SCAN_PERIOD);

        isScanning = true;
        scan.setText("Stop Scan");
    }

    public void stopScan() {
        scan.setText("Scan Again");
        bluetoothAdapter.cancelDiscovery();
        handler.removeCallbacksAndMessages(null); // Remove any pending callbacks
        isScanning = false;
        // Change the image in the ImageView here
        imageView.setImageResource(R.drawable.bluetoothscanningfail);
    }



    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (bluetoothAdapter != null) {
            bluetoothAdapter.cancelDiscovery();
        }
        // Unregister the broadcast receiver
        unregisterReceiver(bluetoothStateReceiver);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_FINE_LOCATION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission granted, start scanning
                startBluetoothScan();
            } else {
                // Permission denied, handle accordingly
                Toast.makeText(this, "Location permission is required for Bluetooth scanning", Toast.LENGTH_SHORT).show();
            }
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_ENABLE_BT) {
            if (resultCode == RESULT_OK) {
                // Bluetooth is enabled, start scanning
                startBluetoothScan();
            } else {
                // User denied enabling Bluetooth, handle this scenario accordingly
                Toast.makeText(this, "Bluetooth is required for this app to function", Toast.LENGTH_SHORT).show();
            }
        }
    }


    private final BroadcastReceiver bluetoothStateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();

            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                        BluetoothAdapter.ERROR);
                switch (state) {
                    case BluetoothAdapter.STATE_OFF:
                        Toast.makeText(context, "Please TURN ON Bluetooth", Toast.LENGTH_SHORT).show();
                        // Bluetooth has been turned off;
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        // Bluetooth is turning off;
                        Toast.makeText(context, "Bluetooth is Turning OFF", Toast.LENGTH_SHORT).show();
                        break;
                    case BluetoothAdapter.STATE_ON:
                        // Bluetooth is on
                        Toast.makeText(context, "Bluetooth is ON", Toast.LENGTH_SHORT).show();
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        // Bluetooth is turning on
                        Toast.makeText(context, "Bluetooth is Turning ON", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        }
    };
}

清单文件权限

 <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.BLUETOOTH_ADVERTISE"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

LogCat 错误

 FATAL EXCEPTION: main
  Process: com.example.digilocktrial, PID: 26896
  java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.digilocktrial/com.example.digilocktrial.BluetoothScanning}: java.lang.SecurityException: Need android.permission.BLUETOOTH_SCAN permission for AttributionSource { uid = 10187, packageName = com.example.digilocktrial, attributionTag = null, token = android.os.BinderProxy@487b245, next = null }: AdapterService isDiscovering
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3782)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:205)
    at android.os.Looper.loop(Looper.java:294)
    at android.app.ActivityThread.main(ActivityThread.java:8177)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
  Caused by: java.lang.SecurityException: Need android.permission.BLUETOOTH_SCAN permission for AttributionSource { uid = 10187, packageName = com.example.digilocktrial, attributionTag = null, token = android.os.BinderProxy@487b245, next = null }: AdapterService isDiscovering
    at com.android.bluetooth.Utils.checkPermissionForDataDelivery(Utils.java:554)
    at com.android.bluetooth.Utils.checkScanPermissionForDataDelivery(Utils.java:613)
    at com.android.bluetooth.btservice.AdapterService$AdapterServiceBinder.isDiscovering(AdapterService.java:2342)
    at com.android.bluetooth.btservice.AdapterService$AdapterServiceBinder.isDiscovering(AdapterService.java:2332)
    at android.bluetooth.IBluetooth$Stub.onTransact(IBluetooth.java:1024)
    at android.os.Binder.execTransactInternal(Binder.java:1344)
    at android.os.Binder.execTransact(Binder.java:1275)
低功耗 Android 权限 logcat android-蓝牙

评论

3赞 Emil 10/27/2023
BLUETOOTH_SCAN 是运行时权限,除了在清单中包含它之外,还必须在运行时请求该权限。请参见 developer.android.com/training/permissions/requesting
1赞 David Wasser 10/27/2023
移除了标记,因为该标记用于解决 Android Studio 产品的问题/问题。您的问题与 Android Studio 无关。android-studio
0赞 Kozmotronik 11/4/2023
在我没有看到包含的权限字符串中。错误消息指示此问题:您不请求 的运行时权限。请参阅我的答案,其中我展示了请求蓝牙运行时权限的示例实现。ActivityCompat.requestPermissions(this, new String[]{});BLUETOOTH_SCANBLUETOOTH_SCAN

答: 暂无答案