Home | History | Annotate | Download | only in tests
      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 
     17 package com.android.server.telecom.tests;
     18 
     19 import android.bluetooth.BluetoothDevice;
     20 import android.bluetooth.BluetoothHeadset;
     21 import android.content.ContentResolver;
     22 import android.os.Parcel;
     23 import android.telecom.Log;
     24 import android.test.suitebuilder.annotation.SmallTest;
     25 
     26 import com.android.internal.os.SomeArgs;
     27 import com.android.server.telecom.BluetoothHeadsetProxy;
     28 import com.android.server.telecom.TelecomSystem;
     29 import com.android.server.telecom.Timeouts;
     30 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
     31 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
     32 
     33 import org.junit.Before;
     34 import org.junit.Test;
     35 import org.junit.runner.RunWith;
     36 import org.junit.runners.JUnit4;
     37 import org.mockito.Mock;
     38 
     39 import java.util.Arrays;
     40 import java.util.Objects;
     41 
     42 import static org.junit.Assert.assertEquals;
     43 import static org.mockito.ArgumentMatchers.nullable;
     44 import static org.mockito.Matchers.eq;
     45 import static org.mockito.Mockito.atLeast;
     46 import static org.mockito.Mockito.doAnswer;
     47 import static org.mockito.Mockito.reset;
     48 import static org.mockito.Mockito.times;
     49 import static org.mockito.Mockito.verify;
     50 import static org.mockito.Mockito.when;
     51 
     52 @RunWith(JUnit4.class)
     53 public class BluetoothRouteManagerTest extends TelecomTestCase {
     54     private static final int TEST_TIMEOUT = 1000;
     55     static final BluetoothDevice DEVICE1 = makeBluetoothDevice("00:00:00:00:00:01");
     56     static final BluetoothDevice DEVICE2 = makeBluetoothDevice("00:00:00:00:00:02");
     57     static final BluetoothDevice DEVICE3 = makeBluetoothDevice("00:00:00:00:00:03");
     58 
     59     @Mock private BluetoothDeviceManager mDeviceManager;
     60     @Mock private BluetoothHeadsetProxy mHeadsetProxy;
     61     @Mock private Timeouts.Adapter mTimeoutsAdapter;
     62     @Mock private BluetoothRouteManager.BluetoothStateListener mListener;
     63 
     64     @Override
     65     @Before
     66     public void setUp() throws Exception {
     67         super.setUp();
     68     }
     69 
     70     @SmallTest
     71     @Test
     72     public void testConnectHfpRetryWhileNotConnected() {
     73         BluetoothRouteManager sm = setupStateMachine(
     74                 BluetoothRouteManager.AUDIO_OFF_STATE_NAME, null);
     75         setupConnectedDevices(new BluetoothDevice[]{DEVICE1}, null);
     76         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
     77                 nullable(ContentResolver.class))).thenReturn(0L);
     78         when(mHeadsetProxy.connectAudio()).thenReturn(false);
     79         executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, DEVICE1.getAddress());
     80         // Wait 3 times: for the first connection attempt, the retry attempt,
     81         // the second retry, and once more to make sure there are only three attempts.
     82         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
     83         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
     84         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
     85         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
     86         verifyConnectionAttempt(DEVICE1, 3);
     87         assertEquals(BluetoothRouteManager.AUDIO_OFF_STATE_NAME, sm.getCurrentState().getName());
     88         sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
     89         sm.quitNow();
     90     }
     91 
     92     @SmallTest
     93     @Test
     94     public void testConnectHfpRetryWhileConnectedToAnotherDevice() {
     95         BluetoothRouteManager sm = setupStateMachine(
     96                 BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
     97         setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null);
     98         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
     99                 nullable(ContentResolver.class))).thenReturn(0L);
    100         when(mHeadsetProxy.connectAudio()).thenReturn(false);
    101         executeRoutingAction(sm, BluetoothRouteManager.CONNECT_HFP, DEVICE2.getAddress());
    102         // Wait 3 times: the first connection attempt is accounted for in executeRoutingAction,
    103         // so wait twice for the retry attempt, again to make sure there are only three attempts,
    104         // and once more for good luck.
    105         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
    106         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
    107         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
    108         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
    109         verifyConnectionAttempt(DEVICE2, 3);
    110         assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
    111                         + ":" + DEVICE1.getAddress(),
    112                 sm.getCurrentState().getName());
    113         sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
    114         sm.quitNow();
    115     }
    116 
    117     private BluetoothRouteManager setupStateMachine(String initialState,
    118             BluetoothDevice initialDevice) {
    119         resetMocks();
    120         BluetoothRouteManager sm = new BluetoothRouteManager(mContext,
    121                 new TelecomSystem.SyncRoot() { }, mDeviceManager, mTimeoutsAdapter);
    122         sm.setListener(mListener);
    123         sm.setInitialStateForTesting(initialState, initialDevice);
    124         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
    125         resetMocks();
    126         return sm;
    127     }
    128 
    129     private void setupConnectedDevices(BluetoothDevice[] devices, BluetoothDevice activeDevice) {
    130         when(mDeviceManager.getNumConnectedDevices()).thenReturn(devices.length);
    131         when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices));
    132         when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
    133         if (activeDevice != null) {
    134             when(mHeadsetProxy.getAudioState(eq(activeDevice)))
    135                     .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
    136         }
    137         doAnswer(invocation -> {
    138             BluetoothDevice first = getFirstExcluding(devices,
    139                     (String) invocation.getArguments()[0]);
    140             return first == null ? null : first.getAddress();
    141         }).when(mDeviceManager).getMostRecentlyConnectedDevice(nullable(String.class));
    142         for (BluetoothDevice device : devices) {
    143             when(mDeviceManager.getDeviceFromAddress(device.getAddress())).thenReturn(device);
    144         }
    145     }
    146 
    147     static void executeRoutingAction(BluetoothRouteManager brm, int message, String
    148             device) {
    149         SomeArgs args = SomeArgs.obtain();
    150         args.arg1 = Log.createSubsession();
    151         args.arg2 = device;
    152         brm.sendMessage(message, args);
    153         waitForHandlerAction(brm.getHandler(), TEST_TIMEOUT);
    154     }
    155 
    156     public static BluetoothDevice makeBluetoothDevice(String address) {
    157         Parcel p1 = Parcel.obtain();
    158         p1.writeString(address);
    159         p1.setDataPosition(0);
    160         BluetoothDevice device = BluetoothDevice.CREATOR.createFromParcel(p1);
    161         p1.recycle();
    162         return device;
    163     }
    164 
    165     private void resetMocks() {
    166         reset(mDeviceManager, mListener, mHeadsetProxy, mTimeoutsAdapter);
    167         when(mDeviceManager.getHeadsetService()).thenReturn(mHeadsetProxy);
    168         when(mHeadsetProxy.connectAudio()).thenReturn(true);
    169         when(mHeadsetProxy.setActiveDevice(nullable(BluetoothDevice.class))).thenReturn(true);
    170         when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
    171                 nullable(ContentResolver.class))).thenReturn(100000L);
    172         when(mTimeoutsAdapter.getBluetoothPendingTimeoutMillis(
    173                 nullable(ContentResolver.class))).thenReturn(100000L);
    174     }
    175 
    176     private void verifyConnectionAttempt(BluetoothDevice device, int numTimes) {
    177         verify(mHeadsetProxy, times(numTimes)).setActiveDevice(device);
    178         verify(mHeadsetProxy, atLeast(numTimes)).connectAudio();
    179     }
    180 
    181     private static BluetoothDevice getFirstExcluding(
    182             BluetoothDevice[] devices, String excludeAddress) {
    183         for (BluetoothDevice x : devices) {
    184             if (!Objects.equals(excludeAddress, x.getAddress())) {
    185                 return x;
    186             }
    187         }
    188         return null;
    189     }
    190 }
    191