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