Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added option to disable Location Providers check before scan #533

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public Observable<ScanResult> scanBleDevices(final ScanSettings scanSettings, fi
return Observable.defer(new Callable<ObservableSource<? extends ScanResult>>() {
@Override
public Observable<ScanResult> call() {
scanPreconditionVerifier.verify();
scanPreconditionVerifier.verify(scanSettings.shouldCheckLocationProviderState());
final ScanSetup scanSetup = scanSetupBuilder.build(scanSettings, scanFilters);
final Operation<RxBleInternalScanResult> scanOperation = scanSetup.scanOperation;
return operationQueue.queue(scanOperation)
Expand All @@ -140,7 +140,7 @@ public Observable<RxBleScanResult> scanBleDevices(@Nullable final UUID... filter
return Observable.defer(new Callable<ObservableSource<? extends RxBleScanResult>>() {
@Override
public ObservableSource<? extends RxBleScanResult> call() throws Exception {
scanPreconditionVerifier.verify();
scanPreconditionVerifier.verify(true);
return initializeScan(filterServiceUUIDs);
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.polidea.rxandroidble2.internal.scan;

/**
* An interface that describes what library extensions should be added to {@link com.polidea.rxandroidble2.scan.ScanSettings}
*/
public interface ExternalScanSettingsExtension {

boolean shouldCheckLocationProviderState();

interface Builder<T extends Builder<T>> {

/**
* Set whether a check if Location Services are (any Location Provider is) turned on before scheduling a BLE scan start.
* Some Android devices will not return any {@link com.polidea.rxandroidble2.scan.ScanResult} if Location Services are
* disabled.
*
* @param shouldCheck true if Location Services should be checked before the scan
* @return the builder
*/
T setShouldCheckLocationServicesState(boolean shouldCheck);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@

public interface ScanPreconditionsVerifier {

void verify() throws BleScanException;
void verify(boolean checkLocationProviderState) throws BleScanException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ public ScanPreconditionsVerifierApi18(RxBleAdapterWrapper rxBleAdapterWrapper, L
}

@Override
public void verify() {
public void verify(boolean checkLocationProviderState) {
if (!rxBleAdapterWrapper.hasBluetoothAdapter()) {
throw new BleScanException(BleScanException.BLUETOOTH_NOT_AVAILABLE);
} else if (!rxBleAdapterWrapper.isBluetoothEnabled()) {
throw new BleScanException(BleScanException.BLUETOOTH_DISABLED);
} else if (!locationServicesStatus.isLocationPermissionOk()) {
throw new BleScanException(BleScanException.LOCATION_PERMISSION_MISSING);
} else if (!locationServicesStatus.isLocationProviderOk()) {
} else if (checkLocationProviderState && !locationServicesStatus.isLocationProviderOk()) {
throw new BleScanException(BleScanException.LOCATION_SERVICES_DISABLED);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public ScanPreconditionsVerifierApi24(
}

@Override
public void verify() {
scanPreconditionVerifierApi18.verify();
public void verify(boolean checkLocationProviderState) {
scanPreconditionVerifierApi18.verify(checkLocationProviderState);

/*
* Android 7.0 (API 24) introduces an undocumented scan throttle for applications that try to scan more than 5 times during
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.IntDef;
import com.polidea.rxandroidble2.internal.scan.ExternalScanSettingsExtension;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

Expand All @@ -18,7 +19,7 @@
* https://code.google.com/p/android/issues/detail?id=178614
* https://code.google.com/p/android/issues/detail?id=228428
*/
public class ScanSettings implements Parcelable {
public class ScanSettings implements Parcelable, ExternalScanSettingsExtension {

@IntDef({SCAN_MODE_OPPORTUNISTIC, SCAN_MODE_LOW_POWER, SCAN_MODE_BALANCED, SCAN_MODE_LOW_LATENCY})
@Retention(RetentionPolicy.SOURCE)
Expand Down Expand Up @@ -137,6 +138,8 @@ public class ScanSettings implements Parcelable {
@MatchNum
private int mNumOfMatchesPerFilter;

private boolean mShouldCheckLocationProviderState;

@ScanMode
public int getScanMode() {
return mScanMode;
Expand Down Expand Up @@ -164,13 +167,19 @@ public long getReportDelayMillis() {
return mReportDelayMillis;
}

@Override
public boolean shouldCheckLocationProviderState() {
return mShouldCheckLocationProviderState;
}

private ScanSettings(int scanMode, int callbackType,
long reportDelayMillis, int matchMode, int numOfMatchesPerFilter) {
long reportDelayMillis, int matchMode, int numOfMatchesPerFilter, boolean shouldCheckLocationServicesState) {
mScanMode = scanMode;
mCallbackType = callbackType;
mReportDelayMillis = reportDelayMillis;
mNumOfMatchesPerFilter = numOfMatchesPerFilter;
mMatchMode = matchMode;
mShouldCheckLocationProviderState = shouldCheckLocationServicesState;
}

private ScanSettings(Parcel in) {
Expand All @@ -183,6 +192,7 @@ private ScanSettings(Parcel in) {
mMatchMode = in.readInt();
//noinspection WrongConstant
mNumOfMatchesPerFilter = in.readInt();
mShouldCheckLocationProviderState = in.readInt() != 0;
}

@Override
Expand All @@ -192,6 +202,7 @@ public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mReportDelayMillis);
dest.writeInt(mMatchMode);
dest.writeInt(mNumOfMatchesPerFilter);
dest.writeInt(mShouldCheckLocationProviderState ? 1 : 0);
}

@Override
Expand All @@ -215,13 +226,14 @@ public ScanSettings createFromParcel(Parcel in) {
/**
* Builder for {@link ScanSettings}.
*/
public static final class Builder {
public static final class Builder implements ExternalScanSettingsExtension.Builder {

private int mScanMode = SCAN_MODE_LOW_POWER;
private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES;
private long mReportDelayMillis = 0;
private int mMatchMode = MATCH_MODE_AGGRESSIVE;
private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
private boolean mShouldCheckLocationProviderState = true;

/**
* Set scan mode for Bluetooth LE scan.
Expand Down Expand Up @@ -254,6 +266,17 @@ public ScanSettings.Builder setCallbackType(@CallbackType int callbackType) {
return this;
}

/**
* {@inheritDoc}
* If set to true and Location Services are off a {@link com.polidea.rxandroidble2.exceptions.BleScanException} will be emitted.
* <p>Default: true.</p>
*/
@Override
public ScanSettings.Builder setShouldCheckLocationServicesState(boolean shouldCheck) {
mShouldCheckLocationProviderState = shouldCheck;
return this;
}

// Returns true if the callbackType is valid.
private boolean isValidCallbackType(int callbackType) {
if (callbackType == CALLBACK_TYPE_ALL_MATCHES
Expand Down Expand Up @@ -321,7 +344,7 @@ private boolean isValidCallbackType(int callbackType) {
*/
public ScanSettings build() {
return new ScanSettings(mScanMode, mCallbackType,
mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter);
mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter, mShouldCheckLocationProviderState);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,47 @@ class RxBleClientTest extends Specification {
scanObservable.test()

then:
1 * mockScanPreconditionVerifier.verify()
1 * mockScanPreconditionVerifier.verify(true)

where:
scanStarter << scanStarters
}

@Unroll
def "should pass shouldCheckLocationServices value to ScanPreconditionVerifier.verify() accordingly when called with RxBleClient.scanBleDevices(ScanSettings, ScanFilters...)"() {

given:
ScanSettings scanSettings = new ScanSettings.Builder().setShouldCheckLocationServicesState(shouldCheck).build()
def scanObservable = objectUnderTest.scanBleDevices(scanSettings)

when:
scanObservable.test()

then:
1 * mockScanPreconditionVerifier.verify(shouldCheck)

where:
shouldCheck << [true, false]
}

def "should call ScanPreconditionVerifier.verify(true) when called with RxBleClient.scanBleDevices(UUID...)"() {

given:
def scanObservable = objectUnderTest.scanBleDevices()

when:
scanObservable.test()

then:
1 * mockScanPreconditionVerifier.verify(true)
}

@Unroll
def "should proxy an error from ScanPreconditionVerifier.verify() when starting a scan"() {
given:
ClientOperationQueue mockQueue = Mock ClientOperationQueue
Throwable testThrowable = new BleScanException(UNKNOWN_ERROR_CODE, new Date())
mockScanPreconditionVerifier.verify() >> { throw testThrowable }
mockScanPreconditionVerifier.verify(_) >> { throw testThrowable }
def scanObservable = scanStarter.call(objectUnderTest)

when:
Expand Down Expand Up @@ -271,7 +300,7 @@ class RxBleClientTest extends Specification {
def "should emit BleScanException if bluetooth has been disabled scan"() {
given:
if (!isBluetoothEnabled)
mockScanPreconditionVerifier.verify() >> { throw new BleScanException(BleScanException.BLUETOOTH_DISABLED) }
mockScanPreconditionVerifier.verify(_) >> { throw new BleScanException(BLUETOOTH_DISABLED) }

when:
def firstSubscriber = objectUnderTest.scanBleDevices(null).test()
Expand All @@ -288,7 +317,7 @@ class RxBleClientTest extends Specification {
def "should emit error if bluetooth is not available"() {
given:
if (!hasBt)
mockScanPreconditionVerifier.verify() >> { throw new BleScanException(BleScanException.BLUETOOTH_NOT_AVAILABLE) }
mockScanPreconditionVerifier.verify(_) >> { throw new BleScanException(BLUETOOTH_NOT_AVAILABLE) }

when:
def firstSubscriber = objectUnderTest.scanBleDevices(null).test()
Expand All @@ -306,7 +335,7 @@ class RxBleClientTest extends Specification {
def "should emit BleScanException if location permission was not granted"() {
given:
if (!permissionOk)
mockScanPreconditionVerifier.verify() >> { throw new BleScanException(BleScanException.LOCATION_PERMISSION_MISSING) }
mockScanPreconditionVerifier.verify(_) >> { throw new BleScanException(LOCATION_PERMISSION_MISSING) }

when:
TestObserver<RxBleScanResult> firstSubscriber = scanStarter.call(objectUnderTest).test()
Expand All @@ -324,7 +353,7 @@ class RxBleClientTest extends Specification {
def "should emit BleScanException if location services are not ok (LocationProviderOk:#providerOk)"() {
given:
if (!providerOk)
mockScanPreconditionVerifier.verify() >> { throw new BleScanException(BleScanException.LOCATION_SERVICES_DISABLED) }
mockScanPreconditionVerifier.verify(_) >> { throw new BleScanException(LOCATION_SERVICES_DISABLED) }


when:
Expand All @@ -343,8 +372,8 @@ class RxBleClientTest extends Specification {
def "should emit BleScanException if ScanPreconditionVerifier will suggest a date to start a scan"() {
given:
if (dateToRetry != null)
mockScanPreconditionVerifier.verify() >> {
throw new BleScanException(BleScanException.UNDOCUMENTED_SCAN_THROTTLE, dateToRetry)
mockScanPreconditionVerifier.verify(_) >> {
throw new BleScanException(UNDOCUMENTED_SCAN_THROTTLE, dateToRetry)
}

when:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.polidea.rxandroidble2.internal.util.RxBleAdapterWrapper
import spock.lang.Specification
import spock.lang.Unroll

public class ScanPreconditionsVerifierApi18Test extends Specification {
class ScanPreconditionsVerifierApi18Test extends Specification {

private RxBleAdapterWrapper mockRxBleAdapterWrapper = Mock RxBleAdapterWrapper

Expand All @@ -19,7 +19,7 @@ public class ScanPreconditionsVerifierApi18Test extends Specification {
def "should perform checks in proper order"() {

when:
objectUnderTest.verify()
objectUnderTest.verify(true)

then:
1 * mockRxBleAdapterWrapper.hasBluetoothAdapter() >> true
Expand All @@ -34,6 +34,7 @@ public class ScanPreconditionsVerifierApi18Test extends Specification {
1 * mockLocationServicesStatus.isLocationProviderOk() >> true
}

@Unroll
def "should not throw any exception if all checks return true"() {

given:
Expand All @@ -43,10 +44,13 @@ public class ScanPreconditionsVerifierApi18Test extends Specification {
mockLocationServicesStatus.isLocationProviderOk() >> true

when:
objectUnderTest.verify()
objectUnderTest.verify(checkLocationServices)

then:
notThrown Throwable

where:
checkLocationServices << TRUE_FALSE
}

@Unroll
Expand All @@ -59,14 +63,14 @@ public class ScanPreconditionsVerifierApi18Test extends Specification {
mockLocationServicesStatus.isLocationProviderOk() >> isLocationProviderOk

when:
objectUnderTest.verify()
objectUnderTest.verify(checkLocationServices)

then:
BleScanException e = thrown(BleScanException)
e.reason == BleScanException.BLUETOOTH_NOT_AVAILABLE

where:
[isBluetoothEnabled, isLocationPermissionOk, isLocationProviderOk] << [TRUE_FALSE, TRUE_FALSE, TRUE_FALSE].combinations()
[isBluetoothEnabled, isLocationPermissionOk, isLocationProviderOk, checkLocationServices] << [TRUE_FALSE, TRUE_FALSE, TRUE_FALSE, TRUE_FALSE].combinations()
}

@Unroll
Expand All @@ -79,14 +83,14 @@ public class ScanPreconditionsVerifierApi18Test extends Specification {
mockLocationServicesStatus.isLocationProviderOk() >> isLocationProviderOk

when:
objectUnderTest.verify()
objectUnderTest.verify(checkLocationServices)

then:
BleScanException e = thrown(BleScanException)
e.reason == BleScanException.BLUETOOTH_DISABLED

where:
[isLocationPermissionOk, isLocationProviderOk] << [TRUE_FALSE, TRUE_FALSE].combinations()
[isLocationPermissionOk, isLocationProviderOk, checkLocationServices] << [TRUE_FALSE, TRUE_FALSE, TRUE_FALSE].combinations()
}

@Unroll
Expand All @@ -99,14 +103,14 @@ public class ScanPreconditionsVerifierApi18Test extends Specification {
mockLocationServicesStatus.isLocationProviderOk() >> isLocationProviderOk

when:
objectUnderTest.verify()
objectUnderTest.verify(checkLocationServices)

then:
BleScanException e = thrown(BleScanException)
e.reason == BleScanException.LOCATION_PERMISSION_MISSING

where:
[isLocationProviderOk] << [TRUE_FALSE].combinations()
[isLocationProviderOk, checkLocationServices] << [TRUE_FALSE, TRUE_FALSE].combinations()
}

def "should throw BleScanException.LOCATION_SERVICES_DISABLED if location services are not enabled"() {
Expand All @@ -118,10 +122,25 @@ public class ScanPreconditionsVerifierApi18Test extends Specification {
mockLocationServicesStatus.isLocationProviderOk() >> false

when:
objectUnderTest.verify()
objectUnderTest.verify(true)

then:
BleScanException e = thrown(BleScanException)
e.reason == BleScanException.LOCATION_SERVICES_DISABLED
}

def "should not check if isLocationProviderOk if checkLocationServices is false"() {

given:
mockRxBleAdapterWrapper.hasBluetoothAdapter() >> true
mockRxBleAdapterWrapper.isBluetoothEnabled() >> true
mockLocationServicesStatus.isLocationPermissionOk() >> true
0 * mockLocationServicesStatus.isLocationProviderOk() >> false

when:
objectUnderTest.verify(false)

then:
noExceptionThrown()
}
}
Loading