From e26d833c4a00bc7c1c23083f28ef891703e7e385 Mon Sep 17 00:00:00 2001 From: Jinsuk Kim Date: Fri, 9 Jan 2015 08:55:41 +0900 Subject: [PATCH] CEC: Keep Playback device awake while it is the active source Alleviates the user experience issue of having to turn on the device manually when it goes to standby mode while the device occupies the display. Bug: 18882764 Change-Id: I10b239a599a310e47e3c2cb98737e4b0fdb4e435 --- .../server/hdmi/HdmiCecLocalDevice.java | 8 +++ .../hdmi/HdmiCecLocalDevicePlayback.java | 59 +++++++++++++------ .../server/hdmi/HdmiControlService.java | 22 +++++-- .../server/hdmi/OneTouchPlayAction.java | 2 +- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 1b478885e9cbf..d17e9b32bd4fa 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -201,6 +201,14 @@ abstract class HdmiCecLocalDevice { return true; } + /** + * Returns true if the local device allows the system to be put to standby. + * The default implementation returns true. + */ + protected boolean canGoToStandby() { + return true; + } + /** * Dispatch incoming message. * diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 85a1a152b9ca8..a8f6954405c33 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -19,6 +19,8 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; +import android.os.PowerManager; +import android.os.PowerManager.WakeLock; import android.os.RemoteException; import android.os.SystemProperties; import android.util.Slog; @@ -34,16 +36,16 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { private boolean mIsActiveSource = false; + // Used to keep the device awake while it is the active source. For devices that + // cannot wake up via CEC commands, this address the inconvenience of having to + // turn them on. + // Lazily initialized - should call getWakeLock() to get the instance. + private WakeLock mWakeLock; + HdmiCecLocalDevicePlayback(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_PLAYBACK); } - @Override - void init() { - super.init(); - mIsActiveSource = false; - } - @Override @ServiceThreadOnly protected void onAddressAllocated(int logicalAddress, int reason) { @@ -129,12 +131,37 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { if (connected && mService.isPowerStandbyOrTransient()) { mService.wakeUp(); } + if (!connected) { + getWakeLock().release(); + } } @ServiceThreadOnly - void markActiveSource() { + void setActiveSource(boolean on) { assertRunOnServiceThread(); - mIsActiveSource = true; + mIsActiveSource = on; + if (on) { + getWakeLock().acquire(); + HdmiLogger.debug("active source: %b. Wake lock acquired", mIsActiveSource); + } else { + getWakeLock().release(); + HdmiLogger.debug("Wake lock released"); + } + } + + @ServiceThreadOnly + private WakeLock getWakeLock() { + assertRunOnServiceThread(); + if (mWakeLock == null) { + mWakeLock = mService.getPowerManager().newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + mWakeLock.setReferenceCounted(false); + } + return mWakeLock; + } + + @Override + protected boolean canGoToStandby() { + return !getWakeLock().isHeld(); } @Override @@ -148,7 +175,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { private void mayResetActiveSource(int physicalAddress) { if (physicalAddress != mService.getPhysicalAddress()) { - mIsActiveSource = false; + setActiveSource(false); } } @@ -163,9 +190,9 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { return true; // Broadcast message. } - // Samsung model, we tested, sends and consecutively, - // Then if there is no response, it will change the input to - // the internal source. To handle this, we'll set ActiveSource aggressively. + // Samsung model we tested sends and + // in a row, and then changes the input to the internal source if there is no + // in response. To handle this, we'll set ActiveSource aggressively. @Override @ServiceThreadOnly protected boolean handleRoutingChange(HdmiCecMessage message) { @@ -185,11 +212,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { } private void maySetActiveSource(int physicalAddress) { - if (physicalAddress == mService.getPhysicalAddress()) { - mIsActiveSource = true; - } else { - mIsActiveSource = false; - } + setActiveSource(physicalAddress == mService.getPhysicalAddress()); } private void wakeUpIfActiveSource() { @@ -226,7 +249,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { mService.sendCecCommand(HdmiCecMessageBuilder.buildInactiveSource( mAddress, mService.getPhysicalAddress())); } - mIsActiveSource = false; + setActiveSource(false); checkIfPendingActionsCleared(); } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index e8cb7204e5f69..2593e2b522ce9 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -277,6 +277,9 @@ public final class HdmiControlService extends SystemService { @Nullable private TvInputManager mTvInputManager; + @Nullable + private PowerManager mPowerManager; + // Last input port before switching to the MHL port. Should switch back to this port // when the mobile device sends the request one touch play with off. // Gets invalidated if we go to other port/input. @@ -353,6 +356,7 @@ public final class HdmiControlService extends SystemService { if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mTvInputManager = (TvInputManager) getContext().getSystemService( Context.TV_INPUT_SERVICE); + mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); } } @@ -370,6 +374,10 @@ public final class HdmiControlService extends SystemService { mTvInputManager.unregisterCallback(callback); } + PowerManager getPowerManager() { + return mPowerManager; + } + /** * Called when the initialization of local devices is complete. */ @@ -1859,8 +1867,7 @@ public final class HdmiControlService extends SystemService { void wakeUp() { assertRunOnServiceThread(); mWakeUpMessageReceived = true; - PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); - pm.wakeUp(SystemClock.uptimeMillis()); + mPowerManager.wakeUp(SystemClock.uptimeMillis()); // PowerManger will send the broadcast Intent.ACTION_SCREEN_ON and after this gets // the intent, the sequence will continue at onWakeUp(). } @@ -1869,8 +1876,7 @@ public final class HdmiControlService extends SystemService { void standby() { assertRunOnServiceThread(); mStandbyMessageReceived = true; - PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); - pm.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0); + mPowerManager.goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_HDMI, 0); // PowerManger will send the broadcast Intent.ACTION_SCREEN_OFF and after this gets // the intent, the sequence will continue at onStandby(). } @@ -1896,6 +1902,7 @@ public final class HdmiControlService extends SystemService { @ServiceThreadOnly private void onStandby() { assertRunOnServiceThread(); + if (!canGoToStandby()) return; mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; invokeVendorCommandListenersOnControlStateChanged(false, HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY); @@ -1916,6 +1923,13 @@ public final class HdmiControlService extends SystemService { }); } + private boolean canGoToStandby() { + for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { + if (!device.canGoToStandby()) return false; + } + return true; + } + @ServiceThreadOnly private void onLanguageChanged(String language) { assertRunOnServiceThread(); diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java index e764a1c85c79a..a711102f84fd7 100644 --- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java +++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java @@ -82,7 +82,7 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { private void broadcastActiveSource() { sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath())); // Because only playback device can create this action, it's safe to cast. - playback().markActiveSource(); + playback().setActiveSource(true); } private void queryDevicePowerStatus() {