HDMI: Process buffered <Active Source> faster on TVs
On OTP the <Active Source> message handled by the TV would be buffered
until the connected OTT device would send a <Report PA> message.
If the device wouldn't send <Report PA> by itself, the TV will send a
<Give PA> message only when DeviceDiscoveryAction polling has been
finished and the Physical Address Stage has started.
In this patch, a device info with the known information from <Active
Source> message is added. This helps the buffered message to be
processed sooner by TvInputCallback#onInputAdded call.
Flag: EXEMPT bugfix
Bug: 354848887
Test: atest com.android.server.hdmi
Change-Id: Ibe8f066542b97e4424c02770f238f2026dac192b
Merged-In: Ibe8f066542b97e4424c02770f238f2026dac192b
(cherry picked from commit bae98b3de36aacbfdee3eba5318f9b429b36aedc)
diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
index 46b4f48..4490745 100644
--- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
+++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
@@ -17,8 +17,10 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiDeviceInfo;
+
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.List;
/**
* Buffer storage to keep incoming messages for later processing. Used to
@@ -83,6 +85,16 @@
return false;
}
+ List<HdmiCecMessage> getBufferedMessagesWithOpcode(int opcode) {
+ List<HdmiCecMessage> messages = new ArrayList<>();
+ for (HdmiCecMessage message : mBuffer) {
+ if (message.getOpcode() == opcode) {
+ messages.add(message);
+ }
+ }
+ return messages;
+ }
+
void processAllMessages() {
// Use the copied buffer.
ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<>(mBuffer);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 803f5d7..978a8c0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -205,7 +205,9 @@
resetSelectRequestBuffer();
launchDeviceDiscovery();
startQueuedActions();
- if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
+ List<HdmiCecMessage> bufferedActiveSource = mDelayedMessageBuffer
+ .getBufferedMessagesWithOpcode(Constants.MESSAGE_ACTIVE_SOURCE);
+ if (bufferedActiveSource.isEmpty()) {
if (hasAction(RequestActiveSourceAction.class)) {
Slog.i(TAG, "RequestActiveSourceAction is in progress. Restarting.");
removeAction(RequestActiveSourceAction.class);
@@ -224,9 +226,33 @@
}
}
}));
+ } else {
+ addCecDeviceForBufferedActiveSource(bufferedActiveSource.get(0));
}
}
+ // Add a new CEC device with known information from the buffered <Active Source> message. This
+ // helps TvInputCallback#onInputAdded to be called such that the message can be processed and
+ // the TV to switch to the new active input.
+ @ServiceThreadOnly
+ private void addCecDeviceForBufferedActiveSource(HdmiCecMessage bufferedActiveSource) {
+ assertRunOnServiceThread();
+ if (bufferedActiveSource == null) {
+ return;
+ }
+ int source = bufferedActiveSource.getSource();
+ int physicalAddress = HdmiUtils.twoBytesToInt(bufferedActiveSource.getParams());
+ List<Integer> deviceTypes = HdmiUtils.getTypeFromAddress(source);
+ HdmiDeviceInfo newDevice = HdmiDeviceInfo.cecDeviceBuilder()
+ .setLogicalAddress(source)
+ .setPhysicalAddress(physicalAddress)
+ .setDisplayName(HdmiUtils.getDefaultDeviceName(source))
+ .setDeviceType(deviceTypes.get(0))
+ .setVendorId(Constants.VENDOR_ID_UNKNOWN)
+ .build();
+ mService.getHdmiCecNetwork().addCecDevice(newDevice);
+ }
+
@ServiceThreadOnly
public void setSelectRequestBuffer(SelectRequestBuffer requestBuffer) {
assertRunOnServiceThread();
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 dca59fe0..128d738 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -2075,4 +2075,32 @@
// NewDeviceAction did not start and <Give OSD Name> was not sent.
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveOsdName);
}
+
+ @Test
+ public void onOneTouchPlay_wakeUp_addCecDevice() {
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+ HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED);
+ mPowerManager.setInteractive(false);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(ADDR_PLAYBACK_1,
+ mTvLogicalAddress);
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+ 0x1000);
+ assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(textViewOn)).isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(activeSource)).isEqualTo(
+ Constants.HANDLED);
+ mTestLooper.dispatchAll();
+ assertThat(mPowerManager.isInteractive()).isTrue();
+
+ // FakePowerManagerWrapper#wakeUp() doesn't broadcast Intent.ACTION_SCREEN_ON so we have to
+ // manually call this method.
+ mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+ }
}