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

Commit e07d0a22 authored by Himanshu Rawat's avatar Himanshu Rawat
Browse files

Test cases for removeBond() API

Test: atest BumbleBluetoothTests:PairingTest
Flag: TEST_ONLY
Bug: 369242979
Change-Id: I018a62919e0b49447aad37b57235354f0bfb2d26
parent 552cbc57
Loading
Loading
Loading
Loading
+274 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;

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

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
@@ -28,9 +29,13 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHidHost;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.PandoraDevice;
import android.bluetooth.StreamObserverSpliterator;
@@ -41,6 +46,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.ParcelUuid;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Log;
@@ -48,6 +54,7 @@ import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import com.android.bluetooth.flags.Flags;
import com.android.compatibility.common.util.AdoptShellPermissionsRule;

import io.grpc.stub.StreamObserver;
@@ -60,6 +67,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -112,8 +120,11 @@ public class PairingTest {
    private final StreamObserverSpliterator<PairingEvent> mPairingEventStreamObserver =
            new StreamObserverSpliterator<>();
    @Mock private BroadcastReceiver mReceiver;
    @Mock private BluetoothProfile.ServiceListener mProfileServiceListener;
    private InOrder mInOrder = null;
    private BluetoothDevice mBumbleDevice;
    private BluetoothHidHost mHidService;
    private BluetoothHeadset mHfpService;

    @Before
    public void setUp() throws Exception {
@@ -143,6 +154,10 @@ public class PairingTest {

        mInOrder = inOrder(mReceiver);

        // Get profile proxies
        mHidService = (BluetoothHidHost) getProfileProxy(BluetoothProfile.HID_HOST);
        mHfpService = (BluetoothHeadset) getProfileProxy(BluetoothProfile.HEADSET);

        mBumbleDevice = mBumble.getRemoteDevice();
        Set<BluetoothDevice> bondedDevices = sAdapter.getBondedDevices();
        if (bondedDevices.contains(mBumbleDevice)) {
@@ -534,6 +549,256 @@ public class PairingTest {
        unregisterIntentActions(BluetoothDevice.ACTION_ACL_CONNECTED);
    }

    /**
     * Test removeDevice API when connected over LE
     *
     * <p>Prerequisites:
     *
     * <ol>
     *   <li>Bumble and Android are not bonded
     * </ol>
     *
     * <p>Steps:
     *
     * <ol>
     *   <li>Bumble is connectable over LE
     *   <li>Android pairs with Bumble over LE
     *   <li>Call BluetoothDevice.removeBond() API
     *   <li>Android disconnects the ACL and removes the bond
     * </ol>
     *
     * <p>Expectation: Bumble is not bonded
     */
    @Test
    @RequiresFlagsEnabled({Flags.FLAG_WAIT_FOR_DISCONNECT_BEFORE_UNBOND})
    public void testRemoveBondLe_WhenConnected() {
        registerIntentActions(
                BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED);

        testStep_BondLe();
        assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);

        assertThat(mBumbleDevice.removeBond()).isTrue();
        verifyIntentReceived(
                hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED),
                hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
        verifyIntentReceived(
                hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
                hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));

        assertThat(sAdapter.getBondedDevices()).doesNotContain(mBumbleDevice);

        verifyNoMoreInteractions(mReceiver);
        unregisterIntentActions(
                BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED);
    }

    /**
     * Test removeDevice API when connected over BR/EDR
     *
     * <p>Prerequisites:
     *
     * <ol>
     *   <li>Bumble and Android are not bonded
     * </ol>
     *
     * <p>Steps:
     *
     * <ol>
     *   <li>Bumble is connectable over BR/EDR
     *   <li>Android pairs with Bumble over BR/EDR
     *   <li>Call BluetoothDevice.removeBond() API
     *   <li>Android disconnects the ACL and removes the bond
     * </ol>
     *
     * <p>Expectation: Bumble is not bonded
     */
    @Test
    @RequiresFlagsEnabled({Flags.FLAG_WAIT_FOR_DISCONNECT_BEFORE_UNBOND})
    public void testRemoveBondBredr_WhenConnected() {
        registerIntentActions(
                BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED);

        testStep_BondBredr();
        assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);

        assertThat(mBumbleDevice.removeBond()).isTrue();
        verifyIntentReceived(
                hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED),
                hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_BREDR),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
        verifyIntentReceived(
                hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
                hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));

        assertThat(sAdapter.getBondedDevices()).doesNotContain(mBumbleDevice);

        verifyNoMoreInteractions(mReceiver);
        unregisterIntentActions(
                BluetoothDevice.ACTION_ACL_DISCONNECTED, BluetoothDevice.ACTION_BOND_STATE_CHANGED);
    }

    /**
     * Test removeDevice API when not connected
     *
     * <p>Prerequisites:
     *
     * <ol>
     *   <li>Bumble and Android are not bonded
     *   <li>Bumble supports HOGP
     * </ol>
     *
     * <p>Steps:
     *
     * <ol>
     *   <li>Bumble is connectable over LE
     *   <li>Android pairs with Bumble over LE
     *   <li>Disconnect the Bumble
     *   <li>Call BluetoothDevice.removeBond() API
     *   <li>Removes the bond
     * </ol>
     *
     * <p>Expectation: Bumble is not bonded
     */
    @Test
    public void testRemoveBondLe_WhenDisconnected() {
        registerIntentActions(
                BluetoothDevice.ACTION_ACL_DISCONNECTED,
                BluetoothDevice.ACTION_BOND_STATE_CHANGED,
                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);

        testStep_BondLe();
        assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);

        // Wait for profiles to get connected
        verifyIntentReceived(
                hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
                hasExtra(BluetoothHidHost.EXTRA_STATE, BluetoothHidHost.STATE_CONNECTING));
        verifyIntentReceived(
                hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
                hasExtra(BluetoothHidHost.EXTRA_STATE, BluetoothHidHost.STATE_CONNECTED));

        // Disconnect Bumble
        assertThat(mBumbleDevice.disconnect()).isEqualTo(BluetoothStatusCodes.SUCCESS);
        verifyIntentReceived(
                hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
                hasExtra(BluetoothHidHost.EXTRA_STATE, BluetoothHidHost.STATE_DISCONNECTING));
        verifyIntentReceived(
                hasAction(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
                hasExtra(BluetoothHidHost.EXTRA_STATE, BluetoothHidHost.STATE_DISCONNECTED));

        // Wait for ACL to get disconnected
        verifyIntentReceived(
                hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED),
                hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));

        // Remove bond
        assertThat(mBumbleDevice.removeBond()).isTrue();
        verifyIntentReceived(
                hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
                hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));
        assertThat(sAdapter.getBondedDevices()).doesNotContain(mBumbleDevice);

        verifyNoMoreInteractions(mReceiver);
        unregisterIntentActions(
                BluetoothDevice.ACTION_ACL_DISCONNECTED,
                BluetoothDevice.ACTION_BOND_STATE_CHANGED,
                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
    }

    /**
     * Test removeDevice API when not connected
     *
     * <p>Prerequisites:
     *
     * <ol>
     *   <li>Bumble and Android are not bonded
     *   <li>Bumble supports HID device role
     * </ol>
     *
     * <p>Steps:
     *
     * <ol>
     *   <li>Bumble is connectable over BR/EDR
     *   <li>Android pairs with Bumble over BR/EDR
     *   <li>Disconnect the Bumble
     *   <li>Call BluetoothDevice.removeBond() API
     *   <li>Removes the bond
     * </ol>
     *
     * <p>Expectation: Bumble is not bonded
     */
    @Test
    public void testRemoveBondBredr_WhenDisconnected() {
        registerIntentActions(
                BluetoothDevice.ACTION_ACL_DISCONNECTED,
                BluetoothDevice.ACTION_BOND_STATE_CHANGED,
                BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);

        // Disable all profiles other than A2DP as profile connections take too long
        assertThat(
                        mHfpService.setConnectionPolicy(
                                mBumbleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN))
                .isTrue();
        assertThat(
                        mHidService.setConnectionPolicy(
                                mBumbleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN))
                .isTrue();

        testStep_BondBredr();
        assertThat(sAdapter.getBondedDevices()).contains(mBumbleDevice);

        // Wait for profiles to get connected
        verifyIntentReceived(
                hasAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED),
                hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_CONNECTING),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
        verifyIntentReceived(
                hasAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED),
                hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_CONNECTED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));

        // Disconnect all profiles
        assertThat(mBumbleDevice.disconnect()).isEqualTo(BluetoothStatusCodes.SUCCESS);
        verifyIntentReceived(
                hasAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED),
                hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_DISCONNECTING),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));
        verifyIntentReceived(
                hasAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED),
                hasExtra(BluetoothA2dp.EXTRA_STATE, BluetoothA2dp.STATE_DISCONNECTED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));

        // Wait for the ACL to get disconnected
        verifyIntentReceived(
                hasAction(BluetoothDevice.ACTION_ACL_DISCONNECTED),
                hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_BREDR),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice));

        // Remove bond
        assertThat(mBumbleDevice.removeBond()).isTrue();
        verifyIntentReceived(
                hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED),
                hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice),
                hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE));
        assertThat(sAdapter.getBondedDevices()).doesNotContain(mBumbleDevice);

        verifyNoMoreInteractions(mReceiver);
        unregisterIntentActions(
                BluetoothDevice.ACTION_ACL_DISCONNECTED,
                BluetoothDevice.ACTION_BOND_STATE_CHANGED,
                BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
    }

    private void testStep_BondBredr() {
        registerIntentActions(
                BluetoothDevice.ACTION_BOND_STATE_CHANGED,
@@ -791,4 +1056,13 @@ public class PairingTest {
    private int getTotalActionRegistrationCounts() {
        return mActionRegistrationCounts.values().stream().reduce(0, Integer::sum);
    }

    private BluetoothProfile getProfileProxy(int profile) {
        sAdapter.getProfileProxy(sTargetContext, mProfileServiceListener, profile);
        ArgumentCaptor<BluetoothProfile> proxyCaptor =
                ArgumentCaptor.forClass(BluetoothProfile.class);
        verify(mProfileServiceListener, timeout(BOND_INTENT_TIMEOUT.toMillis()))
                .onServiceConnected(eq(profile), proxyCaptor.capture());
        return proxyCaptor.getValue();
    }
}