blob: 05ec98a113807565292c929b85f08dac6cd2285a [file] [log] [blame]
/*
* Copyright (C) 2017 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.mapclient;
import static org.mockito.Mockito.*;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.SdpMasRecord;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.filters.Suppress;
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.R;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Suppress // TODO: enable when b/74609188 is debugged
@MediumTest
@RunWith(AndroidJUnit4.class)
public class MapClientStateMachineTest {
private static final String TAG = "MapStateMachineTest";
private static final Integer TIMEOUT = 3000;
private BluetoothAdapter mAdapter;
private MceStateMachine mMceStateMachine = null;
private BluetoothDevice mTestDevice;
private Context mTargetContext;
private FakeMapClientService mFakeMapClientService;
private CountDownLatch mConnectedLatch = null;
private CountDownLatch mDisconnectedLatch = null;
private Handler mHandler;
@Mock
private MasClient mMockMasClient;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTargetContext = InstrumentationRegistry.getTargetContext();
Assume.assumeTrue("Ignore test when MapClientService is not enabled",
mTargetContext.getResources().getBoolean(R.bool.profile_supported_mapmce));
// This line must be called to make sure relevant objects are initialized properly
mAdapter = BluetoothAdapter.getDefaultAdapter();
// Get a device for testing
mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
mConnectedLatch = new CountDownLatch(1);
mDisconnectedLatch = new CountDownLatch(1);
mFakeMapClientService = new FakeMapClientService();
when(mMockMasClient.makeRequest(any(Request.class))).thenReturn(true);
mMceStateMachine = new MceStateMachine(mFakeMapClientService, mTestDevice, mMockMasClient);
Assert.assertNotNull(mMceStateMachine);
if (Looper.myLooper() == null) {
Looper.prepare();
}
mHandler = new Handler();
}
@After
public void tearDown() {
if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_mapmce)) {
return;
}
if (mMceStateMachine != null) {
mMceStateMachine.doQuit();
}
}
/**
* Test that default state is STATE_CONNECTING
*/
@Test
public void testDefaultConnectingState() {
Log.i(TAG, "in testDefaultConnectingState");
Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mMceStateMachine.getState());
}
/**
* Test transition from
* STATE_CONNECTING --> (receive MSG_MAS_DISCONNECTED) --> STATE_DISCONNECTED
*/
@Test
public void testStateTransitionFromConnectingToDisconnected() {
Log.i(TAG, "in testStateTransitionFromConnectingToDisconnected");
setupSdpRecordReceipt();
Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_DISCONNECTED);
mMceStateMachine.getCurrentState().processMessage(msg);
// Wait until the message is processed and a broadcast request is sent to
// to MapClientService to change
// state from STATE_CONNECTING to STATE_DISCONNECTED
boolean result = false;
try {
result = mDisconnectedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Test that the latch reached zero; i.e., that a broadcast of state-change was received.
Assert.assertTrue(result);
// When the state reaches STATE_DISCONNECTED, MceStateMachine object is in the process of
// being dismantled; i.e., can't rely on getting its current state. That means can't
// test its current state = STATE_DISCONNECTED.
}
/**
* Test transition from STATE_CONNECTING --> (receive MSG_MAS_CONNECTED) --> STATE_CONNECTED
*/
@Test
public void testStateTransitionFromConnectingToConnected() {
Log.i(TAG, "in testStateTransitionFromConnectingToConnected");
setupSdpRecordReceipt();
Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_CONNECTED);
mMceStateMachine.getCurrentState().processMessage(msg);
// Wait until the message is processed and a broadcast request is sent to
// to MapClientService to change
// state from STATE_CONNECTING to STATE_CONNECTED
boolean result = false;
try {
result = mConnectedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Test that the latch reached zero; i.e., that a broadcast of state-change was received.
Assert.assertTrue(result);
Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState());
}
private void setupSdpRecordReceipt() {
// Setup receipt of SDP record
SdpMasRecord record = new SdpMasRecord(1, 1, 1, 1, 1, 1, "blah");
Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_SDP_DONE, record);
mMceStateMachine.getCurrentState().processMessage(msg);
}
private class FakeMapClientService extends MapClientService {
@Override
void cleanupDevice(BluetoothDevice device) {}
@Override
public void sendBroadcast(Intent intent, String receiverPermission) {
int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
Log.i(TAG, "received broadcast: prevState = " + prevState
+ ", state = " + state);
if (prevState == BluetoothProfile.STATE_CONNECTING
&& state == BluetoothProfile.STATE_CONNECTED) {
mConnectedLatch.countDown();
} else if (prevState == BluetoothProfile.STATE_CONNECTING
&& state == BluetoothProfile.STATE_DISCONNECTED) {
mDisconnectedLatch.countDown();
}
}
}
}