blob: 29a8dada7d89497d759c6e9d0227e346c8baddd4 [file] [log] [blame]
Jean-Michel Trividc552e92019-06-24 10:39:19 -07001/*
2 * Copyright 2019 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 */
16package com.android.server.audio;
17
18import static org.mockito.Mockito.any;
19import static org.mockito.Mockito.anyInt;
20import static org.mockito.Mockito.mock;
21import static org.mockito.Mockito.spy;
22import static org.mockito.Mockito.times;
23import static org.mockito.Mockito.verify;
24import static org.mockito.Mockito.when;
25
26import android.bluetooth.BluetoothAdapter;
27import android.bluetooth.BluetoothDevice;
28import android.bluetooth.BluetoothProfile;
29import android.content.Context;
30import android.media.AudioManager;
31import android.media.AudioSystem;
32import android.util.Log;
33
34import androidx.test.InstrumentationRegistry;
35import androidx.test.filters.MediumTest;
36import androidx.test.runner.AndroidJUnit4;
37
38import org.junit.After;
39import org.junit.Assert;
40import org.junit.Before;
41import org.junit.Test;
42import org.junit.runner.RunWith;
43import org.mockito.ArgumentMatchers;
44import org.mockito.Mock;
45import org.mockito.Spy;
46
47@MediumTest
48@RunWith(AndroidJUnit4.class)
49public class AudioDeviceBrokerTest {
50
51 private static final String TAG = "AudioDeviceBrokerTest";
52 private static final int MAX_MESSAGE_HANDLING_DELAY_MS = 100;
53
54 private Context mContext;
55 // the actual class under test
56 private AudioDeviceBroker mAudioDeviceBroker;
57
58 @Mock private AudioService mMockAudioService;
59 @Spy private AudioDeviceInventory mSpyDevInventory;
60
61 private BluetoothDevice mFakeBtDevice;
62
63 @Before
64 public void setUp() throws Exception {
65 mContext = InstrumentationRegistry.getTargetContext();
66
67 mMockAudioService = mock(AudioService.class);
68 mSpyDevInventory = spy(new AudioDeviceInventory());
69 mAudioDeviceBroker = new AudioDeviceBroker(mContext, mMockAudioService, mSpyDevInventory);
70 mSpyDevInventory.setDeviceBroker(mAudioDeviceBroker);
71
72 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
73 mFakeBtDevice = adapter.getRemoteDevice("00:01:02:03:04:05");
74 Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
75 }
76
77 @After
78 public void tearDown() throws Exception { }
79
80 @Test
81 public void testSetUpAndTearDown() { }
82
83 /**
84 * Verify call to postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() for connection
85 * calls into AudioDeviceInventory with the right params
86 * @throws Exception
87 */
88 @Test
89 public void testPostA2dpDeviceConnectionChange() throws Exception {
90 Log.i(TAG, "testPostA2dpDeviceConnectionChange");
91 Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
92
93 mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
94 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
95 Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
96 verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState(
97 any(BluetoothDevice.class),
98 ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/,
99 ArgumentMatchers.eq(BluetoothProfile.A2DP) /*profile*/,
100 ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/,
101 ArgumentMatchers.eq(1) /*a2dpVolume*/
102 );
103 }
104
105 /**
106 * Verify call to postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() for
107 * connection > pause > disconnection > connection
108 * keeps the device connected
109 * @throws Exception
110 */
111 @Test
112 public void testA2dpDeviceConnectionDisconnectionConnectionChange() throws Exception {
113 Log.i(TAG, "testA2dpDeviceConnectionDisconnectionConnectionChange");
114
115 doTestConnectionDisconnectionReconnection(0);
116 }
117
118 /**
119 * Verify device disconnection and reconnection within the BECOMING_NOISY window
120 * @throws Exception
121 */
122 @Test
123 public void testA2dpDeviceReconnectionWithinBecomingNoisyDelay() throws Exception {
124 Log.i(TAG, "testA2dpDeviceReconnectionWithinBecomingNoisyDelay");
125
126 doTestConnectionDisconnectionReconnection(AudioService.BECOMING_NOISY_DELAY_MS / 2);
127 }
128
Jean-Michel Trivi261a9f92019-09-13 16:08:18 -0700129 /**
130 * Verify connecting an A2DP sink will call into AudioService to unmute media
131 */
132 @Test
133 public void testA2dpConnectionUnmutesMedia() throws Exception {
134 Log.i(TAG, "testA2dpConnectionUnmutesMedia");
135 Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
136
137 mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
138 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
139 Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
140 verify(mMockAudioService, times(1)).postAccessoryPlugMediaUnmute(
141 ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
142
143 }
144
Jean-Michel Trividc552e92019-06-24 10:39:19 -0700145 private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection)
146 throws Exception {
147 when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
148 .thenReturn(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
149 when(mMockAudioService.isInCommunication()).thenReturn(false);
150 when(mMockAudioService.hasMediaDynamicPolicy()).thenReturn(false);
151 when(mMockAudioService.hasAudioFocusUsers()).thenReturn(false);
152
153 // first connection
154 mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
155 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
156 Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
157
158 // disconnection
159 mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
160 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1);
161 if (delayAfterDisconnection > 0) {
162 Thread.sleep(delayAfterDisconnection);
163 }
164
165 // reconnection
166 mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
167 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2);
168 Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS);
169
170 // Verify disconnection has been cancelled and we're seeing two connections attempts,
171 // with the device connected at the end of the test
172 verify(mSpyDevInventory, times(2)).onSetA2dpSinkConnectionState(
173 any(BtHelper.BluetoothA2dpDeviceInfo.class),
174 ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED));
175 Assert.assertTrue("Mock device not connected",
176 mSpyDevInventory.isA2dpDeviceConnected(mFakeBtDevice));
177 }
178}