Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9756b84 authored by Sungsoo Lim's avatar Sungsoo Lim
Browse files

Add test for BassClientStateMachine#Connected

Bug: 237467631
Test: atest BluetoothInstrumentationTests:BassClientStateMachineTest
Change-Id: I7cfa3d0f41156f4a4f348eec2a6280e3b1f97c6d
parent 3582787b
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ package com.android.bluetooth;

import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.PeriodicAdvertisingCallback;
import android.bluetooth.le.PeriodicAdvertisingManager;
import android.bluetooth.le.ScanResult;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -27,6 +30,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.provider.Telephony;
import android.util.Log;
@@ -180,4 +184,14 @@ public class BluetoothMethodProxy {
    public long telephonyGetOrCreateThreadId(Context context, Set<String> recipients) {
        return Telephony.Threads.getOrCreateThreadId(context, recipients);
    }

    /**
     * Proxies {@link PeriodicAdvertisingManager#registerSync(ScanResult, int, int,
     * PeriodicAdvertisingCallback, Handler)}.
     */
    public void periodicAdvertisingManagerRegisterSync(PeriodicAdvertisingManager manager,
            ScanResult scanResult, int skip, int timeout,
            PeriodicAdvertisingCallback callback, Handler handler) {
        manager.registerSync(scanResult, skip, timeout, callback, handler);
    }
}
+8 −4
Original line number Diff line number Diff line
@@ -88,6 +88,7 @@ import android.os.ParcelUuid;
import android.provider.DeviceConfig;
import android.util.Log;

import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.Utils;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
@@ -113,8 +114,10 @@ import java.util.stream.IntStream;
@VisibleForTesting
public class BassClientStateMachine extends StateMachine {
    private static final String TAG = "BassClientStateMachine";
    private static final byte[] REMOTE_SCAN_STOP = {00};
    private static final byte[] REMOTE_SCAN_START = {01};
    @VisibleForTesting
    static final byte[] REMOTE_SCAN_STOP = {00};
    @VisibleForTesting
    static final byte[] REMOTE_SCAN_START = {01};
    private static final byte OPCODE_ADD_SOURCE = 0x02;
    private static final byte OPCODE_UPDATE_SOURCE = 0x03;
    private static final byte OPCODE_SET_BCAST_PIN = 0x04;
@@ -386,8 +389,9 @@ public class BassClientStateMachine extends StateMachine {
        mNoStopScanOffload = true;
        cancelActiveSync(null);
        try {
            mPeriodicAdvManager.registerSync(scanRes, 0,
                    BassConstants.PSYNC_TIMEOUT, mPeriodicAdvCallback);
            BluetoothMethodProxy.getInstance().periodicAdvertisingManagerRegisterSync(
                    mPeriodicAdvManager, scanRes, 0, BassConstants.PSYNC_TIMEOUT,
                    mPeriodicAdvCallback, null);
        } catch (IllegalArgumentException ex) {
            Log.w(TAG, "registerSync:IllegalArgumentException");
            Message message = obtainMessage(STOP_SCAN_OFFLOAD);
+211 −5
Original line number Diff line number Diff line
@@ -24,6 +24,10 @@ import static com.android.bluetooth.bass_client.BassClientStateMachine.CONNECT_T
import static com.android.bluetooth.bass_client.BassClientStateMachine.DISCONNECT;
import static com.android.bluetooth.bass_client.BassClientStateMachine.PSYNC_ACTIVE_TIMEOUT;
import static com.android.bluetooth.bass_client.BassClientStateMachine.READ_BASS_CHARACTERISTICS;
import static com.android.bluetooth.bass_client.BassClientStateMachine.REMOTE_SCAN_START;
import static com.android.bluetooth.bass_client.BassClientStateMachine.REMOTE_SCAN_STOP;
import static com.android.bluetooth.bass_client.BassClientStateMachine.START_SCAN_OFFLOAD;
import static com.android.bluetooth.bass_client.BassClientStateMachine.STOP_SCAN_OFFLOAD;

import static com.google.common.truth.Truth.assertThat;

@@ -43,6 +47,11 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
import android.bluetooth.BluetoothLeAudioContentMetadata;
import android.bluetooth.BluetoothLeBroadcastChannel;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastSubgroup;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.ScanRecord;
import android.content.Intent;
@@ -53,6 +62,7 @@ import android.os.Message;

import androidx.test.filters.MediumTest;

import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.btservice.AdapterService;

@@ -67,6 +77,7 @@ import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

@@ -87,16 +98,17 @@ public class BassClientStateMachineTest {
    private HandlerThread mHandlerThread;
    private StubBassClientStateMachine mBassClientStateMachine;
    private BluetoothDevice mTestDevice;
    @Mock
    private AdapterService mAdapterService;
    @Mock
    private BassClientService mBassClientService;

    @Mock private AdapterService mAdapterService;
    @Mock private BassClientService mBassClientService;
    @Spy private BluetoothMethodProxy mMethodProxy;

    @Before
    public void setUp() throws Exception {
        TestUtils.setAdapterService(mAdapterService);

        mAdapter = BluetoothAdapter.getDefaultAdapter();
        BluetoothMethodProxy.setInstanceForTesting(mMethodProxy);

        // Get a device for testing
        mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
@@ -344,7 +356,7 @@ public class BassClientStateMachineTest {

        // connected ----STOP_SCAN_OFFLOAD---> connectedProcessing --GATT_TXN_PROCESSED--> connected
        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(BassClientStateMachine.STOP_SCAN_OFFLOAD),
                mBassClientStateMachine.obtainMessage(STOP_SCAN_OFFLOAD),
                BassClientStateMachine.ConnectedProcessing.class);
        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(BassClientStateMachine.GATT_TXN_PROCESSED),
@@ -594,6 +606,132 @@ public class BassClientStateMachineTest {
        verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
    }

    @Test
    public void sendConnectMessage_inConnectedState() {
        initToConnectedState();

        mBassClientStateMachine.sendMessage(CONNECT);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
    }

    @Test
    public void sendDisconnectMessage_inConnectedState() {
        initToConnectedState();

        mBassClientStateMachine.mBluetoothGatt = null;
        mBassClientStateMachine.sendMessage(DISCONNECT);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());

        BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock(
                BassClientStateMachine.BluetoothGattTestableWrapper.class);
        mBassClientStateMachine.mBluetoothGatt = btGatt;
        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(DISCONNECT),
                BassClientStateMachine.Disconnected.class);
        verify(btGatt).disconnect();
        verify(btGatt).close();
    }

    @Test
    public void sendStateChangedMessage_inConnectedState() {
        initToConnectedState();

        Message connectedMsg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED);
        connectedMsg.obj = BluetoothProfile.STATE_CONNECTED;
        mBassClientStateMachine.sendMessage(connectedMsg);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());

        Message noneConnectedMsg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED);
        noneConnectedMsg.obj = BluetoothProfile.STATE_DISCONNECTING;
        sendMessageAndVerifyTransition(noneConnectedMsg, BassClientStateMachine.Disconnected.class);
    }

    @Test
    public void sendReadBassCharacteristicsMessage_inConnectedState() {
        initToConnectedState();
        BluetoothGattCharacteristic gattCharacteristic = Mockito.mock(
                BluetoothGattCharacteristic.class);

        mBassClientStateMachine.sendMessage(READ_BASS_CHARACTERISTICS, gattCharacteristic);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());

        BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock(
                BassClientStateMachine.BluetoothGattTestableWrapper.class);
        mBassClientStateMachine.mBluetoothGatt = btGatt;
        sendMessageAndVerifyTransition(mBassClientStateMachine.obtainMessage(
                READ_BASS_CHARACTERISTICS, gattCharacteristic),
                BassClientStateMachine.ConnectedProcessing.class);
    }

    @Test
    public void sendStartScanOffloadMessage_inConnectedState() {
        initToConnectedState();
        BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock(
                BassClientStateMachine.BluetoothGattTestableWrapper.class);
        mBassClientStateMachine.mBluetoothGatt = btGatt;

        mBassClientStateMachine.sendMessage(START_SCAN_OFFLOAD);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());

        BluetoothGattCharacteristic scanControlPoint = Mockito.mock(
                BluetoothGattCharacteristic.class);
        mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint;

        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(START_SCAN_OFFLOAD),
                BassClientStateMachine.ConnectedProcessing.class);
        verify(btGatt).writeCharacteristic(scanControlPoint);
        verify(scanControlPoint).setValue(REMOTE_SCAN_START);
    }

    @Test
    public void sendStopScanOffloadMessage_inConnectedState() {
        initToConnectedState();
        BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock(
                BassClientStateMachine.BluetoothGattTestableWrapper.class);
        mBassClientStateMachine.mBluetoothGatt = btGatt;

        mBassClientStateMachine.sendMessage(STOP_SCAN_OFFLOAD);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());

        BluetoothGattCharacteristic scanControlPoint = Mockito.mock(
                BluetoothGattCharacteristic.class);
        mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint;

        sendMessageAndVerifyTransition(
                mBassClientStateMachine.obtainMessage(STOP_SCAN_OFFLOAD),
                BassClientStateMachine.ConnectedProcessing.class);
        verify(btGatt).writeCharacteristic(scanControlPoint);
        verify(scanControlPoint).setValue(REMOTE_SCAN_STOP);
    }

    @Test
    public void sendPsyncActiveMessage_inConnectedState() {
        initToConnectedState();

        mBassClientStateMachine.mNoStopScanOffload = true;
        mBassClientStateMachine.sendMessage(PSYNC_ACTIVE_TIMEOUT);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        assertThat(mBassClientStateMachine.mNoStopScanOffload).isFalse();
    }

    @Test
    public void sendInvalidMessage_inConnectedState_doesNotChangeState() {
        initToConnectedState();

        mBassClientStateMachine.sendMessage(-1);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
    }

    // TODO: add sendMessage tests for BCAST related messages

    private void initToDisconnectedState() {
        allowConnection(true);
        allowConnectGatt(true);
@@ -610,6 +748,15 @@ public class BassClientStateMachineTest {
        Mockito.clearInvocations(mBassClientService);
    }

    private void initToConnectedState() {
        initToConnectingState();

        Message msg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED);
        msg.obj = BluetoothProfile.STATE_CONNECTED;
        sendMessageAndVerifyTransition(msg, BassClientStateMachine.Connected.class);
        Mockito.clearInvocations(mBassClientService);
    }

    private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) {
        Mockito.clearInvocations(mBassClientService);
        mBassClientStateMachine.sendMessage(msg);
@@ -620,6 +767,65 @@ public class BassClientStateMachineTest {
        Assert.assertThat(mBassClientStateMachine.getCurrentState(), IsInstanceOf.instanceOf(type));
    }

    private BluetoothLeBroadcastMetadata createBroadcastMetadata() {
        final String testMacAddress = "00:11:22:33:44:55";
        final int testBroadcastId = 42;
        final int testAdvertiserSid = 1234;
        final int testPaSyncInterval = 100;
        final int testPresentationDelayMs = 345;

        BluetoothDevice testDevice =
                mAdapter.getRemoteLeDevice(testMacAddress, BluetoothDevice.ADDRESS_TYPE_RANDOM);

        BluetoothLeBroadcastMetadata.Builder builder = new BluetoothLeBroadcastMetadata.Builder()
                .setEncrypted(false)
                .setSourceDevice(testDevice, BluetoothDevice.ADDRESS_TYPE_RANDOM)
                .setSourceAdvertisingSid(testAdvertiserSid)
                .setBroadcastId(testBroadcastId)
                .setBroadcastCode(null)
                .setPaSyncInterval(testPaSyncInterval)
                .setPresentationDelayMicros(testPresentationDelayMs);
        // builder expect at least one subgroup
        builder.addSubgroup(createBroadcastSubgroup());
        return builder.build();
    }

    private BluetoothLeBroadcastSubgroup createBroadcastSubgroup() {
        final long testAudioLocationFrontLeft = 0x01;
        final long testAudioLocationFrontRight = 0x02;
        // For BluetoothLeAudioContentMetadata
        final String testProgramInfo = "Test";
        // German language code in ISO 639-3
        final String testLanguage = "deu";
        final int testCodecId = 42;
        final int testChannelIndex = 56;

        BluetoothLeAudioCodecConfigMetadata codecMetadata =
                new BluetoothLeAudioCodecConfigMetadata.Builder()
                        .setAudioLocation(testAudioLocationFrontLeft).build();
        BluetoothLeAudioContentMetadata contentMetadata =
                new BluetoothLeAudioContentMetadata.Builder()
                        .setProgramInfo(testProgramInfo).setLanguage(testLanguage).build();
        BluetoothLeBroadcastSubgroup.Builder builder = new BluetoothLeBroadcastSubgroup.Builder()
                .setCodecId(testCodecId)
                .setCodecSpecificConfig(codecMetadata)
                .setContentMetadata(contentMetadata);

        BluetoothLeAudioCodecConfigMetadata channelCodecMetadata =
                new BluetoothLeAudioCodecConfigMetadata.Builder()
                        .setAudioLocation(testAudioLocationFrontRight).build();

        // builder expect at least one channel
        BluetoothLeBroadcastChannel channel =
                new BluetoothLeBroadcastChannel.Builder()
                        .setSelected(true)
                        .setChannelIndex(testChannelIndex)
                        .setCodecMetadata(channelCodecMetadata)
                        .build();
        builder.addChannel(channel);
        return builder.build();
    }

    // It simulates GATT connection for testing.
    public static class StubBassClientStateMachine extends BassClientStateMachine {
        boolean mShouldAllowGatt = true;