From 34037424899b26bf72cad8dd508370dddad87f45 Mon Sep 17 00:00:00 2001 From: Amy Date: Thu, 6 Sep 2018 13:21:08 -0700 Subject: [PATCH] Add setStreamPath handlers and do input switching according to the new active path. ag/4959473 SetStreamPath diff is based on Patch 2 Test: local test. Bug: 112477738 Change-Id: I9628330dd2cbb48c1ccba7da40ebc28a05f3a495 --- .../com/android/server/hdmi/Constants.java | 8 ++ .../hdmi/HdmiCecLocalDeviceAudioSystem.java | 45 ++++++++--- .../hdmi/HdmiCecLocalDevicePlayback.java | 3 +- .../server/hdmi/HdmiCecLocalDeviceSource.java | 75 +++++++++++++++---- .../server/hdmi/HdmiControlService.java | 6 +- 5 files changed, 110 insertions(+), 27 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 7fb738dba33ab..c35c91f7b7e56 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -333,6 +333,14 @@ final class Constants { static final String PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS = "ro.hdmi.property_hdmi_cec_never_claim_playback_logical_address"; + /** + * Property to indicate if the current device is a cec switch device. + * + *

Default is false. + */ + static final String PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH = + "ro.hdmi.property_is_device_hdmi_cec_switch"; + // Set to false to allow playback device to go to suspend mode even // when it's an active source. True by default. static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake"; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 831c437b6c9d4..7c82c793f8440 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -34,6 +34,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.hdmi.Constants.AudioCodec; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; +import java.util.HashMap; + /** * Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android * system. @@ -62,9 +64,17 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { // AVR as audio receiver. @ServiceThreadOnly private boolean mArcEstablished = false; + // If the current device uses TvInput for ARC. We assume all other inputs also use TvInput + // when ARC is using TvInput. private boolean mArcIntentUsed = SystemProperties .get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, "0").contains("tvinput"); + // Keeps the mapping (HDMI port ID to TV input URI) to keep track of the TV inputs ready to + // accept input switching request from HDMI devices. Requests for which the corresponding + // input ID is not yet registered by TV input framework need to be buffered for delayed + // processing. + private final HashMap mTvInputs = new HashMap<>(); + protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) { super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); mSystemAudioControlFeatureEnabled = true; @@ -75,6 +85,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true); mAutoTvOff = mService.readBooleanSetting( Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true); + // TODO(amyjojo): make the map ro property. + mTvInputs.put(Constants.CEC_SWITCH_HDMI1, + "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5"); + mTvInputs.put(Constants.CEC_SWITCH_HDMI2, + "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6"); + mTvInputs.put(Constants.CEC_SWITCH_HDMI3, + "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7"); } @Override @@ -594,20 +611,23 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { protected void switchInputOnReceivingNewActivePath(int physicalAddress) { int port = getLocalPortFromPhysicalAddress(physicalAddress); // Wake up if the new Active Source is the current device or under it - // or if Arc is enabled. - if ((isArcEnabled() || port >= 0) && mService.isPowerStandbyOrTransient()) { + // or if System Audio Control is enabled. + if ((isSystemAudioActivated() || port >= 0) && mService.isPowerStandbyOrTransient()) { mService.wakeUp(); } - if (isArcEnabled() && port < 0) { - // New active source will trigger arc input switching. + if (isSystemAudioActivated() && port < 0) { + // If system audio mode is on and the new active source is not under the current device, + // Will switch to ARC input. + // TODO(b/115637145): handle system aduio without ARC routeToInputFromPortId(Constants.CEC_SWITCH_ARC); - } else if (port >= 0) { - // TODO(amyjojo): Routing Control input swithcing. + } else if (mIsSwitchDevice && port >= 0) { + // If current device is a switch and the new active source is under it, + // will switch to the corresponding active path. + routeToInputFromPortId(port); } } - @Override protected void routeToInputFromPortId(int portId) { if (mArcIntentUsed) { routeToTvInputFromPortId(portId); @@ -622,14 +642,19 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { return; } // TODO(amyjojo): handle if switching to the current input - if (portId == Constants.CEC_SWITCH_HOME) { + if (portId == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) { switchToHomeTvInput(); } else if (portId == Constants.CEC_SWITCH_ARC) { switchToTvInput(SystemProperties.get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT)); - // TODO(amyjojo): check if setParameters is still needed. return; } else { - // TODO(amyjojo): map port number parsed from physical address to LocalActivePort id + String uri = mTvInputs.get(portId); + if (uri != null) { + switchToTvInput(mTvInputs.get(portId)); + } else { + HdmiLogger.debug("Port number does not match any Tv Input."); + return; + } } setLocalActivePort(portId); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index df4d09b0780d2..379cc16101973 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -266,7 +266,8 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { setIsActiveSource(physicalAddress == mService.getPhysicalAddress()); } - private void wakeUpIfActiveSource() { + @Override + protected void wakeUpIfActiveSource() { if (!mIsActiveSource) { return; } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java index fb13612e6c11b..556fa8b2773c3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java @@ -19,6 +19,7 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.IHdmiControlCallback; import android.os.RemoteException; +import android.os.SystemProperties; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -38,6 +39,11 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { // Indicate if current device is Active Source or not private boolean mIsActiveSource = false; + // Device has cec switch functionality or not. + // Default is false. + protected boolean mIsSwitchDevice = SystemProperties.getBoolean( + Constants.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false); + // Local active port number used for Routing Control. // This records the default active port or the previous valid active port. // Default is HOME input. @@ -123,19 +129,52 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { return true; } + @Override + @ServiceThreadOnly + protected boolean handleSetStreamPath(HdmiCecMessage message) { + assertRunOnServiceThread(); + int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); + // If current device is the target path, set to Active Source. + // If the path is under the current device, should switch + if (physicalAddress == mService.getPhysicalAddress() && mService.isPlaybackDevice()) { + setAndBroadcastActiveSource(message, physicalAddress); + } + switchInputOnReceivingNewActivePath(physicalAddress); + return true; + } + + // Method to switch Input with the new Active Path. + // All the devices with Switch functionality should implement this. + protected void switchInputOnReceivingNewActivePath(int physicalAddress) { + // do nothing + } + + // Active source claiming needs to be handled in the parent class + // since we decide who will be the active source when the device supports + // multiple device types in this method. + // This method should only be called when the device can be the active source. + protected void setAndBroadcastActiveSource(HdmiCecMessage message, int physicalAddress) { + // If the device has both playback and audio system logical addresses, + // playback will claim active source. Otherwise audio system will. + HdmiCecLocalDevice deviceToBeActiveSource = mService.playback(); + if (deviceToBeActiveSource == null) { + deviceToBeActiveSource = mService.audioSystem(); + } + if (this == deviceToBeActiveSource) { + ActiveSource activeSource = ActiveSource.of(mAddress, physicalAddress); + setIsActiveSource(true); + setActiveSource(activeSource); + wakeUpIfActiveSource(); + maySendActiveSource(message.getSource()); + } + } + @ServiceThreadOnly void setIsActiveSource(boolean on) { assertRunOnServiceThread(); mIsActiveSource = on; } - protected void maySendActiveSource(int dest) { - if (mIsActiveSource) { - mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( - mAddress, mService.getPhysicalAddress())); - } - } - @ServiceThreadOnly // Check if current device is the Active Source boolean isActiveSource() { @@ -143,16 +182,22 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice { return mIsActiveSource; } - // Method to switch Input with the port id. - // All the devices with Switch functionality should implement this. - protected void routeToInputFromPortId(int portId) { - // do nothing + protected void wakeUpIfActiveSource() { + if (!mIsActiveSource) { + return; + } + // Wake up the device if the power is in standby mode + if (mService.isPowerStandbyOrTransient()) { + mService.wakeUp(); + } + return; } - // Method to switch Input with the new Active Path. - // All the devices with Switch functionality should implement this. - protected void switchInputOnReceivingNewActivePath(int physicalAddress) { - // do nothing + protected void maySendActiveSource(int dest) { + if (mIsActiveSource) { + mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( + mAddress, mService.getPhysicalAddress())); + } } @VisibleForTesting diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 7e369598a9b1c..169e6efbb51a2 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -2114,11 +2114,15 @@ public class HdmiControlService extends SystemService { return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); } + boolean isPlaybackDevice() { + return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_PLAYBACK); + } + boolean isTvDeviceEnabled() { return isTvDevice() && tv() != null; } - private HdmiCecLocalDevicePlayback playback() { + protected HdmiCecLocalDevicePlayback playback() { return (HdmiCecLocalDevicePlayback) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK); }