Skip to content

Commit

Permalink
Functionality to set preferred PHY and to read the set PHY (#840)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesDougherty authored Jan 4, 2024
1 parent 60b99f2 commit e9e45cc
Show file tree
Hide file tree
Showing 21 changed files with 913 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,41 @@

import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import androidx.annotation.NonNull;

import android.bluetooth.BluetoothGattService;
import android.util.Log;

import androidx.annotation.NonNull;

import com.polidea.rxandroidble2.ConnectionParameters;
import com.polidea.rxandroidble2.NotificationSetupMode;
import com.polidea.rxandroidble2.PhyPair;
import com.polidea.rxandroidble2.RxBleConnection;
import com.polidea.rxandroidble2.RxBleCustomOperation;
import com.polidea.rxandroidble2.RxBleDeviceServices;
import com.polidea.rxandroidble2.RxBlePhy;
import com.polidea.rxandroidble2.RxBlePhyOption;
import com.polidea.rxandroidble2.exceptions.BleConflictingNotificationAlreadySetException;
import com.polidea.rxandroidble2.exceptions.BleDisconnectedException;
import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException;
import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException;
import com.polidea.rxandroidble2.exceptions.BleGattOperationType;
import com.polidea.rxandroidble2.internal.PhyPairImpl;
import com.polidea.rxandroidble2.internal.Priority;
import com.polidea.rxandroidble2.internal.connection.ImmediateSerializedBatchAckStrategy;
import com.polidea.rxandroidble2.internal.util.ObservableUtil;
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock;
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock;
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleCharacteristicReadCallback;
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleCharacteristicWriteCallback;
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleDescriptorReadCallback;
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.RxBleDescriptorWriteCallback;
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock;
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -73,6 +79,7 @@ public class RxBleConnectionMock implements RxBleConnection {
private RxBleDeviceServices rxBleDeviceServices;
private int rssi;
private int currentMtu = 23;
private PhyPair phy = new PhyPairImpl(RxBlePhy.PHY_1M, RxBlePhy.PHY_1M);
private Map<UUID, Observable<byte[]>> characteristicNotificationSources;
private Map<UUID, RxBleCharacteristicReadCallback> characteristicReadCallbacks;
private Map<UUID, RxBleCharacteristicWriteCallback> characteristicWriteCallbacks;
Expand Down Expand Up @@ -123,6 +130,23 @@ public int getMtu() {
return currentMtu;
}

@Override
public Single<PhyPair> readPhy() {
return Single.fromCallable(() -> phy);
}

@Override
public Single<PhyPair> setPreferredPhy(Set<RxBlePhy> txPhy, Set<RxBlePhy> rxPhy, RxBlePhyOption phyOptions) {
return Single.fromCallable(() -> {
final Iterator<RxBlePhy> txPhyIterator = txPhy.iterator();
final Iterator<RxBlePhy> rxPhyIterator = rxPhy.iterator();
phy = new PhyPairImpl(
txPhyIterator.hasNext() ? txPhyIterator.next() : RxBlePhy.PHY_1M,
rxPhyIterator.hasNext() ? rxPhyIterator.next() : RxBlePhy.PHY_1M);
return phy;
});
}

public int getRssi() {
return rssi;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.polidea.rxandroidble2.mockrxandroidble


import com.polidea.rxandroidble2.RxBlePhy
import com.polidea.rxandroidble2.RxBleClient
import com.polidea.rxandroidble2.RxBlePhyOption
import com.polidea.rxandroidble2.exceptions.BleDisconnectedException
import com.polidea.rxandroidble2.exceptions.BleGattCharacteristicException
import com.polidea.rxandroidble2.exceptions.BleGattDescriptorException
import com.polidea.rxandroidble2.internal.PhyPairImpl
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattReadResultMock
import com.polidea.rxandroidble2.mockrxandroidble.callbacks.results.RxBleGattWriteResultMock
import io.reactivex.Observable
import io.reactivex.functions.Predicate
import io.reactivex.subjects.PublishSubject
import spock.lang.Specification

Expand Down Expand Up @@ -113,6 +116,20 @@ public class RxBleConnectionMockTest extends Specification {
testSubscriber.assertValue(72)
}

def "should return the BluetoothDevice PHY"() {
when:
def testSubscriber = rxBleConnectionMock
.setPreferredPhy(
new LinkedHashSet<RxBlePhy>(List.of(RxBlePhy.PHY_2M, RxBlePhy.PHY_1M)),
new LinkedHashSet<RxBlePhy>(List.of(RxBlePhy.PHY_2M, RxBlePhy.PHY_1M)),
RxBlePhyOption.PHY_OPTION_NO_PREFERRED
)
.test()

then:
testSubscriber.assertValue ({ new PhyPairImpl(RxBlePhy.PHY_2M, RxBlePhy.PHY_2M) == it } as Predicate)
}

def "should return services list"() {
when:
def testSubscriber = rxBleConnectionMock
Expand Down
23 changes: 23 additions & 0 deletions rxandroidble/src/main/java/com/polidea/rxandroidble2/PhyPair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.polidea.rxandroidble2;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.Set;

/**
* The interface used for results of {@link RxBleConnection#readPhy()} and {@link RxBleConnection#setPreferredPhy(Set, Set, RxBlePhyOption)}
*/
public interface PhyPair {

@NonNull
RxBlePhy getTxPhy();

@NonNull
RxBlePhy getRxPhy();

int hashCode();

boolean equals(@Nullable Object obj);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;

import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
Expand All @@ -21,6 +22,7 @@

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -582,6 +584,31 @@ Completable requestConnectionPriority(
*/
int getMtu();

/**
* Performs GATT read PHY operation.
*
* @return Observable emitting the read Tx and Rx values.
*/
@RequiresApi(26 /* Build.VERSION_CODES.O */)
Single<PhyPair> readPhy();

/**
* Performs set preferred PHY request.
*
* @param txPhy Sets the preferred transmitter (Tx) PHY. Use static values defined by the library, e.g. {@link RxBlePhy#PHY_1M}.
* @param rxPhy Sets the preferred receiver (Rx) PHY. Use static values defined by the library, e.g. {@link RxBlePhy#PHY_1M}.
* @param phyOptions Sets the preferred coding to use when transmitting on the LE Coded PHY. Use static values defined by the library,
* e.g. {@link RxBlePhyOption}.
* @return Observable emitting negotiated PHY values pair.
* @throws BleGattException in case of GATT operation error with {@link BleGattOperationType#PHY_UPDATE} type.
* @implNote In case the library is outdated and does not implement expected pre-defined static objects to use, one can implement their
* own objects and pass as parameters which should unblock the use-case. In this case please consider making a PR to the
* library. Please keep in mind that passing custom implementations of RxBlePhy or RxBlePhyOption is permitted for unblocking,
* it is also considered an undefined behaviour and may break with subsequent releases.
*/
@RequiresApi(26 /* Build.VERSION_CODES.O */)
Single<PhyPair> setPreferredPhy(Set<RxBlePhy> txPhy, Set<RxBlePhy> rxPhy, RxBlePhyOption phyOptions);

/**
* <b>This method requires deep knowledge of RxAndroidBLE internals. Use it only as a last resort if you know
* what your are doing.</b>
Expand Down
40 changes: 40 additions & 0 deletions rxandroidble/src/main/java/com/polidea/rxandroidble2/RxBlePhy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.polidea.rxandroidble2;

import android.bluetooth.BluetoothDevice;

import com.polidea.rxandroidble2.internal.RxBlePhyImpl;

import java.util.Set;

/**
* The interface used in {@link Set} for requesting PHY when calling {@link RxBleConnection#setPreferredPhy(Set, Set, RxBlePhyOption)} and
* inside {@link PhyPair} as results of {@link RxBleConnection#readPhy()} and
* {@link RxBleConnection#setPreferredPhy(Set, Set, RxBlePhyOption)}
*/
public interface RxBlePhy {

/**
* Bluetooth LE 1M PHY.
*/
RxBlePhy PHY_1M = RxBlePhyImpl.PHY_1M;

/**
* Bluetooth LE 2M PHY.
*/
RxBlePhy PHY_2M = RxBlePhyImpl.PHY_2M;

/**
* Bluetooth LE Coded PHY.
*/
RxBlePhy PHY_CODED = RxBlePhyImpl.PHY_CODED;

/**
* Corresponds to e.g. {@link BluetoothDevice#PHY_LE_CODED_MASK}
*/
int getMask();

/**
* Corresponds to e.g. {@link BluetoothDevice#PHY_LE_CODED}
*/
int getValue();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.polidea.rxandroidble2;

import com.polidea.rxandroidble2.internal.RxBlePhyOptionImpl;

/**
* Coding to be used when transmitting on the LE Coded PHY.
*/
public interface RxBlePhyOption {
/**
* No preferred coding.
*/
RxBlePhyOption PHY_OPTION_NO_PREFERRED = RxBlePhyOptionImpl.PHY_OPTION_NO_PREFERRED;

/**
* Prefer the S=2 coding.
*/
RxBlePhyOption PHY_OPTION_S2 = RxBlePhyOptionImpl.PHY_OPTION_S2;

/**
* Prefer the S=8 coding.
*/
RxBlePhyOption PHY_OPTION_S8 = RxBlePhyOptionImpl.PHY_OPTION_S8;

/**
*
* @return integer value representing PHY option, e.g. {@link android.bluetooth.BluetoothDevice#PHY_OPTION_S2}
*/
int getValue();
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class BleGattOperationType {
public static final BleGattOperationType RELIABLE_WRITE_COMPLETED = new BleGattOperationType("RELIABLE_WRITE_COMPLETED");
public static final BleGattOperationType READ_RSSI = new BleGattOperationType("READ_RSSI");
public static final BleGattOperationType ON_MTU_CHANGED = new BleGattOperationType("ON_MTU_CHANGED");
public static final BleGattOperationType PHY_READ = new BleGattOperationType("PHY_READ");
public static final BleGattOperationType PHY_UPDATE = new BleGattOperationType("PHY_UPDATE");
public static final BleGattOperationType CONNECTION_PRIORITY_CHANGE = new BleGattOperationType("CONNECTION_PRIORITY_CHANGE");
private final String description;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.polidea.rxandroidble2.internal;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.polidea.rxandroidble2.PhyPair;
import com.polidea.rxandroidble2.RxBlePhy;

import java.util.Objects;

public class PhyPairImpl implements PhyPair {
public final RxBlePhy txPhy;
public final RxBlePhy rxPhy;

public PhyPairImpl(@NonNull final RxBlePhy txPhy, @NonNull final RxBlePhy rxPhy) {
this.txPhy = txPhy;
this.rxPhy = rxPhy;
}

@NonNull
@Override
public RxBlePhy getTxPhy() {
return txPhy;
}

@NonNull
@Override
public RxBlePhy getRxPhy() {
return rxPhy;
}

@Override
public int hashCode() {
return Objects.hash(rxPhy, txPhy);
}

@Override
public boolean equals(@Nullable Object obj) {
if (obj == this) return true;
if (!(obj instanceof PhyPair)) return false;
PhyPair phyPair = (PhyPair) obj;
return txPhy.equals(phyPair.getTxPhy()) && rxPhy.equals(phyPair.getRxPhy());
}

@NonNull
@Override
public String toString() {
return "PhyPair{"
+ "txPhy=" + txPhy
+ ", rxPhy=" + rxPhy
+ '}';
}
}
Loading

0 comments on commit e9e45cc

Please sign in to comment.