summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Shubang Lu <shubang@google.com> 2019-01-11 17:38:44 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2019-01-11 17:38:44 +0000
commit0c891e8f4e5d7e1798e564cee80694264751aa9a (patch)
treed0758acedd80ef0eca40d6f2732b057948f3d864
parent0b22084bf307cfde95db2edc2cc06e913767a551 (diff)
parent8b4a4a9056f6e6a881a9a5de9fe776689ff9b832 (diff)
Merge changes Ie889f696,Ie3d7189b,Ibeceb4c4,I08055c7e,I7b01513b, ...
* changes: playback device should handle the active source claiming when it exists. Set local active port to HOME when oneTouchPlay is triggered. Update local active source at the same time. Add ro.hdmi.property_hdmi_cec_never_assign_logical_addresses to skip the logical addresses that will not be assigned. Fix routing logic on handling routing change/info. Add routingChange and routingInformation handlers. Add setStreamPath handlers and do input switching according to the new active path. Add ActiveSouce handling logic to switch to ARC input when the new Active is not under the current device.
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java67
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecController.java20
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java26
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java160
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java139
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java8
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java64
9 files changed, 468 insertions, 28 deletions
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 1b0eb19df598..6f5a19612895 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -256,6 +256,41 @@ final class Constants {
static final int USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 1;
static final int NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON = 2;
+ // Port id to record local active port for Routing Control features
+ // They are used to map to corresponding Inputs
+ // Current interface is only implemented for specific device.
+ // Developers can add more port number and map them to corresponding inputs on demand.
+ @IntDef({
+ CEC_SWITCH_HOME,
+ CEC_SWITCH_HDMI1,
+ CEC_SWITCH_HDMI2,
+ CEC_SWITCH_HDMI3,
+ CEC_SWITCH_HDMI4,
+ CEC_SWITCH_HDMI5,
+ CEC_SWITCH_HDMI6,
+ CEC_SWITCH_HDMI7,
+ CEC_SWITCH_HDMI8,
+ CEC_SWITCH_ARC,
+ CEC_SWITCH_BLUETOOTH,
+ CEC_SWITCH_OPTICAL,
+ CEC_SWITCH_AUX
+ })
+ @interface LocalActivePort {}
+ static final int CEC_SWITCH_HOME = 0;
+ static final int CEC_SWITCH_HDMI1 = 1;
+ static final int CEC_SWITCH_HDMI2 = 2;
+ static final int CEC_SWITCH_HDMI3 = 3;
+ static final int CEC_SWITCH_HDMI4 = 4;
+ static final int CEC_SWITCH_HDMI5 = 5;
+ static final int CEC_SWITCH_HDMI6 = 6;
+ static final int CEC_SWITCH_HDMI7 = 7;
+ static final int CEC_SWITCH_HDMI8 = 8;
+ static final int CEC_SWITCH_ARC = 17;
+ static final int CEC_SWITCH_BLUETOOTH = 18;
+ static final int CEC_SWITCH_OPTICAL = 19;
+ static final int CEC_SWITCH_AUX = 20;
+ static final int CEC_SWITCH_PORT_MAX = 21;
+
static final String PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM =
"persist.sys.hdmi.addr.audiosystem";
static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
@@ -274,6 +309,13 @@ final class Constants {
static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language";
/**
+ * Property to save the ARC port id on system audio device.
+ * <p>When ARC is initiated, this port will be used to turn on ARC.
+ */
+ static final String PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT =
+ "ro.hdmi.property_sytem_audio_device_arc_port";
+
+ /**
* Property to disable muting logic in System Audio Control handling. Default is true.
*
* <p>True means enabling muting logic.
@@ -291,6 +333,24 @@ final class Constants {
static final String PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS =
"ro.hdmi.property_hdmi_cec_never_claim_playback_logical_address";
+ /**
+ * A comma separated list of logical addresses that HdmiControlService
+ * will never assign local CEC devices to.
+ *
+ * <p> This is useful when HDMI CEC hardware module can't assign multiple logical addresses
+ * in the range same range of 0-7 or 8-15.
+ */
+ static final String PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES =
+ "ro.hdmi.property_hdmi_cec_never_assign_logical_addresses";
+
+ /**
+ * Property to indicate if the current device is a cec switch device.
+ *
+ * <p> 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";
@@ -331,13 +391,6 @@ final class Constants {
"persist.sys.hdmi.property_sytem_audio_mode_audio_port";
/**
- * Property to save the ARC port id on system audio device.
- * <p>When ARC is initiated, this port will be used to turn on ARC.
- */
- static final String PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT =
- "persist.sys.hdmi.property_sytem_audio_device_arc_port";
-
- /**
* Property to indicate if a CEC audio device should forward volume keys when system audio mode
* is off.
*
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index de6cdd48fb09..e777ce8166ac 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -22,20 +22,25 @@ import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
+import android.os.SystemProperties;
import android.util.Slog;
import android.util.SparseArray;
+
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import libcore.util.EmptyArray;
+
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
-import java.util.function.Predicate;
import java.util.concurrent.ArrayBlockingQueue;
-import libcore.util.EmptyArray;
+import java.util.function.Predicate;
+
import sun.util.locale.LanguageTag;
/**
@@ -112,10 +117,18 @@ final class HdmiCecController {
private final NativeWrapper mNativeWrapperImpl;
+ /** List of logical addresses that should not be assigned to the current device.
+ *
+ * <p>Parsed from {@link Constants#PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES}
+ */
+ private final List<Integer> mNeverAssignLogicalAddresses;
+
// Private constructor. Use HdmiCecController.create().
private HdmiCecController(HdmiControlService service, NativeWrapper nativeWrapper) {
mService = service;
mNativeWrapperImpl = nativeWrapper;
+ mNeverAssignLogicalAddresses = mService.getIntList(SystemProperties.get(
+ Constants.PROPERTY_HDMI_CEC_NEVER_ASSIGN_LOGICAL_ADDRESSES));
}
/**
@@ -208,7 +221,8 @@ final class HdmiCecController {
for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
if (curAddress != Constants.ADDR_UNREGISTERED
- && deviceType == HdmiUtils.getTypeFromAddress(curAddress)) {
+ && deviceType == HdmiUtils.getTypeFromAddress(curAddress)
+ && !mNeverAssignLogicalAddresses.contains(curAddress)) {
boolean acked = false;
for (int j = 0; j < HdmiConfig.ADDRESS_ALLOCATION_RETRY; ++j) {
if (sendPollMessage(curAddress, curAddress, 1)) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index c338e21105b8..528e0a4e4940 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -30,6 +30,7 @@ import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.hdmi.Constants.LocalActivePort;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
@@ -1051,7 +1052,7 @@ abstract class HdmiCecLocalDevice {
* <p>This check assumes target address is valid.
* @param targetPhysicalAddress is the physical address of the target device
* @return
- * <p>If the target device is under the current device, return the port number of current device
+ * If the target device is under the current device, return the port number of current device
* that the target device is connected to.
*
* <p>If the target device has the same physical address as the current device, return
@@ -1085,4 +1086,27 @@ abstract class HdmiCecLocalDevice {
}
return TARGET_NOT_UNDER_LOCAL_DEVICE;
}
+
+ /** Calculates the physical address for {@code activePortId}.
+ *
+ * <p>This method assumes current device physical address is valid.
+ * <p>If the current device is already the leaf of the whole CEC system
+ * and can't have devices under it, will return its own physical address.
+ *
+ * @param activePortId is the local active port Id
+ * @return the calculated physical address of the port
+ */
+ protected int getActivePathOnSwitchFromActivePortId(@LocalActivePort int activePortId) {
+ int myPhysicalAddress = mService.getPhysicalAddress();
+ int finalMask = activePortId << 8;
+ int mask;
+ for (mask = 0x0F00; mask > 0x000F; mask >>= 4) {
+ if ((myPhysicalAddress & mask) == 0) {
+ break;
+ } else {
+ finalMask >>= 4;
+ }
+ }
+ return finalMask | myPhysicalAddress;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 20908b6d21e2..048a0e413a9b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -20,10 +20,12 @@ import static com.android.server.hdmi.Constants.PROPERTY_SYSTEM_AUDIO_CONTROL_ON
import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON;
import android.annotation.Nullable;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioSystem;
+import android.media.tv.TvContract;
import android.os.SystemProperties;
import android.provider.Settings.Global;
@@ -32,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.
@@ -60,16 +64,16 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
// AVR as audio receiver.
@ServiceThreadOnly private boolean mArcEstablished = false;
- /**
- * Return value of {@link #getLocalPortFromPhysicalAddress(int)}
- */
- private static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
- private static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
+ // 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");
- // Local active port number used for Routing Control.
- // Default 0 means HOME is the current active path. Temp solution only.
- // TODO(amyjojo): adding system constants for Atom inputs port and TIF mapping.
- private int mLocalActivePath = 0;
+ // 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<Integer, String> mTvInputs = new HashMap<>();
protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
@@ -81,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
@@ -595,4 +606,135 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
assertRunOnServiceThread();
mAutoDeviceOff = autoDeviceOff;
}
+
+ @Override
+ 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 System Audio Control is enabled.
+ if ((isSystemAudioActivated() || port >= 0) && mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ }
+
+ 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 (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);
+ }
+ }
+
+ protected void routeToInputFromPortId(int portId) {
+ if (mArcIntentUsed) {
+ routeToTvInputFromPortId(portId);
+ } else {
+ // TODO(): implement input switching for devices not using TvInput.
+ }
+ }
+
+ protected void routeToTvInputFromPortId(int portId) {
+ if (portId < 0 || portId >= Constants.CEC_SWITCH_PORT_MAX) {
+ HdmiLogger.debug("Invalid port number for Tv Input switching.");
+ return;
+ }
+ // TODO(amyjojo): handle if switching to the current input
+ 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));
+ return;
+ } else {
+ 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);
+ }
+
+ // For device to switch to specific TvInput with corresponding URI.
+ private void switchToTvInput(String uri) {
+ mService.getContext().startActivity(new Intent(Intent.ACTION_VIEW,
+ TvContract.buildChannelUriForPassthroughInput(uri))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ // For device using TvInput to switch to Home.
+ private void switchToHomeTvInput() {
+ Intent activityIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ mService.getContext().startActivity(activityIntent);
+ }
+
+ @Override
+ protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
+ int port = getLocalPortFromPhysicalAddress(physicalAddress);
+ // Routing change or information sent from switches under the current device can be ignored.
+ if (port > 0) {
+ return;
+ }
+ // When other switches route to some other devices not under the current device,
+ // check system audio mode status and do ARC switch if needed.
+ if (port < 0 && isSystemAudioActivated()) {
+ handleRoutingChangeAndInformationForSystemAudio();
+ return;
+ }
+ // When other switches route to the current device
+ // and the current device is also a switch.
+ if (port == 0) {
+ handleRoutingChangeAndInformationForSwitch(message);
+ }
+ }
+
+ // Handle the system audio(ARC) part of the logic on receiving routing change or information.
+ private void handleRoutingChangeAndInformationForSystemAudio() {
+ if (mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ }
+ // TODO(b/115637145): handle system aduio without ARC
+ routeToInputFromPortId(Constants.CEC_SWITCH_ARC);
+ }
+
+ // Handle the routing control part of the logic on receiving routing change or information.
+ private void handleRoutingChangeAndInformationForSwitch(HdmiCecMessage message) {
+ if (mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ }
+ if (getLocalActivePort() == Constants.CEC_SWITCH_HOME && mService.isPlaybackDevice()) {
+ routeToInputFromPortId(Constants.CEC_SWITCH_HOME);
+ if (mService.playback() != null) {
+ mService.playback().setAndBroadcastActiveSource(
+ message, mService.getPhysicalAddress());
+ } else {
+ setAndBroadcastActiveSource(message, mService.getPhysicalAddress());
+ }
+ return;
+ }
+
+ int routingInformationPath =
+ getActivePathOnSwitchFromActivePortId(getLocalActivePort());
+ // If current device is already the leaf of the whole HDMI system, will do nothing.
+ if (routingInformationPath == mService.getPhysicalAddress()) {
+ HdmiLogger.debug("Current device can't assign valid physical address"
+ + "to devices under it any more. "
+ + "It's physical address is " + routingInformationPath);
+ return;
+ }
+ // Otherwise will switch to the current active port and broadcast routing information.
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingInformation(
+ mAddress, routingInformationPath));
+ routeToInputFromPortId(getLocalActivePort());
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index e9dd6822366e..379cc1610197 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;
}
@@ -277,7 +278,8 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
}
}
- private void maySendActiveSource(int dest) {
+ @Override
+ protected void maySendActiveSource(int dest) {
if (mIsActiveSource) {
mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
mAddress, mService.getPhysicalAddress()));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index fed66225f839..8d55299243b2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -19,8 +19,12 @@ 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;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.hdmi.Constants.LocalActivePort;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import java.util.List;
@@ -35,6 +39,20 @@ 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.
+ // Note that we don't save active path here because for source device,
+ // new Active Source physical address might not match the local active path
+ @GuardedBy("mLock")
+ @LocalActivePort
+ private int mLocalActivePort = Constants.CEC_SWITCH_HOME;
+
protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) {
super(service, deviceType);
}
@@ -99,6 +117,7 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
setActiveSource(activeSource);
}
setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
+ switchInputOnReceivingNewActivePath(physicalAddress);
return true;
}
@@ -106,16 +125,130 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
@ServiceThreadOnly
protected boolean handleRequestActiveSource(HdmiCecMessage message) {
assertRunOnServiceThread();
- if (mIsActiveSource) {
- mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
- mAddress, mService.getPhysicalAddress()));
+ maySendActiveSource(message.getSource());
+ 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;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleRoutingChange(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
+ // if the current device is a pure playback device
+ if (!mIsSwitchDevice
+ && newPath == mService.getPhysicalAddress()
+ && mService.isPlaybackDevice()) {
+ setAndBroadcastActiveSource(message, newPath);
}
+ handleRoutingChangeAndInformation(newPath, message);
return true;
}
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleRoutingInformation(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+ // if the current device is a pure playback device
+ if (!mIsSwitchDevice
+ && physicalAddress == mService.getPhysicalAddress()
+ && mService.isPlaybackDevice()) {
+ setAndBroadcastActiveSource(message, physicalAddress);
+ }
+ handleRoutingChangeAndInformation(physicalAddress, message);
+ 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
+ }
+
+ // Source device with Switch functionality should implement this method.
+ // TODO(): decide which type will handle the routing when multi device type is supported
+ protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
+ // 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;
}
+
+ @ServiceThreadOnly
+ // Check if current device is the Active Source
+ boolean isActiveSource() {
+ assertRunOnServiceThread();
+ return mIsActiveSource;
+ }
+
+ protected void wakeUpIfActiveSource() {
+ if (!mIsActiveSource) {
+ return;
+ }
+ // Wake up the device if the power is in standby mode
+ if (mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ }
+ return;
+ }
+
+ protected void maySendActiveSource(int dest) {
+ if (mIsActiveSource) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
+ mAddress, mService.getPhysicalAddress()));
+ }
+ }
+
+ @VisibleForTesting
+ protected void setLocalActivePort(@LocalActivePort int portId) {
+ synchronized (mLock) {
+ mLocalActivePort = portId;
+ }
+ }
+
+ // To get the local active port to switch to
+ // when receivng routing change or information.
+ @LocalActivePort
+ protected int getLocalActivePort() {
+ synchronized (mLock) {
+ return mLocalActivePort;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 7e369598a9b1..903045d9b68f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -417,7 +417,7 @@ public class HdmiControlService extends SystemService {
mSettingsObserver = new SettingsObserver(mHandler);
}
- private static List<Integer> getIntList(String string) {
+ protected static List<Integer> getIntList(String string) {
ArrayList<Integer> list = new ArrayList<>();
TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
splitter.setString(string);
@@ -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);
}
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 48c71d8b738a..46611dd1b66e 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -85,7 +85,11 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction {
private void broadcastActiveSource() {
sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
// Because only source device can create this action, it's safe to cast.
- source().setIsActiveSource(true);
+ HdmiCecLocalDeviceSource source = source();
+ source.setIsActiveSource(true);
+ source.setActiveSource(getSourceAddress(), getSourcePath());
+ // Set local active port to HOME when One Touch Play.
+ source.setLocalActivePort(Constants.CEC_SWITCH_HOME);
}
private void queryDevicePowerStatus() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 7049b215083a..c442ae845d4b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -17,6 +17,7 @@ package com.android.server.hdmi;
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_TUNER_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -53,6 +54,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
private FakeNativeWrapper mNativeWrapper;
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
@@ -133,7 +135,9 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mMyLooper = mTestLooper.getLooper();
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
+ mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
mHdmiCecLocalDeviceAudioSystem.init();
+ mHdmiCecLocalDevicePlayback.init();
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController =
@@ -142,6 +146,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+ mLocalDevices.add(mHdmiCecLocalDevicePlayback);
mHdmiControlService.initPortInfo();
// No TV device interacts with AVR so system audio control won't be turned on here
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
@@ -557,4 +562,63 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
+
+ @Test
+ public void handleActiveSource_activeSourceFromTV_swithToArc() {
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+
+ ActiveSource expectedActiveSource = ActiveSource.of(ADDR_TV, 0x0000);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message)).isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource())
+ .isEqualTo(expectedActiveSource);
+ }
+
+ @Test
+ public void handleRoutingChange_currentActivePortIsHome() {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x3000, mAvrPhysicalAddress);
+
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_AUDIO_SYSTEM, mAvrPhysicalAddress);
+ ActiveSource expectedActiveSource = ActiveSource.of(ADDR_AUDIO_SYSTEM, mAvrPhysicalAddress);
+ int expectedLocalActivePort = Constants.CEC_SWITCH_HOME;
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource())
+ .isEqualTo(expectedActiveSource);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getLocalActivePort())
+ .isEqualTo(expectedLocalActivePort);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void handleRoutingInformation_currentActivePortIsHDMI1() {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x2000);
+ mHdmiCecLocalDeviceAudioSystem.setLocalActivePort(Constants.CEC_SWITCH_HDMI1);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildRoutingInformation(ADDR_AUDIO_SYSTEM, 0x2100);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingInformation(message)).isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void handleRoutingChange_homeIsActive_playbackSendActiveSource() {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000, 0x2000);
+
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, 0x2000);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
}