Skip to content

Commit

Permalink
#370 fix Android 13 access fine location error
Browse files Browse the repository at this point in the history
  • Loading branch information
FritzMatthaeus authored and chipweinberger committed Jul 17, 2023
1 parent d59f87a commit f70249e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 46 deletions.
112 changes: 69 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,26 @@ For basic BLE apps, I highly recommend QuickBlue (https://pub.dev/packages/quick

## Introduction

FlutterBluePlus is a bluetooth plugin for [Flutter](https://flutter.dev), a new app SDK to help developers build modern multi-platform apps.
FlutterBluePlus is a bluetooth plugin for [Flutter](https://flutter.dev), a new app SDK to help developers build modern multi-platform apps.

## Cross-Platform Bluetooth LE

FlutterBluePlus aims to offer the most from both platforms (iOS and Android).

Using the FlutterBluePlus instance, you can scan for and connect to nearby devices ([BluetoothDevice](#bluetoothdevice-api)).
Once connected to a device, the BluetoothDevice object can discover services ([BluetoothService](lib/src/bluetooth_service.dart)), characteristics ([BluetoothCharacteristic](lib/src/bluetooth_characteristic.dart)), and descriptors ([BluetoothDescriptor](lib/src/bluetooth_descriptor.dart)).
The BluetoothDevice object is then used to directly interact with characteristics and descriptors.

## Usage

### Obtain an instance

```dart
FlutterBluePlus flutterBlue = FlutterBluePlus.instance;
```

### Scan for devices

```dart
// Start scanning
flutterBlue.startScan(timeout: Duration(seconds: 4));
Expand All @@ -47,6 +51,7 @@ flutterBlue.stopScan();
```

### Connect to a device

```dart
// Connect to the device
await device.connect();
Expand All @@ -56,6 +61,7 @@ device.disconnect();
```

### Discover services

```dart
List<BluetoothService> services = await device.discoverServices();
services.forEach((service) {
Expand All @@ -64,6 +70,7 @@ services.forEach((service) {
```

### Read and write characteristics

```dart
// Reads all characteristics
var characteristics = service.characteristics;
Expand All @@ -77,6 +84,7 @@ await c.write([0x12, 0x34])
```

### Read and write descriptors

```dart
// Reads all descriptors
var descriptors = characteristic.descriptors;
Expand All @@ -90,6 +98,7 @@ await d.write([0x12, 0x34])
```

### Set notifications and listen to changes

```dart
await characteristic.setNotifyValue(true);
characteristic.value.listen((value) {
Expand All @@ -98,90 +107,107 @@ characteristic.value.listen((value) {
```

### Read the MTU and request larger size

```dart
final mtu = await device.mtu.first;
await device.requestMtu(512);
```

Note that iOS will not allow requests of MTU size, and will always try to negotiate the highest possible MTU (iOS supports up to MTU size 185)

## Getting Started

### Change the minSdkVersion for Android

flutter_blue_plus is compatible only from version 19 of Android SDK so you should change this in **android/app/build.gradle**:

```dart
Android {
defaultConfig {
minSdkVersion: 19
```

### Add permissions for Bluetooth

We need to add the permission to use Bluetooth and access location:

#### **Android**

In the **android/app/src/main/AndroidManifest.xml** let’s add:

```xml
```xml
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application
```

#### **IOS**

In the **ios/Runner/Info.plist** let’s add:

```dart
<dict>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Need BLE permission</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Need BLE permission</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Need Location permission</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Need Location permission</string>
<key>NSLocationWhenInUseUsageDescription</key>
```dart
<dict>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Need BLE permission</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Need BLE permission</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Need Location permission</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Need Location permission</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need Location permission</string>
```

For location permissions on iOS see more at: [https://developer.apple.com/documentation/corelocation/requesting_authorization_for_location_services](https://developer.apple.com/documentation/corelocation/requesting_authorization_for_location_services)

## Reference

### FlutterBlue API
| | Android | iOS | Description |
| :--------------- | :----------------: | :------------------: | :-------------------------------- |
| scan | :white_check_mark: | :white_check_mark: | Starts a scan for Bluetooth Low Energy devices. |
| state | :white_check_mark: | :white_check_mark: | Stream of state changes for the Bluetooth Adapter. |
| isAvailable | :white_check_mark: | :white_check_mark: | Checks whether the device supports Bluetooth. |
| isOn | :white_check_mark: | :white_check_mark: | Checks if Bluetooth functionality is turned on. |

| | Android | iOS | Description |
| :---------- | :----------------: | :----------------: | :------------------------------------------------- |
| scan | :white_check_mark: | :white_check_mark: | Starts a scan for Bluetooth Low Energy devices. |
| state | :white_check_mark: | :white_check_mark: | Stream of state changes for the Bluetooth Adapter. |
| isAvailable | :white_check_mark: | :white_check_mark: | Checks whether the device supports Bluetooth. |
| isOn | :white_check_mark: | :white_check_mark: | Checks if Bluetooth functionality is turned on. |

### BluetoothDevice API
| | Android | iOS | Description |
| :-------------------------- | :------------------: | :------------------: | :-------------------------------- |
| connect | :white_check_mark: | :white_check_mark: | Establishes a connection to the device. |
| disconnect | :white_check_mark: | :white_check_mark: | Cancels an active or pending connection to the device. |
| discoverServices | :white_check_mark: | :white_check_mark: | Discovers services offered by the remote device as well as their characteristics and descriptors. |
| services | :white_check_mark: | :white_check_mark: | Gets a list of services. Requires that discoverServices() has completed. |
| state | :white_check_mark: | :white_check_mark: | Stream of state changes for the Bluetooth Device. |
| mtu | :white_check_mark: | :white_check_mark: | Stream of mtu size changes. |
| requestMtu | :white_check_mark: | | Request to change the MTU for the device. |
| readRssi | :white_check_mark: | :white_check_mark: | Read RSSI from a connected device. |
| requestConnectionPriority | :white_check_mark: | | Request to update a high priority, low latency connection. An application should only request high priority connection parameters to transfer large amounts of data over LE quickly. |
| removeBond | :white_check_mark: | | Remove Bluetooth Bond of device |
| setPreferredPhy | :white_check_mark: | | Set preferred RX and TX phy for connection and phy options

| | Android | iOS | Description |
| :------------------------ | :----------------: | :----------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| connect | :white_check_mark: | :white_check_mark: | Establishes a connection to the device. |
| disconnect | :white_check_mark: | :white_check_mark: | Cancels an active or pending connection to the device. |
| discoverServices | :white_check_mark: | :white_check_mark: | Discovers services offered by the remote device as well as their characteristics and descriptors. |
| services | :white_check_mark: | :white_check_mark: | Gets a list of services. Requires that discoverServices() has completed. |
| state | :white_check_mark: | :white_check_mark: | Stream of state changes for the Bluetooth Device. |
| mtu | :white_check_mark: | :white_check_mark: | Stream of mtu size changes. |
| requestMtu | :white_check_mark: | | Request to change the MTU for the device. |
| readRssi | :white_check_mark: | :white_check_mark: | Read RSSI from a connected device. |
| requestConnectionPriority | :white_check_mark: | | Request to update a high priority, low latency connection. An application should only request high priority connection parameters to transfer large amounts of data over LE quickly. |
| removeBond | :white_check_mark: | | Remove Bluetooth Bond of device |
| setPreferredPhy | :white_check_mark: | | Set preferred RX and TX phy for connection and phy options |

### BluetoothCharacteristic API
| | Android | iOS | Description |
| :-------------------------- | :------------------: | :------------------: | :-------------------------------- |
| read | :white_check_mark: | :white_check_mark: | Retrieves the value of the characteristic. |
| write | :white_check_mark: | :white_check_mark: | Writes the value of the characteristic. |
| setNotifyValue | :white_check_mark: | :white_check_mark: | Sets notifications or indications on the characteristic. |
| value | :white_check_mark: | :white_check_mark: | Stream of characteristic's value when changed. |

| | Android | iOS | Description |
| :------------- | :----------------: | :----------------: | :------------------------------------------------------- |
| read | :white_check_mark: | :white_check_mark: | Retrieves the value of the characteristic. |
| write | :white_check_mark: | :white_check_mark: | Writes the value of the characteristic. |
| setNotifyValue | :white_check_mark: | :white_check_mark: | Sets notifications or indications on the characteristic. |
| value | :white_check_mark: | :white_check_mark: | Stream of characteristic's value when changed. |

### BluetoothDescriptor API
| | Android | iOS | Description |
| :-------------------------- | :------------------: | :------------------: | :-------------------------------- |
| read | :white_check_mark: | :white_check_mark: | Retrieves the value of the descriptor. |
| write | :white_check_mark: | :white_check_mark: | Writes the value of the descriptor. |

| | Android | iOS | Description |
| :---- | :----------------: | :----------------: | :------------------------------------- |
| read | :white_check_mark: | :white_check_mark: | Retrieves the value of the descriptor. |
| write | :white_check_mark: | :white_check_mark: | Writes the value of the descriptor. |

## Troubleshooting

### When I scan using a service UUID filter, it doesn't find any devices.
Make sure the device is advertising which service UUID's it supports. This is found in the advertisement

Make sure the device is advertising which service UUID's it supports. This is found in the advertisement
packet as **UUID 16 bit complete list** or **UUID 128 bit complete list**.
3 changes: 2 additions & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

This comment has been minimized.

Copy link
@MrCsabaToth

MrCsabaToth Jul 18, 2023

Contributor

I'm trying to understand why the ACCESS_FINE_LOCATION would be needed even for Android 30+. See also https://stackoverflow.com/questions/73024638/should-i-request-for-location-permissions-when-using-bluetooth-for-pre-android-1
The purpose of the permission changes around Bluetooth is to NOT require location permissions any more on newer Androids if only Bluetooth is involved. See https://www.androidpolice.com/2021/05/19/android-12-apps-wont-ask-for-location-permissions-when-all-they-want-is-bluetooth-scanning-which-yes-was-a-thing/ "This change breaks out Bluetooth scanning (and Bluetooth pairing) into its own separate permission by itself, free of the location permission silo and shackles. That means apps can skip the sketchy location permission access prompts if they just need to scan or connect to Bluetooth devices."


<!-- legacy for Android 11 or lower -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />


<!-- legacy for Android 9 or lower -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28" />
Expand Down
2 changes: 1 addition & 1 deletion example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

<!-- legacy for Android 11 or lower -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />

<!-- legacy for Android 9 or lower -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="28" />
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ packages:
path: ".."
relative: true
source: path
version: "1.7.2"
version: "1.7.3"
js:
dependency: transitive
description:
Expand Down

0 comments on commit f70249e

Please sign in to comment.