/* * Copyright 2018 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.bluetooth.btservice; import static org.mockito.Mockito.*; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.os.HandlerThread; import android.os.ParcelUuid; import android.os.UserHandle; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; import com.android.bluetooth.TestUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @MediumTest @RunWith(AndroidJUnit4.class) public class BondStateMachineTest { private static final int TEST_BOND_REASON = 0; private static final byte[] TEST_BT_ADDR_BYTES = {00, 11, 22, 33, 44, 55}; private static final ParcelUuid[] TEST_UUIDS = {ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB")}; private static final int BOND_NONE = BluetoothDevice.BOND_NONE; private static final int BOND_BONDING = BluetoothDevice.BOND_BONDING; private static final int BOND_BONDED = BluetoothDevice.BOND_BONDED; private AdapterProperties mAdapterProperties; private BluetoothDevice mDevice; private Context mTargetContext; private RemoteDevices mRemoteDevices; private BondStateMachine mBondStateMachine; private HandlerThread mHandlerThread; private RemoteDevices.DeviceProperties mDeviceProperties; private int mVerifyCount = 0; @Mock private AdapterService mAdapterService; @Before public void setUp() throws Exception { mTargetContext = InstrumentationRegistry.getTargetContext(); MockitoAnnotations.initMocks(this); TestUtils.setAdapterService(mAdapterService); mHandlerThread = new HandlerThread("BondStateMachineTestHandlerThread"); mHandlerThread.start(); mRemoteDevices = new RemoteDevices(mAdapterService, mHandlerThread.getLooper()); mRemoteDevices.reset(); when(mAdapterService.getResources()).thenReturn( mTargetContext.getResources()); mAdapterProperties = new AdapterProperties(mAdapterService); mAdapterProperties.init(mRemoteDevices); mBondStateMachine = BondStateMachine.make(mAdapterService, mAdapterProperties, mRemoteDevices); } @After public void tearDown() throws Exception { mHandlerThread.quit(); TestUtils.clearAdapterService(mAdapterService); } @Test public void testSendIntent() { int badBondState = 42; mVerifyCount = 0; // Reset mRemoteDevices for the test. mRemoteDevices.reset(); mDeviceProperties = mRemoteDevices.addDeviceProperties(TEST_BT_ADDR_BYTES); mDevice = mDeviceProperties.getDevice(); Assert.assertNotNull(mDevice); /* Classic / Dualmode test cases*/ // Uuid not available, mPendingBondedDevice is empty. testSendIntentNoPendingDevice(BOND_NONE, BOND_NONE, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDevice(BOND_NONE, BOND_BONDING, BOND_BONDING, true, BOND_NONE, BOND_BONDING); testSendIntentNoPendingDevice(BOND_NONE, BOND_BONDED, BOND_BONDED, true, BOND_NONE, BOND_BONDING); testSendIntentNoPendingDevice(BOND_NONE, badBondState, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDevice(BOND_BONDING, BOND_NONE, BOND_NONE, true, BOND_BONDING, BOND_NONE); testSendIntentNoPendingDevice(BOND_BONDING, BOND_BONDING, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDevice(BOND_BONDING, BOND_BONDED, BOND_BONDED, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDevice(BOND_BONDING, badBondState, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDevice(BOND_BONDED, BOND_NONE, BOND_NONE, true, BOND_BONDED, BOND_NONE); testSendIntentNoPendingDevice(BOND_BONDED, BOND_BONDING, BOND_BONDING, true, BOND_BONDED, BOND_BONDING); testSendIntentNoPendingDevice(BOND_BONDED, BOND_BONDED, BOND_BONDED, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDevice(BOND_BONDED, badBondState, BOND_BONDED, false, BOND_NONE, BOND_NONE); // Uuid not available, mPendingBondedDevice contains a remote device. testSendIntentPendingDevice(BOND_NONE, BOND_NONE, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_NONE, BOND_BONDING, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_NONE, BOND_BONDED, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_NONE, badBondState, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_BONDING, BOND_NONE, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_BONDING, BOND_BONDING, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_BONDING, BOND_BONDED, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_BONDING, badBondState, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_BONDED, BOND_NONE, BOND_NONE, true, BOND_BONDING, BOND_NONE); testSendIntentPendingDevice(BOND_BONDED, BOND_BONDING, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_BONDED, BOND_BONDED, BOND_BONDED, false, BOND_NONE, BOND_NONE); testSendIntentPendingDevice(BOND_BONDED, badBondState, BOND_BONDED, false, BOND_NONE, BOND_NONE); // Uuid available, mPendingBondedDevice is empty. testSendIntentNoPendingDeviceWithUuid(BOND_NONE, BOND_NONE, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDeviceWithUuid(BOND_NONE, BOND_BONDING, BOND_BONDING, true, BOND_NONE, BOND_BONDING); testSendIntentNoPendingDeviceWithUuid(BOND_NONE, BOND_BONDED, BOND_BONDED, true, BOND_NONE, BOND_BONDED); testSendIntentNoPendingDeviceWithUuid(BOND_NONE, badBondState, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDeviceWithUuid(BOND_BONDING, BOND_NONE, BOND_NONE, true, BOND_BONDING, BOND_NONE); testSendIntentNoPendingDeviceWithUuid(BOND_BONDING, BOND_BONDING, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDeviceWithUuid(BOND_BONDING, BOND_BONDED, BOND_BONDED, true, BOND_BONDING, BOND_BONDED); testSendIntentNoPendingDeviceWithUuid(BOND_BONDING, badBondState, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDeviceWithUuid(BOND_BONDED, BOND_NONE, BOND_NONE, true, BOND_BONDED, BOND_NONE); testSendIntentNoPendingDeviceWithUuid(BOND_BONDED, BOND_BONDING, BOND_BONDING, true, BOND_BONDED, BOND_BONDING); testSendIntentNoPendingDeviceWithUuid(BOND_BONDED, BOND_BONDED, BOND_BONDED, false, BOND_NONE, BOND_NONE); testSendIntentNoPendingDeviceWithUuid(BOND_BONDED, badBondState, BOND_BONDED, false, BOND_NONE, BOND_NONE); // Uuid available, mPendingBondedDevice contains a remote device. testSendIntentPendingDeviceWithUuid(BOND_NONE, BOND_NONE, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_NONE, BOND_BONDING, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_NONE, BOND_BONDED, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_NONE, badBondState, BOND_NONE, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_BONDING, BOND_NONE, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_BONDING, BOND_BONDING, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_BONDING, BOND_BONDED, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_BONDING, badBondState, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_BONDED, BOND_NONE, BOND_NONE, true, BOND_BONDING, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_BONDED, BOND_BONDING, BOND_BONDING, false, BOND_NONE, BOND_NONE); testSendIntentPendingDeviceWithUuid(BOND_BONDED, BOND_BONDED, BOND_BONDED, true, BOND_BONDING, BOND_BONDED); testSendIntentPendingDeviceWithUuid(BOND_BONDED, badBondState, BOND_BONDED, false, BOND_NONE, BOND_NONE); /* Low energy test cases */ testSendIntentBle(BOND_NONE, BOND_NONE, BOND_NONE); testSendIntentBle(BOND_NONE, BOND_BONDING, BOND_BONDING); testSendIntentBle(BOND_NONE, BOND_BONDED, BOND_BONDED); testSendIntentBle(BOND_BONDING, BOND_NONE, BOND_NONE); testSendIntentBle(BOND_BONDING, BOND_BONDING, BOND_BONDING); testSendIntentBle(BOND_BONDING, BOND_BONDED, BOND_BONDED); testSendIntentBle(BOND_BONDED, BOND_NONE, BOND_NONE); testSendIntentBle(BOND_BONDED, BOND_BONDING, BOND_BONDING); testSendIntentBle(BOND_BONDED, BOND_BONDED, BOND_BONDED); } private void testSendIntentCase(int oldState, int newState, int expectedNewState, boolean shouldBroadcast, int broadcastOldState, int broadcastNewState) { ArgumentCaptor intentArgument = ArgumentCaptor.forClass(Intent.class); // Setup old state before start test. mDeviceProperties.mBondState = oldState; try { mBondStateMachine.sendIntent(mDevice, newState, TEST_BOND_REASON); } catch (IllegalArgumentException e) { // Do nothing. } Assert.assertEquals(expectedNewState, mDeviceProperties.getBondState()); // Check for bond state Intent status. if (shouldBroadcast) { verify(mAdapterService, times(++mVerifyCount)).sendBroadcastAsUser( intentArgument.capture(), eq(UserHandle.ALL), eq(AdapterService.BLUETOOTH_PERM)); verifyBondStateChangeIntent(broadcastOldState, broadcastNewState, intentArgument.getValue()); } else { verify(mAdapterService, times(mVerifyCount)).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class), anyString()); } } private void testSendIntentNoPendingDeviceWithUuid(int oldState, int newState, int expectedNewState, boolean shouldBroadcast, int broadcastOldState, int broadcastNewState) { // Add dummy UUID for the device. mDeviceProperties.mUuids = TEST_UUIDS; testSendIntentNoPendingDevice(oldState, newState, expectedNewState, shouldBroadcast, broadcastOldState, broadcastNewState); } private void testSendIntentPendingDeviceWithUuid(int oldState, int newState, int expectedNewState, boolean shouldBroadcast, int broadcastOldState, int broadcastNewState) { // Add dummy UUID for the device. mDeviceProperties.mUuids = TEST_UUIDS; testSendIntentPendingDevice(oldState, newState, expectedNewState, shouldBroadcast, broadcastOldState, broadcastNewState); } private void testSendIntentPendingDevice(int oldState, int newState, int expectedNewState, boolean shouldBroadcast, int broadcastOldState, int broadcastNewState) { // Test for classic remote device. mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_CLASSIC; mBondStateMachine.mPendingBondedDevices.clear(); mBondStateMachine.mPendingBondedDevices.add(mDevice); testSendIntentCase(oldState, newState, expectedNewState, shouldBroadcast, broadcastOldState, broadcastNewState); // Test for dual-mode remote device. mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_DUAL; mBondStateMachine.mPendingBondedDevices.clear(); mBondStateMachine.mPendingBondedDevices.add(mDevice); testSendIntentCase(oldState, newState, expectedNewState, shouldBroadcast, broadcastOldState, broadcastNewState); } private void testSendIntentNoPendingDevice(int oldState, int newState, int expectedNewState, boolean shouldBroadcast, int broadcastOldState, int broadcastNewState) { // Test for classic remote device. mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_CLASSIC; mBondStateMachine.mPendingBondedDevices.clear(); testSendIntentCase(oldState, newState, expectedNewState, shouldBroadcast, broadcastOldState, broadcastNewState); // Test for dual-mode remote device. mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_DUAL; mBondStateMachine.mPendingBondedDevices.clear(); testSendIntentCase(oldState, newState, expectedNewState, shouldBroadcast, broadcastOldState, broadcastNewState); } private void testSendIntentBle(int oldState, int newState, int expectedNewState) { // Test for low energy remote device. mDeviceProperties.mDeviceType = BluetoothDevice.DEVICE_TYPE_LE; mBondStateMachine.mPendingBondedDevices.clear(); testSendIntentCase(oldState, newState, newState, (oldState != newState), oldState, newState); } private void verifyBondStateChangeIntent(int oldState, int newState, Intent intent) { Assert.assertNotNull(intent); Assert.assertEquals(BluetoothDevice.ACTION_BOND_STATE_CHANGED, intent.getAction()); Assert.assertEquals(mDevice, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); Assert.assertEquals(newState, intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1)); Assert.assertEquals(oldState, intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, -1)); if (newState == BOND_NONE) { Assert.assertEquals(TEST_BOND_REASON, intent.getIntExtra(BluetoothDevice.EXTRA_REASON, -1)); } else { Assert.assertEquals(-1, intent.getIntExtra(BluetoothDevice.EXTRA_REASON, -1)); } } }