1 /*
2  * Copyright (C) 2022 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.hdmi;
18 
19 import static android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE;
20 
21 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
22 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_ARC_PENDING;
23 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_CONNECTED;
24 import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_EARC_PENDING;
25 
26 import static com.google.common.truth.Truth.assertThat;
27 
28 import static org.mockito.ArgumentMatchers.any;
29 import static org.mockito.ArgumentMatchers.anyInt;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.Mockito.spy;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 
35 import android.content.Context;
36 import android.hardware.hdmi.HdmiDeviceInfo;
37 import android.media.AudioDescriptor;
38 import android.media.AudioDeviceAttributes;
39 import android.os.Looper;
40 import android.os.test.TestLooper;
41 import android.platform.test.annotations.Presubmit;
42 
43 import androidx.test.InstrumentationRegistry;
44 import androidx.test.filters.SmallTest;
45 
46 import org.junit.Before;
47 import org.junit.Test;
48 import org.junit.runner.RunWith;
49 import org.junit.runners.JUnit4;
50 import org.mockito.ArgumentCaptor;
51 import org.mockito.Captor;
52 import org.mockito.Mockito;
53 import org.mockito.MockitoAnnotations;
54 
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.Collections;
58 import java.util.List;
59 
60 @SmallTest
61 @Presubmit
62 @RunWith(JUnit4.class)
63 /** Tests for {@link HdmiEarcLocalDeviceTx} class. */
64 public class HdmiEarcLocalDeviceTxTest {
65 
66     private HdmiControlService mHdmiControlService;
67     private HdmiCecController mHdmiCecController;
68     private HdmiEarcLocalDevice mHdmiEarcLocalDeviceTx;
69     private FakeNativeWrapper mNativeWrapper;
70     private HdmiEarcController mHdmiEarcController;
71     private FakeEarcNativeWrapper mEarcNativeWrapper;
72     private FakePowerManagerWrapper mPowerManager;
73     private byte[] mEarcCapabilities = new byte[]{
74             0x01, 0x01, 0x1a, 0x35, 0x0f, 0x7f, 0x07, 0x15, 0x07, 0x50, 0x3d, 0x1f, (byte) 0xc0,
75             0x57, 0x06, 0x03, 0x67, 0x7e, 0x03, 0x5f, 0x7e, 0x03, 0x5f, 0x7e, 0x01, (byte) 0x83,
76             0x5f, 0x00, 0x00, 0x00, 0x00, 0x00};
77     private Looper mMyLooper;
78     private TestLooper mTestLooper = new TestLooper();
79 
80     private AudioManagerWrapper mAudioManager;
81 
82     @Captor
83     ArgumentCaptor<AudioDeviceAttributes> mAudioAttributesCaptor;
84 
85     @Before
setUp()86     public void setUp() {
87         MockitoAnnotations.initMocks(this);
88 
89         Context context = InstrumentationRegistry.getTargetContext();
90         mMyLooper = mTestLooper.getLooper();
91 
92         FakeAudioFramework audioFramework = new FakeAudioFramework();
93         mAudioManager = spy(audioFramework.getAudioManager());
94 
95         mHdmiControlService =
96                 new HdmiControlService(InstrumentationRegistry.getTargetContext(),
97                         Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
98                         mAudioManager, audioFramework.getAudioDeviceVolumeManager()) {
99                     @Override
100                     boolean isCecControlEnabled() {
101                         return true;
102                     }
103 
104                     @Override
105                     boolean isTvDevice() {
106                         return true;
107                     }
108 
109                     @Override
110                     protected void writeStringSystemProperty(String key, String value) {
111                         // do nothing
112                     }
113 
114                     @Override
115                     boolean isPowerStandby() {
116                         return false;
117                     }
118                 };
119 
120         mHdmiControlService.setIoLooper(mMyLooper);
121         mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
122         mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
123         mNativeWrapper = new FakeNativeWrapper();
124         mHdmiCecController = HdmiCecController.createWithNativeWrapper(
125                 mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
126         mHdmiControlService.setCecController(mHdmiCecController);
127         mEarcNativeWrapper = new FakeEarcNativeWrapper();
128         mHdmiEarcController = HdmiEarcController.createWithNativeWrapper(
129                 mHdmiControlService, mEarcNativeWrapper);
130         mHdmiControlService.setEarcController(mHdmiEarcController);
131         mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
132         mHdmiControlService.initService();
133         mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
134         mPowerManager = new FakePowerManagerWrapper(context);
135         mHdmiControlService.setPowerManager(mPowerManager);
136         mTestLooper.dispatchAll();
137         mHdmiControlService.initializeEarcLocalDevice(HdmiControlService.INITIATED_BY_BOOT_UP);
138         mHdmiEarcLocalDeviceTx = mHdmiControlService.getEarcLocalDevice();
139     }
140 
141     @Test
earcGetsConnected_capsReportedInTime_sad()142     public void earcGetsConnected_capsReportedInTime_sad() {
143         mHdmiEarcLocalDeviceTx.handleEarcStateChange(HDMI_EARC_STATUS_EARC_CONNECTED);
144         mTestLooper.moveTimeForward(HdmiEarcLocalDeviceTx.REPORT_CAPS_MAX_DELAY_MS - 200);
145         mTestLooper.dispatchAll();
146         mHdmiEarcLocalDeviceTx.handleEarcCapabilitiesReported(new byte[]{
147                 0x01, 0x01, 0x1a, 0x35, 0x0f, 0x7f, 0x07, 0x15, 0x07, 0x50, 0x3d, 0x1f, (byte) 0xc0,
148                 0x57, 0x06, 0x03, 0x67, 0x7e, 0x03, 0x5f, 0x7e, 0x03, 0x5f, 0x7e, 0x01, 0x00, 0x5f,
149                 0x00, 0x00, 0x00, 0x00, 0x00
150         });
151         mTestLooper.dispatchAll();
152         verify(mAudioManager, times(1)).setWiredDeviceConnectionState(
153                 mAudioAttributesCaptor.capture(), eq(1));
154         AudioDeviceAttributes attributes = mAudioAttributesCaptor.getValue();
155         List<AudioDescriptor> descriptors = attributes.getAudioDescriptors();
156         List<AudioDescriptor> expectedDescriptors = new ArrayList<AudioDescriptor>(Arrays.asList(
157                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
158                         new byte[] {15, 127, 7}),
159                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
160                         new byte[] {21, 7, 80}),
161                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
162                         new byte[] {61, 31, -64}),
163                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
164                         new byte[] {87, 6, 3}),
165                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
166                         new byte[] {103, 126, 3}),
167                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
168                         new byte[] {95, 126, 3}),
169                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
170                         new byte[] {95, 126, 1})));
171         assertThat(descriptors).isEqualTo(expectedDescriptors);
172     }
173 
174     @Test
earcGetsConnected_capsReportedInTime_sad_sadb()175     public void earcGetsConnected_capsReportedInTime_sad_sadb() {
176         mHdmiEarcLocalDeviceTx.handleEarcStateChange(HDMI_EARC_STATUS_EARC_CONNECTED);
177         mTestLooper.moveTimeForward(HdmiEarcLocalDeviceTx.REPORT_CAPS_MAX_DELAY_MS - 200);
178         mTestLooper.dispatchAll();
179         mHdmiEarcLocalDeviceTx.handleEarcCapabilitiesReported(new byte[]{
180                 0x01, 0x01, 0x1a, 0x35, 0x0f, 0x7f, 0x07, 0x15, 0x07, 0x50, 0x3d, 0x1f, (byte) 0xc0,
181                 0x57, 0x06, 0x03, 0x67, 0x7e, 0x03, 0x5f, 0x7e, 0x03, 0x5f, 0x7e, 0x01, (byte) 0x83,
182                 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00});
183         mTestLooper.dispatchAll();
184         verify(mAudioManager, times(1)).setWiredDeviceConnectionState(
185                 mAudioAttributesCaptor.capture(), eq(1));
186         AudioDeviceAttributes attributes = mAudioAttributesCaptor.getValue();
187         List<AudioDescriptor> descriptors = attributes.getAudioDescriptors();
188         List<AudioDescriptor> expectedDescriptors = new ArrayList<AudioDescriptor>(Arrays.asList(
189                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
190                         new byte[] {15, 127, 7}),
191                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
192                         new byte[] {21, 7, 80}),
193                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
194                         new byte[] {61, 31, -64}),
195                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
196                         new byte[] {87, 6, 3}),
197                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
198                         new byte[] {103, 126, 3}),
199                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
200                         new byte[] {95, 126, 3}),
201                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
202                         new byte[] {95, 126, 1}),
203                 new AudioDescriptor(AudioDescriptor.STANDARD_SADB, AUDIO_ENCAPSULATION_TYPE_NONE,
204                         new byte[] {-125, 95, 0, 0})));
205         assertThat(descriptors).isEqualTo(expectedDescriptors);
206     }
207 
208     @Test
earcGetsConnected_capsReportedInTime_sad_sadb_vsadb()209     public void earcGetsConnected_capsReportedInTime_sad_sadb_vsadb() {
210         mHdmiEarcLocalDeviceTx.handleEarcStateChange(HDMI_EARC_STATUS_EARC_CONNECTED);
211         mTestLooper.moveTimeForward(HdmiEarcLocalDeviceTx.REPORT_CAPS_MAX_DELAY_MS - 200);
212         mTestLooper.dispatchAll();
213         mHdmiEarcLocalDeviceTx.handleEarcCapabilitiesReported(new byte[]{
214                 0x01, 0x01, 0x21, 0x35, 0x5F, 0x7E, 0x03, 0x5F, 0x7E, 0x01, 0x67, 0x7E, 0x03, 0x57,
215                 0x06, 0x03, 0x3D, 0x1E, (byte) 0xC0, 0x15, 0x07, 0x50, 0x0F, 0x7F, 0x07,
216                 (byte) 0x83, 0x5F, 0x00, 0x00, (byte) 0xE6, 0x11, 0x46, (byte) 0xD0, 0x00, 0x70,
217                 0x00, 0x03, 0x01, (byte) 0x80, 0x00, (byte) 0x9D, (byte) 0xAD, (byte) 0x9E, 0x7B,
218                 0x08, (byte) 0xC1, (byte) 0xA8, 0x23, (byte) 0x9B, 0x49, 0x5C, (byte) 0xF5, 0x6B,
219                 (byte) 0xAC, 0x22, (byte) 0xC2, (byte) 0x80, 0x48, 0x67, 0x7F, 0x59, 0x1C, 0x20,
220                 0x71, 0x35, 0x25, (byte) 0x9F, 0x43, 0x70, 0x1E, 0x32, 0x15, 0x60, (byte) 0xED,
221                 (byte) 0xC8, 0x77, (byte) 0xA3, 0x24, 0x2E, (byte) 0xDA, (byte) 0x94, 0x6D, 0x35,
222                 0x34, 0x0F, 0x30, 0x62, 0x1A, 0x3B, (byte) 0xC9, 0x5A, (byte) 0xE6, (byte) 0xD8,
223                 0x22, 0x11, 0x56, (byte) 0xA6, (byte) 0x99, (byte) 0xCF, (byte) 0xE3, 0x1B,
224                 (byte) 0x88, (byte) 0xA0, 0x2A, 0x5B, 0x6C, 0x5E, 0x53, 0x01, 0x47, 0x69, 0x51,
225                 0x61, (byte) 0xC7, (byte) 0xCB, 0x1B, 0x28, 0x14, 0x23, 0x10, (byte) 0xB1, 0x34,
226                 0x5E, 0x57, (byte) 0x97, (byte) 0xB3, 0x78, 0x03, 0x79, (byte) 0x8A, (byte) 0xFE,
227                 0x1E, (byte) 0xC8, (byte) 0xAB, 0x14, 0x74, 0x73, (byte) 0xFA, (byte) 0xBB,
228                 (byte) 0xF7, 0x4E, 0x00, (byte) 0xFC, 0x5C, (byte) 0xDC, (byte) 0x8B, (byte) 0xC9,
229                 0x1E, 0x16, 0x35, (byte) 0xB1, (byte) 0x98, (byte) 0xEB, 0x2B, (byte) 0xE6,
230                 (byte) 0xFC, (byte) 0xCC, 0x3C, 0x30, 0x19, 0x40, (byte) 0xC0, 0x50, (byte) 0xF2,
231                 0x58, 0x30, 0x4B, 0x0C, 0x7A, (byte) 0xE0, (byte) 0xFF, 0x7A, 0x64, 0x78,
232                 (byte) 0xF8, 0x56, (byte) 0xF8, 0x6E, 0x72, 0x42, 0x49, 0x4E, (byte) 0xA6,
233                 (byte) 0x95, (byte) 0xF5, 0x4C, 0x4F, (byte) 0xFF, 0x7F, 0x21, (byte) 0xA2,
234                 (byte) 0x98, 0x33, (byte) 0x90, (byte) 0xFD, 0x17, 0x08, 0x13, (byte) 0xB2, 0x00,
235                 (byte) 0xA9, (byte) 0xB5, (byte) 0xBD, (byte) 0xB5, (byte) 0xC1, (byte) 0xC7, 0x45,
236                 (byte) 0xD9, (byte) 0xDC, (byte) 0x8B, 0x58, (byte) 0xB3, 0x5D, 0x5E, 0x72,
237                 (byte) 0xE6, (byte) 0x8D, (byte) 0xDD, 0x0B, 0x21, (byte) 0xF3, (byte) 0x9A,
238                 (byte) 0x8E, 0x1B, 0x79, 0x59, (byte) 0xE1, 0x3F, (byte) 0xAC, 0x24, (byte) 0xA0,
239                 (byte) 0xC8, 0x56, (byte) 0xFD, (byte) 0x85, (byte) 0x8F, 0x6A, (byte) 0x80, 0x41,
240                 (byte) 0xA8, 0x5D, 0x2C, (byte) 0xC2, 0x69, (byte) 0xA1, 0x0D, (byte) 0x82, 0x04,
241                 0x5D, (byte) 0xCA, (byte) 0xB4, (byte) 0x9F, 0x3A, 0x2D, (byte) 0xBF, 0x24});
242         mTestLooper.dispatchAll();
243         verify(mAudioManager, times(1)).setWiredDeviceConnectionState(
244                 mAudioAttributesCaptor.capture(), eq(1));
245         AudioDeviceAttributes attributes = mAudioAttributesCaptor.getValue();
246         List<AudioDescriptor> descriptors = attributes.getAudioDescriptors();
247         List<AudioDescriptor> expectedDescriptors = new ArrayList<AudioDescriptor>(Arrays.asList(
248                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
249                         new byte[] {95, 126, 3}),
250                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
251                         new byte[] {95, 126, 1}),
252                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
253                         new byte[] {103, 126, 3}),
254                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
255                         new byte[] {87, 6, 3}),
256                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
257                         new byte[] {61, 30, -64}),
258                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
259                         new byte[] {21, 7, 80}),
260                 new AudioDescriptor(AudioDescriptor.STANDARD_EDID, AUDIO_ENCAPSULATION_TYPE_NONE,
261                         new byte[] {15, 127, 7}),
262                 new AudioDescriptor(AudioDescriptor.STANDARD_SADB, AUDIO_ENCAPSULATION_TYPE_NONE,
263                         new byte[] {-125, 95, 0, 0}),
264                 new AudioDescriptor(AudioDescriptor.STANDARD_VSADB, AUDIO_ENCAPSULATION_TYPE_NONE,
265                         new byte[] {-26, 17, 70, -48, 0, 112, 0})));
266         assertThat(descriptors).isEqualTo(expectedDescriptors);
267     }
268 
269     @Test
earcGetsConnected_capsReportedTooLate()270     public void earcGetsConnected_capsReportedTooLate() {
271         mHdmiEarcLocalDeviceTx.handleEarcStateChange(HDMI_EARC_STATUS_EARC_CONNECTED);
272         mTestLooper.moveTimeForward(HdmiEarcLocalDeviceTx.REPORT_CAPS_MAX_DELAY_MS + 1);
273         mTestLooper.dispatchAll();
274         verify(mAudioManager, times(1)).setWiredDeviceConnectionState(
275                 mAudioAttributesCaptor.capture(), eq(1));
276         AudioDeviceAttributes attributes = mAudioAttributesCaptor.getValue();
277         List<AudioDescriptor> descriptors = attributes.getAudioDescriptors();
278         assertThat(descriptors).hasSize(0);
279         Mockito.clearInvocations(mAudioManager);
280 
281         mHdmiEarcLocalDeviceTx.handleEarcCapabilitiesReported(mEarcCapabilities);
282         mTestLooper.dispatchAll();
283         verify(mAudioManager, times(0)).setWiredDeviceConnectionState(any(), anyInt());
284     }
285 
286     @Test
earcGetsConnected_earcGetsDisconnectedBeforeCapsReported()287     public void earcGetsConnected_earcGetsDisconnectedBeforeCapsReported() {
288         mHdmiEarcLocalDeviceTx.handleEarcStateChange(HDMI_EARC_STATUS_EARC_CONNECTED);
289         mTestLooper.dispatchAll();
290         mHdmiEarcLocalDeviceTx.handleEarcStateChange(HDMI_EARC_STATUS_ARC_PENDING);
291         mTestLooper.dispatchAll();
292         verify(mAudioManager, times(0)).setWiredDeviceConnectionState(any(), eq(1));
293         verify(mAudioManager, times(1)).setWiredDeviceConnectionState(
294                 mAudioAttributesCaptor.capture(), eq(0));
295         AudioDeviceAttributes attributes = mAudioAttributesCaptor.getValue();
296         List<AudioDescriptor> descriptors = attributes.getAudioDescriptors();
297         assertThat(descriptors).hasSize(0);
298         Mockito.clearInvocations(mAudioManager);
299 
300         mHdmiEarcLocalDeviceTx.handleEarcCapabilitiesReported(mEarcCapabilities);
301         mTestLooper.dispatchAll();
302         verify(mAudioManager, times(0)).setWiredDeviceConnectionState(any(), anyInt());
303     }
304 
305     @Test
earcGetsConnected_earcBecomesPendingBeforeCapsReported()306     public void earcGetsConnected_earcBecomesPendingBeforeCapsReported() {
307         mHdmiEarcLocalDeviceTx.handleEarcStateChange(HDMI_EARC_STATUS_EARC_CONNECTED);
308         mTestLooper.dispatchAll();
309         mHdmiEarcLocalDeviceTx.handleEarcStateChange(HDMI_EARC_STATUS_EARC_PENDING);
310         mTestLooper.dispatchAll();
311         verify(mAudioManager, times(0)).setWiredDeviceConnectionState(any(), anyInt());
312         Mockito.clearInvocations(mAudioManager);
313 
314         mHdmiEarcLocalDeviceTx.handleEarcCapabilitiesReported(mEarcCapabilities);
315         mTestLooper.dispatchAll();
316         verify(mAudioManager, times(0)).setWiredDeviceConnectionState(any(), anyInt());
317     }
318 }
319