blob: 80bde6313cc0f4987de0c7f7265e4e7e94892d68 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.nearby.common.bluetooth.gatt;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothStatusCodes;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import androidx.test.filters.SdkSuppress;
import com.android.server.nearby.common.bluetooth.BluetoothConsts;
import com.android.server.nearby.common.bluetooth.BluetoothException;
import com.android.server.nearby.common.bluetooth.BluetoothGattException;
import com.android.server.nearby.common.bluetooth.ReservedUuids;
import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection.ChangeObserver;
import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattHelper.OperationType;
import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothDevice;
import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothGattWrapper;
import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor;
import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.Operation;
import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor.SynchronousOperation;
import junit.framework.TestCase;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Captor;
import org.mockito.Mock;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* Unit tests for {@link BluetoothGattConnection}.
*/
public class BluetoothGattConnectionTest extends TestCase {
private static final UUID SERVICE_UUID = UUID.randomUUID();
private static final UUID CHARACTERISTIC_UUID = UUID.randomUUID();
private static final UUID DESCRIPTOR_UUID = UUID.randomUUID();
private static final byte[] DATA = "data".getBytes();
private static final int RSSI = -63;
private static final int CONNECTION_PRIORITY = 128;
private static final int MTU_REQUEST = 512;
private static final BluetoothGattHelper.ConnectionOptions CONNECTION_OPTIONS =
BluetoothGattHelper.ConnectionOptions.builder().build();
@Mock
private BluetoothGattWrapper mMockBluetoothGattWrapper;
@Mock
private BluetoothDevice mMockBluetoothDevice;
@Mock
private BluetoothOperationExecutor mMockBluetoothOperationExecutor;
@Mock
private BluetoothGattService mMockBluetoothGattService;
@Mock
private BluetoothGattService mMockBluetoothGattService2;
@Mock
private BluetoothGattCharacteristic mMockBluetoothGattCharacteristic;
@Mock
private BluetoothGattCharacteristic mMockBluetoothGattCharacteristic2;
@Mock
private BluetoothGattDescriptor mMockBluetoothGattDescriptor;
@Mock
private BluetoothGattConnection.CharacteristicChangeListener mMockCharChangeListener;
@Mock
private BluetoothGattConnection.ChangeObserver mMockChangeObserver;
@Mock
private BluetoothGattConnection.ConnectionCloseListener mMockConnectionCloseListener;
@Captor
private ArgumentCaptor<Operation<?>> mOperationCaptor;
@Captor
private ArgumentCaptor<SynchronousOperation<?>> mSynchronousOperationCaptor;
@Captor
private ArgumentCaptor<BluetoothGattCharacteristic> mCharacteristicCaptor;
@Captor
private ArgumentCaptor<BluetoothGattDescriptor> mDescriptorCaptor;
private BluetoothGattConnection mBluetoothGattConnection;
@Override
protected void setUp() throws Exception {
super.setUp();
initMocks(this);
mBluetoothGattConnection = new BluetoothGattConnection(
mMockBluetoothGattWrapper,
mMockBluetoothOperationExecutor,
CONNECTION_OPTIONS);
mBluetoothGattConnection.onConnected();
when(mMockBluetoothGattWrapper.getDevice()).thenReturn(mMockBluetoothDevice);
when(mMockBluetoothGattWrapper.discoverServices()).thenReturn(true);
when(mMockBluetoothGattWrapper.refresh()).thenReturn(true);
when(mMockBluetoothGattWrapper.readCharacteristic(mMockBluetoothGattCharacteristic))
.thenReturn(true);
when(mMockBluetoothGattWrapper
.writeCharacteristic(ArgumentMatchers.<BluetoothGattCharacteristic>any(), any(),
anyInt()))
.thenReturn(BluetoothStatusCodes.SUCCESS);
when(mMockBluetoothGattWrapper.readDescriptor(mMockBluetoothGattDescriptor))
.thenReturn(true);
when(mMockBluetoothGattWrapper.writeDescriptor(
ArgumentMatchers.<BluetoothGattDescriptor>any(), any()))
.thenReturn(BluetoothStatusCodes.SUCCESS);
when(mMockBluetoothGattWrapper.readRemoteRssi()).thenReturn(true);
when(mMockBluetoothGattWrapper.requestConnectionPriority(CONNECTION_PRIORITY))
.thenReturn(true);
when(mMockBluetoothGattWrapper.requestMtu(MTU_REQUEST)).thenReturn(true);
when(mMockBluetoothGattWrapper.getServices())
.thenReturn(Arrays.asList(mMockBluetoothGattService));
when(mMockBluetoothGattService.getUuid()).thenReturn(SERVICE_UUID);
when(mMockBluetoothGattService.getCharacteristics())
.thenReturn(Arrays.asList(mMockBluetoothGattCharacteristic));
when(mMockBluetoothGattCharacteristic.getUuid()).thenReturn(CHARACTERISTIC_UUID);
when(mMockBluetoothGattCharacteristic.getProperties())
.thenReturn(
BluetoothGattCharacteristic.PROPERTY_NOTIFY
| BluetoothGattCharacteristic.PROPERTY_WRITE);
BluetoothGattDescriptor clientConfigDescriptor =
new BluetoothGattDescriptor(
ReservedUuids.Descriptors.CLIENT_CHARACTERISTIC_CONFIGURATION,
BluetoothGattDescriptor.PERMISSION_WRITE);
when(mMockBluetoothGattCharacteristic.getDescriptor(
ReservedUuids.Descriptors.CLIENT_CHARACTERISTIC_CONFIGURATION))
.thenReturn(clientConfigDescriptor);
when(mMockBluetoothGattCharacteristic.getDescriptors())
.thenReturn(Arrays.asList(mMockBluetoothGattDescriptor, clientConfigDescriptor));
when(mMockBluetoothGattDescriptor.getUuid()).thenReturn(DESCRIPTOR_UUID);
when(mMockBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getDevice() {
BluetoothDevice result = mBluetoothGattConnection.getDevice();
assertThat(result).isEqualTo(mMockBluetoothDevice);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getConnectionOptions() {
BluetoothGattHelper.ConnectionOptions result = mBluetoothGattConnection
.getConnectionOptions();
assertThat(result).isSameInstanceAs(CONNECTION_OPTIONS);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_isConnected_false_beforeConnection() {
mBluetoothGattConnection = new BluetoothGattConnection(
mMockBluetoothGattWrapper,
mMockBluetoothOperationExecutor,
CONNECTION_OPTIONS);
boolean result = mBluetoothGattConnection.isConnected();
assertThat(result).isFalse();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_isConnected_true_afterConnection() {
boolean result = mBluetoothGattConnection.isConnected();
assertThat(result).isTrue();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_isConnected_false_afterDisconnection() {
mBluetoothGattConnection.onClosed();
boolean result = mBluetoothGattConnection.isConnected();
assertThat(result).isFalse();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getService_notDiscovered() throws Exception {
BluetoothGattService result = mBluetoothGattConnection.getService(SERVICE_UUID);
verify(mMockBluetoothOperationExecutor).execute(mSynchronousOperationCaptor.capture());
mSynchronousOperationCaptor.getValue().call();
verify(mMockBluetoothOperationExecutor)
.execute(
mOperationCaptor.capture(),
eq(BluetoothGattConnection.SLOW_OPERATION_TIMEOUT_MILLIS));
mOperationCaptor.getValue().run();
assertThat(result).isEqualTo(mMockBluetoothGattService);
verify(mMockBluetoothGattWrapper).discoverServices();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getService_alreadyDiscovered() throws Exception {
mBluetoothGattConnection.getService(SERVICE_UUID);
verify(mMockBluetoothOperationExecutor).execute(mSynchronousOperationCaptor.capture());
mSynchronousOperationCaptor.getValue().call();
reset(mMockBluetoothOperationExecutor);
BluetoothGattService result = mBluetoothGattConnection.getService(SERVICE_UUID);
assertThat(result).isEqualTo(mMockBluetoothGattService);
// Verify that service discovery has been done only once
verifyNoMoreInteractions(mMockBluetoothOperationExecutor);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getService_notFound() throws Exception {
when(mMockBluetoothGattWrapper.getServices()).thenReturn(
Arrays.<BluetoothGattService>asList());
try {
mBluetoothGattConnection.getService(SERVICE_UUID);
fail("Expected BluetoothException");
} catch (BluetoothException expected) {
}
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getService_moreThanOne() throws Exception {
when(mMockBluetoothGattWrapper.getServices())
.thenReturn(Arrays.asList(mMockBluetoothGattService, mMockBluetoothGattService));
try {
mBluetoothGattConnection.getService(SERVICE_UUID);
fail("Expected BluetoothException");
} catch (BluetoothException expected) {
}
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getCharacteristic() throws Exception {
BluetoothGattCharacteristic result =
mBluetoothGattConnection.getCharacteristic(SERVICE_UUID, CHARACTERISTIC_UUID);
assertThat(result).isEqualTo(mMockBluetoothGattCharacteristic);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getCharacteristic_notFound() throws Exception {
when(mMockBluetoothGattService.getCharacteristics())
.thenReturn(Arrays.<BluetoothGattCharacteristic>asList());
try {
mBluetoothGattConnection.getCharacteristic(SERVICE_UUID, CHARACTERISTIC_UUID);
fail("Expected BluetoothException");
} catch (BluetoothException expected) {
}
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getCharacteristic_moreThanOne() throws Exception {
when(mMockBluetoothGattService.getCharacteristics())
.thenReturn(
Arrays.asList(mMockBluetoothGattCharacteristic,
mMockBluetoothGattCharacteristic));
try {
mBluetoothGattConnection.getCharacteristic(SERVICE_UUID, CHARACTERISTIC_UUID);
fail("Expected BluetoothException");
} catch (BluetoothException expected) {
}
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getCharacteristic_moreThanOneService() throws Exception {
// Add a new service with the same service UUID as our existing one, but add a different
// characteristic inside of it.
when(mMockBluetoothGattWrapper.getServices())
.thenReturn(Arrays.asList(mMockBluetoothGattService, mMockBluetoothGattService2));
when(mMockBluetoothGattService2.getUuid()).thenReturn(SERVICE_UUID);
when(mMockBluetoothGattService2.getCharacteristics())
.thenReturn(Arrays.asList(mMockBluetoothGattCharacteristic2));
when(mMockBluetoothGattCharacteristic2.getUuid())
.thenReturn(
new UUID(
CHARACTERISTIC_UUID.getMostSignificantBits(),
CHARACTERISTIC_UUID.getLeastSignificantBits() + 1));
when(mMockBluetoothGattCharacteristic2.getProperties())
.thenReturn(
BluetoothGattCharacteristic.PROPERTY_NOTIFY
| BluetoothGattCharacteristic.PROPERTY_WRITE);
mBluetoothGattConnection.getCharacteristic(SERVICE_UUID, CHARACTERISTIC_UUID);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getDescriptor() throws Exception {
when(mMockBluetoothGattCharacteristic.getDescriptors())
.thenReturn(Arrays.asList(mMockBluetoothGattDescriptor));
BluetoothGattDescriptor result =
mBluetoothGattConnection
.getDescriptor(SERVICE_UUID, CHARACTERISTIC_UUID, DESCRIPTOR_UUID);
assertThat(result).isEqualTo(mMockBluetoothGattDescriptor);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getDescriptor_notFound() throws Exception {
when(mMockBluetoothGattCharacteristic.getDescriptors())
.thenReturn(Arrays.<BluetoothGattDescriptor>asList());
try {
mBluetoothGattConnection
.getDescriptor(SERVICE_UUID, CHARACTERISTIC_UUID, DESCRIPTOR_UUID);
fail("Expected BluetoothException");
} catch (BluetoothException expected) {
}
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getDescriptor_moreThanOne() throws Exception {
when(mMockBluetoothGattCharacteristic.getDescriptors())
.thenReturn(
Arrays.asList(mMockBluetoothGattDescriptor, mMockBluetoothGattDescriptor));
try {
mBluetoothGattConnection
.getDescriptor(SERVICE_UUID, CHARACTERISTIC_UUID, DESCRIPTOR_UUID);
fail("Expected BluetoothException");
} catch (BluetoothException expected) {
}
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_discoverServices() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new SynchronousOperation<>(
mMockBluetoothOperationExecutor, OperationType.NOTIFICATION_CHANGE)))
.thenReturn(mMockChangeObserver);
mBluetoothGattConnection.discoverServices();
verify(mMockBluetoothOperationExecutor).execute(mSynchronousOperationCaptor.capture());
mSynchronousOperationCaptor.getValue().call();
verify(mMockBluetoothOperationExecutor)
.execute(
mOperationCaptor.capture(),
eq(BluetoothGattConnection.SLOW_OPERATION_TIMEOUT_MILLIS));
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).discoverServices();
verify(mMockBluetoothGattWrapper, never()).refresh();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_discoverServices_serviceChange() throws Exception {
when(mMockBluetoothGattWrapper.getService(ReservedUuids.Services.GENERIC_ATTRIBUTE))
.thenReturn(mMockBluetoothGattService);
when(mMockBluetoothGattService
.getCharacteristic(ReservedUuids.Characteristics.SERVICE_CHANGE))
.thenReturn(mMockBluetoothGattCharacteristic);
mBluetoothGattConnection.discoverServices();
verify(mMockBluetoothOperationExecutor).execute(mSynchronousOperationCaptor.capture());
mSynchronousOperationCaptor.getValue().call();
verify(mMockBluetoothOperationExecutor, times(2))
.execute(
mOperationCaptor.capture(),
eq(BluetoothGattConnection.SLOW_OPERATION_TIMEOUT_MILLIS));
verify(mMockBluetoothGattWrapper).refresh();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_discoverServices_SelfDefinedServiceDynamic() throws Exception {
when(mMockBluetoothGattWrapper.getService(BluetoothConsts.SERVICE_DYNAMIC_SERVICE))
.thenReturn(mMockBluetoothGattService);
when(mMockBluetoothGattService
.getCharacteristic(BluetoothConsts.SERVICE_DYNAMIC_CHARACTERISTIC))
.thenReturn(mMockBluetoothGattCharacteristic);
mBluetoothGattConnection.discoverServices();
verify(mMockBluetoothOperationExecutor).execute(mSynchronousOperationCaptor.capture());
mSynchronousOperationCaptor.getValue().call();
verify(mMockBluetoothOperationExecutor, times(2))
.execute(
mOperationCaptor.capture(),
eq(BluetoothGattConnection.SLOW_OPERATION_TIMEOUT_MILLIS));
verify(mMockBluetoothGattWrapper).refresh();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_discoverServices_refreshWithGattErrorOnMncAbove() throws Exception {
if (VERSION.SDK_INT <= VERSION_CODES.LOLLIPOP_MR1) {
return;
}
mBluetoothGattConnection.discoverServices();
verify(mMockBluetoothOperationExecutor).execute(mSynchronousOperationCaptor.capture());
doThrow(new BluetoothGattException("fail", BluetoothGattConnection.GATT_ERROR))
.doReturn(null)
.when(mMockBluetoothOperationExecutor)
.execute(isA(Operation.class),
eq(BluetoothGattConnection.SLOW_OPERATION_TIMEOUT_MILLIS));
mSynchronousOperationCaptor.getValue().call();
verify(mMockBluetoothOperationExecutor, times(2))
.execute(
mOperationCaptor.capture(),
eq(BluetoothGattConnection.SLOW_OPERATION_TIMEOUT_MILLIS));
verify(mMockBluetoothGattWrapper).refresh();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_discoverServices_refreshWithGattInternalErrorOnMncAbove() throws Exception {
if (VERSION.SDK_INT <= VERSION_CODES.LOLLIPOP_MR1) {
return;
}
mBluetoothGattConnection.discoverServices();
verify(mMockBluetoothOperationExecutor).execute(mSynchronousOperationCaptor.capture());
doThrow(new BluetoothGattException("fail", BluetoothGattConnection.GATT_INTERNAL_ERROR))
.doReturn(null)
.when(mMockBluetoothOperationExecutor)
.execute(isA(Operation.class),
eq(BluetoothGattConnection.SLOW_OPERATION_TIMEOUT_MILLIS));
mSynchronousOperationCaptor.getValue().call();
verify(mMockBluetoothOperationExecutor, times(2))
.execute(
mOperationCaptor.capture(),
eq(BluetoothGattConnection.SLOW_OPERATION_TIMEOUT_MILLIS));
verify(mMockBluetoothGattWrapper).refresh();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_discoverServices_dynamicServices_notBonded() throws Exception {
when(mMockBluetoothGattWrapper.getService(ReservedUuids.Services.GENERIC_ATTRIBUTE))
.thenReturn(mMockBluetoothGattService);
when(mMockBluetoothGattService
.getCharacteristic(ReservedUuids.Characteristics.SERVICE_CHANGE))
.thenReturn(mMockBluetoothGattCharacteristic);
when(mMockBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
mBluetoothGattConnection.discoverServices();
verify(mMockBluetoothGattWrapper, never()).refresh();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_readCharacteristic() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new Operation<byte[]>(
OperationType.READ_CHARACTERISTIC,
mMockBluetoothGattWrapper,
mMockBluetoothGattCharacteristic),
BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS))
.thenReturn(DATA);
byte[] result = mBluetoothGattConnection
.readCharacteristic(mMockBluetoothGattCharacteristic);
assertThat(result).isEqualTo(DATA);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(mOperationCaptor.capture(), anyLong());
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).readCharacteristic(mMockBluetoothGattCharacteristic);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_readCharacteristic_by_uuid() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new Operation<byte[]>(
OperationType.READ_CHARACTERISTIC,
mMockBluetoothGattWrapper,
mMockBluetoothGattCharacteristic),
BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS))
.thenReturn(DATA);
byte[] result = mBluetoothGattConnection
.readCharacteristic(SERVICE_UUID, CHARACTERISTIC_UUID);
assertThat(result).isEqualTo(DATA);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(mOperationCaptor.capture(), anyLong());
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).readCharacteristic(mMockBluetoothGattCharacteristic);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_writeCharacteristic() throws Exception {
BluetoothGattCharacteristic characteristic =
new BluetoothGattCharacteristic(
CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE, 0);
mBluetoothGattConnection.writeCharacteristic(characteristic, DATA);
verify(mMockBluetoothOperationExecutor)
.execute(mOperationCaptor.capture(),
eq(BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS));
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).writeCharacteristic(mCharacteristicCaptor.capture(),
eq(DATA), eq(characteristic.getWriteType()));
BluetoothGattCharacteristic writtenCharacteristic = mCharacteristicCaptor.getValue();
assertThat(writtenCharacteristic.getUuid()).isEqualTo(CHARACTERISTIC_UUID);
assertThat(writtenCharacteristic).isEqualTo(characteristic);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_writeCharacteristic_by_uuid() throws Exception {
mBluetoothGattConnection.writeCharacteristic(SERVICE_UUID, CHARACTERISTIC_UUID, DATA);
verify(mMockBluetoothOperationExecutor)
.execute(mOperationCaptor.capture(),
eq(BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS));
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).writeCharacteristic(mCharacteristicCaptor.capture(),
eq(DATA), anyInt());
BluetoothGattCharacteristic writtenCharacteristic = mCharacteristicCaptor.getValue();
assertThat(writtenCharacteristic.getUuid()).isEqualTo(CHARACTERISTIC_UUID);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_readDescriptor() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new Operation<byte[]>(
OperationType.READ_DESCRIPTOR, mMockBluetoothGattWrapper,
mMockBluetoothGattDescriptor),
BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS))
.thenReturn(DATA);
byte[] result = mBluetoothGattConnection.readDescriptor(mMockBluetoothGattDescriptor);
assertThat(result).isEqualTo(DATA);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(mOperationCaptor.capture(), anyLong());
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).readDescriptor(mMockBluetoothGattDescriptor);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_readDescriptor_by_uuid() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new Operation<byte[]>(
OperationType.READ_DESCRIPTOR, mMockBluetoothGattWrapper,
mMockBluetoothGattDescriptor),
BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS))
.thenReturn(DATA);
byte[] result =
mBluetoothGattConnection
.readDescriptor(SERVICE_UUID, CHARACTERISTIC_UUID, DESCRIPTOR_UUID);
assertThat(result).isEqualTo(DATA);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(mOperationCaptor.capture(), anyLong());
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).readDescriptor(mMockBluetoothGattDescriptor);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_writeDescriptor() throws Exception {
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(DESCRIPTOR_UUID, 0);
mBluetoothGattConnection.writeDescriptor(descriptor, DATA);
verify(mMockBluetoothOperationExecutor)
.execute(mOperationCaptor.capture(),
eq(BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS));
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).writeDescriptor(mDescriptorCaptor.capture(), eq(DATA));
BluetoothGattDescriptor writtenDescriptor = mDescriptorCaptor.getValue();
assertThat(writtenDescriptor.getUuid()).isEqualTo(DESCRIPTOR_UUID);
assertThat(writtenDescriptor).isEqualTo(descriptor);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_writeDescriptor_by_uuid() throws Exception {
mBluetoothGattConnection.writeDescriptor(
SERVICE_UUID, CHARACTERISTIC_UUID, DESCRIPTOR_UUID, DATA);
verify(mMockBluetoothOperationExecutor)
.execute(mOperationCaptor.capture(),
eq(BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS));
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).writeDescriptor(mDescriptorCaptor.capture(), eq(DATA));
BluetoothGattDescriptor writtenDescriptor = mDescriptorCaptor.getValue();
assertThat(writtenDescriptor.getUuid()).isEqualTo(DESCRIPTOR_UUID);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_readRemoteRssi() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new Operation<Integer>(OperationType.READ_RSSI, mMockBluetoothGattWrapper),
BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS))
.thenReturn(RSSI);
int result = mBluetoothGattConnection.readRemoteRssi();
assertThat(result).isEqualTo(RSSI);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(
mOperationCaptor.capture(),
eq(BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS));
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).readRemoteRssi();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getMaxDataPacketSize() throws Exception {
int result = mBluetoothGattConnection.getMaxDataPacketSize();
assertThat(result).isEqualTo(mBluetoothGattConnection.getMtu() - 3);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testSetNotificationEnabled_indication_enable() throws Exception {
when(mMockBluetoothGattCharacteristic.getProperties())
.thenReturn(BluetoothGattCharacteristic.PROPERTY_INDICATE);
mBluetoothGattConnection.setNotificationEnabled(mMockBluetoothGattCharacteristic, true);
verify(mMockBluetoothGattWrapper)
.setCharacteristicNotification(mMockBluetoothGattCharacteristic, true);
verify(mMockBluetoothOperationExecutor).execute(mOperationCaptor.capture(), anyLong());
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).writeDescriptor(mDescriptorCaptor.capture(),
eq(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE));
BluetoothGattDescriptor writtenDescriptor = mDescriptorCaptor.getValue();
assertThat(writtenDescriptor.getUuid())
.isEqualTo(ReservedUuids.Descriptors.CLIENT_CHARACTERISTIC_CONFIGURATION);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_getNotificationEnabled_notification_enable() throws Exception {
mBluetoothGattConnection.setNotificationEnabled(mMockBluetoothGattCharacteristic, true);
verify(mMockBluetoothGattWrapper)
.setCharacteristicNotification(mMockBluetoothGattCharacteristic, true);
verify(mMockBluetoothOperationExecutor).execute(mOperationCaptor.capture(), anyLong());
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).writeDescriptor(mDescriptorCaptor.capture(),
eq(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE));
BluetoothGattDescriptor writtenDescriptor = mDescriptorCaptor.getValue();
assertThat(writtenDescriptor.getUuid())
.isEqualTo(ReservedUuids.Descriptors.CLIENT_CHARACTERISTIC_CONFIGURATION);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_setNotificationEnabled_indication_disable() throws Exception {
when(mMockBluetoothGattCharacteristic.getProperties())
.thenReturn(BluetoothGattCharacteristic.PROPERTY_INDICATE);
mBluetoothGattConnection.setNotificationEnabled(mMockBluetoothGattCharacteristic, false);
verify(mMockBluetoothGattWrapper)
.setCharacteristicNotification(mMockBluetoothGattCharacteristic, false);
verify(mMockBluetoothOperationExecutor).execute(mOperationCaptor.capture(), anyLong());
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).writeDescriptor(mDescriptorCaptor.capture(),
eq(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE));
BluetoothGattDescriptor writtenDescriptor = mDescriptorCaptor.getValue();
assertThat(writtenDescriptor.getUuid())
.isEqualTo(ReservedUuids.Descriptors.CLIENT_CHARACTERISTIC_CONFIGURATION);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_setNotificationEnabled_notification_disable() throws Exception {
mBluetoothGattConnection.setNotificationEnabled(mMockBluetoothGattCharacteristic, false);
verify(mMockBluetoothGattWrapper)
.setCharacteristicNotification(mMockBluetoothGattCharacteristic, false);
verify(mMockBluetoothOperationExecutor).execute(mOperationCaptor.capture(), anyLong());
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).writeDescriptor(mDescriptorCaptor.capture(),
eq(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE));
BluetoothGattDescriptor writtenDescriptor = mDescriptorCaptor.getValue();
assertThat(writtenDescriptor.getUuid())
.isEqualTo(ReservedUuids.Descriptors.CLIENT_CHARACTERISTIC_CONFIGURATION);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_setNotificationEnabled_failure() throws Exception {
when(mMockBluetoothGattCharacteristic.getProperties())
.thenReturn(BluetoothGattCharacteristic.PROPERTY_READ);
try {
mBluetoothGattConnection.setNotificationEnabled(mMockBluetoothGattCharacteristic,
true);
fail("BluetoothException was expected");
} catch (BluetoothException expected) {
}
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_enableNotification_Uuid() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new SynchronousOperation<>(
mMockBluetoothOperationExecutor,
OperationType.NOTIFICATION_CHANGE,
mMockBluetoothGattCharacteristic)))
.thenReturn(mMockChangeObserver);
mBluetoothGattConnection.enableNotification(SERVICE_UUID, CHARACTERISTIC_UUID);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(mSynchronousOperationCaptor.capture());
((ChangeObserver) mSynchronousOperationCaptor.getValue().call())
.setListener(mMockCharChangeListener);
mBluetoothGattConnection.onCharacteristicChanged(mMockBluetoothGattCharacteristic, DATA);
verify(mMockCharChangeListener).onValueChange(DATA);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_enableNotification() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new SynchronousOperation<>(
mMockBluetoothOperationExecutor,
OperationType.NOTIFICATION_CHANGE,
mMockBluetoothGattCharacteristic)))
.thenReturn(mMockChangeObserver);
mBluetoothGattConnection.enableNotification(mMockBluetoothGattCharacteristic);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(mSynchronousOperationCaptor.capture());
((ChangeObserver) mSynchronousOperationCaptor.getValue().call())
.setListener(mMockCharChangeListener);
mBluetoothGattConnection.onCharacteristicChanged(mMockBluetoothGattCharacteristic, DATA);
verify(mMockCharChangeListener).onValueChange(DATA);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_enableNotification_observe() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new SynchronousOperation<>(
mMockBluetoothOperationExecutor,
OperationType.NOTIFICATION_CHANGE,
mMockBluetoothGattCharacteristic)))
.thenReturn(mMockChangeObserver);
mBluetoothGattConnection.enableNotification(mMockBluetoothGattCharacteristic);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(mSynchronousOperationCaptor.capture());
ChangeObserver changeObserver = (ChangeObserver) mSynchronousOperationCaptor.getValue()
.call();
mBluetoothGattConnection.onCharacteristicChanged(mMockBluetoothGattCharacteristic, DATA);
assertThat(changeObserver.waitForUpdate(TimeUnit.SECONDS.toMillis(1))).isEqualTo(DATA);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_disableNotification_Uuid() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new SynchronousOperation<>(
OperationType.NOTIFICATION_CHANGE, mMockBluetoothGattCharacteristic)))
.thenReturn(mMockChangeObserver);
mBluetoothGattConnection
.enableNotification(SERVICE_UUID, CHARACTERISTIC_UUID)
.setListener(mMockCharChangeListener);
mBluetoothGattConnection.disableNotification(SERVICE_UUID, CHARACTERISTIC_UUID);
mBluetoothGattConnection.onCharacteristicChanged(mMockBluetoothGattCharacteristic, DATA);
verify(mMockCharChangeListener, never()).onValueChange(DATA);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_disableNotification() throws Exception {
when(mMockBluetoothOperationExecutor.executeNonnull(
new SynchronousOperation<ChangeObserver>(
OperationType.NOTIFICATION_CHANGE, mMockBluetoothGattCharacteristic)))
.thenReturn(mMockChangeObserver);
mBluetoothGattConnection
.enableNotification(mMockBluetoothGattCharacteristic)
.setListener(mMockCharChangeListener);
verify(mMockBluetoothOperationExecutor)
.executeNonnull(mSynchronousOperationCaptor.capture());
mSynchronousOperationCaptor.getValue().call();
mBluetoothGattConnection.disableNotification(mMockBluetoothGattCharacteristic);
verify(mMockBluetoothOperationExecutor).execute(mSynchronousOperationCaptor.capture());
mSynchronousOperationCaptor.getValue().call();
mBluetoothGattConnection.onCharacteristicChanged(mMockBluetoothGattCharacteristic, DATA);
verify(mMockCharChangeListener, never()).onValueChange(DATA);
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_addCloseListener() throws Exception {
mBluetoothGattConnection.addCloseListener(mMockConnectionCloseListener);
mBluetoothGattConnection.onClosed();
verify(mMockConnectionCloseListener).onClose();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_removeCloseListener() throws Exception {
mBluetoothGattConnection.addCloseListener(mMockConnectionCloseListener);
mBluetoothGattConnection.removeCloseListener(mMockConnectionCloseListener);
mBluetoothGattConnection.onClosed();
verify(mMockConnectionCloseListener, never()).onClose();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_close() throws Exception {
mBluetoothGattConnection.close();
verify(mMockBluetoothOperationExecutor)
.execute(mOperationCaptor.capture(),
eq(BluetoothGattConnection.OPERATION_TIMEOUT_MILLIS));
mOperationCaptor.getValue().run();
verify(mMockBluetoothGattWrapper).disconnect();
verify(mMockBluetoothGattWrapper).close();
}
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_onClosed() throws Exception {
mBluetoothGattConnection.onClosed();
verify(mMockBluetoothOperationExecutor, never())
.execute(mOperationCaptor.capture(), anyLong());
verify(mMockBluetoothGattWrapper).close();
}
}