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