blob: ace3333b5b6818fc3c9ceecabe7f563e24a58f11 [file] [log] [blame]
/*
* Copyright (C) 2019 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.tradefed.util;
import static com.android.tradefed.util.Sl4aBluetoothUtil.BT_SNOOP_LOG_CMD;
import static com.android.tradefed.util.Sl4aBluetoothUtil.BT_SNOOP_LOG_CMD_LEGACY;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.util.Sl4aBluetoothUtil.BluetoothConnectionState;
import com.android.tradefed.util.Sl4aBluetoothUtil.BluetoothProfile;
import com.android.tradefed.util.Sl4aBluetoothUtil.Commands;
import com.android.tradefed.util.Sl4aBluetoothUtil.Events;
import com.android.tradefed.util.sl4a.Sl4aClient;
import com.android.tradefed.util.sl4a.Sl4aEventDispatcher;
import com.android.tradefed.util.sl4a.Sl4aEventDispatcher.EventSl4aObject;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/** Unit Test for {@link Sl4aBluetoothUtil} utility */
@RunWith(JUnit4.class)
public class Sl4aBluetoothUtilTest {
private static final String BT_CONNECTION_EVENT_FORMAT = "{profile: %d, state: %d, addr: %s}";
private static final Set<BluetoothProfile> PROFILES =
new HashSet<>(
Arrays.asList(
BluetoothProfile.A2DP_SINK,
BluetoothProfile.MAP_CLIENT,
BluetoothProfile.PBAP_CLIENT,
BluetoothProfile.HEADSET_CLIENT,
BluetoothProfile.PAN));
@Mock private ITestDevice mPrimary;
@Mock private ITestDevice mSecondary;
@Mock private Sl4aClient mPrimaryClient;
@Mock private Sl4aClient mSecondaryClient;
@Mock private Sl4aEventDispatcher mEventDispatcher;
private Sl4aBluetoothUtil mBluetoothUtil = Sl4aBluetoothUtil.createInstance();
@Before
public void setup() {
initMocks(this);
when(mPrimary.getSerialNumber()).thenReturn("serial1");
when(mSecondary.getSerialNumber()).thenReturn("serial2");
when(mPrimaryClient.getEventDispatcher()).thenReturn(mEventDispatcher);
mBluetoothUtil.setSl4a(mPrimary, mPrimaryClient);
mBluetoothUtil.setSl4a(mSecondary, mSecondaryClient);
}
@Test
public void testEnable_alreadyEnabled() throws IOException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(eq(Commands.BLUETOOTH_CHECK_STATE))).thenReturn(Boolean.TRUE);
assertTrue(mBluetoothUtil.enable(mPrimary));
}
@Test
public void testEnable_notEnabled()
throws IOException, JSONException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_CHECK_STATE)).thenReturn(Boolean.FALSE);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_TOGGLE_STATE, Boolean.TRUE))
.thenReturn(Boolean.TRUE);
when(mEventDispatcher.popEvent(eq(Events.BLUETOOTH_STATE_CHANGED_ON), anyLong()))
.thenReturn(createEvent("fake"));
assertTrue(mBluetoothUtil.enable(mPrimary));
}
@Test
public void testDisable_alreadyDisabled() throws IOException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(eq(Commands.BLUETOOTH_CHECK_STATE))).thenReturn(Boolean.FALSE);
assertTrue(mBluetoothUtil.disable(mPrimary));
}
@Test
public void testDisable_notDisabled()
throws IOException, JSONException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_CHECK_STATE)).thenReturn(Boolean.TRUE);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_TOGGLE_STATE, Boolean.FALSE))
.thenReturn(Boolean.TRUE);
when(mEventDispatcher.popEvent(eq(Events.BLUETOOTH_STATE_CHANGED_OFF), anyLong()))
.thenReturn(createEvent("fake"));
assertTrue(mBluetoothUtil.disable(mPrimary));
}
@Test
public void testGetAddress() throws IOException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS)).thenReturn("address");
assertEquals("address", mBluetoothUtil.getAddress(mPrimary));
}
@Test
public void testGetAddress_calledTwice() throws IOException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS)).thenReturn("address");
assertEquals("address", mBluetoothUtil.getAddress(mPrimary));
assertEquals("address", mBluetoothUtil.getAddress(mPrimary));
verify(mPrimaryClient, times(1)).rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS);
}
@Test
public void testGetBondedDevices()
throws JSONException, IOException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[{address: address_1}, {address: address_2}]"));
Set<String> addresses = mBluetoothUtil.getBondedDevices(mPrimary);
assertEquals(2, addresses.size());
assertTrue(addresses.contains("address_1"));
assertTrue(addresses.contains("address_2"));
}
@Test
public void testPair_alreadyPaired()
throws IOException, JSONException, DeviceNotAvailableException {
when(mSecondaryClient.rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS))
.thenReturn("address_2");
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[{address: address_2}]"));
assertTrue(mBluetoothUtil.pair(mPrimary, mSecondary));
verify(mSecondaryClient, never()).rpcCall(Commands.BLUETOOTH_MAKE_DISCOVERABLE);
verify(mPrimaryClient, never()).rpcCall(Commands.BLUETOOTH_START_PAIRING_HELPER);
verify(mSecondaryClient, never()).rpcCall(Commands.BLUETOOTH_START_PAIRING_HELPER);
verify(mPrimaryClient, never()).rpcCall(Commands.BLUETOOTH_DISCOVER_AND_BOND, "address_2");
}
@Test
public void testPair_success() throws IOException, JSONException, DeviceNotAvailableException {
when(mSecondaryClient.rpcCall(Commands.BLUETOOTH_GET_SCAN_MODE)).thenReturn(3);
when(mSecondaryClient.rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS))
.thenReturn("address_2");
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[]"))
.thenReturn(new JSONArray("[]"))
.thenReturn(new JSONArray("[]"))
.thenReturn(new JSONArray("[{address: address_2}]"));
assertTrue(mBluetoothUtil.pair(mPrimary, mSecondary));
verify(mSecondaryClient).rpcCall(Commands.BLUETOOTH_MAKE_DISCOVERABLE);
verify(mPrimaryClient).rpcCall(Commands.BLUETOOTH_START_PAIRING_HELPER);
verify(mSecondaryClient).rpcCall(Commands.BLUETOOTH_START_PAIRING_HELPER);
verify(mPrimaryClient).rpcCall(Commands.BLUETOOTH_DISCOVER_AND_BOND, "address_2");
}
@Test
public void testPair_timeout() throws IOException, JSONException, DeviceNotAvailableException {
when(mSecondaryClient.rpcCall(Commands.BLUETOOTH_GET_SCAN_MODE)).thenReturn(3);
when(mSecondaryClient.rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS))
.thenReturn("address_2");
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[]"));
mBluetoothUtil.mBtPairTimeoutMs = 1000;
assertFalse(mBluetoothUtil.pair(mPrimary, mSecondary));
}
@Test
public void testUnpair_success()
throws IOException, JSONException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[{address: address_1}, {address: address_2}]"))
.thenReturn(new JSONArray("[]"));
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_UNBOND, "address_1")).thenReturn(true);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_UNBOND, "address_2")).thenReturn(true);
assertTrue(mBluetoothUtil.unpairAll(mPrimary));
}
@Test
public void testUnpair_successWithWarning()
throws IOException, JSONException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[{address: address_1}, {address: address_2}]"))
.thenReturn(new JSONArray("[]"));
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_UNBOND, "address_1")).thenReturn(true);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_UNBOND, "address_2")).thenReturn(false);
assertTrue(mBluetoothUtil.unpairAll(mPrimary));
}
@Test
public void testUnpair_fail() throws IOException, JSONException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[{address: address_1}, {address: address_2}]"))
.thenReturn(new JSONArray("[{address: address_1}]"));
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_UNBOND, "address_1")).thenReturn(false);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_UNBOND, "address_2")).thenReturn(true);
assertFalse(mBluetoothUtil.unpairAll(mPrimary));
}
@Test
public void testConnect_notPaired()
throws IOException, JSONException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[]"));
when(mSecondaryClient.rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS))
.thenReturn("address_2");
assertFalse(mBluetoothUtil.connect(mPrimary, mSecondary, PROFILES));
}
@Test
public void testConnect_success()
throws IOException, JSONException, DeviceNotAvailableException {
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_GET_BONDED_DEVICES))
.thenReturn(new JSONArray("[{address: address_2}]"));
when(mSecondaryClient.rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS))
.thenReturn("address_2");
when(mEventDispatcher.popEvent(
eq(Events.BLUETOOTH_PROFILE_CONNECTION_STATE_CHANGED), anyLong()))
.thenReturn(
createEvent(
String.format(
BT_CONNECTION_EVENT_FORMAT,
BluetoothProfile.A2DP_SINK.getProfile(),
BluetoothConnectionState.CONNECTED.getState(),
"address_2")),
createEvent(
String.format(
BT_CONNECTION_EVENT_FORMAT,
BluetoothProfile.HEADSET_CLIENT.getProfile(),
BluetoothConnectionState.CONNECTED.getState(),
"address_2")),
createEvent(
String.format(
BT_CONNECTION_EVENT_FORMAT,
BluetoothProfile.PBAP_CLIENT.getProfile(),
BluetoothConnectionState.CONNECTED.getState(),
"address_2")),
createEvent(
String.format(
BT_CONNECTION_EVENT_FORMAT,
BluetoothProfile.MAP_CLIENT.getProfile(),
BluetoothConnectionState.CONNECTED.getState(),
"address_2")),
null);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_PAN_GET_CONNECTED_DEVICES))
.thenReturn(new JSONArray("[]"))
.thenReturn(new JSONArray("[]"))
.thenReturn(new JSONArray("[{address: address_2}]"));
mBluetoothUtil.mBtConnectionTimeoutMs = 1000;
assertTrue(mBluetoothUtil.connect(mPrimary, mSecondary, PROFILES));
verify(mPrimaryClient)
.rpcCall(Commands.BLUETOOTH_START_CONNECTION_STATE_CHANGE_MONITOR, "address_2");
verify(mPrimaryClient).rpcCall(Commands.BLUETOOTH_CONNECT_BONDED, "address_2");
}
@Test
public void testDisconnect_success()
throws IOException, JSONException, DeviceNotAvailableException {
when(mSecondaryClient.rpcCall(Commands.BLUETOOTH_GET_LOCAL_ADDRESS))
.thenReturn("address_2");
when(mEventDispatcher.popEvent(
eq(Events.BLUETOOTH_PROFILE_CONNECTION_STATE_CHANGED), anyLong()))
.thenReturn(
createEvent(
String.format(
BT_CONNECTION_EVENT_FORMAT,
BluetoothProfile.A2DP_SINK.getProfile(),
BluetoothConnectionState.DISCONNECTED.getState(),
"address_2")),
createEvent(
String.format(
BT_CONNECTION_EVENT_FORMAT,
BluetoothProfile.HEADSET_CLIENT.getProfile(),
BluetoothConnectionState.DISCONNECTED.getState(),
"address_2")),
createEvent(
String.format(
BT_CONNECTION_EVENT_FORMAT,
BluetoothProfile.PBAP_CLIENT.getProfile(),
BluetoothConnectionState.DISCONNECTED.getState(),
"address_2")),
createEvent(
String.format(
BT_CONNECTION_EVENT_FORMAT,
BluetoothProfile.MAP_CLIENT.getProfile(),
BluetoothConnectionState.DISCONNECTED.getState(),
"address_2")),
null);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_PAN_GET_CONNECTED_DEVICES))
.thenReturn(new JSONArray("[{address: address_2}]"))
.thenReturn(new JSONArray("[{address: address_2}]"))
.thenReturn(new JSONArray("[]"));
mBluetoothUtil.mBtConnectionTimeoutMs = 1000;
assertTrue(mBluetoothUtil.disconnect(mPrimary, mSecondary, PROFILES));
verify(mPrimaryClient)
.rpcCall(Commands.BLUETOOTH_START_CONNECTION_STATE_CHANGE_MONITOR, "address_2");
verify(mPrimaryClient)
.rpcCall(
eq(Commands.BLUETOOTH_DISCONNECT_CONNECTED_PROFILE),
eq("address_2"),
any(JSONArray.class));
}
@Test
public void testEnableBluetoothSnoopLog_AndroidQAndAbove()
throws DeviceNotAvailableException, JSONException, IOException {
when(mPrimary.getApiLevel()).thenReturn(29);
// Mock for disable
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_CHECK_STATE)).thenReturn(Boolean.TRUE);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_TOGGLE_STATE, Boolean.FALSE))
.thenReturn(Boolean.TRUE);
when(mEventDispatcher.popEvent(eq(Events.BLUETOOTH_STATE_CHANGED_OFF), anyLong()))
.thenReturn(createEvent("fake"));
// Mock for enable
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_CHECK_STATE)).thenReturn(Boolean.FALSE);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_TOGGLE_STATE, Boolean.TRUE))
.thenReturn(Boolean.TRUE);
when(mEventDispatcher.popEvent(eq(Events.BLUETOOTH_STATE_CHANGED_ON), anyLong()))
.thenReturn(createEvent("fake"));
mBluetoothUtil.enableBluetoothSnoopLog(mPrimary);
verify(mPrimary).executeShellCommand(String.format(BT_SNOOP_LOG_CMD, "full"));
}
@Test
public void testEnableBluetoothSnoopLog_AndroidPAndBelow()
throws DeviceNotAvailableException, JSONException, IOException {
when(mPrimary.getApiLevel()).thenReturn(28);
// Mock for disable
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_CHECK_STATE)).thenReturn(Boolean.TRUE);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_TOGGLE_STATE, Boolean.FALSE))
.thenReturn(Boolean.TRUE);
when(mEventDispatcher.popEvent(eq(Events.BLUETOOTH_STATE_CHANGED_OFF), anyLong()))
.thenReturn(createEvent("fake"));
// Mock for enable
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_CHECK_STATE)).thenReturn(Boolean.FALSE);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_TOGGLE_STATE, Boolean.TRUE))
.thenReturn(Boolean.TRUE);
when(mEventDispatcher.popEvent(eq(Events.BLUETOOTH_STATE_CHANGED_ON), anyLong()))
.thenReturn(createEvent("fake"));
mBluetoothUtil.enableBluetoothSnoopLog(mPrimary);
verify(mPrimary).executeShellCommand(String.format(BT_SNOOP_LOG_CMD_LEGACY, "true"));
}
@Test
public void testDisableBluetoothSnoopLog_AndroidQAndAbove()
throws DeviceNotAvailableException, IOException, JSONException {
when(mPrimary.getApiLevel()).thenReturn(29);
// Mock for disable
when(mPrimaryClient.rpcCall(eq(Commands.BLUETOOTH_CHECK_STATE))).thenReturn(Boolean.FALSE);
// Mock for enable
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_CHECK_STATE)).thenReturn(Boolean.FALSE);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_TOGGLE_STATE, Boolean.TRUE))
.thenReturn(Boolean.TRUE);
when(mEventDispatcher.popEvent(eq(Events.BLUETOOTH_STATE_CHANGED_ON), anyLong()))
.thenReturn(createEvent("fake"));
mBluetoothUtil.disableBluetoothSnoopLog(mPrimary);
verify(mPrimary).executeShellCommand(String.format(BT_SNOOP_LOG_CMD, "disabled"));
}
@Test
public void testDisableBluetoothSnoopLog_AndroidPAndBelow()
throws DeviceNotAvailableException, IOException, JSONException {
when(mPrimary.getApiLevel()).thenReturn(28);
// Mock for disable
when(mPrimaryClient.rpcCall(eq(Commands.BLUETOOTH_CHECK_STATE))).thenReturn(Boolean.FALSE);
// Mock for enable
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_CHECK_STATE)).thenReturn(Boolean.FALSE);
when(mPrimaryClient.rpcCall(Commands.BLUETOOTH_TOGGLE_STATE, Boolean.TRUE))
.thenReturn(Boolean.TRUE);
when(mEventDispatcher.popEvent(eq(Events.BLUETOOTH_STATE_CHANGED_ON), anyLong()))
.thenReturn(createEvent("fake"));
mBluetoothUtil.disableBluetoothSnoopLog(mPrimary);
verify(mPrimary).executeShellCommand(String.format(BT_SNOOP_LOG_CMD_LEGACY, "false"));
}
private EventSl4aObject createEvent(String jsonData) throws JSONException {
return new EventSl4aObject(
new JSONObject(String.format("{name: my_event, data: %s, time: 1111}", jsonData)));
}
}