Home | History | Annotate | Download | only in mapclient
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.bluetooth.mapclient;
     18 
     19 import static org.mockito.Mockito.*;
     20 
     21 import android.bluetooth.BluetoothAdapter;
     22 import android.bluetooth.BluetoothDevice;
     23 import android.bluetooth.BluetoothProfile;
     24 import android.bluetooth.SdpMasRecord;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.os.Handler;
     28 import android.os.Looper;
     29 import android.os.Message;
     30 import android.support.test.InstrumentationRegistry;
     31 import android.support.test.filters.MediumTest;
     32 import android.support.test.filters.Suppress;
     33 import android.support.test.runner.AndroidJUnit4;
     34 import android.util.Log;
     35 
     36 import com.android.bluetooth.R;
     37 
     38 import org.junit.After;
     39 import org.junit.Assert;
     40 import org.junit.Assume;
     41 import org.junit.Before;
     42 import org.junit.Test;
     43 import org.junit.runner.RunWith;
     44 import org.mockito.Mock;
     45 import org.mockito.MockitoAnnotations;
     46 
     47 import java.util.concurrent.CountDownLatch;
     48 import java.util.concurrent.TimeUnit;
     49 
     50 @Suppress  // TODO: enable when b/74609188 is debugged
     51 @MediumTest
     52 @RunWith(AndroidJUnit4.class)
     53 public class MapClientStateMachineTest {
     54     private static final String TAG = "MapStateMachineTest";
     55     private static final Integer TIMEOUT = 3000;
     56 
     57     private BluetoothAdapter mAdapter;
     58     private MceStateMachine mMceStateMachine = null;
     59     private BluetoothDevice mTestDevice;
     60     private Context mTargetContext;
     61 
     62     private FakeMapClientService mFakeMapClientService;
     63     private CountDownLatch mConnectedLatch = null;
     64     private CountDownLatch mDisconnectedLatch = null;
     65     private Handler mHandler;
     66 
     67     @Mock
     68     private MasClient mMockMasClient;
     69 
     70 
     71     @Before
     72     public void setUp() {
     73         MockitoAnnotations.initMocks(this);
     74         mTargetContext = InstrumentationRegistry.getTargetContext();
     75         Assume.assumeTrue("Ignore test when MapClientService is not enabled",
     76                 mTargetContext.getResources().getBoolean(R.bool.profile_supported_mapmce));
     77 
     78         // This line must be called to make sure relevant objects are initialized properly
     79         mAdapter = BluetoothAdapter.getDefaultAdapter();
     80         // Get a device for testing
     81         mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
     82 
     83         mConnectedLatch = new CountDownLatch(1);
     84         mDisconnectedLatch = new CountDownLatch(1);
     85         mFakeMapClientService = new FakeMapClientService();
     86         when(mMockMasClient.makeRequest(any(Request.class))).thenReturn(true);
     87         mMceStateMachine = new MceStateMachine(mFakeMapClientService, mTestDevice, mMockMasClient);
     88         Assert.assertNotNull(mMceStateMachine);
     89         if (Looper.myLooper() == null) {
     90             Looper.prepare();
     91         }
     92         mHandler = new Handler();
     93     }
     94 
     95     @After
     96     public void tearDown() {
     97         if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_mapmce)) {
     98             return;
     99         }
    100         if (mMceStateMachine != null) {
    101             mMceStateMachine.doQuit();
    102         }
    103     }
    104 
    105     /**
    106      * Test that default state is STATE_CONNECTING
    107      */
    108     @Test
    109     public void testDefaultConnectingState() {
    110         Log.i(TAG, "in testDefaultConnectingState");
    111         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mMceStateMachine.getState());
    112     }
    113 
    114     /**
    115      * Test transition from
    116      *      STATE_CONNECTING --> (receive MSG_MAS_DISCONNECTED) --> STATE_DISCONNECTED
    117      */
    118     @Test
    119     public void testStateTransitionFromConnectingToDisconnected() {
    120         Log.i(TAG, "in testStateTransitionFromConnectingToDisconnected");
    121         setupSdpRecordReceipt();
    122         Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_DISCONNECTED);
    123         mMceStateMachine.getCurrentState().processMessage(msg);
    124 
    125         // Wait until the message is processed and a broadcast request is sent to
    126         // to MapClientService to change
    127         // state from STATE_CONNECTING to STATE_DISCONNECTED
    128         boolean result = false;
    129         try {
    130             result = mDisconnectedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
    131         } catch (InterruptedException e) {
    132             e.printStackTrace();
    133         }
    134         // Test that the latch reached zero; i.e., that a broadcast of state-change was received.
    135         Assert.assertTrue(result);
    136         // When the state reaches STATE_DISCONNECTED, MceStateMachine object is in the process of
    137         // being dismantled; i.e., can't rely on getting its current state. That means can't
    138         // test its current state = STATE_DISCONNECTED.
    139     }
    140 
    141     /**
    142      * Test transition from STATE_CONNECTING --> (receive MSG_MAS_CONNECTED) --> STATE_CONNECTED
    143      */
    144     @Test
    145     public void testStateTransitionFromConnectingToConnected() {
    146         Log.i(TAG, "in testStateTransitionFromConnectingToConnected");
    147 
    148         setupSdpRecordReceipt();
    149         Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_CONNECTED);
    150         mMceStateMachine.getCurrentState().processMessage(msg);
    151 
    152         // Wait until the message is processed and a broadcast request is sent to
    153         // to MapClientService to change
    154         // state from STATE_CONNECTING to STATE_CONNECTED
    155         boolean result = false;
    156         try {
    157             result = mConnectedLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
    158         } catch (InterruptedException e) {
    159             e.printStackTrace();
    160         }
    161         // Test that the latch reached zero; i.e., that a broadcast of state-change was received.
    162         Assert.assertTrue(result);
    163         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState());
    164     }
    165 
    166     private void setupSdpRecordReceipt() {
    167         // Setup receipt of SDP record
    168         SdpMasRecord record = new SdpMasRecord(1, 1, 1, 1, 1, 1, "blah");
    169         Message msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_SDP_DONE, record);
    170         mMceStateMachine.getCurrentState().processMessage(msg);
    171     }
    172 
    173     private class FakeMapClientService extends MapClientService {
    174         @Override
    175         void cleanupDevice(BluetoothDevice device) {}
    176         @Override
    177         public void sendBroadcast(Intent intent, String receiverPermission) {
    178             int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
    179             int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
    180             Log.i(TAG, "received broadcast: prevState = " + prevState
    181                     + ", state = " + state);
    182             if (prevState == BluetoothProfile.STATE_CONNECTING
    183                     && state == BluetoothProfile.STATE_CONNECTED) {
    184                 mConnectedLatch.countDown();
    185             } else if (prevState == BluetoothProfile.STATE_CONNECTING
    186                     && state == BluetoothProfile.STATE_DISCONNECTED) {
    187                 mDisconnectedLatch.countDown();
    188             }
    189         }
    190     }
    191 }
    192