Skip to content

Commit

Permalink
BLUETOOTH_CONNECT Handling and Helper Functions Added
Browse files Browse the repository at this point in the history
Get Recommended BLE Connect Runtime Permissions
LocationServicesStatusApi31.isLocationPermissionOk() Always Returns True
RxBleClient State Now Also Returns BLUETOOTH_PERMISSION_NOT_GRANTED
BlePermissionException Class Added to Throw Missing BLUETOOTH_SCAN and BLUETOOTH_CONNECT Exceptions
  • Loading branch information
Abdul.Khaliq authored and Abdul.Khaliq committed Mar 1, 2022
1 parent f30abcc commit 2464147
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -412,4 +412,14 @@ public boolean isScanRuntimePermissionGranted() {
public String[] getRecommendedScanRuntimePermissions() {
return new String[0];
}

@Override
public boolean isConnectRuntimePermissionGranted() {
return true;
}

@Override
public String[] getRecommendedConnectRuntimePermissions() {
return new String[0];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class PlatformConstants {
public static final String BOOL_IS_ANDROID_WEAR = "android-wear";
public static final String BOOL_IS_NEARBY_PERMISSION_NEVER_FOR_LOCATION = "nearby-permission-never-for-location";
public static final String STRING_ARRAY_SCAN_PERMISSIONS = "scan-permissions";
public static final String STRING_ARRAY_CONNECT_PERMISSIONS = "connect-permissions";
public static final String PACKAGE_INFO = "package-info";

private PlatformConstants() {
Expand Down Expand Up @@ -189,6 +190,22 @@ static String[][] provideRecommendedScanRuntimePermissionNames(
};
}

@Provides
@Named(PlatformConstants.STRING_ARRAY_CONNECT_PERMISSIONS)
static String[][] provideRecommendedConnectRuntimePermissionNames(
@Named(PlatformConstants.INT_DEVICE_SDK) int deviceSdk,
@Named(PlatformConstants.INT_TARGET_SDK) int targetSdk
) {
int sdkVersion = Math.min(deviceSdk, targetSdk);
if (sdkVersion < 31 /* pre Android 12 */) {
// Before API 31 (Android 12) no connect permissions are needed
return new String[][]{};
}

// Since API 31 (Android 12) BLUETOOTH_CONNECT is required to establish a connection to a device
return new String[][]{new String[]{Manifest.permission.BLUETOOTH_CONNECT}};
}

@Provides
@Named(PlatformConstants.PACKAGE_INFO)
static PackageInfo providePackageInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public enum State {
* Bluetooth Adapter is not available on the given OS. Most functions will throw {@link UnsupportedOperationException} when called.
*/
BLUETOOTH_NOT_AVAILABLE,
/**
* TODO - Add Doc
*/
BLUETOOTH_PERMISSION_NOT_GRANTED,
/**
* Runtime location permission is not given. Scanning will not work. Used on API >=23.
* <p>APIs 23-28 – ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION
Expand Down Expand Up @@ -222,4 +226,17 @@ public static void updateLogOptions(LogOptions logOptions) {
* @return an ordered array of possible scan permissions
*/
public abstract String[] getRecommendedScanRuntimePermissions();

/**
* Returns whether runtime permissions needed to establish BLE connection are granted. If permissions are not granted then one may check
* {@link #getRecommendedConnectRuntimePermissions()} to get Android runtime permission strings needed to establish a connection.
*
* @return true if needed permissions are granted, false otherwise
*/
public abstract boolean isConnectRuntimePermissionGranted();

/**
* TODO - Add Doc
*/
public abstract String[] getRecommendedConnectRuntimePermissions();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.polidea.rxandroidble2;

import android.Manifest;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.polidea.rxandroidble2.RxBleAdapterStateObservable.BleAdapterState;
import com.polidea.rxandroidble2.exceptions.BlePermissionException;
import com.polidea.rxandroidble2.exceptions.BleScanException;
import com.polidea.rxandroidble2.internal.RxBleDeviceProvider;
import com.polidea.rxandroidble2.internal.RxBleLog;
Expand Down Expand Up @@ -113,15 +115,18 @@ public RxBleDevice getBleDevice(@NonNull String macAddress) {

@Override
public Set<RxBleDevice> getBondedDevices() {
guardBluetoothAdapterAvailable();
Set<RxBleDevice> rxBleDevices = new HashSet<>();
// TODO: check BLUETOOTH_CONNECT permission
Set<BluetoothDevice> bluetoothDevices = rxBleAdapterWrapper.getBondedDevices();
for (BluetoothDevice bluetoothDevice : bluetoothDevices) {
rxBleDevices.add(getBleDevice(bluetoothDevice.getAddress()));
}
if (locationServicesStatus.isConnectPermissionOk()) {
guardBluetoothAdapterAvailable();
Set<RxBleDevice> rxBleDevices = new HashSet<>();
Set<BluetoothDevice> bluetoothDevices = rxBleAdapterWrapper.getBondedDevices();
for (BluetoothDevice bluetoothDevice : bluetoothDevices) {
rxBleDevices.add(getBleDevice(bluetoothDevice.getAddress()));
}

return rxBleDevices;
return rxBleDevices;
} else {
throw new BlePermissionException(Manifest.permission.BLUETOOTH_CONNECT);
}
}

@Override
Expand Down Expand Up @@ -258,6 +263,9 @@ public State getState() {
if (!rxBleAdapterWrapper.hasBluetoothAdapter()) {
return State.BLUETOOTH_NOT_AVAILABLE;
}
if (!locationServicesStatus.isScanPermissionOk()) {
return State.BLUETOOTH_PERMISSION_NOT_GRANTED;
}
if (!locationServicesStatus.isLocationPermissionOk()) {
return State.LOCATION_PERMISSION_NOT_GRANTED;
}
Expand All @@ -280,4 +288,14 @@ public boolean isScanRuntimePermissionGranted() {
public String[] getRecommendedScanRuntimePermissions() {
return checkerScanPermission.getRecommendedScanRuntimePermissions();
}

@Override
public boolean isConnectRuntimePermissionGranted() {
return checkerScanPermission.isConnectRuntimePermissionGranted();
}

@Override
public String[] getRecommendedConnectRuntimePermissions() {
return checkerScanPermission.getRecommendedConnectRuntimePermissions();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.polidea.rxandroidble2.exceptions;


import android.Manifest;

import androidx.annotation.StringDef;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public class BlePermissionException extends BleException {

@StringDef({Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT})
@Retention(RetentionPolicy.SOURCE)
public @interface Permission {
}

public BlePermissionException(@Permission String permission) {
super(createMessage(permission));
}

private static String createMessage(@Permission String permission) {
return "BLE permission exception: " + permission;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.polidea.rxandroidble2.internal;

import android.Manifest;
import android.bluetooth.BluetoothDevice;
import androidx.annotation.Nullable;

Expand All @@ -9,9 +10,12 @@
import com.polidea.rxandroidble2.RxBleDevice;
import com.polidea.rxandroidble2.Timeout;
import com.polidea.rxandroidble2.exceptions.BleAlreadyConnectedException;
import com.polidea.rxandroidble2.exceptions.BlePermissionException;
import com.polidea.rxandroidble2.internal.connection.Connector;

import com.polidea.rxandroidble2.internal.logger.LoggerUtil;
import com.polidea.rxandroidble2.internal.util.LocationServicesStatus;

import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;

Expand All @@ -26,17 +30,20 @@ class RxBleDeviceImpl implements RxBleDevice {
final BluetoothDevice bluetoothDevice;
final Connector connector;
private final BehaviorRelay<RxBleConnection.RxBleConnectionState> connectionStateRelay;
private final LocationServicesStatus locationServicesStatus;
final AtomicBoolean isConnected = new AtomicBoolean(false);

@Inject
RxBleDeviceImpl(
BluetoothDevice bluetoothDevice,
Connector connector,
BehaviorRelay<RxBleConnection.RxBleConnectionState> connectionStateRelay
BehaviorRelay<RxBleConnection.RxBleConnectionState> connectionStateRelay,
LocationServicesStatus locationServicesStatus
) {
this.bluetoothDevice = bluetoothDevice;
this.connector = connector;
this.connectionStateRelay = connectionStateRelay;
this.locationServicesStatus = locationServicesStatus;
}

@Override
Expand Down Expand Up @@ -72,17 +79,20 @@ public Observable<RxBleConnection> establishConnection(final ConnectionSetup opt
return Observable.defer(new Callable<ObservableSource<RxBleConnection>>() {
@Override
public ObservableSource<RxBleConnection> call() {
// TODO: Check BLUETOOTH_CONNECT permission
if (isConnected.compareAndSet(false, true)) {
return connector.prepareConnection(options)
.doFinally(new Action() {
@Override
public void run() {
isConnected.set(false);
}
});
if (locationServicesStatus.isConnectPermissionOk()) {
if (isConnected.compareAndSet(false, true)) {
return connector.prepareConnection(options)
.doFinally(new Action() {
@Override
public void run() {
isConnected.set(false);
}
});
} else {
return Observable.error(new BleAlreadyConnectedException(bluetoothDevice.getAddress()));
}
} else {
return Observable.error(new BleAlreadyConnectedException(bluetoothDevice.getAddress()));
return Observable.error(new BlePermissionException(Manifest.permission.BLUETOOTH_CONNECT));
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,38 @@ public class CheckerScanPermission {

private final Context context;
private final String[][] scanPermissions;
private final String[][] connectPermissions;

@Inject
CheckerScanPermission(
Context context,
@Named(ClientComponent.PlatformConstants.STRING_ARRAY_SCAN_PERMISSIONS) String[][] scanPermissions
@Named(ClientComponent.PlatformConstants.STRING_ARRAY_SCAN_PERMISSIONS) String[][] scanPermissions,
@Named(ClientComponent.PlatformConstants.STRING_ARRAY_CONNECT_PERMISSIONS) String[][] connectPermissions
) {
this.context = context;
this.scanPermissions = scanPermissions;
this.connectPermissions = connectPermissions;
}

public boolean isScanRuntimePermissionGranted() {
boolean allNeededPermissionsGranted = true;
for (String[] neededPermissions : scanPermissions) {
allNeededPermissionsGranted &= isAnyPermissionGranted(neededPermissions);
}
return allNeededPermissionsGranted;
return isAllPermissionsGranted(scanPermissions);
}

public boolean isConnectRuntimePermissionGranted() {
return isAllPermissionsGranted(connectPermissions);
}

public boolean isLocationRuntimePermissionGranted() {
return isPermissionGranted(Manifest.permission.ACCESS_COARSE_LOCATION)
|| isPermissionGranted(Manifest.permission.ACCESS_FINE_LOCATION);
}

public boolean isConnectRuntimePermissionGranted() {
return isPermissionGranted(Manifest.permission.BLUETOOTH_CONNECT);
private boolean isAllPermissionsGranted(String[][] neededPermissions) {
boolean allNeededPermissionsGranted = true;
for (String[] permissions : neededPermissions) {
allNeededPermissionsGranted &= isAnyPermissionGranted(permissions);
}
return allNeededPermissionsGranted;
}

private boolean isAnyPermissionGranted(String[] acceptablePermissions) {
Expand All @@ -54,13 +61,21 @@ private boolean isAnyPermissionGranted(String[] acceptablePermissions) {
}

public String[] getRecommendedScanRuntimePermissions() {
return getRecommendedRuntimePermissions(scanPermissions);
}

public String[] getRecommendedConnectRuntimePermissions() {
return getRecommendedRuntimePermissions(connectPermissions);
}

private String[] getRecommendedRuntimePermissions(String[][] permissions) {
int allPermissionsCount = 0;
for (String[] permissionsArray : scanPermissions) {
for (String[] permissionsArray : permissions) {
allPermissionsCount += permissionsArray.length;
}
String[] resultPermissions = new String[allPermissionsCount];
int i = 0;
for (String[] permissionsArray : scanPermissions) {
for (String[] permissionsArray : permissions) {
for (String permission : permissionsArray) {
resultPermissions[i++] = permission;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class LocationServicesStatusApi31 implements LocationServicesStatus {
}

public boolean isLocationPermissionOk() {
return checkerScanPermission.isLocationRuntimePermissionGranted();
return true;
}

public boolean isLocationProviderOk() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,10 @@ public void stopLeScan(ScanCallback scanCallback) {
bluetoothLeScanner.stopScan(scanCallback);
}

public Set<BluetoothDevice> getBondedDevices() {
public Set<BluetoothDevice> getBondedDevices() throws SecurityException {
if (bluetoothAdapter == null) {
throw nullBluetoothAdapter;
}
// TODO: check BLUETOOTH_CONNECT permission
return bluetoothAdapter.getBondedDevices();
}
}

0 comments on commit 2464147

Please sign in to comment.