• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
setUp()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
tearDown()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
testDefaultConnectingState()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
testStateTransitionFromConnectingToDisconnected()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
testStateTransitionFromConnectingToConnected()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 
setupSdpRecordReceipt()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
cleanupDevice(BluetoothDevice device)175         void cleanupDevice(BluetoothDevice device) {}
176         @Override
sendBroadcast(Intent intent, String receiverPermission)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