summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nathalie Le Clair <lcnathalie@google.com> 2020-11-02 19:35:21 +0100
committer Nathalie Le Clair <lcnathalie@google.com> 2020-11-17 14:01:38 +0100
commit6815a7ce808695282dd7256059686c5b922a57ee (patch)
tree9739f98844383ade01d69c544987b24aa306cd52
parentce2de700073f2c39282067dfe6be9d1cc7e7e4f6 (diff)
Source device to toggle and follow TV power
For source devices, toggleAndFollowTvPower() will result in 1. Toggling of the HDMI-connected display (=TV) power state over CEC 2. The source device following the TV power state Bug: 167962015 Test: atest HdmiCecLocalDevicePlaybackTest Change-Id: I1881c279143075629e590501de82fadc3e1d56d6
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlManager.java16
-rw-r--r--core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java8
-rw-r--r--core/java/android/hardware/hdmi/IHdmiControlService.aidl1
-rw-r--r--core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java4
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java50
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java73
7 files changed, 179 insertions, 6 deletions
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 500b2998e009..dec0552f6cd4 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -832,6 +832,22 @@ public final class HdmiControlManager {
}
/**
+ * For CEC source devices (OTT/STB/Audio system): toggle the power status of the HDMI-connected
+ * display and follow the display's new power status.
+ * For all other devices: no functionality.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+ public void toggleAndFollowTvPower() {
+ try {
+ mService.toggleAndFollowTvPower();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Controls whether volume control commands via HDMI CEC are enabled.
*
* <p>When disabled:
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index fab56b8cea49..202e0907f803 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -67,6 +67,11 @@ public final class HdmiControlServiceWrapper {
}
@Override
+ public void toggleAndFollowTvPower() {
+ HdmiControlServiceWrapper.this.toggleAndFollowTvPower();
+ }
+
+ @Override
public void queryDisplayStatus(IHdmiControlCallback callback) {
HdmiControlServiceWrapper.this.queryDisplayStatus(callback);
}
@@ -360,6 +365,9 @@ public final class HdmiControlServiceWrapper {
public void oneTouchPlay(IHdmiControlCallback callback) {}
/** @hide */
+ public void toggleAndFollowTvPower() {}
+
+ /** @hide */
public void queryDisplayStatus(IHdmiControlCallback callback) {}
/** @hide */
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index af9d3accd00e..6d0c688f701e 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -42,6 +42,7 @@ interface IHdmiControlService {
int[] getSupportedTypes();
HdmiDeviceInfo getActiveSource();
void oneTouchPlay(IHdmiControlCallback callback);
+ void toggleAndFollowTvPower();
void queryDisplayStatus(IHdmiControlCallback callback);
void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 4094f836ff5a..306388f30e20 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -191,6 +191,10 @@ public class HdmiAudioSystemClientTest {
}
@Override
+ public void toggleAndFollowTvPower() {
+ }
+
+ @Override
public void queryDisplayStatus(final IHdmiControlCallback callback) {
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 62b7d8f95f5a..4b589255b251 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -87,10 +87,14 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
@ServiceThreadOnly
protected void sendStandby(int deviceId) {
assertRunOnServiceThread();
-
- // Send standby to TV only for now
- int targetAddress = Constants.ADDR_TV;
- mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, targetAddress));
+ String sendStandbyOnSleep = mService.getHdmiCecConfig().getStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP);
+ if (sendStandbyOnSleep.equals(HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST)) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
+ return;
+ }
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
}
@ServiceThreadOnly
@@ -113,6 +117,44 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
}
@ServiceThreadOnly
+ void toggleAndFollowTvPower() {
+ assertRunOnServiceThread();
+ // Wake up Android framework to take over CEC control from the microprocessor.
+ mService.wakeUp();
+ mService.queryDisplayStatus(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int status) {
+ if (status == HdmiControlManager.POWER_STATUS_UNKNOWN) {
+ Slog.i(TAG, "TV power toggle: TV power status unknown");
+ sendUserControlPressedAndReleased(Constants.ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
+ // Source device remains awake.
+ } else if (status == HdmiControlManager.POWER_STATUS_ON
+ || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
+ Slog.i(TAG, "TV power toggle: turning off TV");
+ sendStandby(0 /*unused */);
+ // Source device goes to standby, to follow the toggled TV power state.
+ mService.standby();
+ } else if (status == HdmiControlManager.POWER_STATUS_STANDBY
+ || status == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
+ Slog.i(TAG, "TV power toggle: turning on TV");
+ oneTouchPlay(new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ if (result != HdmiControlManager.RESULT_SUCCESS) {
+ Slog.w(TAG, "Failed to complete One Touch Play. result=" + result);
+ sendUserControlPressedAndReleased(Constants.ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
+ }
+ }
+ });
+ // Source device remains awake, to follow the toggled TV power state.
+ }
+ }
+ });
+ }
+
+ @ServiceThreadOnly
protected void onActiveSourceLost() {
// Nothing to do.
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index dcd6c0260cff..1ff8803e0af0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1686,7 +1686,7 @@ public class HdmiControlService extends SystemService {
public void oneTouchPlay(final IHdmiControlCallback callback) {
enforceAccessPermission();
int pid = Binder.getCallingPid();
- Slog.d(TAG, "Proccess pid: " + pid + " is calling oneTouchPlay.");
+ Slog.d(TAG, "Process pid: " + pid + " is calling oneTouchPlay.");
runOnServiceThread(new Runnable() {
@Override
public void run() {
@@ -1696,6 +1696,19 @@ public class HdmiControlService extends SystemService {
}
@Override
+ public void toggleAndFollowTvPower() {
+ enforceAccessPermission();
+ int pid = Binder.getCallingPid();
+ Slog.d(TAG, "Process pid: " + pid + " is calling toggleAndFollowTvPower.");
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiControlService.this.toggleAndFollowTvPower();
+ }
+ });
+ }
+
+ @Override
public void queryDisplayStatus(final IHdmiControlCallback callback) {
enforceAccessPermission();
runOnServiceThread(new Runnable() {
@@ -2362,7 +2375,23 @@ public class HdmiControlService extends SystemService {
}
@ServiceThreadOnly
- private void queryDisplayStatus(final IHdmiControlCallback callback) {
+ @VisibleForTesting
+ protected void toggleAndFollowTvPower() {
+ assertRunOnServiceThread();
+ HdmiCecLocalDeviceSource source = playback();
+ if (source == null) {
+ source = audioSystem();
+ }
+
+ if (source == null) {
+ Slog.w(TAG, "Local source device not available");
+ return;
+ }
+ source.toggleAndFollowTvPower();
+ }
+
+ @ServiceThreadOnly
+ protected void queryDisplayStatus(final IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (!mAddressAllocated) {
mDisplayStatusCallback = callback;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 9646b5d2ec2c..8f56fc893020 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -1181,4 +1181,77 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiControlService.getActiveSource().getPhysicalAddress()).isEqualTo(
externalDevice.getPhysicalAddress());
}
+
+ @Test
+ public void toggleAndFollowTvPower_ToTv_TvStatusOn() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_TO_TV);
+ mStandby = false;
+ mHdmiControlService.toggleAndFollowTvPower();
+ HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
+ mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON);
+ assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildStandby(
+ mPlaybackLogicalAddress, ADDR_TV);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mStandby).isTrue();
+ }
+
+ @Test
+ public void toggleAndFollowTvPower_Broadcast_TvStatusOn() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.SEND_STANDBY_ON_SLEEP_BROADCAST);
+ mStandby = false;
+ mHdmiControlService.toggleAndFollowTvPower();
+ HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
+ mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_ON);
+ assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildStandby(
+ mPlaybackLogicalAddress, ADDR_BROADCAST);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mStandby).isTrue();
+ }
+
+ @Test
+ public void toggleAndFollowTvPower_TvStatusStandby() {
+ mStandby = false;
+ mHdmiControlService.toggleAndFollowTvPower();
+ HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
+ mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_STANDBY);
+ assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(mPlaybackLogicalAddress,
+ ADDR_TV);
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+ mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+ assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+ assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+ assertThat(mStandby).isFalse();
+ }
+
+ @Test
+ public void toggleAndFollowTvPower_TvStatusUnknown() {
+ mStandby = false;
+ mHdmiControlService.toggleAndFollowTvPower();
+ HdmiCecMessage tvPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(ADDR_TV,
+ mPlaybackLogicalAddress, HdmiControlManager.POWER_STATUS_UNKNOWN);
+ assertThat(mHdmiCecLocalDevicePlayback.dispatchMessage(tvPowerStatus)).isTrue();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage userControlPressed = HdmiCecMessageBuilder.buildUserControlPressed(
+ mPlaybackLogicalAddress, Constants.ADDR_TV,
+ HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
+ HdmiCecMessage userControlReleased = HdmiCecMessageBuilder.buildUserControlReleased(
+ mPlaybackLogicalAddress, Constants.ADDR_TV);
+ assertThat(mNativeWrapper.getResultMessages()).contains(userControlPressed);
+ assertThat(mNativeWrapper.getResultMessages()).contains(userControlReleased);
+ assertThat(mStandby).isFalse();
+ }
}