diff options
| author | 2024-05-16 23:26:06 +0000 | |
|---|---|---|
| committer | 2024-05-16 23:26:06 +0000 | |
| commit | bca91b92f5e7111dfacadb49e5cb19c41cac1cfd (patch) | |
| tree | f8c6df5ecbb48ccc8bd5e7764e0374a4829cba8d | |
| parent | 332d0f1ca080e7095995b46dd3b446df5ddfaa03 (diff) | |
| parent | 18d9609d45429351d5191de0775b2dc4e50b9ae6 (diff) | |
Merge "HDMI: Improve RequestActiveSourceAction" into main
4 files changed, 138 insertions, 19 deletions
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 46061a56631c..275c9309ffc9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -206,6 +206,10 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { launchDeviceDiscovery(); startQueuedActions(); if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) { + if (hasAction(RequestActiveSourceAction.class)) { + Slog.i(TAG, "RequestActiveSourceAction is in progress. Restarting."); + removeAction(RequestActiveSourceAction.class); + } addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() { @Override public void onComplete(int result) { @@ -1308,6 +1312,8 @@ public final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.sendCecCommand( HdmiCecMessageBuilder.buildActiveSource( getDeviceInfo().getLogicalAddress(), activePath)); + updateActiveSource(getDeviceInfo().getLogicalAddress(), activePath, + "HdmiCecLocalDeviceTv#launchRoutingControl()"); } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 936e8b6a28b6..05c4aa67a480 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1646,6 +1646,13 @@ public class HdmiControlService extends SystemService { case Constants.MESSAGE_ROUTING_CHANGE: case Constants.MESSAGE_SET_STREAM_PATH: case Constants.MESSAGE_TEXT_VIEW_ON: + // RequestActiveSourceAction is started after the TV finished logical address + // allocation. This action is used by the TV to get the active source from the CEC + // network. If the TV sent a source changing CEC message, this action does not have + // to continue anymore. + if (isTvDevice()) { + tv().removeAction(RequestActiveSourceAction.class); + } sendCecCommandWithRetries(command, callback); break; default: diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java index d2504164a6df..539a00db45b8 100644 --- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java +++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java @@ -21,13 +21,20 @@ import android.hardware.hdmi.IHdmiControlCallback; import android.util.Slog; /** - * Feature action that sends <Request Active Source> message and waits for <Active Source>. + * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV + * panels. + * This action has a delay before sending <Request Active Source>. This is because it should wait + * for a possible request from LauncherX and can be cancelled if an <Active Source> message was + * received or the TV switched to another input. */ public class RequestActiveSourceAction extends HdmiCecFeatureAction { private static final String TAG = "RequestActiveSourceAction"; + // State to wait for the LauncherX to call the CEC API. + private static final int STATE_WAIT_FOR_LAUNCHERX_API_CALL = 1; + // State to wait for the <Active Source> message. - private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 1; + private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 2; // Number of retries <Request Active Source> is sent if no device answers this message. private static final int MAX_SEND_RETRY_COUNT = 1; @@ -43,10 +50,12 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction { boolean start() { Slog.v(TAG, "RequestActiveSourceAction started."); - sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress())); + mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL; - mState = STATE_WAIT_FOR_ACTIVE_SOURCE; - addTimer(mState, HdmiConfig.TIMEOUT_MS); + // We wait for default timeout to allow the message triggered by the LauncherX API call to + // be sent by the TV and another default timeout in case the message has to be answered + // (e.g. TV sent a <Set Stream Path> or <Routing Change>). + addTimer(mState, HdmiConfig.TIMEOUT_MS * 2); return true; } @@ -65,13 +74,23 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction { if (mState != state) { return; } - if (mState == STATE_WAIT_FOR_ACTIVE_SOURCE) { - if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) { + + switch (mState) { + case STATE_WAIT_FOR_LAUNCHERX_API_CALL: + mState = STATE_WAIT_FOR_ACTIVE_SOURCE; sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress())); addTimer(mState, HdmiConfig.TIMEOUT_MS); - } else { - finishWithCallback(HdmiControlManager.RESULT_TIMEOUT); - } + return; + case STATE_WAIT_FOR_ACTIVE_SOURCE: + if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) { + sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress())); + addTimer(mState, HdmiConfig.TIMEOUT_MS); + } else { + finishWithCallback(HdmiControlManager.RESULT_TIMEOUT); + } + return; + default: + return; } } } 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 b5f0a527de7b..b50684bb7a25 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -26,6 +26,8 @@ import static com.android.server.hdmi.Constants.ADDR_TV; import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE; +import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; +import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON; import static com.google.common.truth.Truth.assertThat; @@ -1780,9 +1782,17 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1791,6 +1801,10 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); mTestLooper.dispatchAll(); + // Assume there was a retry and the action did not finish earlier. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); } @@ -1800,9 +1814,18 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); + + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1827,8 +1850,18 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1847,8 +1880,16 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.getHdmiCecNetwork().clearLocalDevices(); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + HdmiDeviceInfo playbackDevice = HdmiDeviceInfo.cecDeviceBuilder() .setLogicalAddress(ADDR_PLAYBACK_1) .setPhysicalAddress(0x1000) @@ -1862,6 +1903,10 @@ public class HdmiCecLocalDeviceTvTest { mHdmiControlService.getHdmiCecNetwork().addCecDevice(playbackDevice); mTestLooper.dispatchAll(); + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); + mTestLooper.dispatchAll(); + assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); mNativeWrapper.clearResultMessages(); mHdmiCecLocalDeviceTv.deviceSelect(playbackDevice.getId(), null); @@ -1874,6 +1919,41 @@ public class HdmiCecLocalDeviceTvTest { assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); } + @Test + public void onAddressAllocated_sendSourceChangingMessage_noRequestActiveSourceMessage() { + HdmiCecMessage requestActiveSource = + HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV); + HdmiCecMessage activeSourceFromTv = + HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); + HdmiCecMessage setStreamPathFromTv = + HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2000); + + // Go to standby to invalidate the active source on the local device s.t. the + // RequestActiveSourceAction will start. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + // Even if the device at the end of this path doesn't answer to this message, TV should not + // continue the RequestActiveSourceAction. + mHdmiControlService.sendCecCommand(setStreamPathFromTv); + + // Skip the LauncherX API timeout. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestActiveSource); + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + + // Assume there was a retry and the action did not finish earlier. + mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); + mTestLooper.dispatchAll(); + + assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv); + } @Test public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() { @@ -1900,7 +1980,12 @@ public class HdmiCecLocalDeviceTvTest { HdmiCecMessage activeSourceFromTv = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000); - mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_WAKE_UP_MESSAGE); + // Go to standby to invalidate the active source on the local device s.t. the + // TV will send <Active Source> when it selects its internal source. + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); mTestLooper.dispatchAll(); mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS); @@ -1930,6 +2015,8 @@ public class HdmiCecLocalDeviceTvTest { public void handleStandby_fromActiveSource_standby() { mPowerManager.setInteractive(true); mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC); + mTestLooper.dispatchAll(); + mHdmiControlService.setActiveSource(ADDR_PLAYBACK_1, 0x1000, "HdmiCecLocalDeviceTvTest"); mTestLooper.dispatchAll(); |