blob: 5e1fb65c0c5dd43ee3b7958b3fa480669234d404 [file] [log] [blame]
/*
* Copyright (C) 2022 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.car.bluetooth;
import static android.car.test.mocks.AndroidMockitoHelper.mockCarGetPlatformVersion;
import static com.android.car.bluetooth.FastPairAccountKeyStorage.AccountKey;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertisingSet;
import android.bluetooth.le.AdvertisingSetCallback;
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.car.Car;
import android.car.PlatformVersion;
import android.car.builtin.bluetooth.le.AdvertisingSetCallbackHelper;
import android.car.builtin.bluetooth.le.AdvertisingSetHelper;
import android.content.Context;
import android.os.Looper;
import android.os.ParcelUuid;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Unit tests for {@link FastPairAdvertiser}
*
* Run: atest FastPairAdvertiserTest
*/
@RunWith(MockitoJUnitRunner.class)
public class FastPairAdvertiserTest {
public static final ParcelUuid SERVICE_UUID = ParcelUuid
.fromString("0000FE2C-0000-1000-8000-00805f9b34fb");
private static final int TEST_MODEL_ID = 0x112233;
private static final byte[] TEST_MODEL_ID_DATA = new byte[]{0x11, 0x22, 0x33};
private static final int MODEL_ID_ADVERTISING_INTERVAL =
AdvertisingSetParameters.INTERVAL_LOW;
private static final byte[] TEST_ACCOUNT_KEY_1 = new byte[]{0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
0x77, (byte) 0x88, (byte) 0x99, 0x00, (byte) 0xAA, (byte) 0xBB, (byte) 0xCC,
(byte) 0xDD, (byte) 0xEE, (byte) 0xFF};
private static final byte[] TEST_ACCOUNT_KEY_2 = new byte[]{0x11, 0x11, 0x22, 0x22, 0x33, 0x33,
0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, 0x77, (byte) 0x88, (byte) 0x88};
private static final List<AccountKey> TEST_ACCOUNT_KEYS = new ArrayList<AccountKey>(List.of(
new AccountKey(TEST_ACCOUNT_KEY_1),
new AccountKey(TEST_ACCOUNT_KEY_2)
));
private static final List<AccountKey> TEST_EMPTY_ACCOUNT_KEYS = new ArrayList<>();
static final byte TEST_SALT = (byte) 0x00;
private static final byte[] TEST_ACCOUNT_KEY_FILTER_DATA_NO_KEYS = new byte[]{0x00, 0x00};
private static final int TEST_ACCOUNT_KEY_FILTER_DATA_WITH_KEYS_LENGTH = 9;
private static final byte TEST_ACCOUNT_KEY_FILTER_ADVERTISEMENT_RESERVED_BYTE = 0x00;
private static final byte TEST_ACCOUNT_KEY_FILTER_ADVERTISEMENT_FILTER_FLAGS_BYTE = (byte) 0x50;
private static final byte[] TEST_ACCOUNT_KEY_FILTER_ADVERTISEMENT_WITH_KEYS_FILTER_BYTES =
new byte[]{(byte) 0xC3, 0x15, 0x22, 0x08, 0x3A};
private static final byte TEST_ACCOUNT_KEY_FILTER_ADVERTISEMENT_SALT_FLAGS_BYTE = 0x11;
private static final int ACCOUNT_KEY_FILTER_ADVERTISING_INTERVAL =
AdvertisingSetParameters.INTERVAL_MEDIUM;
private static final int ADVERTISING_EVENT_SETTLE_MS = 3000;
private static final int ADVERTISING_EVENT_TIMEOUT_MS = 4500;
private static final int ADVERTISING_STATE_CHANGE_MS = 150;
MockitoSession mMockitoSession;
@Mock Context mMockContext;
@Mock BluetoothAdapter mMockBluetoothAdapter;
@Mock BluetoothManager mMockBluetoothManager;
@Mock BluetoothDevice mMockBluetoothDevice;
@Mock BluetoothLeAdvertiser mMockBluetoothLeAdvertiser;
@Mock AdvertisingSet mMockAdvertisingSet;
@Captor ArgumentCaptor<AdvertisingSetCallback> mAdvertisingSetCallbackCaptor;
@Captor ArgumentCaptor<AdvertisingSetParameters> mAdvertisingSetParametersCaptor;
@Captor ArgumentCaptor<AdvertiseData> mAdvertiseDataCaptor;
private FastPairAdvertiser mFastPairAdvertiser;
private final FastPairAdvertiser.Callbacks mCallback = new FastPairAdvertiser.Callbacks() {
@Override
public void onRpaUpdated(BluetoothDevice device) {
// TODO(196233989): Add tests for this when the API becomes available and the code can
// be uncommented.
}
};
@Rule
public final TestRule mClearInlineMocksRule = new TestRule() {
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
// When using inline mock maker, clean up inline mocks to prevent OutOfMemory
// errors. See https://github.com/mockito/mockito/issues/1614 and b/259280359.
Mockito.framework().clearInlineMocks();
}
};
}
};
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMockContext.getSystemService(BluetoothManager.class))
.thenReturn(mMockBluetoothManager);
when(mMockBluetoothManager.getAdapter()).thenReturn(mMockBluetoothAdapter);
when(mMockBluetoothAdapter.getBluetoothLeAdvertiser())
.thenReturn(mMockBluetoothLeAdvertiser);
when(mMockBluetoothAdapter.getRemoteDevice(any(String.class)))
.thenReturn(mMockBluetoothDevice);
when(mMockBluetoothAdapter.getRemoteDevice(any(byte[].class)))
.thenReturn(mMockBluetoothDevice);
mAdvertisingSetCallbackCaptor = ArgumentCaptor.forClass(AdvertisingSetCallback.class);
mAdvertiseDataCaptor = ArgumentCaptor.forClass(AdvertiseData.class);
mAdvertisingSetParametersCaptor = ArgumentCaptor.forClass(AdvertisingSetParameters.class);
mMockitoSession = ExtendedMockito.mockitoSession()
.strictness(Strictness.WARN)
.spyStatic(BluetoothAdapter.class)
.spyStatic(Car.class)
.spyStatic(AdvertisingSetCallbackHelper.class)
.spyStatic(AdvertisingSetHelper.class)
.startMocking();
Looper looper = Looper.myLooper();
if (looper == null) {
Looper.prepare();
}
mFastPairAdvertiser = new FastPairAdvertiser(mMockContext);
}
@After
public void tearDown() {
mMockitoSession.finishMocking();
}
private void waitForAdvertisingHandlerToSettle() {
// TODO (243518804): Remove the need for this by adding a way to wait on state transitions
try {
Thread.sleep(ADVERTISING_EVENT_SETTLE_MS);
} catch (InterruptedException e) {
// pass
}
}
private void waitForAdvertisingHandlerStateChange() {
// TODO (243518804): Remove the need for this by adding a way to wait on state transitions
try {
Thread.sleep(ADVERTISING_STATE_CHANGE_MS);
} catch (InterruptedException e) {
// pass
}
}
private void assertAdvertisingParameters(AdvertisingSetParameters params, int interval) {
assertThat(params.isLegacy()).isTrue();
assertThat(params.isScannable()).isTrue();
assertThat(params.isConnectable()).isTrue();
assertThat(params.getInterval()).isEqualTo(interval);
}
private void assertAdvertisingData(AdvertiseData actual, byte[] data) {
AdvertiseData expected = new AdvertiseData.Builder()
.addServiceUuid(SERVICE_UUID)
.addServiceData(SERVICE_UUID, data)
.setIncludeTxPowerLevel(true)
.build();
assertThat(actual).isEqualTo(expected);
}
@Test
public void testAdvertiseModelIdFromStopped_advertisingSucceeds() {
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTING);
mAdvertisingSetCallbackCaptor.getValue().onAdvertisingSetStarted(mMockAdvertisingSet,
mAdvertisingSetParametersCaptor.getValue().getTxPowerLevel(),
AdvertisingSetCallback.ADVERTISE_SUCCESS);
waitForAdvertisingHandlerToSettle();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTED);
assertThat(mFastPairAdvertiser.isAdvertising()).isTrue();
assertAdvertisingParameters(mAdvertisingSetParametersCaptor.getValue(),
MODEL_ID_ADVERTISING_INTERVAL);
assertAdvertisingData(mAdvertiseDataCaptor.getValue(), TEST_MODEL_ID_DATA);
}
@Test
public void testAdvertiseModelIdWhileStarted_doNothing() {
testAdvertiseModelIdFromStopped_advertisingSucceeds();
clearInvocations(mMockBluetoothLeAdvertiser);
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, after(ADVERTISING_EVENT_SETTLE_MS).never())
.startAdvertisingSet(any(), any(), any(), any(), any(), any());
verify(mMockBluetoothLeAdvertiser, never()).stopAdvertisingSet(any());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTED);
assertThat(mFastPairAdvertiser.isAdvertising()).isTrue();
}
@Test
public void testAdvertiseAccountKeyFilterNoKeys_advertisingSucceeds() {
mFastPairAdvertiser.advertiseAccountKeys(TEST_EMPTY_ACCOUNT_KEYS, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
mAdvertisingSetCallbackCaptor.getValue().onAdvertisingSetStarted(mMockAdvertisingSet,
mAdvertisingSetParametersCaptor.getValue().getTxPowerLevel(),
AdvertisingSetCallback.ADVERTISE_SUCCESS);
waitForAdvertisingHandlerToSettle();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTED);
assertThat(mFastPairAdvertiser.isAdvertising()).isTrue();
assertAdvertisingParameters(mAdvertisingSetParametersCaptor.getValue(),
ACCOUNT_KEY_FILTER_ADVERTISING_INTERVAL);
assertAdvertisingData(mAdvertiseDataCaptor.getValue(),
TEST_ACCOUNT_KEY_FILTER_DATA_NO_KEYS);
}
@Test
public void testCreateAccountKeyFilterWithKeys_returnsBytes() {
byte[] filter = mFastPairAdvertiser.getAccountKeyFilter(TEST_ACCOUNT_KEYS, TEST_SALT);
assertThat(filter).isEqualTo(TEST_ACCOUNT_KEY_FILTER_ADVERTISEMENT_WITH_KEYS_FILTER_BYTES);
}
@Test
public void testCreateAccountKeyFilterNoKeys_returnsNull() {
byte[] filter = mFastPairAdvertiser.getAccountKeyFilter(TEST_EMPTY_ACCOUNT_KEYS, TEST_SALT);
assertThat(filter).isEqualTo(null);
}
@Test
public void testCreateAccountKeyFilterNullKeys_returnsNull() {
byte[] filter = mFastPairAdvertiser.getAccountKeyFilter(null, TEST_SALT);
assertThat(filter).isEqualTo(null);
}
@Test
public void testAdvertiseAccountKeyFilterWithKeys_advertisingSucceeds() {
mFastPairAdvertiser.advertiseAccountKeys(TEST_ACCOUNT_KEYS, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTING);
mAdvertisingSetCallbackCaptor.getValue().onAdvertisingSetStarted(mMockAdvertisingSet,
mAdvertisingSetParametersCaptor.getValue().getTxPowerLevel(),
AdvertisingSetCallback.ADVERTISE_SUCCESS);
waitForAdvertisingHandlerToSettle();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTED);
assertThat(mFastPairAdvertiser.isAdvertising()).isTrue();
assertAdvertisingParameters(mAdvertisingSetParametersCaptor.getValue(),
ACCOUNT_KEY_FILTER_ADVERTISING_INTERVAL);
AdvertiseData actual = mAdvertiseDataCaptor.getValue();
// The filter created relies on the salt used, which is random. We cannot mock that, so
// instead we'll check the other parts of the packet that matter and test the filter
// creation itself in other tests
assertThat(actual).isNotNull();
Map<ParcelUuid, byte[]> actualServiceData = actual.getServiceData();
assertThat(actualServiceData).isNotNull();
byte[] actualData = actualServiceData.get(SERVICE_UUID);
int actualSize = actualData.length;
assertThat(actualData).isNotNull();
assertThat(actualSize).isEqualTo(TEST_ACCOUNT_KEY_FILTER_DATA_WITH_KEYS_LENGTH);
assertThat(actualData[0]).isEqualTo(TEST_ACCOUNT_KEY_FILTER_ADVERTISEMENT_RESERVED_BYTE);
assertThat(actualData[1])
.isEqualTo(TEST_ACCOUNT_KEY_FILTER_ADVERTISEMENT_FILTER_FLAGS_BYTE);
assertThat(actualData[actualSize - 2])
.isEqualTo(TEST_ACCOUNT_KEY_FILTER_ADVERTISEMENT_SALT_FLAGS_BYTE);
byte salt = actualData[actualSize - 1];
byte[] filter = mFastPairAdvertiser.getAccountKeyFilter(TEST_ACCOUNT_KEYS, salt);
assertThat(Arrays.copyOfRange(actualData, 2, 7)).isEqualTo(filter);
}
@Test
public void testAdvertiseAccountKeyFilterWhileStarted_doNothing() {
testAdvertiseModelIdFromStopped_advertisingSucceeds();
clearInvocations(mMockBluetoothLeAdvertiser);
mFastPairAdvertiser.advertiseAccountKeys(TEST_ACCOUNT_KEYS, mCallback);
verify(mMockBluetoothLeAdvertiser, after(ADVERTISING_EVENT_SETTLE_MS).never())
.startAdvertisingSet(any(), any(), any(), any(), any(), any());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTED);
assertThat(mFastPairAdvertiser.isAdvertising()).isTrue();
}
@Test
public void testAdvertiseNewDataWhileStarted_doNothing() {
testAdvertiseModelIdFromStopped_advertisingSucceeds();
clearInvocations(mMockBluetoothLeAdvertiser);
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, after(ADVERTISING_EVENT_SETTLE_MS).never())
.startAdvertisingSet(any(), any(), any(), any(), any(), any());
verify(mMockBluetoothLeAdvertiser, never()).stopAdvertisingSet(any());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTED);
assertThat(mFastPairAdvertiser.isAdvertising()).isTrue();
}
@Test
public void testFailToGetAdvertiserOnStart_doesNotAdvertise() {
when(mMockBluetoothAdapter.getBluetoothLeAdvertiser()).thenReturn(null);
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
waitForAdvertisingHandlerToSettle();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPED);
assertThat(mFastPairAdvertiser.isAdvertising()).isFalse();
}
@Test
public void testAdvertisingStartCallbackUnsuccessful_advertisingStops() {
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTING);
mAdvertisingSetCallbackCaptor.getValue().onAdvertisingSetStarted(mMockAdvertisingSet,
0, AdvertisingSetCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
waitForAdvertisingHandlerToSettle();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPED);
assertThat(mFastPairAdvertiser.isAdvertising()).isFalse();
}
@Test
public void testAdvertisingStartCallbackNullSet_advertisingStops() {
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTING);
mAdvertisingSetCallbackCaptor.getValue().onAdvertisingSetStarted(null,
0, AdvertisingSetCallback.ADVERTISE_SUCCESS);
waitForAdvertisingHandlerToSettle();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPED);
assertThat(mFastPairAdvertiser.isAdvertising()).isFalse();
}
@Test
public void testAdvertisingStartTimeout_doesNotAdvertise() {
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(any(), any(), any(), any(), any(), any());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTING);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_TIMEOUT_MS))
.stopAdvertisingSet(any());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPED);
assertThat(mFastPairAdvertiser.isAdvertising()).isFalse();
}
@Test
public void testStopAdvertising() {
testAdvertiseModelIdFromStopped_advertisingSucceeds();
clearInvocations(mMockBluetoothLeAdvertiser);
mFastPairAdvertiser.stopAdvertising();
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.stopAdvertisingSet(mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPING);
mAdvertisingSetCallbackCaptor.getValue().onAdvertisingSetStopped(mMockAdvertisingSet);
waitForAdvertisingHandlerToSettle();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPED);
assertThat(mFastPairAdvertiser.isAdvertising()).isFalse();
}
@Test
public void testStopAdvertisingWhileStopped() {
testStopAdvertising();
clearInvocations(mMockBluetoothLeAdvertiser);
mFastPairAdvertiser.stopAdvertising();
waitForAdvertisingHandlerToSettle();
verify(mMockBluetoothLeAdvertiser, after(ADVERTISING_EVENT_SETTLE_MS).never())
.stopAdvertisingSet(any());
assertThat(mFastPairAdvertiser.isAdvertising()).isFalse();
}
@Test
public void testAdvertisingStartWhileStopping_startProcessed() {
testAdvertiseModelIdFromStopped_advertisingSucceeds();
clearInvocations(mMockBluetoothLeAdvertiser);
mFastPairAdvertiser.stopAdvertising();
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.stopAdvertisingSet(mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPING);
mAdvertisingSetCallbackCaptor.getValue().onAdvertisingSetStopped(mMockAdvertisingSet);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
}
@Test
public void testAdvertisingStopWhileStarting_stopProcessed() {
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTING);
mFastPairAdvertiser.stopAdvertising();
mAdvertisingSetCallbackCaptor.getValue().onAdvertisingSetStarted(mMockAdvertisingSet,
mAdvertisingSetParametersCaptor.getValue().getTxPowerLevel(),
AdvertisingSetCallback.ADVERTISE_SUCCESS);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.stopAdvertisingSet(any());
}
@Test
public void testAdvertisingStartWhileStoppingTimeout_startProcessed() {
testAdvertiseModelIdFromStopped_advertisingSucceeds();
clearInvocations(mMockBluetoothLeAdvertiser);
mFastPairAdvertiser.stopAdvertising();
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.stopAdvertisingSet(mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPING);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_TIMEOUT_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
}
@Test
public void testAdvertisingStopWhileStartingTimeout_stopProcessed() {
mFastPairAdvertiser.advertiseModelId(TEST_MODEL_ID, mCallback);
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.startAdvertisingSet(mAdvertisingSetParametersCaptor.capture(),
mAdvertiseDataCaptor.capture(), any(), any(), any(),
mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STARTING);
mFastPairAdvertiser.stopAdvertising();
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_TIMEOUT_MS))
.stopAdvertisingSet(any());
}
@Test
public void testAdvertisingStopTimeoutNothingQueue_advertisingStateStopped() {
testAdvertiseModelIdFromStopped_advertisingSucceeds();
clearInvocations(mMockBluetoothLeAdvertiser);
mFastPairAdvertiser.stopAdvertising();
verify(mMockBluetoothLeAdvertiser, timeout(ADVERTISING_EVENT_SETTLE_MS))
.stopAdvertisingSet(mAdvertisingSetCallbackCaptor.capture());
waitForAdvertisingHandlerStateChange();
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPING);
clearInvocations(mMockBluetoothLeAdvertiser);
verify(mMockBluetoothLeAdvertiser, after(ADVERTISING_EVENT_TIMEOUT_MS).never())
.stopAdvertisingSet(any());
verify(mMockBluetoothLeAdvertiser, never())
.startAdvertisingSet(any(), any(), any(), any(), any(), any());
assertThat(mFastPairAdvertiser.getAdvertisingState())
.isEqualTo(FastPairAdvertiser.STATE_STOPPED);
}
/**
* {@link AdvertisingSetCallbackHelper} and {@link AdvertisingSetHelper} were introduced in
* TM-QPR-1 (maj=33, min=1) to help with {@link FastPairAdvertiser} hidden API usages. A
* version check was added to the constructor of {@link FastPairAdvertiser} to ensure backwards
* compatibility with respect to the availability of these helper classes. One way to test
* which branch the check took is to check whether
* {@link AdvertisingSetCallbackHelper#createRealCallbackFromProxy} was invoked or not.
*/
@Test
public void testFPAdvertiserBackCompat_tiramisu1_createRealCallbackFromProxyInvoked() {
mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_1);
// reset invocation count
clearInvocations(staticMockMarker(AdvertisingSetCallbackHelper.class));
// version check lies in constructor
new FastPairAdvertiser(mMockContext);
verify(() -> AdvertisingSetCallbackHelper.createRealCallbackFromProxy(any()));
}
/**
* {@link AdvertisingSetCallbackHelper} and {@link AdvertisingSetHelper} were introduced in
* TM-QPR-1 (maj=33, min=1) to help with {@link FastPairAdvertiser} hidden API usages. A
* version check was added to the constructor of {@link FastPairAdvertiser} to ensure backwards
* compatibility with respect to the availability of these helper classes. One way to test
* which branch the check took is to check whether
* {@link AdvertisingSetCallbackHelper#createRealCallbackFromProxy} was invoked or not.
*/
@Test
public void testFPAdvertiserBackCompat_tiramisu0_createRealCallbackFromProxyNotInvoked() {
mockCarGetPlatformVersion(PlatformVersion.VERSION_CODES.TIRAMISU_0);
// reset invocation count
clearInvocations(staticMockMarker(AdvertisingSetCallbackHelper.class));
// version check lies in constructor
new FastPairAdvertiser(mMockContext);
verify(() -> AdvertisingSetCallbackHelper.createRealCallbackFromProxy(any()), never());
}
}