HDMI: TV should ignore <Standby> from non-AS devices
Source devices can send the TV to sleep by sending <Standby> even when they are not the active source.
This patch avoids this issue by ignoring <Standby> messages from non-active source devices.
It is possible that the active source known by the TV panel is
not the true active source, but the chances are much lower than OTT
knowing a wrong active source. Otherwise, many other bugs would've
occured on ATV CEC implementation about TV panels knowing a wrong AS.
Bug: 322298325
Bug: 308832215
Test: atest HdmiCecLocalDeviceTvTest
Change-Id: I4dc00ebd99ddb8f9393424749d9117a9e76b32bf
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 34e75c0..46061a5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -482,6 +482,22 @@
@Override
@ServiceThreadOnly
@Constants.HandleMessageResult
+ protected int handleStandby(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+
+ // Ignore <Standby> from non-active source device.
+ if (getActiveSource().logicalAddress != message.getSource()) {
+ Slog.d(TAG, "<Standby> was not sent by the current active source, ignoring."
+ + " Current active source has logical address "
+ + getActiveSource().logicalAddress);
+ return Constants.HANDLED;
+ }
+ return super.handleStandby(message);
+ }
+
+ @Override
+ @ServiceThreadOnly
+ @Constants.HandleMessageResult
protected int handleInactiveSource(HdmiCecMessage message) {
assertRunOnServiceThread();
// Seq #10
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 5e38010..67ae998 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -170,6 +170,11 @@
}
@Override
+ boolean isPowerOnOrTransient() {
+ return true;
+ }
+
+ @Override
void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
mDeviceEventListeners.add(new DeviceEventListener(device, status));
}
@@ -1839,4 +1844,38 @@
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
}
+
+ @Test
+ public void handleStandby_fromActiveSource_standby() {
+ mPowerManager.setInteractive(true);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_1, 0x1000,
+ "HdmiCecLocalDeviceTvTest");
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1,
+ ADDR_TV);
+ assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ assertThat(mPowerManager.isInteractive()).isFalse();
+ }
+
+ @Test
+ public void handleStandby_fromNonActiveSource_noStandby() {
+ mPowerManager.setInteractive(true);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.setActiveSource(ADDR_PLAYBACK_2, 0x2000,
+ "HdmiCecLocalDeviceTvTest");
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage standbyMessage = HdmiCecMessageBuilder.buildStandby(ADDR_PLAYBACK_1,
+ ADDR_TV);
+ assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(standbyMessage))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ assertThat(mPowerManager.isInteractive()).isTrue();
+ }
}