diff options
| author | 2020-11-02 19:35:21 +0100 | |
|---|---|---|
| committer | 2020-11-17 14:01:38 +0100 | |
| commit | 6815a7ce808695282dd7256059686c5b922a57ee (patch) | |
| tree | 9739f98844383ade01d69c544987b24aa306cd52 | |
| parent | ce2de700073f2c39282067dfe6be9d1cc7e7e4f6 (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
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(); + } } |