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);
}