diff options
| author | 2024-09-17 18:42:28 +0000 | |
|---|---|---|
| committer | 2024-10-07 13:13:14 -0700 | |
| commit | 5c3a99a08181ad7d58b636d69a470ad7d8127b0f (patch) | |
| tree | 27823ceeb874f5460e833058bca976cf6438403e | |
| parent | db6e75f5946cab1ab75bd1b99b1fceef5b3c1408 (diff) | |
HDMI: Disable CEC on standby when Low Power Standby is enabled
Add the option for OEMs to set a custom property
(`persist.sys.hdmi.property_disable_cec_on_standby_in_low_energy_mode`) to allow CEC to be disabled when TV is in turned off when low energy mode is used.
Test: atest com.android.server.hdmi
Bug: 371617005
Flag: EXEMPT urgent feature
Change-Id: I65bb94bc2ddedd0863d6e5cc868a60f6ae0634fb
5 files changed, 248 insertions, 2 deletions
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index b78f8a7d8ee7..0e7d2b631833 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -509,6 +509,21 @@ final class Constants { static final String PROPERTY_STRIP_AUDIO_TV_NO_SYSTEM_AUDIO = "persist.sys.hdmi.property_strip_audio_tv_no_system_audio"; + /** + * Property that decides whether CEC should be disabled on standby when the low energy mode + * option is used. + */ + static final String PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE = + "persist.sys.hdmi.property_was_cec_disabled_on_standby_by_low_energy_mode"; + + /** + * Property that checks if CEC was disabled on standby by low energy mode. With the help of this + * property we avoid re-enabling CEC if the user explicitly disabled it, unrelated to the + * selected energy mode. + */ + static final String PROPERTY_DISABLE_CEC_ON_STANDBY_IN_LOW_ENERGY_MODE = + "persist.sys.hdmi.property_disable_cec_on_standby_in_low_energy_mode"; + static final int RECORDING_TYPE_DIGITAL_RF = 1; static final int RECORDING_TYPE_ANALOGUE_RF = 2; static final int RECORDING_TYPE_EXTERNAL_PHYSICAL_ADDRESS = 3; @@ -644,6 +659,11 @@ final class Constants { }) @interface FeatureFlag {} + /** + * Identifier key for Low energy mode. + */ + static final String KEY_LOW_ENERGY_USE = "low_energy_use"; + private Constants() { /* cannot be instantiated */ } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 8e41d18f0953..81be0baefd7a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -22,6 +22,7 @@ import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE; import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE; import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_DISABLED; import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_ENABLED; +import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_DISABLED; import static android.hardware.hdmi.HdmiControlManager.HDMI_CEC_CONTROL_ENABLED; import static android.hardware.hdmi.HdmiControlManager.POWER_CONTROL_MODE_NONE; import static android.hardware.hdmi.HdmiControlManager.SOUNDBAR_MODE_DISABLED; @@ -478,7 +479,8 @@ public class HdmiControlService extends SystemService { @Nullable private HdmiCecController mCecController; - private HdmiCecPowerStatusController mPowerStatusController; + @VisibleForTesting + protected HdmiCecPowerStatusController mPowerStatusController; @Nullable private HdmiEarcController mEarcController; @@ -3814,7 +3816,32 @@ public class HdmiControlService extends SystemService { mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON, false); if (mCecController != null) { - if (isCecControlEnabled()) { + if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) { + Slog.w(TAG, "Re-enable CEC on wake-up since it was disabled due to low energy " + + " mode."); + getHdmiCecConfig().setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HDMI_CEC_CONTROL_ENABLED); + setWasCecDisabledOnStandbyByLowEnergyMode(false); + int controlStateChangedReason = -1; + switch (wakeUpAction) { + case WAKE_UP_SCREEN_ON: + controlStateChangedReason = + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP; + break; + case WAKE_UP_BOOT_UP: + controlStateChangedReason = + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START; + break; + default: + Slog.e(TAG, "wakeUpAction " + wakeUpAction + " not defined."); + return; + + } + // Since CEC is going to be initialized by the setting value update, we must invoke + // the vendor command listeners here with the reason TV woke up. + invokeVendorCommandListenersOnControlStateChanged(true, + controlStateChangedReason); + } else if (isCecControlEnabled()) { int startReason = -1; switch (wakeUpAction) { case WAKE_UP_SCREEN_ON: @@ -3988,6 +4015,14 @@ public class HdmiControlService extends SystemService { if (isAudioSystemDevice() || !isPowerStandby()) { return; } + if (isTvDevice() && getDisableCecOnStandbyByLowEnergyMode() + && mPowerManager.isLowPowerStandbyEnabled()) { + Slog.w(TAG, "Disable CEC on standby due to low power energy mode."); + setWasCecDisabledOnStandbyByLowEnergyMode(true); + getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HDMI_CEC_CONTROL_DISABLED); + } mCecController.enableSystemCecControl(false); mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED); } @@ -5148,4 +5183,34 @@ public class HdmiControlService extends SystemService { protected boolean isHdmiControlEnhancedBehaviorFlagEnabled() { return hdmiControlEnhancedBehavior(); } + + /** + * Reads the property value that decides whether CEC should be disabled on standby when the low + * energy mode option is used. + */ + @VisibleForTesting + protected boolean getDisableCecOnStandbyByLowEnergyMode() { + return SystemProperties.getBoolean( + Constants.PROPERTY_DISABLE_CEC_ON_STANDBY_IN_LOW_ENERGY_MODE, false); + } + + /** + * Reads the property that checks if CEC was disabled on standby by low energy mode. + */ + @VisibleForTesting + protected boolean getWasCecDisabledOnStandbyByLowEnergyMode() { + return SystemProperties.getBoolean( + Constants.PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE, false); + } + + /** + * Sets the truth value of the property that checks if CEC was disabled on standby by low energy + * mode. + */ + @VisibleForTesting + protected void setWasCecDisabledOnStandbyByLowEnergyMode(boolean value) { + writeStringSystemProperty( + Constants.PROPERTY_WAS_CEC_DISABLED_ON_STANDBY_BY_LOW_ENERGY_MODE, + String.valueOf(value)); + } } diff --git a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java index 7530b3b239b4..5292cbbb9336 100644 --- a/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java +++ b/services/core/java/com/android/server/hdmi/PowerManagerWrapper.java @@ -16,6 +16,8 @@ package com.android.server.hdmi; +import static com.android.server.hdmi.Constants.KEY_LOW_ENERGY_USE; + import android.content.Context; import android.os.PowerManager; @@ -47,6 +49,12 @@ public class PowerManagerWrapper { return new DefaultWakeLockWrapper(mPowerManager.newWakeLock(levelAndFlags, tag)); } + boolean isLowPowerStandbyEnabled() { + PowerManager.LowPowerStandbyPolicy lowPowerStandbyPolicy + = mPowerManager.getLowPowerStandbyPolicy(); + return lowPowerStandbyPolicy.getIdentifier().equals(KEY_LOW_ENERGY_USE); + } + /** * "Default" wrapper for {@link PowerManager.WakeLock}, as opposed to a "Fake" wrapper for * testing - see {@link FakePowerManagerWrapper.FakeWakeLockWrapper}. diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java index 04f921f495a2..629f9683a547 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java @@ -26,6 +26,7 @@ final class FakePowerManagerWrapper extends PowerManagerWrapper { private boolean mInteractive; private WakeLockWrapper mWakeLock; private boolean mWasWakeLockInstanceCreated = false; + private boolean mIsLowPowerStandbyEnabled = false; FakePowerManagerWrapper(@NonNull Context context) { @@ -60,6 +61,15 @@ final class FakePowerManagerWrapper extends PowerManagerWrapper { } @Override + boolean isLowPowerStandbyEnabled() { + return mIsLowPowerStandbyEnabled; + } + + void setIsLowPowerStandbyEnabled(boolean isLowPowerStandbyEnabled) { + mIsLowPowerStandbyEnabled = isLowPowerStandbyEnabled; + } + + @Override WakeLockWrapper newWakeLock(int levelAndFlags, String tag) { if (mWakeLock == null) { mWakeLock = new FakeWakeLockWrapper(); 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 2d957401e6bd..935c8b8720fb 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -70,6 +70,7 @@ import org.junit.runners.JUnit4; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.concurrent.TimeUnit; @SmallTest @@ -113,6 +114,10 @@ public class HdmiCecLocalDeviceTvTest { private boolean mWokenUp; private boolean mEarcBlocksArc; private List<DeviceEventListener> mDeviceEventListeners = new ArrayList<>(); + private List<VendorCommandListener> mVendorCommandListeners = new ArrayList<>(); + private boolean mDisableCecOnStandbyByLowEnergyMode; + private boolean mWasCecDisabledOnStandbyByLowEnergyMode; + private boolean mUseHdmiCecPowerStatusController; private class DeviceEventListener { private HdmiDeviceInfo mDevice; @@ -132,6 +137,30 @@ public class HdmiCecLocalDeviceTvTest { } } + private class VendorCommandListener { + private boolean mEnabled; + private int mReason; + + VendorCommandListener(boolean enabled, int reason) { + this.mEnabled = enabled; + this.mReason = reason; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof VendorCommandListener)) { + return false; + } + VendorCommandListener other = (VendorCommandListener) obj; + return other.mReason == mReason && other.mEnabled == mEnabled; + } + + @Override + public int hashCode() { + return Objects.hash(mEnabled, mReason); + } + } + private FakeAudioFramework mAudioFramework; private AudioManagerWrapper mAudioManager; @@ -169,6 +198,9 @@ public class HdmiCecLocalDeviceTvTest { @Override boolean isPowerStandby() { + if (mUseHdmiCecPowerStatusController) { + return mPowerStatusController.isPowerStatusStandby(); + } return false; } @@ -188,6 +220,13 @@ public class HdmiCecLocalDeviceTvTest { } @Override + boolean invokeVendorCommandListenersOnControlStateChanged( + boolean enabled, int reason) { + mVendorCommandListeners.add(new VendorCommandListener(enabled, reason)); + return true; + } + + @Override protected boolean earcBlocksArcConnection() { return mEarcBlocksArc; } @@ -196,6 +235,21 @@ public class HdmiCecLocalDeviceTvTest { protected void sendBroadcastAsUser(@RequiresPermission Intent intent) { // do nothing } + + @Override + protected boolean getDisableCecOnStandbyByLowEnergyMode() { + return mDisableCecOnStandbyByLowEnergyMode; + } + + @Override + protected boolean getWasCecDisabledOnStandbyByLowEnergyMode() { + return mWasCecDisabledOnStandbyByLowEnergyMode; + } + + @Override + protected void setWasCecDisabledOnStandbyByLowEnergyMode(boolean value) { + mWasCecDisabledOnStandbyByLowEnergyMode = value; + } }; mHdmiControlService.setIoLooper(mMyLooper); @@ -241,6 +295,9 @@ public class HdmiCecLocalDeviceTvTest { mHdmiControlService.getHdmiCecConfig().setIntValue( sad, HdmiControlManager.QUERY_SAD_DISABLED); } + mWasCecDisabledOnStandbyByLowEnergyMode = false; + mDisableCecOnStandbyByLowEnergyMode = false; + mUseHdmiCecPowerStatusController = false; mNativeWrapper.clearResultMessages(); } @@ -2238,6 +2295,92 @@ public class HdmiCecLocalDeviceTvTest { assertThat(mHdmiCecLocalDeviceTv.getActions(SystemAudioActionFromTv.class)).hasSize(1); } + @Test + public void lowEnergyMode_disableCecOnStandby_reEnableOnWakeup() { + mDisableCecOnStandbyByLowEnergyMode = true; + mUseHdmiCecPowerStatusController = true; + mPowerManager.setIsLowPowerStandbyEnabled(true); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + assertTrue(mWasCecDisabledOnStandbyByLowEnergyMode); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + assertFalse(mWasCecDisabledOnStandbyByLowEnergyMode); + } + + @Test + public void lowEnergyMode_disableCecBeforeStandby_cecStaysDisabledOnWakeup() { + mDisableCecOnStandbyByLowEnergyMode = true; + mUseHdmiCecPowerStatusController = true; + mPowerManager.setIsLowPowerStandbyEnabled(true); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + mTestLooper.dispatchAll(); + + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + assertFalse(mWasCecDisabledOnStandbyByLowEnergyMode); + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + } + + @Test + public void lowEnergyMode_onWakeUp_reEnableCec_invokeVendorCommandListeners() { + mDisableCecOnStandbyByLowEnergyMode = true; + mUseHdmiCecPowerStatusController = true; + mPowerManager.setIsLowPowerStandbyEnabled(true); + VendorCommandListener vendorCommandListenerInvocationWakeup = new VendorCommandListener( + true, HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP); + VendorCommandListener vendorCommandListenerInvocationSettingChange = + new VendorCommandListener(true, + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_ENABLED); + mHdmiControlService.onStandby(STANDBY_SCREEN_OFF); + mTestLooper.dispatchAll(); + + assertEquals(mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().getIntValue( + HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED), + HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + assertTrue(mWasCecDisabledOnStandbyByLowEnergyMode); + mVendorCommandListeners.clear(); + mTestLooper.dispatchAll(); + + mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON); + mTestLooper.dispatchAll(); + + assertThat(mVendorCommandListeners.size()).isEqualTo(2); + assertTrue(mVendorCommandListeners.contains(vendorCommandListenerInvocationWakeup)); + assertTrue(mVendorCommandListeners.contains(vendorCommandListenerInvocationSettingChange)); + } + protected static class MockTvDevice extends HdmiCecLocalDeviceTv { MockTvDevice(HdmiControlService service) { super(service); |