diff --git a/README.md b/README.md index 0323fb8c1..d4ed3299c 100644 --- a/README.md +++ b/README.md @@ -53,27 +53,45 @@ maven { url "https://oss.sonatype.org/content/repositories/snapshots" } ``` ### Permissions -Android since API 23 (6.0 / Marshmallow) requires location permissions declared in the manifest for an app to run a BLE scan. RxAndroidBle already provides all the necessary bluetooth permissions for you in AndroidManifest. +Android since API 23 (6.0 / Marshmallow) requires additional permissions declared in the manifest for an app to run a BLE scan. RxAndroidBle provides minimal required bluetooth permissions for you in AndroidManifest — it assumes to be used in the foreground and not deriving actual user location from BLE signal. +#### Scanning Runtime permissions required for running a BLE scan: | from API | to API (inclusive) | Acceptable runtime permissions | |:---:|:---:| --- | | 18 | 22 | (No runtime permissions needed) | | 23 | 28 | One of below:
- `android.permission.ACCESS_COARSE_LOCATION`
- `android.permission.ACCESS_FINE_LOCATION` | -| 29 | current | - `android.permission.ACCESS_FINE_LOCATION` | +| 29 | 30 | - `android.permission.ACCESS_FINE_LOCATION`
- `android.permission.ACCESS_BACKGROUND_LOCATION`\* | +| 31 | current | - `android.permission.BLUETOOTH_SCAN`\*\*
- `android.permission.ACCESS_FINE_LOCATION`\*\*\* | + +\* Needed if [scan is performed in background](https://developer.android.com/about/versions/10/privacy/changes#app-access-device-location) +\*\* It is assumed in [AndroidManifest](https://github.com/Polidea/RxAndroidBle/blob/master/rxandroidble/src/main/AndroidManifest.xml) that the application is trying to derive user's location from BLE signal. If that is not the case look below into [Potential permission issues](https://github.com/Polidea/RxAndroidBle#potential-permission-issues). +\*\*\* Needed if `BLUETOOTH_SCAN` is not using `neverForLocation` flag + +#### Connecting +Runtime permissions required for connecting to a BLE peripheral: +| from API | to API (inclusive) | Acceptable runtime permissions | +|:---:|:---:| --- | +| 18 | 30 | (No runtime permissions needed) | +| 31 | current | - `android.permission.BLUETOOTH_CONNECT` | #### Potential permission issues -Google is checking `AndroidManifest` for declaring permissions when releasing to the Play Store. If you have `ACCESS_COARSE_LOCATION` or `ACCESS_FINE_LOCATION` set manually using tag `uses-permission` (as opposed to `uses-permission-sdk-23`) you may run into an issue where your manifest does not merge with RxAndroidBle's, resulting in a failure to upload to the Play Store. These permissions are only required on SDK 23+. If you need any of these permissions on a lower version of Android replace your statement with: +Google is checking `AndroidManifest` for declaring permissions when releasing to the Play Store. If you have `ACCESS_COARSE_LOCATION` or `ACCESS_FINE_LOCATION` set manually using tag `uses-permission` (as opposed to `uses-permission-sdk-23`) you may run into an issue where your manifest does not merge with [RxAndroidBle's AndroidManifest.xml](https://github.com/Polidea/RxAndroidBle/blob/master/rxandroidble/src/main/AndroidManifest.xml), resulting in a failure to upload to the Play Store. These permissions are required only on APIs 23-30 assuming your app is not accessing location otherwise. If you need any of these permissions other versions of Android replace your statement with: +```xml + + +``` +If you only want to scan BLE peripherals and do not access location otherwise you can restrict location permissions to only the required range by using [Manifest Merger tool directives](https://developer.android.com/studio/build/manifest-merge.html#marker_selector): ```xml - + + + ``` +After API 31 (Android 12) there are new Bluetooth permissions. One of them comes in a flavour that restricts deriving user's location from BLE signal acquired while scanning — this is assumed by the library. If you need to locate user by scanning BLE use below but keep in mind that you will still need `ACCESS_FINE_LOCATION` then: ```xml - + + ``` ## Usage diff --git a/mockrxandroidble/build.gradle b/mockrxandroidble/build.gradle index ab706167a..e75f0c1e6 100644 --- a/mockrxandroidble/build.gradle +++ b/mockrxandroidble/build.gradle @@ -3,11 +3,11 @@ apply plugin: 'groovyx.android' apply from: rootProject.file('gradle/gradle-mvn-push.gradle') android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 18 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 1 versionName VERSION_NAME } diff --git a/rxandroidble/build.gradle b/rxandroidble/build.gradle index 5f35a84ed..dca118164 100644 --- a/rxandroidble/build.gradle +++ b/rxandroidble/build.gradle @@ -3,11 +3,11 @@ apply plugin: 'groovyx.android' apply from: rootProject.file('gradle/gradle-mvn-push.gradle') android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { minSdkVersion 18 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 1 versionName VERSION_NAME } diff --git a/rxandroidble/src/main/AndroidManifest.xml b/rxandroidble/src/main/AndroidManifest.xml index 1aeb22ed6..d4ee81fa1 100644 --- a/rxandroidble/src/main/AndroidManifest.xml +++ b/rxandroidble/src/main/AndroidManifest.xml @@ -1,10 +1,19 @@ - - - - - - - + + + + + + + + + + + diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble2/ClientComponent.java b/rxandroidble/src/main/java/com/polidea/rxandroidble2/ClientComponent.java index 44005bce9..7586cd988 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble2/ClientComponent.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble2/ClientComponent.java @@ -7,14 +7,17 @@ import android.bluetooth.BluetoothManager; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.location.LocationManager; import android.os.Build; + import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; import com.polidea.rxandroidble2.helpers.LocationServicesOkObservable; import com.polidea.rxandroidble2.internal.DeviceComponent; +import com.polidea.rxandroidble2.internal.RxBleLog; import com.polidea.rxandroidble2.internal.scan.BackgroundScannerImpl; import com.polidea.rxandroidble2.internal.scan.InternalToExternalScanResultConverter; import com.polidea.rxandroidble2.internal.scan.RxBleInternalScanResult; @@ -32,6 +35,7 @@ import com.polidea.rxandroidble2.internal.util.LocationServicesStatus; import com.polidea.rxandroidble2.internal.util.LocationServicesStatusApi18; import com.polidea.rxandroidble2.internal.util.LocationServicesStatusApi23; +import com.polidea.rxandroidble2.internal.util.LocationServicesStatusApi31; import com.polidea.rxandroidble2.internal.util.ObservableUtil; import com.polidea.rxandroidble2.scan.BackgroundScanner; import com.polidea.rxandroidble2.scan.ScanResult; @@ -60,6 +64,7 @@ class NamedExecutors { public static final String BLUETOOTH_INTERACTION = "executor_bluetooth_interaction"; public static final String CONNECTION_QUEUE = "executor_connection_queue"; + private NamedExecutors() { } @@ -71,6 +76,7 @@ class NamedSchedulers { public static final String TIMEOUT = "timeout"; public static final String BLUETOOTH_INTERACTION = "bluetooth_interaction"; public static final String BLUETOOTH_CALLBACKS = "bluetooth_callbacks"; + private NamedSchedulers() { } @@ -81,7 +87,9 @@ class PlatformConstants { public static final String INT_TARGET_SDK = "target-sdk"; public static final String INT_DEVICE_SDK = "device-sdk"; 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"; + private PlatformConstants() { } @@ -90,6 +98,7 @@ private PlatformConstants() { class NamedBooleanObservables { public static final String LOCATION_SERVICES_OK = "location-ok-boolean-observable"; + private NamedBooleanObservables() { } @@ -100,6 +109,7 @@ class BluetoothConstants { public static final String ENABLE_NOTIFICATION_VALUE = "enable-notification-value"; public static final String ENABLE_INDICATION_VALUE = "enable-indication-value"; public static final String DISABLE_NOTIFICATION_VALUE = "disable-notification-value"; + private BluetoothConstants() { } @@ -141,24 +151,40 @@ static int provideDeviceSdk() { @Provides @Named(PlatformConstants.STRING_ARRAY_SCAN_PERMISSIONS) - static String[] provideRecommendedScanRuntimePermissionNames( + static String[][] provideRecommendedScanRuntimePermissionNames( @Named(PlatformConstants.INT_DEVICE_SDK) int deviceSdk, - @Named(PlatformConstants.INT_TARGET_SDK) int targetSdk + @Named(PlatformConstants.INT_TARGET_SDK) int targetSdk, + @Named(PlatformConstants.BOOL_IS_NEARBY_PERMISSION_NEVER_FOR_LOCATION) boolean isNearbyServicesNeverForLocation ) { int sdkVersion = Math.min(deviceSdk, targetSdk); if (sdkVersion < 23 /* pre Android M */) { // Before API 23 (Android M) no runtime permissions are needed - return new String[]{}; + return new String[][]{}; } if (sdkVersion < 29 /* pre Android 10 */) { // Since API 23 (Android M) ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION allows for getting scan results - return new String[]{ - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION + return new String[][]{ + new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION} }; } - // Since API 29 (Android 10) only ACCESS_FINE_LOCATION allows for getting scan results - return new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; + if (sdkVersion < 31 /* pre Android 12 */) { + // Since API 29 (Android 10) only ACCESS_FINE_LOCATION allows for getting scan results + return new String[][]{ + new String[]{Manifest.permission.ACCESS_FINE_LOCATION} + }; + } + // Since API 31 (Android 12) only BLUETOOTH_SCAN allows for getting scan results + if (isNearbyServicesNeverForLocation) { + // if neverForLocation flag is used on BLUETOOTH_SCAN then it is the only permission needed + return new String[][]{ + new String[]{Manifest.permission.BLUETOOTH_SCAN} + }; + } + // otherwise ACCESS_FINE_LOCATION is needed as well + return new String[][]{ + new String[]{Manifest.permission.BLUETOOTH_SCAN}, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION} + }; } @Provides @@ -170,11 +196,17 @@ static ContentResolver provideContentResolver(Context context) { static LocationServicesStatus provideLocationServicesStatus( @Named(PlatformConstants.INT_DEVICE_SDK) int deviceSdk, Provider locationServicesStatusApi18Provider, - Provider locationServicesStatusApi23Provider + Provider locationServicesStatusApi23Provider, + Provider locationServicesStatusApi31Provider ) { - return deviceSdk < Build.VERSION_CODES.M - ? locationServicesStatusApi18Provider.get() - : locationServicesStatusApi23Provider.get(); + if (deviceSdk < 23 /* Build.VERSION_CODES.M */) { + return locationServicesStatusApi18Provider.get(); + } + if (deviceSdk < 31 /* Build.VERSION_CODES.S */) { + return locationServicesStatusApi23Provider.get(); + } + /* deviceSdk >= Build.VERSION_CODES.S */ + return locationServicesStatusApi31Provider.get(); } @Provides @@ -255,6 +287,28 @@ static boolean provideIsAndroidWear(Context context, @Named(PlatformConstants.IN && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); } + @Provides + @Named(PlatformConstants.BOOL_IS_NEARBY_PERMISSION_NEVER_FOR_LOCATION) + @ClientScope + static boolean provideIsNearbyPermissionNeverForLocation(Context context) { + try { + PackageInfo packageInfo = context.getPackageManager().getPackageInfo( + context.getPackageName(), + PackageManager.GET_PERMISSIONS + ); + for (int i = 0; i < packageInfo.requestedPermissions.length; i++) { + if (!Manifest.permission.BLUETOOTH_SCAN.equals(packageInfo.requestedPermissions[i])) { + continue; + } + return (packageInfo.requestedPermissionsFlags[i] & PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION) != 0; + } + } catch (PackageManager.NameNotFoundException e) { + RxBleLog.e(e, "Could not find application PackageInfo"); + } + // default to a safe value + return false; + } + @Provides @ClientScope static ScanSetupBuilder provideScanSetupProvider( diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleClient.java b/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleClient.java index 64ef1f274..1a46badd2 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleClient.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleClient.java @@ -196,8 +196,9 @@ public static void updateLogOptions(LogOptions logOptions) { /** * Returns permission strings needed by the application to run a BLE scan or an empty array if no runtime permissions are needed. Since * Android 6.0 runtime permissions were introduced. To run a BLE scan a runtime permission is needed ever since. Since Android 10.0 - * a different (finer) permission is needed. Only a single permission returned by this function is needed to perform a scan. It is up - * to the user to decide which one. The result array is sorted with the least permissive values first. + * a different (finer) permission is needed. Prior to Android 12.0 only a single permission returned by this function is needed to + * perform a scan. It is up to the user to decide which one. The result array is sorted with the least permissive values first. Since + * Android 12 all permissions returned by this function are needed. *

* Returned values: *

@@ -208,8 +209,12 @@ public static void updateLogOptions(LogOptions logOptions) { * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} *

- * case: 29 <= API

+ * case: 29 <= API < 31

* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} + *

+ * case: 31 <= API

+ * {@link android.Manifest.permission#BLUETOOTH_SCAN} + * optionally {@link android.Manifest.permission#ACCESS_FINE_LOCATION} if BLUETOOTH_SCAN does not have a "neverForLocation" flag * * @return an ordered array of possible scan permissions */ diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleClientImpl.java b/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleClientImpl.java index 98535b8ce..838bed9ec 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleClientImpl.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBleClientImpl.java @@ -16,7 +16,7 @@ import com.polidea.rxandroidble2.internal.scan.ScanSetup; import com.polidea.rxandroidble2.internal.scan.ScanSetupBuilder; import com.polidea.rxandroidble2.internal.serialization.ClientOperationQueue; -import com.polidea.rxandroidble2.internal.util.CheckerLocationPermission; +import com.polidea.rxandroidble2.internal.util.CheckerScanPermission; import com.polidea.rxandroidble2.internal.util.ClientStateObservable; import com.polidea.rxandroidble2.internal.util.LocationServicesStatus; import com.polidea.rxandroidble2.internal.util.RxBleAdapterWrapper; @@ -66,7 +66,7 @@ class RxBleClientImpl extends RxBleClient { private final LocationServicesStatus locationServicesStatus; private final Lazy lazyClientStateObservable; private final BackgroundScanner backgroundScanner; - private final CheckerLocationPermission checkerLocationPermission; + private final CheckerScanPermission checkerScanPermission; @Inject RxBleClientImpl(RxBleAdapterWrapper rxBleAdapterWrapper, @@ -82,7 +82,7 @@ class RxBleClientImpl extends RxBleClient { @Named(ClientComponent.NamedSchedulers.BLUETOOTH_INTERACTION) Scheduler bluetoothInteractionScheduler, ClientComponent.ClientComponentFinalizer clientComponentFinalizer, BackgroundScanner backgroundScanner, - CheckerLocationPermission checkerLocationPermission) { + CheckerScanPermission checkerScanPermission) { this.operationQueue = operationQueue; this.rxBleAdapterWrapper = rxBleAdapterWrapper; this.rxBleAdapterStateObservable = adapterStateObservable; @@ -96,7 +96,7 @@ class RxBleClientImpl extends RxBleClient { this.bluetoothInteractionScheduler = bluetoothInteractionScheduler; this.clientComponentFinalizer = clientComponentFinalizer; this.backgroundScanner = backgroundScanner; - this.checkerLocationPermission = checkerLocationPermission; + this.checkerScanPermission = checkerScanPermission; } @Override @@ -272,11 +272,11 @@ public State getState() { @Override public boolean isScanRuntimePermissionGranted() { - return checkerLocationPermission.isScanRuntimePermissionGranted(); + return checkerScanPermission.isScanRuntimePermissionGranted(); } @Override public String[] getRecommendedScanRuntimePermissions() { - return checkerLocationPermission.getRecommendedScanRuntimePermissions(); + return checkerScanPermission.getRecommendedScanRuntimePermissions(); } } diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/CheckerLocationPermission.java b/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/CheckerScanPermission.java similarity index 52% rename from rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/CheckerLocationPermission.java rename to rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/CheckerScanPermission.java index e7dab33f1..9d58509ce 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/CheckerLocationPermission.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/CheckerScanPermission.java @@ -12,31 +12,50 @@ import com.polidea.rxandroidble2.ClientScope; @ClientScope -public class CheckerLocationPermission { +public class CheckerScanPermission { private final Context context; - private final String[] scanPermissions; + private final String[][] scanPermissions; @Inject - CheckerLocationPermission( + CheckerScanPermission( Context context, - @Named(ClientComponent.PlatformConstants.STRING_ARRAY_SCAN_PERMISSIONS) String[] scanPermissions + @Named(ClientComponent.PlatformConstants.STRING_ARRAY_SCAN_PERMISSIONS) String[][] scanPermissions ) { this.context = context; this.scanPermissions = scanPermissions; } public boolean isScanRuntimePermissionGranted() { - for (String locationPermission : scanPermissions) { - if (isPermissionGranted(locationPermission)) { + boolean allNeededPermissionsGranted = true; + for (String[] neededPermissions : scanPermissions) { + allNeededPermissionsGranted &= isAnyPermissionGranted(neededPermissions); + } + return allNeededPermissionsGranted; + } + + private boolean isAnyPermissionGranted(String[] acceptablePermissions) { + for (String acceptablePermission : acceptablePermissions) { + if (isPermissionGranted(acceptablePermission)) { return true; } } - return scanPermissions.length == 0; + return false; } public String[] getRecommendedScanRuntimePermissions() { - return scanPermissions; + int allPermissionsCount = 0; + for (String[] permissionsArray : scanPermissions) { + allPermissionsCount += permissionsArray.length; + } + String[] resultPermissions = new String[allPermissionsCount]; + int i = 0; + for (String[] permissionsArray : scanPermissions) { + for (String permission : permissionsArray) { + resultPermissions[i++] = permission; + } + } + return resultPermissions; } /** diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi23.java b/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi23.java index 834746599..21e13ed41 100644 --- a/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi23.java +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi23.java @@ -8,7 +8,7 @@ public class LocationServicesStatusApi23 implements LocationServicesStatus { private final CheckerLocationProvider checkerLocationProvider; - private final CheckerLocationPermission checkerLocationPermission; + private final CheckerScanPermission checkerScanPermission; private final boolean isAndroidWear; private final int targetSdk; private final int deviceSdk; @@ -16,20 +16,20 @@ public class LocationServicesStatusApi23 implements LocationServicesStatus { @Inject LocationServicesStatusApi23( CheckerLocationProvider checkerLocationProvider, - CheckerLocationPermission checkerLocationPermission, + CheckerScanPermission checkerScanPermission, @Named(ClientComponent.PlatformConstants.INT_TARGET_SDK) int targetSdk, @Named(ClientComponent.PlatformConstants.INT_DEVICE_SDK) int deviceSdk, @Named(ClientComponent.PlatformConstants.BOOL_IS_ANDROID_WEAR) boolean isAndroidWear ) { this.checkerLocationProvider = checkerLocationProvider; - this.checkerLocationPermission = checkerLocationPermission; + this.checkerScanPermission = checkerScanPermission; this.targetSdk = targetSdk; this.deviceSdk = deviceSdk; this.isAndroidWear = isAndroidWear; } public boolean isLocationPermissionOk() { - return checkerLocationPermission.isScanRuntimePermissionGranted(); + return checkerScanPermission.isScanRuntimePermissionGranted(); } public boolean isLocationProviderOk() { diff --git a/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi31.java b/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi31.java new file mode 100644 index 000000000..a94c23d0d --- /dev/null +++ b/rxandroidble/src/main/java/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi31.java @@ -0,0 +1,52 @@ +package com.polidea.rxandroidble2.internal.util; + +import android.annotation.TargetApi; + +import com.polidea.rxandroidble2.ClientComponent; + +import bleshadow.javax.inject.Inject; +import bleshadow.javax.inject.Named; + +@TargetApi(31 /* Build.VERSION_CODES.S */) +public class LocationServicesStatusApi31 implements LocationServicesStatus { + + private final CheckerLocationProvider checkerLocationProvider; + private final CheckerScanPermission checkerScanPermission; + private final boolean isAndroidWear; + private final boolean isNearbyPermissionNeverForLoc; + + @Inject + LocationServicesStatusApi31( + CheckerLocationProvider checkerLocationProvider, + CheckerScanPermission checkerScanPermission, + @Named(ClientComponent.PlatformConstants.BOOL_IS_ANDROID_WEAR) boolean isAndroidWear, + @Named(ClientComponent.PlatformConstants.BOOL_IS_NEARBY_PERMISSION_NEVER_FOR_LOCATION) boolean isNearbyPermissionNeverForLoc + ) { + this.checkerLocationProvider = checkerLocationProvider; + this.checkerScanPermission = checkerScanPermission; + this.isAndroidWear = isAndroidWear; + this.isNearbyPermissionNeverForLoc = isNearbyPermissionNeverForLoc; + } + + public boolean isLocationPermissionOk() { + return checkerScanPermission.isScanRuntimePermissionGranted(); + } + + public boolean isLocationProviderOk() { + return !isLocationProviderEnabledRequired() || checkerLocationProvider.isLocationProviderEnabled(); + } + + /** + * A function that returns true if the location services may be needed to be turned ON. Since there are no official guidelines + * for Android Wear check is disabled. + * + * @return true if Location Services need to be turned ON + * @see Google Groups Discussion + */ + private boolean isLocationProviderEnabledRequired() { + if (isAndroidWear) { + return false; + } + return !isNearbyPermissionNeverForLoc; + } +} diff --git a/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/RxBleClientTest.groovy b/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/RxBleClientTest.groovy index 6a8772d5f..c9f4dfdd7 100644 --- a/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/RxBleClientTest.groovy +++ b/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/RxBleClientTest.groovy @@ -9,7 +9,7 @@ import com.polidea.rxandroidble2.internal.RxBleDeviceProvider import com.polidea.rxandroidble2.internal.operations.Operation import com.polidea.rxandroidble2.internal.scan.* import com.polidea.rxandroidble2.internal.serialization.ClientOperationQueue -import com.polidea.rxandroidble2.internal.util.CheckerLocationPermission +import com.polidea.rxandroidble2.internal.util.CheckerScanPermission import com.polidea.rxandroidble2.internal.util.ClientStateObservable import com.polidea.rxandroidble2.internal.util.ScanRecordParser import com.polidea.rxandroidble2.scan.BackgroundScanner @@ -53,7 +53,7 @@ class RxBleClientTest extends ElectricSpecification { ScanSetup mockScanSetup = new ScanSetup(mockOperationScan, mockObservableTransformer) ScanPreconditionsVerifier mockScanPreconditionVerifier = Mock ScanPreconditionsVerifier InternalToExternalScanResultConverter mockMapper = Mock InternalToExternalScanResultConverter - CheckerLocationPermission mockCheckerLocationPermission = Mock CheckerLocationPermission + CheckerScanPermission mockCheckerLocationPermission = Mock CheckerScanPermission private static someUUID = UUID.randomUUID() private static otherUUID = UUID.randomUUID() private static Date suggestedDateToRetry = new Date() diff --git a/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/internal/util/CheckerScanPermissionTest.groovy b/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/internal/util/CheckerScanPermissionTest.groovy new file mode 100644 index 000000000..262e3d7df --- /dev/null +++ b/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/internal/util/CheckerScanPermissionTest.groovy @@ -0,0 +1,38 @@ +package com.polidea.rxandroidble2.internal.util + + +import spock.lang.Specification +import spock.lang.Unroll + +class CheckerScanPermissionTest extends Specification { + CheckerScanPermission objectUnderTest + + @Unroll + def "return input data concatenated if needed"() { + + given: + objectUnderTest = new CheckerScanPermission(null, repackToArray(permissions)) + + expect: + objectUnderTest.getRecommendedScanRuntimePermissions() == (expectedPermissions.toArray(new String[0])) + + where: + permissions | expectedPermissions + [] | [] + [[], []] | [] + [["p0", "p1"]] | ["p0", "p1"] + [["p0", "p1"], []] | ["p0", "p1"] + [["p0"], ["p1"]] | ["p0", "p1"] + [["p0", "p1"], ["p2"]] | ["p0", "p1", "p2"] + [["p0", "p1"], ["p2"], ["p3"]] | ["p0", "p1", "p2", "p3"] + } + + String[][] repackToArray(ArrayList> permissions) { + def result = new String[permissions.size()][] + for (i in 0.. permissionsArrayInner = permissions.get(i) + result[i] = permissionsArrayInner.toArray(new String[0]) + } + return result + } +} diff --git a/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi23Test.groovy b/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi23Test.groovy index ca42cf82c..a294a0002 100644 --- a/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi23Test.groovy +++ b/rxandroidble/src/test/groovy/com/polidea/rxandroidble2/internal/util/LocationServicesStatusApi23Test.groovy @@ -8,14 +8,14 @@ import spock.lang.Unroll class LocationServicesStatusApi23Test extends Specification { def mockCheckerLocationProvider = Mock CheckerLocationProvider - def mockCheckerLocationPermission = Mock CheckerLocationPermission + def mockCheckerScanPermission = Mock CheckerScanPermission int mockApplicationTargetSdk int mockApplicationDeviceSdk boolean mockIsAndroidWear LocationServicesStatusApi23 objectUnderTest private prepareObjectUnderTest() { - objectUnderTest = new LocationServicesStatusApi23(mockCheckerLocationProvider, mockCheckerLocationPermission, mockApplicationTargetSdk, mockApplicationDeviceSdk, mockIsAndroidWear) + objectUnderTest = new LocationServicesStatusApi23(mockCheckerLocationProvider, mockCheckerScanPermission, mockApplicationTargetSdk, mockApplicationDeviceSdk, mockIsAndroidWear) } // API 18 is the first version with official AOSP BLE, API 21 is the last w/o need of location @@ -37,7 +37,7 @@ class LocationServicesStatusApi23Test extends Specification { given: prepareObjectUnderTest() - mockCheckerLocationPermission.isScanRuntimePermissionGranted() >> permissionGranted + mockCheckerScanPermission.isScanRuntimePermissionGranted() >> permissionGranted expect: objectUnderTest.isLocationPermissionOk() == permissionGranted diff --git a/sample-kotlin/build.gradle b/sample-kotlin/build.gradle index 5848238ef..f42dd3847 100644 --- a/sample-kotlin/build.gradle +++ b/sample-kotlin/build.gradle @@ -3,12 +3,12 @@ apply plugin: 'org.jetbrains.kotlin.android' apply plugin: 'org.jetbrains.kotlin.android.extensions' android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { applicationId "com.polidea.rxandroidble.sample" minSdkVersion 18 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 1 versionName "1.0" } diff --git a/sample-kotlin/src/main/AndroidManifest.xml b/sample-kotlin/src/main/AndroidManifest.xml index 7db7e7515..392bdc4dd 100644 --- a/sample-kotlin/src/main/AndroidManifest.xml +++ b/sample-kotlin/src/main/AndroidManifest.xml @@ -14,7 +14,8 @@ + android:label="@string/title_example1" + android:exported="true"> diff --git a/sample/build.gradle b/sample/build.gradle index b1b245385..74ab034a9 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -12,12 +12,12 @@ repositories { } android { - compileSdkVersion 29 + compileSdkVersion 31 defaultConfig { applicationId "com.polidea.rxandroidble.sample" minSdkVersion 18 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 1 versionName "1.0" } diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index ff03e0c82..bc0c11746 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -11,10 +11,10 @@ android:theme="@style/AppTheme"> + android:label="@string/title_example1" + android:exported="true"> -