summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Paul Colta <donpaul@google.com> 2024-09-17 18:42:28 +0000
committer Paul Colta <donpaul@google.com> 2024-10-07 13:13:14 -0700
commit5c3a99a08181ad7d58b636d69a470ad7d8127b0f (patch)
tree27823ceeb874f5460e833058bca976cf6438403e
parentdb6e75f5946cab1ab75bd1b99b1fceef5b3c1408 (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
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java20
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java69
-rw-r--r--services/core/java/com/android/server/hdmi/PowerManagerWrapper.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakePowerManagerWrapper.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java143
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);