BluetoothRouteManagerTest.java revision d0fe76e8e74ac515cacc492fa2c783cf2247ac05
1/*
2 * Copyright (C) 2016 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
17package com.android.server.telecom.tests;
18
19import android.bluetooth.BluetoothDevice;
20import android.content.ContentResolver;
21import android.os.Parcel;
22import android.telecom.Log;
23import android.test.suitebuilder.annotation.LargeTest;
24import android.test.suitebuilder.annotation.SmallTest;
25import android.util.Pair;
26
27import com.android.internal.os.SomeArgs;
28import com.android.server.telecom.BluetoothHeadsetProxy;
29import com.android.server.telecom.CallAudioModeStateMachine;
30import com.android.server.telecom.TelecomSystem;
31import com.android.server.telecom.Timeouts;
32import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
33import com.android.server.telecom.bluetooth.BluetoothRouteManager;
34
35import org.mockito.Mock;
36
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.List;
40import java.util.Objects;
41
42import static org.mockito.ArgumentMatchers.nullable;
43import static org.mockito.Matchers.anyInt;
44import static org.mockito.Matchers.eq;
45import static org.mockito.Mockito.doAnswer;
46import static org.mockito.Mockito.mock;
47import static org.mockito.Mockito.never;
48import static org.mockito.Mockito.reset;
49import static org.mockito.Mockito.times;
50import static org.mockito.Mockito.verify;
51import static org.mockito.Mockito.when;
52
53public class BluetoothRouteManagerTest extends StateMachineTestBase<BluetoothRouteManager> {
54    private static class BluetoothRouteTestParametersBuilder {
55        private String name;
56        private String initialBluetoothState;
57        private BluetoothDevice initialDevice;
58        private BluetoothDevice audioOnDevice;
59        private int messageType;
60        private String messageDevice;
61        private Pair<Integer, Integer> expectedListenerUpdate;
62        private int expectedBluetoothInteraction;
63        private String expectedConnectionAddress;
64        private String expectedFinalStateName;
65        private BluetoothDevice[] connectedDevices;
66
67        public BluetoothRouteTestParametersBuilder setName(String name) {
68            this.name = name;
69            return this;
70        }
71
72        public BluetoothRouteTestParametersBuilder setInitialBluetoothState(
73                String initialBluetoothState) {
74            this.initialBluetoothState = initialBluetoothState;
75            return this;
76        }
77
78        public BluetoothRouteTestParametersBuilder setInitialDevice(BluetoothDevice
79                initialDevice) {
80            this.initialDevice = initialDevice;
81            return this;
82        }
83
84        public BluetoothRouteTestParametersBuilder setMessageType(int messageType) {
85            this.messageType = messageType;
86            return this;
87        }
88
89        public BluetoothRouteTestParametersBuilder setMessageDevice(String messageDevice) {
90            this.messageDevice = messageDevice;
91            return this;
92        }
93
94        public BluetoothRouteTestParametersBuilder setExpectedListenerUpdate(Pair<Integer,
95                Integer> expectedListenerUpdate) {
96            this.expectedListenerUpdate = expectedListenerUpdate;
97            return this;
98        }
99
100        public BluetoothRouteTestParametersBuilder setExpectedBluetoothInteraction(
101                int expectedBluetoothInteraction) {
102            this.expectedBluetoothInteraction = expectedBluetoothInteraction;
103            return this;
104        }
105
106        public BluetoothRouteTestParametersBuilder setExpectedConnectionAddress(String
107                expectedConnectionAddress) {
108            this.expectedConnectionAddress = expectedConnectionAddress;
109            return this;
110        }
111
112        public BluetoothRouteTestParametersBuilder setExpectedFinalStateName(
113                String expectedFinalStateName) {
114            this.expectedFinalStateName = expectedFinalStateName;
115            return this;
116        }
117
118        public BluetoothRouteTestParametersBuilder setConnectedDevices(
119                BluetoothDevice... connectedDevices) {
120            this.connectedDevices = connectedDevices;
121            return this;
122        }
123
124        public BluetoothRouteTestParametersBuilder setAudioOnDevice(BluetoothDevice device) {
125            this.audioOnDevice = device;
126            return this;
127        }
128
129        public BluetoothRouteTestParameters build() {
130            return new BluetoothRouteTestParameters(name,
131                    initialBluetoothState,
132                    initialDevice,
133                    messageType,
134                    expectedListenerUpdate,
135                    expectedBluetoothInteraction,
136                    expectedConnectionAddress,
137                    expectedFinalStateName,
138                    connectedDevices,
139                    messageDevice,
140                    audioOnDevice);
141        }
142    }
143
144    private static class BluetoothRouteTestParameters extends TestParameters {
145        public String name;
146        public String initialBluetoothState; // One of the state names or prefixes from BRM.
147        public BluetoothDevice initialDevice; // null if we start from AudioOff
148        public BluetoothDevice audioOnDevice; // The device (if any) that is active
149        public int messageType; // Any of the commands from the state machine
150        public String messageDevice; // The device that should be specified in the message.
151        // TODO: Change this when refactoring CARSM.
152        public Pair<Integer, Integer> expectedListenerUpdate; // (old state, new state)
153        public int expectedBluetoothInteraction; // NONE, CONNECT, or DISCONNECT
154        // TODO: this will always be none for now. Change once BT changes their API.
155        public String expectedConnectionAddress; // Expected device to connect to.
156        public String expectedFinalStateName; // Expected name of the final state.
157        public BluetoothDevice[] connectedDevices; // array of connected devices
158
159        public BluetoothRouteTestParameters(String name, String initialBluetoothState,
160                BluetoothDevice initialDevice, int messageType, Pair<Integer, Integer>
161                expectedListenerUpdate, int expectedBluetoothInteraction, String
162                expectedConnectionAddress, String expectedFinalStateName,
163                BluetoothDevice[] connectedDevices, String messageDevice,
164                BluetoothDevice audioOnDevice) {
165            this.name = name;
166            this.initialBluetoothState = initialBluetoothState;
167            this.initialDevice = initialDevice;
168            this.messageType = messageType;
169            this.expectedListenerUpdate = expectedListenerUpdate;
170            this.expectedBluetoothInteraction = expectedBluetoothInteraction;
171            this.expectedConnectionAddress = expectedConnectionAddress;
172            this.expectedFinalStateName = expectedFinalStateName;
173            this.connectedDevices = connectedDevices;
174            this.messageDevice = messageDevice;
175            this.audioOnDevice = audioOnDevice;
176        }
177
178        @Override
179        public String toString() {
180            return "BluetoothRouteTestParameters{" +
181                    "name='" + name + '\'' +
182                    ", initialBluetoothState='" + initialBluetoothState + '\'' +
183                    ", initialDevice=" + initialDevice +
184                    ", messageType=" + messageType +
185                    ", messageDevice='" + messageDevice + '\'' +
186                    ", expectedListenerUpdate=" + expectedListenerUpdate +
187                    ", expectedBluetoothInteraction=" + expectedBluetoothInteraction +
188                    ", expectedConnectionAddress='" + expectedConnectionAddress + '\'' +
189                    ", expectedFinalStateName='" + expectedFinalStateName + '\'' +
190                    ", connectedDevices=" + Arrays.toString(connectedDevices) +
191                    '}';
192        }
193    }
194
195    private static final int NONE = 1;
196    private static final int CONNECT = 2;
197    private static final int DISCONNECT = 3;
198
199    @Mock private BluetoothDeviceManager mDeviceManager;
200    @Mock private BluetoothHeadsetProxy mHeadsetProxy;
201    @Mock private Timeouts.Adapter mTimeoutsAdapter;
202    @Mock private BluetoothRouteManager.BluetoothStateListener mListener;
203
204    private BluetoothDevice device1;
205    private BluetoothDevice device2;
206    private BluetoothDevice device3;
207
208    @Override
209    public void setUp() throws Exception {
210        super.setUp();
211        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
212
213        device1 = makeBluetoothDevice("00:00:00:00:00:01");
214        device2 = makeBluetoothDevice("00:00:00:00:00:02");
215        device3 = makeBluetoothDevice("00:00:00:00:00:03");
216    }
217
218    @LargeTest
219    public void testTransitions() throws Throwable {
220        List<BluetoothRouteTestParameters> testCases = generateTestCases();
221        parametrizedTestStateMachine(testCases);
222    }
223
224    @SmallTest
225    public void testConnectHfpRetryWhileNotConnected() {
226        BluetoothRouteManager sm = setupStateMachine(
227                BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
228        setupConnectedDevices(new BluetoothDevice[]{device1}, null);
229        when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
230                nullable(ContentResolver.class))).thenReturn(0L);
231        when(mHeadsetProxy.connectAudio()).thenReturn(false);
232        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, null);
233        // Wait 3 times: for the first connection attempt, the retry attempt, and once more to
234        // make sure there are only two attempts.
235        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
236        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
237        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
238        // TODO: verify address
239        verify(mHeadsetProxy, times(2)).connectAudio();
240        assertEquals(BluetoothRouteManager.AUDIO_OFF_STATE_NAME, sm.getCurrentState().getName());
241        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
242        sm.quitNow();
243    }
244
245    @SmallTest
246    public void testConnectHfpRetryWhileConnectedToAnotherDevice() {
247        BluetoothRouteManager sm = setupStateMachine(
248                BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, device1);
249        setupConnectedDevices(new BluetoothDevice[]{device1, device2}, null);
250        when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
251                nullable(ContentResolver.class))).thenReturn(0L);
252        when(mHeadsetProxy.connectAudio()).thenReturn(false);
253        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device2.getAddress());
254        // Wait 3 times: the first connection attempt is accounted for in executeRoutingAction,
255        // so wait for the retry attempt, again to make sure there are only two attempts, and
256        // once more for good luck.
257        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
258        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
259        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
260        // TODO: verify address of device2
261        verify(mHeadsetProxy, times(2)).connectAudio();
262        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
263                        + ":" + device1.getAddress(),
264                sm.getCurrentState().getName());
265        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
266        sm.quitNow();
267    }
268
269    @SmallTest
270    public void testProperFallbackOrder1() {
271        // Device 1, 2, 3 are connected in that order. Device 1 is activated, then device 2.
272        // Disconnect device 2, verify fallback to device 1. Disconnect device 1, fallback to
273        // device 3.
274        BluetoothRouteManager sm = setupStateMachine(
275                BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
276        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, null);
277        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device1.getAddress());
278        // TODO: verify address
279        verify(mHeadsetProxy, times(1)).connectAudio();
280
281        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, device1);
282        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device1.getAddress());
283
284        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device2.getAddress());
285        // TODO: verify address
286        verify(mHeadsetProxy, times(2)).connectAudio();
287
288        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, device2);
289        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device2.getAddress());
290        // Disconnect device 2
291        setupConnectedDevices(new BluetoothDevice[]{device3, device1}, null);
292        executeRoutingAction(sm, BluetoothRouteManager.LOST_DEVICE, device2.getAddress());
293        // Verify that we've fallen back to device 1
294        verify(mHeadsetProxy, times(3)).connectAudio();
295        assertEquals(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
296                        + ":" + device1.getAddress(),
297                sm.getCurrentState().getName());
298        setupConnectedDevices(new BluetoothDevice[]{device3, device1}, device1);
299        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device1.getAddress());
300        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
301                        + ":" + device1.getAddress(),
302                sm.getCurrentState().getName());
303
304        // Disconnect device 1
305        setupConnectedDevices(new BluetoothDevice[]{device3}, null);
306        executeRoutingAction(sm, BluetoothRouteManager.LOST_DEVICE, device1.getAddress());
307        // Verify that we've fallen back to device 3
308        verify(mHeadsetProxy, times(4)).connectAudio();
309        assertEquals(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
310                        + ":" + device3.getAddress(),
311                sm.getCurrentState().getName());
312        setupConnectedDevices(new BluetoothDevice[]{device3}, device3);
313        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device3.getAddress());
314        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
315                        + ":" + device3.getAddress(),
316                sm.getCurrentState().getName());
317
318        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
319        sm.quitNow();
320    }
321
322    @SmallTest
323    public void testProperFallbackOrder2() {
324        // Device 1, 2, 3 are connected in that order. Device 3 is activated.
325        // Disconnect device 3, verify fallback to device 2. Disconnect device 2, fallback to
326        // device 1.
327        BluetoothRouteManager sm = setupStateMachine(
328                BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
329        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, null);
330        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, device3.getAddress());
331        // TODO: verify address
332        verify(mHeadsetProxy, times(1)).connectAudio();
333
334        setupConnectedDevices(new BluetoothDevice[]{device3, device2, device1}, device3);
335        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device3.getAddress());
336
337        // Disconnect device 2
338        setupConnectedDevices(new BluetoothDevice[]{device2, device1}, null);
339        executeRoutingAction(sm, BluetoothRouteManager.LOST_DEVICE, device3.getAddress());
340        // Verify that we've fallen back to device 2
341        verify(mHeadsetProxy, times(2)).connectAudio();
342        assertEquals(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
343                        + ":" + device2.getAddress(),
344                sm.getCurrentState().getName());
345        setupConnectedDevices(new BluetoothDevice[]{device2, device1}, device2);
346        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device2.getAddress());
347        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
348                        + ":" + device2.getAddress(),
349                sm.getCurrentState().getName());
350
351        // Disconnect device 2
352        setupConnectedDevices(new BluetoothDevice[]{device1}, null);
353        executeRoutingAction(sm, BluetoothRouteManager.LOST_DEVICE, device2.getAddress());
354        // Verify that we've fallen back to device 1
355        verify(mHeadsetProxy, times(3)).connectAudio();
356        assertEquals(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
357                        + ":" + device1.getAddress(),
358                sm.getCurrentState().getName());
359        setupConnectedDevices(new BluetoothDevice[]{device1}, device1);
360        executeRoutingAction(sm, BluetoothRouteManager.HFP_IS_ON, device1.getAddress());
361        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
362                        + ":" + device1.getAddress(),
363                sm.getCurrentState().getName());
364
365        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
366        sm.quitNow();
367    }
368
369    @Override
370    protected void runParametrizedTestCase(TestParameters _params) {
371        BluetoothRouteTestParameters params = (BluetoothRouteTestParameters) _params;
372        BluetoothRouteManager sm = setupStateMachine(
373                params.initialBluetoothState, params.initialDevice);
374
375        setupConnectedDevices(params.connectedDevices, params.audioOnDevice);
376        executeRoutingAction(sm, params.messageType, params.messageDevice);
377
378        assertEquals(params.expectedFinalStateName, sm.getCurrentState().getName());
379
380        if (params.expectedListenerUpdate != null) {
381            verify(mListener).onBluetoothStateChange(params.expectedListenerUpdate.first,
382                    params.expectedListenerUpdate.second);
383        } else {
384            verify(mListener, never()).onBluetoothStateChange(anyInt(), anyInt());
385        }
386        // TODO: work the address in here
387        switch (params.expectedBluetoothInteraction) {
388            case NONE:
389                verify(mHeadsetProxy, never()).connectAudio();
390                verify(mHeadsetProxy, never()).disconnectAudio();
391                break;
392            case CONNECT:
393                verify(mHeadsetProxy).connectAudio();
394                verify(mHeadsetProxy, never()).disconnectAudio();
395                break;
396            case DISCONNECT:
397                verify(mHeadsetProxy, never()).connectAudio();
398                verify(mHeadsetProxy).disconnectAudio();
399                break;
400        }
401
402        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
403        sm.quitNow();
404    }
405
406    private BluetoothRouteManager setupStateMachine(String initialState,
407            BluetoothDevice initialDevice) {
408        resetMocks(true);
409        BluetoothRouteManager sm = new BluetoothRouteManager(mContext,
410                new TelecomSystem.SyncRoot() { }, mDeviceManager, mTimeoutsAdapter);
411        sm.setListener(mListener);
412        sm.setInitialStateForTesting(initialState, initialDevice);
413        waitForStateMachineActionCompletion(sm, BluetoothRouteManager.RUN_RUNNABLE);
414        resetMocks(false);
415        return sm;
416    }
417
418    private void setupConnectedDevices(BluetoothDevice[] devices, BluetoothDevice activeDevice) {
419        when(mDeviceManager.getNumConnectedDevices()).thenReturn(devices.length);
420        when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
421        if (activeDevice != null) {
422            when(mHeadsetProxy.isAudioConnected(eq(activeDevice))).thenReturn(true);
423        }
424        doAnswer(invocation -> {
425            BluetoothDevice first = getFirstExcluding(devices,
426                    (String) invocation.getArguments()[0]);
427            return first == null ? null : first.getAddress();
428        }).when(mDeviceManager).getMostRecentlyConnectedDevice(nullable(String.class));
429    }
430
431    private void executeRoutingAction(BluetoothRouteManager brm, int message, String device) {
432        SomeArgs args = SomeArgs.obtain();
433        args.arg1 = Log.createSubsession();
434        args.arg2 = device;
435        brm.sendMessage(message, args);
436        waitForStateMachineActionCompletion(brm, CallAudioModeStateMachine.RUN_RUNNABLE);
437    }
438
439    private BluetoothDevice makeBluetoothDevice(String address) {
440        Parcel p1 = Parcel.obtain();
441        p1.writeString(address);
442        p1.setDataPosition(0);
443        BluetoothDevice device = BluetoothDevice.CREATOR.createFromParcel(p1);
444        p1.recycle();
445        return device;
446    }
447
448    private void resetMocks(boolean createNewMocks) {
449        reset(mDeviceManager, mListener, mHeadsetProxy, mTimeoutsAdapter);
450        if (createNewMocks) {
451            mDeviceManager = mock(BluetoothDeviceManager.class);
452            mListener = mock(BluetoothRouteManager.BluetoothStateListener.class);
453            mHeadsetProxy = mock(BluetoothHeadsetProxy.class);
454            mTimeoutsAdapter = mock(Timeouts.Adapter.class);
455        }
456        when(mDeviceManager.getHeadsetService()).thenReturn(mHeadsetProxy);
457        when(mHeadsetProxy.connectAudio()).thenReturn(true);
458        when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
459                nullable(ContentResolver.class))).thenReturn(100000L);
460        when(mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
461                nullable(ContentResolver.class))).thenReturn(100000L);
462    }
463
464    private static BluetoothDevice getFirstExcluding(
465            BluetoothDevice[] devices, String excludeAddress) {
466        for (BluetoothDevice x : devices) {
467            if (!Objects.equals(excludeAddress, x.getAddress())) {
468                return x;
469            }
470        }
471        return null;
472    }
473
474    private List<BluetoothRouteTestParameters> generateTestCases() {
475        List<BluetoothRouteTestParameters> result = new ArrayList<>();
476        result.add(new BluetoothRouteTestParametersBuilder()
477                .setName("New device connected while audio off")
478                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
479                .setInitialDevice(null)
480                .setConnectedDevices(device1)
481                .setMessageType(BluetoothRouteManager.NEW_DEVICE_CONNECTED)
482                .setMessageDevice(device1.getAddress())
483                .setExpectedListenerUpdate(Pair.create(
484                        BluetoothRouteManager.BLUETOOTH_DISCONNECTED,
485                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED))
486                .setExpectedBluetoothInteraction(NONE)
487                .setExpectedConnectionAddress(null)
488                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
489                .build());
490
491        result.add(new BluetoothRouteTestParametersBuilder()
492                .setName("Nonspecific connection request while audio off.")
493                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
494                .setInitialDevice(null)
495                .setConnectedDevices(device2, device1)
496                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
497                .setExpectedListenerUpdate(Pair.create(
498                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED,
499                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
500                .setExpectedBluetoothInteraction(CONNECT)
501                .setExpectedConnectionAddress(device2.getAddress())
502                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
503                        + ":" + device2.getAddress())
504                .build());
505
506        result.add(new BluetoothRouteTestParametersBuilder()
507                .setName("Connection to a device succeeds after pending")
508                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
509                .setInitialDevice(device2)
510                .setAudioOnDevice(device2)
511                .setConnectedDevices(device2, device1)
512                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
513                .setMessageDevice(device2.getAddress())
514                .setExpectedListenerUpdate(Pair.create(
515                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
516                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED))
517                .setExpectedBluetoothInteraction(NONE)
518                .setExpectedConnectionAddress(null)
519                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
520                        + ":" + device2.getAddress())
521                .build());
522
523        result.add(new BluetoothRouteTestParametersBuilder()
524                .setName("Device loses HFP audio but remains connected. No fallback.")
525                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
526                .setInitialDevice(device2)
527                .setConnectedDevices(device2)
528                .setMessageType(BluetoothRouteManager.HFP_LOST)
529                .setMessageDevice(device2.getAddress())
530                .setExpectedListenerUpdate(Pair.create(
531                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
532                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED))
533                .setExpectedBluetoothInteraction(NONE)
534                .setExpectedConnectionAddress(null)
535                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
536                .build());
537
538        result.add(new BluetoothRouteTestParametersBuilder()
539                .setName("Device loses HFP audio but remains connected. Fallback.")
540                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
541                .setInitialDevice(device2)
542                .setConnectedDevices(device2, device1, device3)
543                .setMessageType(BluetoothRouteManager.HFP_LOST)
544                .setMessageDevice(device2.getAddress())
545                .setExpectedListenerUpdate(Pair.create(
546                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
547                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
548                .setExpectedBluetoothInteraction(CONNECT)
549                .setExpectedConnectionAddress(device1.getAddress())
550                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
551                        + ":" + device1.getAddress())
552                .build());
553
554        result.add(new BluetoothRouteTestParametersBuilder()
555                .setName("Switch active devices")
556                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
557                .setInitialDevice(device2)
558                .setConnectedDevices(device2, device1, device3)
559                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
560                .setMessageDevice(device3.getAddress())
561                .setExpectedListenerUpdate(Pair.create(
562                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
563                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
564                .setExpectedBluetoothInteraction(CONNECT)
565                .setExpectedConnectionAddress(device3.getAddress())
566                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
567                        + ":" + device3.getAddress())
568                .build());
569
570        result.add(new BluetoothRouteTestParametersBuilder()
571                .setName("Switch to another device before first device has connected")
572                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
573                .setInitialDevice(device2)
574                .setConnectedDevices(device2, device1, device3)
575                .setMessageType(BluetoothRouteManager.CONNECT_HFP)
576                .setMessageDevice(device3.getAddress())
577                .setExpectedListenerUpdate(Pair.create(
578                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
579                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
580                .setExpectedBluetoothInteraction(CONNECT)
581                .setExpectedConnectionAddress(device3.getAddress())
582                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
583                        + ":" + device3.getAddress())
584                .build());
585
586        result.add(new BluetoothRouteTestParametersBuilder()
587                .setName("Device gets disconnected while active. No fallback.")
588                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
589                .setInitialDevice(device2)
590                .setConnectedDevices()
591                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
592                .setMessageDevice(device2.getAddress())
593                .setExpectedListenerUpdate(Pair.create(
594                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
595                        BluetoothRouteManager.BLUETOOTH_DISCONNECTED))
596                .setExpectedBluetoothInteraction(NONE)
597                .setExpectedConnectionAddress(null)
598                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
599                .build());
600
601        result.add(new BluetoothRouteTestParametersBuilder()
602                .setName("Device gets disconnected while active. Fallback.")
603                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
604                .setInitialDevice(device2)
605                .setConnectedDevices(device3)
606                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
607                .setMessageDevice(device2.getAddress())
608                .setExpectedListenerUpdate(Pair.create(
609                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
610                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
611                .setExpectedBluetoothInteraction(CONNECT)
612                .setExpectedConnectionAddress(device3.getAddress())
613                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
614                        + ":" + device3.getAddress())
615                .build());
616
617        result.add(new BluetoothRouteTestParametersBuilder()
618                .setName("Connection to device2 times out but device 1 still connected.")
619                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
620                .setInitialDevice(device2)
621                .setConnectedDevices(device2, device1)
622                .setAudioOnDevice(device1)
623                .setMessageType(BluetoothRouteManager.CONNECTION_TIMEOUT)
624                .setExpectedListenerUpdate(Pair.create(
625                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
626                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED))
627                .setExpectedBluetoothInteraction(NONE)
628                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
629                        + ":" + device1.getAddress())
630                .build());
631
632        result.add(new BluetoothRouteTestParametersBuilder()
633                .setName("device1 somehow becomes active when device2 is still pending.")
634                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
635                .setInitialDevice(device2)
636                .setConnectedDevices(device2, device1)
637                .setAudioOnDevice(device1)
638                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
639                .setMessageDevice(device1.getAddress())
640                .setExpectedListenerUpdate(Pair.create(
641                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
642                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED))
643                .setExpectedBluetoothInteraction(NONE)
644                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
645                        + ":" + device1.getAddress())
646                .build());
647
648        result.add(new BluetoothRouteTestParametersBuilder()
649                .setName("Device gets disconnected while pending. Fallback.")
650                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
651                .setInitialDevice(device2)
652                .setConnectedDevices(device3)
653                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
654                .setMessageDevice(device2.getAddress())
655                .setExpectedListenerUpdate(Pair.create(
656                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
657                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING))
658                .setExpectedBluetoothInteraction(CONNECT)
659                .setExpectedConnectionAddress(device3.getAddress())
660                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX
661                        + ":" + device3.getAddress())
662                .build());
663
664        result.add(new BluetoothRouteTestParametersBuilder()
665                .setName("Device gets disconnected while pending. No fallback.")
666                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
667                .setInitialDevice(device2)
668                .setConnectedDevices()
669                .setMessageType(BluetoothRouteManager.LOST_DEVICE)
670                .setMessageDevice(device2.getAddress())
671                .setExpectedListenerUpdate(Pair.create(
672                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
673                        BluetoothRouteManager.BLUETOOTH_DISCONNECTED))
674                .setExpectedBluetoothInteraction(NONE)
675                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
676                .build());
677
678        result.add(new BluetoothRouteTestParametersBuilder()
679                .setName("Audio routing requests HFP disconnection while a device is active")
680                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX)
681                .setInitialDevice(device2)
682                .setConnectedDevices(device2, device3)
683                .setMessageType(BluetoothRouteManager.DISCONNECT_HFP)
684                .setExpectedListenerUpdate(Pair.create(
685                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED,
686                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED))
687                .setExpectedBluetoothInteraction(DISCONNECT)
688                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
689                .build());
690
691        result.add(new BluetoothRouteTestParametersBuilder()
692                .setName("Audio routing requests HFP disconnection while a device is pending")
693                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_CONNECTING_STATE_NAME_PREFIX)
694                .setInitialDevice(device2)
695                .setConnectedDevices(device2, device3)
696                .setMessageType(BluetoothRouteManager.DISCONNECT_HFP)
697                .setExpectedListenerUpdate(Pair.create(
698                        BluetoothRouteManager.BLUETOOTH_AUDIO_PENDING,
699                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED))
700                .setExpectedBluetoothInteraction(DISCONNECT)
701                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
702                .build());
703
704        result.add(new BluetoothRouteTestParametersBuilder()
705                .setName("Bluetooth turns itself on.")
706                .setInitialBluetoothState(BluetoothRouteManager.AUDIO_OFF_STATE_NAME)
707                .setInitialDevice(null)
708                .setConnectedDevices(device2, device3)
709                .setMessageType(BluetoothRouteManager.HFP_IS_ON)
710                .setMessageDevice(device3.getAddress())
711                .setExpectedListenerUpdate(Pair.create(
712                        BluetoothRouteManager.BLUETOOTH_DEVICE_CONNECTED,
713                        BluetoothRouteManager.BLUETOOTH_AUDIO_CONNECTED))
714                .setExpectedBluetoothInteraction(NONE)
715                .setExpectedFinalStateName(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
716                        + ":" + device3.getAddress())
717                .build());
718
719        return result;
720    }
721}
722