HDMI: Invalidate messages with short physical address

Avoid an ArrayIndexOutOfBoundsException by checking the length of the parameters array.
Broadcasted messages would be ignored and directed messages will be answered with a <Feature Abort>["Invalid Operand"].

Bug: 281636646
Test: atest HdmiControlServiceTest
Change-Id: Idc5bec0f81d028420d63e881e2fc1f6ce1428ff7
(cherry picked from commit 353b852797)
This commit is contained in:
Paul Colta
2023-05-18 16:15:17 +02:00
committed by Paul Colța
parent 5734096da8
commit 29e82d2786
2 changed files with 146 additions and 0 deletions

View File

@@ -1579,6 +1579,10 @@ public class HdmiControlService extends SystemService {
// If the device is not TV, we can't convert path to port-id, so stop here.
return true;
}
// Invalidate the physical address if parameters length is too short.
if (params.length < offset + 2) {
return false;
}
int path = HdmiUtils.twoBytesToInt(params, offset);
if (path != Constants.INVALID_PHYSICAL_ADDRESS && path == getPhysicalAddress()) {
return true;

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2023 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.SystemService.PHASE_SYSTEM_SERVICES_READY;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
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 java.util.Collections;
/**
* TV specific tests for {@link HdmiControlService} class.
*/
@SmallTest
@Presubmit
@RunWith(JUnit4.class)
public class HdmiControlServiceTvTest {
private static final String TAG = "HdmiControlServiceTvTest";
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private FakeNativeWrapper mNativeWrapper;
private HdmiEarcController mHdmiEarcController;
private FakeEarcNativeWrapper mEarcNativeWrapper;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
@Before
public void setUp() throws Exception {
Context context = InstrumentationRegistry.getTargetContext();
mMyLooper = mTestLooper.getLooper();
FakeAudioFramework audioFramework = new FakeAudioFramework();
mHdmiControlService =
new HdmiControlService(InstrumentationRegistry.getTargetContext(),
Collections.singletonList(HdmiDeviceInfo.DEVICE_TV),
audioFramework.getAudioManager(),
audioFramework.getAudioDeviceVolumeManager()) {
@Override
int pathToPortId(int path) {
return Constants.INVALID_PORT_ID + 1;
}
};
mMyLooper = mTestLooper.getLooper();
mHdmiControlService.setIoLooper(mMyLooper);
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
mHdmiControlService.setDeviceConfig(new FakeDeviceConfigWrapper());
mHdmiControlService.onBootPhase(PHASE_SYSTEM_SERVICES_READY);
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
mHdmiControlService.setCecController(mHdmiCecController);
mEarcNativeWrapper = new FakeEarcNativeWrapper();
mHdmiEarcController = HdmiEarcController.createWithNativeWrapper(
mHdmiControlService, mEarcNativeWrapper);
mHdmiControlService.setEarcController(mHdmiEarcController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(
mHdmiControlService));
mHdmiControlService.initService();
mTestLooper.dispatchAll();
}
@Test
public void onCecMessage_shortPhysicalAddress_featureAbortInvalidOperand() {
// Invalid <Inactive Source> message.
HdmiCecMessage message = HdmiUtils.buildMessage("40:9D:14");
mNativeWrapper.onCecMessage(message);
mTestLooper.dispatchAll();
HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
Constants.ADDR_TV, Constants.ADDR_PLAYBACK_1, Constants.MESSAGE_INACTIVE_SOURCE,
Constants.ABORT_INVALID_OPERAND);
assertThat(mNativeWrapper.getResultMessages()).contains(featureAbort);
}
@Test
public void handleCecCommand_shortPhysicalAddress_returnsAbortInvalidOperand() {
// Invalid <Active Source> message.
HdmiCecMessage message = HdmiUtils.buildMessage("4F:82:10");
// In case of a broadcasted message <Feature Abort> is not expected.
// See CEC 1.4b specification, 12.2 Protocol General Rules for detail.
assertThat(mHdmiControlService.handleCecCommand(message))
.isEqualTo(Constants.ABORT_INVALID_OPERAND);
}
@Test
public void test_verifyPhysicalAddresses() {
// <Routing Change>
assertThat(mHdmiControlService
.verifyPhysicalAddresses(HdmiUtils.buildMessage("0F:80:10:00:40:00"))).isTrue();
assertThat(mHdmiControlService
.verifyPhysicalAddresses(HdmiUtils.buildMessage("0F:80:10:00:40"))).isFalse();
assertThat(mHdmiControlService
.verifyPhysicalAddresses(HdmiUtils.buildMessage("0F:80:10"))).isFalse();
// <System Audio Mode Request>
assertThat(mHdmiControlService
.verifyPhysicalAddresses(HdmiUtils.buildMessage("40:70:00:00"))).isTrue();
assertThat(mHdmiControlService
.verifyPhysicalAddresses(HdmiUtils.buildMessage("40:70:00"))).isFalse();
// <Active Source>
assertThat(mHdmiControlService
.verifyPhysicalAddresses(HdmiUtils.buildMessage("4F:82:10:00"))).isTrue();
assertThat(mHdmiControlService
.verifyPhysicalAddresses(HdmiUtils.buildMessage("4F:82:10"))).isFalse();
}
}