blob: 865eb7a3b56ddda02b5604000e1f3f0bc9415506 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.hdmi;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.SystemAudioAutoInitiationAction.RETRIES_ON_TIMEOUT;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiPortInfo;
import android.media.AudioManager;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
import android.os.test.TestLooper;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
/**
* Test for {@link SystemAudioAutoInitiationAction}.
*/
@SmallTest
@RunWith(JUnit4.class)
public class SystemAudioAutoInitiationActionTest {
private Context mContextSpy;
private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
private int mPhysicalAddress;
@Mock
private IPowerManager mIPowerManagerMock;
@Mock
private IThermalService mIThermalServiceMock;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
Looper myLooper = mTestLooper.getLooper();
PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock,
mIThermalServiceMock, new Handler(myLooper));
when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mHdmiControlService = new HdmiControlService(mContextSpy) {
@Override
AudioManager getAudioManager() {
return new AudioManager() {
@Override
public void setWiredDeviceConnectionState(
int type, int state, String address, String name) {
// Do nothing.
}
};
}
@Override
void wakeUp() {
}
@Override
boolean isPowerStandby() {
return false;
}
@Override
protected PowerManager getPowerManager() {
return powerManager;
}
@Override
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
};
mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
mHdmiCecLocalDeviceTv.init();
mHdmiControlService.setIoLooper(myLooper);
mNativeWrapper = new FakeNativeWrapper();
HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(hdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDeviceTv);
HdmiPortInfo[] hdmiPortInfos = new HdmiPortInfo[2];
hdmiPortInfos[0] =
new HdmiPortInfo(1, HdmiPortInfo.PORT_INPUT, 0x1000, true, false, false);
hdmiPortInfos[1] =
new HdmiPortInfo(2, HdmiPortInfo.PORT_INPUT, 0x2000, true, false, true);
mNativeWrapper.setPortInfo(hdmiPortInfos);
mHdmiControlService.initService();
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mPhysicalAddress = 0x0000;
mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
mTestLooper.dispatchAll();
mPhysicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress();
mNativeWrapper.clearResultMessages();
}
private void setSystemAudioSetting(boolean on) {
mHdmiCecLocalDeviceTv.setSystemAudioControlFeatureEnabled(on);
}
private void setTvHasSystemAudioChangeAction() {
mHdmiCecLocalDeviceTv.addAndStartAction(
new SystemAudioActionFromTv(mHdmiCecLocalDeviceTv, Constants.ADDR_AUDIO_SYSTEM,
true, null));
}
@Test
public void testReceiveSystemAudioMode_systemAudioOn() {
// Record that previous system audio mode is on.
setSystemAudioSetting(true);
HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
ADDR_AUDIO_SYSTEM);
mHdmiCecLocalDeviceTv.addAndStartAction(action);
mTestLooper.dispatchAll();
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
assertThat(mHdmiControlService.isSystemAudioActivated()).isTrue();
}
@Test
public void testReceiveSystemAudioMode_systemAudioOnAndImpossibleToChangeSystemAudio() {
// Turn on system audio.
setSystemAudioSetting(true);
// Impossible to change system audio mode while SystemAudioActionFromTv is in progress.
setTvHasSystemAudioChangeAction();
HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
ADDR_AUDIO_SYSTEM);
mHdmiCecLocalDeviceTv.addAndStartAction(action);
mTestLooper.dispatchAll();
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
assertThat(mHdmiControlService.isSystemAudioActivated()).isFalse();
}
@Test
public void testReceiveSystemAudioMode_systemAudioOnAndResponseOff() {
// Record that previous system audio mode is on.
setSystemAudioSetting(true);
HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
ADDR_AUDIO_SYSTEM);
mHdmiCecLocalDeviceTv.addAndStartAction(action);
mTestLooper.dispatchAll();
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, false);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
SystemAudioActionFromTv.class).get(0);
assertThat(resultingAction.mTargetAudioStatus).isTrue();
}
@Test
public void testReceiveSystemAudioMode_settingOffAndResponseOn() {
// Turn off system audio.
setSystemAudioSetting(false);
HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
ADDR_AUDIO_SYSTEM);
mHdmiCecLocalDeviceTv.addAndStartAction(action);
mTestLooper.dispatchAll();
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, true);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
SystemAudioActionFromTv.class).get(0);
assertThat(resultingAction.mTargetAudioStatus).isFalse();
}
@Test
public void testReceiveSystemAudioMode_settingOffAndResponseOff() {
// Turn off system audio.
setSystemAudioSetting(false);
HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
ADDR_AUDIO_SYSTEM);
mHdmiCecLocalDeviceTv.addAndStartAction(action);
mTestLooper.dispatchAll();
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
HdmiCecMessage reportSystemAudioMode = HdmiCecMessageBuilder.buildReportSystemAudioMode(
ADDR_AUDIO_SYSTEM, mHdmiCecLocalDeviceTv.mAddress, false);
mHdmiControlService.handleCecCommand(reportSystemAudioMode);
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isEmpty();
assertThat(mHdmiControlService.isSystemAudioActivated()).isFalse();
}
@Test
public void testTimeout_systemAudioOn_retries() {
// Turn on system audio.
setSystemAudioSetting(true);
HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
ADDR_AUDIO_SYSTEM);
mHdmiCecLocalDeviceTv.addAndStartAction(action);
mTestLooper.dispatchAll();
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
mNativeWrapper.clearResultMessages();
mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
mTestLooper.dispatchAll();
// Retry sends <Give System Audio Mode Status> again
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
}
@Test
public void testTimeout_systemAudioOn_allRetriesFail() {
boolean targetStatus = true;
// Turn on system audio.
setSystemAudioSetting(targetStatus);
HdmiCecFeatureAction action = new SystemAudioAutoInitiationAction(mHdmiCecLocalDeviceTv,
ADDR_AUDIO_SYSTEM);
mHdmiCecLocalDeviceTv.addAndStartAction(action);
mTestLooper.dispatchAll();
HdmiCecMessage giveSystemAudioModeStatus =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(
mHdmiCecLocalDeviceTv.mAddress, ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
for (int i = 0; i < RETRIES_ON_TIMEOUT; i++) {
mNativeWrapper.clearResultMessages();
// Target device doesn't respond within timeout
mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
mTestLooper.dispatchAll();
// Retry sends <Give System Audio Mode Status> again
assertThat(mNativeWrapper.getResultMessages()).contains(giveSystemAudioModeStatus);
}
// Target device doesn't respond within timeouts
mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).isNotEmpty();
SystemAudioActionFromTv resultingAction = mHdmiCecLocalDeviceTv.getActions(
SystemAudioActionFromTv.class).get(0);
assertThat(resultingAction.mTargetAudioStatus).isEqualTo(targetStatus);
}
}