summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/provider/Settings.java12
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java27
-rw-r--r--services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java3
-rwxr-xr-xservices/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java29
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java118
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java63
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java108
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java24
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java114
-rw-r--r--services/core/java/com/android/server/hdmi/OneTouchPlayAction.java10
-rw-r--r--services/core/java/com/android/server/hdmi/SelectRequestBuffer.java14
-rw-r--r--services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java163
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java56
18 files changed, 545 insertions, 267 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4649776b3d50..574ccaf8d1cc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9369,8 +9369,7 @@ public final class Settings {
"hdmi_control_auto_wakeup_enabled";
/**
- * Whether TV or Audio System will also turn off other CEC devices when it goes to standby
- * mode.
+ * Whether TV will also turn off other CEC devices when it goes to standby mode.
* (0 = false, 1 = true)
*
* @hide
@@ -9379,15 +9378,6 @@ public final class Settings {
"hdmi_control_auto_device_off_enabled";
/**
- * Whether Audio System will also turn off TV when it goes to standby mode.
- * (0 = false, 1 = true)
- *
- * @hide
- */
- public static final String HDMI_CONTROL_AUTO_TV_OFF_ENABLED =
- "hdmi_control_auto_tv_off_enabled";
-
- /**
* If <b>true</b>, enables out-of-the-box execution for priv apps.
* Default: false
* Values: 0 = false, 1 = true
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 4e405cab19b8..1ed5ce4a3929 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -271,7 +271,6 @@ public class SettingsBackupTest {
Settings.Global.GNSS_SATELLITE_BLACKLIST,
Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
- Settings.Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED,
Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
Settings.Global.HDMI_CONTROL_ENABLED,
Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
diff --git a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
index ed17de5e8c35..18d328d3f971 100644
--- a/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/ArcInitiationActionFromAvr.java
@@ -21,14 +21,15 @@ import android.hardware.tv.cec.V1_0.SendMessageResult;
* Feature action that handles Audio Return Channel initiated by AVR devices.
*/
public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction {
- // TODO(shubang): add tests
-
// State in which waits for ARC response.
private static final int STATE_WAITING_FOR_INITIATE_ARC_RESPONSE = 1;
private static final int STATE_ARC_INITIATED = 2;
// the required maximum response time specified in CEC 9.2
private static final int TIMEOUT_MS = 1000;
+ private static final int MAX_RETRY_COUNT = 5;
+
+ private int mSendRequestActiveSourceRetryCount = 0;
ArcInitiationActionFromAvr(HdmiCecLocalDevice source) {
super(source);
@@ -56,7 +57,12 @@ public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction {
return true;
case Constants.MESSAGE_REPORT_ARC_INITIATED:
mState = STATE_ARC_INITIATED;
- finish();
+ if (audioSystem().getActiveSource().physicalAddress != getSourcePath()
+ && audioSystem().isSystemAudioActivated()) {
+ sendRequestActiveSource();
+ } else {
+ finish();
+ }
return true;
}
return false;
@@ -91,4 +97,19 @@ public class ArcInitiationActionFromAvr extends HdmiCecFeatureAction {
finish();
}
+ protected void sendRequestActiveSource() {
+ sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()),
+ result -> {
+ if (result != SendMessageResult.SUCCESS) {
+ if (mSendRequestActiveSourceRetryCount < MAX_RETRY_COUNT) {
+ mSendRequestActiveSourceRetryCount++;
+ sendRequestActiveSource();
+ } else {
+ finish();
+ }
+ } else {
+ finish();
+ }
+ });
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
index 7e7332180f8a..eb7c0cd61132 100644
--- a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
@@ -50,6 +50,9 @@ public class ArcTerminationActionFromAvr extends HdmiCecFeatureAction {
case Constants.MESSAGE_REPORT_ARC_TERMINATED:
mState = STATE_ARC_TERMINATED;
audioSystem().setArcStatus(false);
+ if (audioSystem().getLocalActivePort() == Constants.CEC_SWITCH_ARC) {
+ audioSystem().routeToInputFromPortId(audioSystem().getRoutingPort());
+ }
finish();
return true;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 528e0a4e4940..6e1b0181adbf 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -18,10 +18,12 @@ package com.android.server.hdmi;
import android.annotation.Nullable;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
import android.view.InputDevice;
@@ -133,9 +135,6 @@ abstract class HdmiCecLocalDevice {
return s.toString();
}
}
- // Logical address of the active source.
- @GuardedBy("mLock")
- protected final ActiveSource mActiveSource = new ActiveSource();
// Active routing path. Physical address of the active source but not all the time, such as
// when the new active source does not claim itself to be one. Note that we don't keep
@@ -867,9 +866,7 @@ abstract class HdmiCecLocalDevice {
}
ActiveSource getActiveSource() {
- synchronized (mLock) {
- return mActiveSource;
- }
+ return mService.getActiveSource();
}
void setActiveSource(ActiveSource newActive) {
@@ -881,10 +878,7 @@ abstract class HdmiCecLocalDevice {
}
void setActiveSource(int logicalAddress, int physicalAddress) {
- synchronized (mLock) {
- mActiveSource.logicalAddress = logicalAddress;
- mActiveSource.physicalAddress = physicalAddress;
- }
+ mService.setActiveSource(logicalAddress, physicalAddress);
mService.setLastInputForMhl(Constants.INVALID_PORT_ID);
}
@@ -1029,6 +1023,19 @@ abstract class HdmiCecLocalDevice {
return Constants.ADDR_INVALID;
}
+ @ServiceThreadOnly
+ void invokeCallback(IHdmiControlCallback callback, int result) {
+ assertRunOnServiceThread();
+ if (callback == null) {
+ return;
+ }
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
+
void sendUserControlPressedAndReleased(int targetAddress, int cecKeycode) {
mService.sendCecCommand(
HdmiCecMessageBuilder.buildUserControlPressed(mAddress, targetAddress, cecKeycode));
@@ -1042,7 +1049,7 @@ abstract class HdmiCecLocalDevice {
pw.println("mAddress: " + mAddress);
pw.println("mPreferredAddress: " + mPreferredAddress);
pw.println("mDeviceInfo: " + mDeviceInfo);
- pw.println("mActiveSource: " + mActiveSource);
+ pw.println("mActiveSource: " + getActiveSource());
pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 048a0e413a9b..8cfe47f3d75e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -21,13 +21,14 @@ import static com.android.server.hdmi.Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONT
import android.annotation.Nullable;
import android.content.Intent;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.IHdmiControlCallback;
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;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -55,11 +56,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
private boolean mTvSystemAudioModeSupport;
- // Whether the auido system will turn TV off when it's powering off
- private boolean mAutoTvOff;
- // Whether the auido system will broadcast standby to the system when it's powering off
- private boolean mAutoDeviceOff;
-
// Whether ARC is available or not. "true" means that ARC is established between TV and
// AVR as audio receiver.
@ServiceThreadOnly private boolean mArcEstablished = false;
@@ -81,10 +77,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
// TODO(amyjojo) make System Audio Control controllable by users
/*mSystemAudioControlFeatureEnabled =
mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
- mAutoDeviceOff = mService.readBooleanSetting(
- 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");
@@ -106,21 +98,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
mSystemAudioActivated ? "true" : "false");
}
terminateSystemAudioMode();
-
- HdmiLogger.debug(TAG + " onStandby, initiatedByCec:" + initiatedByCec
- + ", mAutoDeviceOff: " + mAutoDeviceOff + ", mAutoTvOff: " + mAutoTvOff);
- if (!mService.isControlEnabled() || initiatedByCec) {
- return;
- }
- if (mAutoDeviceOff) {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
- } else if (mAutoTvOff) {
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
- }
- return;
-
}
@Override
@@ -232,6 +209,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
@ServiceThreadOnly
protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
assertRunOnServiceThread();
+ removeAction(ArcInitiationActionFromAvr.class);
if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
} else if (!isDirectConnectToTv()) {
@@ -253,6 +231,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
HdmiLogger.debug("ARC is not established between TV and AVR device");
mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
} else {
+ removeAction(ArcTerminationActionFromAvr.class);
addAndStartAction(new ArcTerminationActionFromAvr(this));
}
return true;
@@ -471,11 +450,17 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
}
// Init arc whenever System Audio Mode is on
- // Since some TV like LG don't request ARC on with System Audio Mode on request
- if (newSystemAudioMode
- && SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)
- && !isArcEnabled() && isDirectConnectToTv()) {
- addAndStartAction(new ArcInitiationActionFromAvr(this));
+ // Terminate arc when System Audio Mode is off
+ // Since some TVs don't request ARC on with System Audio Mode on request
+ if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)
+ && isDirectConnectToTv()) {
+ if (newSystemAudioMode && !isArcEnabled()) {
+ removeAction(ArcInitiationActionFromAvr.class);
+ addAndStartAction(new ArcInitiationActionFromAvr(this));
+ } else if (!newSystemAudioMode && isArcEnabled()) {
+ removeAction(ArcTerminationActionFromAvr.class);
+ addAndStartAction(new ArcTerminationActionFromAvr(this));
+ }
}
}
@@ -501,6 +486,33 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
}
+ @ServiceThreadOnly
+ void doManualPortSwitching(int portId, IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ // TODO: validate port ID
+ if (portId == getLocalActivePort()) {
+ invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
+ return;
+ }
+ getActiveSource().invalidate();
+ if (!mService.isControlEnabled()) {
+ setLocalActivePort(portId);
+ invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
+ return;
+ }
+ int oldPath = getLocalActivePort() != Constants.CEC_SWITCH_HOME
+ ? getActivePathOnSwitchFromActivePortId(getLocalActivePort())
+ : getDeviceInfo().getPhysicalAddress();
+ int newPath = getActivePathOnSwitchFromActivePortId(portId);
+ if (oldPath == newPath) {
+ return;
+ }
+ setLocalActivePort(portId);
+ HdmiCecMessage routingChange =
+ HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
+ mService.sendCecCommand(routingChange);
+ }
+
boolean isSystemAudioControlFeatureEnabled() {
synchronized (mLock) {
return mSystemAudioControlFeatureEnabled;
@@ -594,28 +606,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
}
- @ServiceThreadOnly
- protected void setAutoTvOff(boolean autoTvOff) {
- assertRunOnServiceThread();
- mAutoTvOff = autoTvOff;
- }
-
- @Override
- @ServiceThreadOnly
- void setAutoDeviceOff(boolean autoDeviceOff) {
- 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.
@@ -641,11 +634,15 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
HdmiLogger.debug("Invalid port number for Tv Input switching.");
return;
}
- // TODO(amyjojo): handle if switching to the current input
+ // Wake up if the current device if ready to route.
+ if (mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ }
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));
+ setLocalActivePort(portId);
return;
} else {
String uri = mTvInputs.get(portId);
@@ -658,6 +655,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
setLocalActivePort(portId);
+ setRoutingPort(portId);
}
// For device to switch to specific TvInput with corresponding URI.
@@ -700,31 +698,21 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
// 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()) {
+ if (getRoutingPort() == 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());
- }
+ mService.setAndBroadcastActiveSourceFromOneDeviceType(
+ message.getSource(), mService.getPhysicalAddress());
return;
}
int routingInformationPath =
- getActivePathOnSwitchFromActivePortId(getLocalActivePort());
+ getActivePathOnSwitchFromActivePortId(getRoutingPort());
// 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"
@@ -735,6 +723,6 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
// Otherwise will switch to the current active port and broadcast routing information.
mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingInformation(
mAddress, routingInformationPath));
- routeToInputFromPortId(getLocalActivePort());
+ routeToInputFromPortId(getRoutingPort());
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 379cc1610197..07db9716497c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -21,7 +21,6 @@ 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.provider.Settings.Global;
import android.util.Slog;
@@ -39,7 +38,7 @@ import java.util.Locale;
/**
* Represent a logical device of type Playback residing in Android system.
*/
-final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
+public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
private static final String TAG = "HdmiCecLocalDevicePlayback";
private static final boolean WAKE_ON_HOTPLUG =
@@ -48,8 +47,6 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
private static final boolean SET_MENU_LANGUAGE =
SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false);
- 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. True by default, and can be disabled (i.e. device can go to sleep
@@ -122,16 +119,6 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
addAndStartAction(action);
}
- @ServiceThreadOnly
- private void invokeCallback(IHdmiControlCallback callback, int result) {
- assertRunOnServiceThread();
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- Slog.e(TAG, "Invoking callback failed:" + e);
- }
- }
-
@Override
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
@@ -174,6 +161,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
}
@ServiceThreadOnly
+ @VisibleForTesting
void setIsActiveSource(boolean on) {
assertRunOnServiceThread();
mIsActiveSource = on;
@@ -220,53 +208,6 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
}
@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
- int port = getLocalPortFromPhysicalAddress(physicalAddress);
- if (port == 0) {
- setIsActiveSource(true);
- maySendActiveSource(message.getSource());
- wakeUpIfActiveSource();
- } else if (port > 0) {
- // Wake up the device if the power is in standby mode for routing
- if (mService.isPowerStandbyOrTransient()) {
- mService.wakeUp();
- }
- routeToPort(port);
- }
- return true;
- }
-
- // Samsung model we tested sends <Routing Change> and <Request Active Source>
- // in a row, and then changes the input to the internal source if there is no
- // <Active Source> in response. To handle this, we'll set ActiveSource aggressively.
- @Override
- @ServiceThreadOnly
- protected boolean handleRoutingChange(HdmiCecMessage message) {
- assertRunOnServiceThread();
- int newPath = HdmiUtils.twoBytesToInt(message.getParams(), 2);
- maySetActiveSource(newPath);
- return true; // Broadcast message.
- }
-
- @Override
- @ServiceThreadOnly
- protected boolean handleRoutingInformation(HdmiCecMessage message) {
- assertRunOnServiceThread();
- int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
- maySetActiveSource(physicalAddress);
- return true; // Broadcast message.
- }
-
- private void maySetActiveSource(int physicalAddress) {
- setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
- }
-
- @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 8d55299243b2..6532e16cffe5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -18,7 +18,6 @@ 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;
@@ -37,21 +36,32 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDeviceSource";
// Indicate if current device is Active Source or not
- private boolean mIsActiveSource = false;
+ @VisibleForTesting
+ protected 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.
+ // Routing port number used for Routing Control.
+ // This records the default routing port or the previous valid routing 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
+ // new Active Source physical address might not match the active path
+ @GuardedBy("mLock")
+ @LocalActivePort
+ private int mRoutingPort = Constants.CEC_SWITCH_HOME;
+
+ // This records the current input of the device.
+ // When device is switched to ARC input, mRoutingPort does not record it
+ // since it's not an HDMI port used for Routing Control.
+ // mLocalActivePort will record whichever input we switch to to keep tracking on
+ // the current input status of the device.
+ // This can help prevent duplicate switching and provide status information.
@GuardedBy("mLock")
@LocalActivePort
- private int mLocalActivePort = Constants.CEC_SWITCH_HOME;
+ protected int mLocalActivePort = Constants.CEC_SWITCH_HOME;
protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) {
super(service, deviceType);
@@ -98,22 +108,12 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
}
@ServiceThreadOnly
- private void invokeCallback(IHdmiControlCallback callback, int result) {
- assertRunOnServiceThread();
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- Slog.e(TAG, "Invoking callback failed:" + e);
- }
- }
-
- @ServiceThreadOnly
protected boolean handleActiveSource(HdmiCecMessage message) {
assertRunOnServiceThread();
int logicalAddress = message.getSource();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
- if (!mActiveSource.equals(activeSource)) {
+ if (!getActiveSource().equals(activeSource)) {
setActiveSource(activeSource);
}
setIsActiveSource(physicalAddress == mService.getPhysicalAddress());
@@ -185,24 +185,13 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
// 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
+ // Active source claiming needs to be handled in Service
+ // since service can 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());
- }
+ mService.setAndBroadcastActiveSource(
+ message, physicalAddress, getDeviceInfo().getDeviceType());
}
@ServiceThreadOnly
@@ -211,13 +200,6 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
mIsActiveSource = on;
}
- @ServiceThreadOnly
- // Check if current device is the Active Source
- boolean isActiveSource() {
- assertRunOnServiceThread();
- return mIsActiveSource;
- }
-
protected void wakeUpIfActiveSource() {
if (!mIsActiveSource) {
return;
@@ -236,19 +218,59 @@ abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
}
}
+ /**
+ * Set {@link #mRoutingPort} to a specific {@link LocalActivePort} to record the current active
+ * CEC Routing Control related port.
+ *
+ * @param portId The portId of the new routing port.
+ */
@VisibleForTesting
- protected void setLocalActivePort(@LocalActivePort int portId) {
+ protected void setRoutingPort(@LocalActivePort int portId) {
+ synchronized (mLock) {
+ mRoutingPort = portId;
+ }
+ }
+
+ /**
+ * Get {@link #mRoutingPort}. This is useful when the device needs to route to the last valid
+ * routing port.
+ */
+ @LocalActivePort
+ protected int getRoutingPort() {
synchronized (mLock) {
- mLocalActivePort = portId;
+ return mRoutingPort;
}
}
- // To get the local active port to switch to
- // when receivng routing change or information.
+ /**
+ * Get {@link #mLocalActivePort}. This is useful when device needs to know the current active
+ * port.
+ */
@LocalActivePort
protected int getLocalActivePort() {
synchronized (mLock) {
return mLocalActivePort;
}
}
+
+ /**
+ * Set {@link #mLocalActivePort} to a specific {@link LocalActivePort} to record the current
+ * active port.
+ *
+ * <p>It does not have to be a Routing Control related port. For example it can be
+ * set to {@link Constants#CEC_SWITCH_ARC} but this port is System Audio related.
+ *
+ * @param activePort The portId of the new active port.
+ */
+ protected void setLocalActivePort(@LocalActivePort int activePort) {
+ synchronized (mLock) {
+ mLocalActivePort = activePort;
+ }
+ }
+
+ // Check if the device is trying to switch to the same input that is active right now.
+ // This can help avoid redundant port switching.
+ protected boolean isSwitchingToTheSameInput(@LocalActivePort int activePort) {
+ return activePort == getLocalActivePort();
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 25ca27836aa7..b91d8c637c79 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -41,17 +41,18 @@ import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
-import android.os.RemoteException;
import android.provider.Settings.Global;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -307,7 +308,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
private void handleSelectInternalSource() {
assertRunOnServiceThread();
// Seq #18
- if (mService.isControlEnabled() && mActiveSource.logicalAddress != mAddress) {
+ if (mService.isControlEnabled() && getActiveSource().logicalAddress != mAddress) {
updateActiveSource(mAddress, mService.getPhysicalAddress());
if (mSkipRoutingControl) {
mSkipRoutingControl = false;
@@ -329,7 +330,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
void updateActiveSource(ActiveSource newActive) {
assertRunOnServiceThread();
// Seq #14
- if (mActiveSource.equals(newActive)) {
+ if (getActiveSource().equals(newActive)) {
return;
}
setActiveSource(newActive);
@@ -402,7 +403,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
return;
}
- mActiveSource.invalidate();
+ getActiveSource().invalidate();
if (!mService.isControlEnabled()) {
setActivePortId(portId);
invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
@@ -452,17 +453,6 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
return Constants.ADDR_INVALID;
}
- private static void invokeCallback(IHdmiControlCallback callback, int result) {
- if (callback == null) {
- return;
- }
- try {
- callback.onComplete(result);
- } catch (RemoteException e) {
- Slog.e(TAG, "Invoking callback failed:" + e);
- }
- }
-
@Override
@ServiceThreadOnly
protected boolean handleActiveSource(HdmiCecMessage message) {
@@ -518,7 +508,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
} else {
// No HDMI port to switch to was found. Notify the input change listers to
// switch to the lastly shown internal input.
- mActiveSource.invalidate();
+ getActiveSource().invalidate();
setActivePath(Constants.INVALID_PHYSICAL_ADDRESS);
mService.invokeInputChangeListener(HdmiDeviceInfo.INACTIVE_DEVICE);
}
@@ -685,7 +675,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
byte[] params = message.getParams();
int currentPath = HdmiUtils.twoBytesToInt(params);
if (HdmiUtils.isAffectingActiveRoutingPath(getActivePath(), currentPath)) {
- mActiveSource.invalidate();
+ getActiveSource().invalidate();
removeAction(RoutingControlAction.class);
int newPath = HdmiUtils.twoBytesToInt(params, 2);
addAndStartAction(new RoutingControlAction(this, newPath, true, null));
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 903045d9b68f..833091df5f1c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -140,6 +140,10 @@ public class HdmiControlService extends SystemService {
static final int STANDBY_SCREEN_OFF = 0;
static final int STANDBY_SHUTDOWN = 1;
+ // Logical address of the active source.
+ @GuardedBy("mLock")
+ protected final ActiveSource mActiveSource = new ActiveSource();
+
private static final boolean isHdmiCecNeverClaimPlaybackLogicAddr =
SystemProperties.getBoolean(
Constants.PROPERTY_HDMI_CEC_NEVER_CLAIM_PLAYBACK_LOGICAL_ADDRESS, false);
@@ -596,11 +600,6 @@ public class HdmiControlService extends SystemService {
}
// No need to propagate to HAL.
break;
- case Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED:
- if (isAudioSystemDevice()) {
- audioSystem().setAutoTvOff(enabled);
- }
- break;
case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
if (isTvDeviceEnabled()) {
tv().setSystemAudioControlFeatureEnabled(enabled);
@@ -1009,6 +1008,10 @@ public class HdmiControlService extends SystemService {
assertRunOnServiceThread();
if (connected && !isTvDevice()) {
+ if (getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT && isSwitchDevice()) {
+ initPortInfo();
+ HdmiLogger.debug("initPortInfo for switch device when onHotplug from tx.");
+ }
ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>();
for (int type : mLocalDevices) {
if (type == HdmiDeviceInfo.DEVICE_PLAYBACK
@@ -1394,17 +1397,24 @@ public class HdmiControlService extends SystemService {
return;
}
HdmiCecLocalDeviceTv tv = tv();
- if (tv == null) {
- if (!mAddressAllocated) {
- mSelectRequestBuffer.set(SelectRequestBuffer.newPortSelect(
- HdmiControlService.this, portId, callback));
- return;
- }
- Slog.w(TAG, "Local tv device not available");
- invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
+ if (tv != null) {
+ tv.doManualPortSwitching(portId, callback);
return;
}
- tv.doManualPortSwitching(portId, callback);
+ HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+ if (audioSystem != null) {
+ audioSystem.doManualPortSwitching(portId, callback);
+ return;
+ }
+
+ if (!mAddressAllocated) {
+ mSelectRequestBuffer.set(SelectRequestBuffer.newPortSelect(
+ HdmiControlService.this, portId, callback));
+ return;
+ }
+ Slog.w(TAG, "Local device not available");
+ invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
+ return;
}
});
}
@@ -2118,6 +2128,11 @@ public class HdmiControlService extends SystemService {
return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_PLAYBACK);
}
+ boolean isSwitchDevice() {
+ return SystemProperties.getBoolean(
+ Constants.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH, false);
+ }
+
boolean isTvDeviceEnabled() {
return isTvDevice() && tv() != null;
}
@@ -2493,6 +2508,77 @@ public class HdmiControlService extends SystemService {
setLastInputForMhl(Constants.INVALID_PORT_ID);
}
+ ActiveSource getActiveSource() {
+ synchronized (mLock) {
+ return mActiveSource;
+ }
+ }
+
+ void setActiveSource(int logicalAddress, int physicalAddress) {
+ synchronized (mLock) {
+ mActiveSource.logicalAddress = logicalAddress;
+ mActiveSource.physicalAddress = physicalAddress;
+ }
+ }
+
+ // This method should only be called when the device can be the active source
+ // and all the device types call into this method.
+ // For example, when receiving broadcast messages, all the device types will call this
+ // method but only one of them will be the Active Source.
+ protected void setAndBroadcastActiveSource(
+ HdmiCecMessage message, int physicalAddress, int deviceType) {
+ // If the device has both playback and audio system logical addresses,
+ // playback will claim active source. Otherwise audio system will.
+ if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) {
+ HdmiCecLocalDevicePlayback playback = playback();
+ playback.setIsActiveSource(true);
+ playback.wakeUpIfActiveSource();
+ playback.maySendActiveSource(message.getSource());
+ setActiveSource(playback.mAddress, physicalAddress);
+ }
+
+ if (deviceType == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
+ HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+ if (playback() != null) {
+ audioSystem.setIsActiveSource(false);
+ } else {
+ audioSystem.setIsActiveSource(true);
+ audioSystem.wakeUpIfActiveSource();
+ audioSystem.maySendActiveSource(message.getSource());
+ setActiveSource(audioSystem.mAddress, physicalAddress);
+ }
+ }
+ }
+
+ // This method should only be called when the device can be the active source
+ // and only one of the device types calls into this method.
+ // For example, when receiving One Touch Play, only playback device handles it
+ // and this method updates Active Source in all the device types sharing the same
+ // Physical Address.
+ protected void setAndBroadcastActiveSourceFromOneDeviceType(
+ int sourceAddress, int physicalAddress) {
+ // If the device has both playback and audio system logical addresses,
+ // playback will claim active source. Otherwise audio system will.
+ HdmiCecLocalDevicePlayback playback = playback();
+ HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+ if (playback != null) {
+ playback.setIsActiveSource(true);
+ playback.wakeUpIfActiveSource();
+ playback.maySendActiveSource(sourceAddress);
+ if (audioSystem != null) {
+ audioSystem.setIsActiveSource(false);
+ }
+ setActiveSource(playback.mAddress, physicalAddress);
+ } else {
+ if (audioSystem != null) {
+ audioSystem.setIsActiveSource(true);
+ audioSystem.wakeUpIfActiveSource();
+ audioSystem.maySendActiveSource(sourceAddress);
+ setActiveSource(audioSystem.mAddress, physicalAddress);
+ }
+ }
+ }
+
@ServiceThreadOnly
void setLastInputForMhl(int portId) {
assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 46611dd1b66e..41bf01f842cd 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -83,12 +83,16 @@ 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.
HdmiCecLocalDeviceSource source = source();
- source.setIsActiveSource(true);
- source.setActiveSource(getSourceAddress(), getSourcePath());
+ source.mService.setAndBroadcastActiveSourceFromOneDeviceType(
+ mTargetAddress, getSourcePath());
// Set local active port to HOME when One Touch Play.
+ // Active Port and Current Input are handled by the switch functionality device.
+ if (source.mService.audioSystem() != null) {
+ source = source.mService.audioSystem();
+ }
+ source.setRoutingPort(Constants.CEC_SWITCH_HOME);
source.setLocalActivePort(Constants.CEC_SWITCH_HOME);
}
diff --git a/services/core/java/com/android/server/hdmi/SelectRequestBuffer.java b/services/core/java/com/android/server/hdmi/SelectRequestBuffer.java
index 75986c7ba1c1..ba16260bbfb7 100644
--- a/services/core/java/com/android/server/hdmi/SelectRequestBuffer.java
+++ b/services/core/java/com/android/server/hdmi/SelectRequestBuffer.java
@@ -56,6 +56,10 @@ public class SelectRequestBuffer {
return mService.tv();
}
+ protected HdmiCecLocalDeviceAudioSystem audioSystem() {
+ return mService.audioSystem();
+ }
+
protected boolean isLocalDeviceReady() {
if (tv() == null) {
Slog.e(TAG, "Local tv device not available");
@@ -105,7 +109,15 @@ public class SelectRequestBuffer {
public void process() {
if (isLocalDeviceReady()) {
Slog.v(TAG, "calling delayed portSelect id:" + mId);
- tv().doManualPortSwitching(mId, mCallback);
+ HdmiCecLocalDeviceTv tv = tv();
+ if (tv != null) {
+ tv.doManualPortSwitching(mId, mCallback);
+ return;
+ }
+ HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem();
+ if (audioSystem != null) {
+ audioSystem.doManualPortSwitching(mId, mCallback);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
index a6e69656a956..b6ebcd7c3ec2 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
@@ -41,7 +41,7 @@ public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction {
@Override
boolean start() {
- if (audioSystem().mActiveSource.physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
+ if (audioSystem().getActiveSource().physicalAddress == Constants.INVALID_PHYSICAL_ADDRESS) {
mState = STATE_WAITING_FOR_ACTIVE_SOURCE;
addTimer(mState, HdmiConfig.TIMEOUT_MS);
sendRequestActiveSource();
@@ -60,10 +60,8 @@ public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction {
return false;
}
mActionTimer.clearTimerMessage();
- int physicalAddress = HdmiUtils.twoBytesToInt(cmd.getParams());
- if (physicalAddress != getSourcePath()) {
- audioSystem().setActiveSource(cmd.getSource(), physicalAddress);
- }
+ // Broadcast message is also handled by other device types
+ audioSystem().handleActiveSource(cmd);
mState = STATE_WAITING_FOR_TV_SUPPORT;
queryTvSystemAudioModeSupport();
return true;
@@ -116,7 +114,16 @@ public class SystemAudioInitiationActionFromAvr extends HdmiCecFeatureAction {
private void handleActiveSourceTimeout() {
HdmiLogger.debug("Cannot get active source.");
- audioSystem().checkSupportAndSetSystemAudioMode(false);
+ // If not able to find Active Source and the current device has playbcak functionality,
+ // claim Active Source and start to query TV system audio mode support.
+ if (audioSystem().mService.isPlaybackDevice()) {
+ audioSystem().mService.setAndBroadcastActiveSourceFromOneDeviceType(
+ Constants.ADDR_BROADCAST, getSourcePath());
+ mState = STATE_WAITING_FOR_TV_SUPPORT;
+ queryTvSystemAudioModeSupport();
+ } else {
+ audioSystem().checkSupportAndSetSystemAudioMode(false);
+ }
finish();
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
new file mode 100644
index 000000000000..8b0e8abf069d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.Nullable;
+import android.app.Instrumentation;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+
+/** Tests for {@link ArcInitiationActionFromAvrTest} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class ArcInitiationActionFromAvrTest {
+
+ private HdmiDeviceInfo mDeviceInfoForTests;
+ private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private HdmiCecController mHdmiCecController;
+ private HdmiControlService mHdmiControlService;
+ private FakeNativeWrapper mNativeWrapper;
+ private ArcInitiationActionFromAvr mAction;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private boolean mSendCecCommandSuccess;
+ private boolean mShouldDispatchARCInitiated;
+ private boolean mArcInitSent;
+ private boolean mRequestActiveSourceSent;
+ private Instrumentation mInstrumentation;
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+ mHdmiControlService =
+ new HdmiControlService(mInstrumentation.getTargetContext()) {
+ @Override
+ void sendCecCommand(
+ HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
+ if (callback != null) {
+ callback.onSendCompleted(
+ mSendCecCommandSuccess
+ ? SendMessageResult.SUCCESS
+ : SendMessageResult.NACK);
+ }
+ mRequestActiveSourceSent = true;
+ break;
+ case Constants.MESSAGE_INITIATE_ARC:
+ if (callback != null) {
+ callback.onSendCompleted(
+ mSendCecCommandSuccess
+ ? SendMessageResult.SUCCESS
+ : SendMessageResult.NACK);
+ }
+ mArcInitSent = true;
+ if (mShouldDispatchARCInitiated) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildReportArcInitiated(
+ Constants.ADDR_TV,
+ Constants.ADDR_AUDIO_SYSTEM));
+ }
+ break;
+ default:
+ }
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ boolean isAddressAllocated() {
+ return true;
+ }
+
+ @Override
+ Looper getServiceLooper() {
+ return mTestLooper.getLooper();
+ }
+ };
+
+ mHdmiCecLocalDeviceAudioSystem =
+ new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ @Override
+ HdmiDeviceInfo getDeviceInfo() {
+ return mDeviceInfoForTests;
+ }
+
+ @Override
+ void setArcStatus(boolean enabled) {
+ // do nothing
+ }
+
+ @Override
+ protected boolean isSystemAudioActivated() {
+ return true;
+ }
+ };
+
+ mHdmiCecLocalDeviceAudioSystem.init();
+ Looper looper = mTestLooper.getLooper();
+ mHdmiControlService.setIoLooper(looper);
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(this.mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mHdmiControlService.initPortInfo();
+ mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
+
+ mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void arcInitiation_requestActiveSource() {
+ mSendCecCommandSuccess = true;
+ mShouldDispatchARCInitiated = true;
+ mRequestActiveSourceSent = false;
+ mArcInitSent = false;
+
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+ mTestLooper.dispatchAll();
+
+ assertThat(mArcInitSent).isTrue();
+ assertThat(mRequestActiveSourceSent).isTrue();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index da840be9bca7..93a09dc3ff04 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -18,6 +18,7 @@ package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
+
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
@@ -25,16 +26,21 @@ import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_3;
import static com.android.server.hdmi.Constants.ADDR_SPECIFIC_USE;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
+
import static junit.framework.Assert.assertEquals;
import android.content.Context;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.test.TestLooper;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+
import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
+
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -136,6 +142,7 @@ public class HdmiCecControllerTest {
assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
}
+ @Ignore("b/110413065 Support multiple device types 4 and 5.")
@Test
public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
@@ -151,6 +158,7 @@ public class HdmiCecControllerTest {
assertEquals(ADDR_PLAYBACK_2, mLogicalAddress);
}
+ @Ignore("b/110413065 Support multiple device types 4 and 5.")
@Test
public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
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 c442ae845d4b..b47f269bc721 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -126,16 +127,16 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Override
void wakeUp() {}
-
- @Override
- boolean isControlEnabled() {
- return true;
- }
};
mMyLooper = mTestLooper.getLooper();
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
- mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
+ mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
+ @Override
+ void setIsActiveSource(boolean on) {
+ mIsActiveSource = on;
+ }
+ };
mHdmiCecLocalDeviceAudioSystem.init();
mHdmiCecLocalDevicePlayback.init();
mHdmiControlService.setIoLooper(mMyLooper);
@@ -297,8 +298,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Test
public void onStandbyAudioSystem_currentSystemAudioControlOn() throws Exception {
- mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(false);
- mHdmiCecLocalDeviceAudioSystem.setAutoTvOff(false);
// Set system audio control on first
mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(true);
// Check if standby correctly turns off the feature
@@ -518,17 +517,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
- @Test
- public void onStandby_setAutoDeviceOff_true() throws Exception {
- HdmiCecMessage expectedMessage =
- HdmiCecMessageBuilder.buildStandby(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST);
- mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(true);
- mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
-
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
- }
-
public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSystemAudioModeRequest(
@@ -577,30 +565,31 @@ public class HdmiCecLocalDeviceAudioSystemTest {
.isEqualTo(expectedActiveSource);
}
+ @Ignore("b/110413065 Support multiple device types 4 and 5.")
@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);
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1, mAvrPhysicalAddress);
+ ActiveSource expectedActiveSource = ActiveSource.of(ADDR_PLAYBACK_1, mAvrPhysicalAddress);
int expectedLocalActivePort = Constants.CEC_SWITCH_HOME;
assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource())
.isEqualTo(expectedActiveSource);
- assertThat(mHdmiCecLocalDeviceAudioSystem.getLocalActivePort())
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getRoutingPort())
.isEqualTo(expectedLocalActivePort);
- assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@Test
public void handleRoutingInformation_currentActivePortIsHDMI1() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_TV, 0x2000);
- mHdmiCecLocalDeviceAudioSystem.setLocalActivePort(Constants.CEC_SWITCH_HDMI1);
+ mHdmiCecLocalDeviceAudioSystem.setRoutingPort(Constants.CEC_SWITCH_HDMI1);
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildRoutingInformation(ADDR_AUDIO_SYSTEM, 0x2100);
@@ -609,6 +598,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
+ @Ignore("b/110413065 Support multiple device types 4 and 5.")
@Test
public void handleRoutingChange_homeIsActive_playbackSendActiveSource() {
HdmiCecMessage message =
@@ -619,6 +609,6 @@ public class HdmiCecLocalDeviceAudioSystemTest {
assertThat(mHdmiCecLocalDeviceAudioSystem.handleRoutingChange(message)).isTrue();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
}
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 76f638cb4351..792c617b99ea 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -27,6 +27,7 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -80,12 +81,14 @@ public class HdmiCecLocalDevicePlaybackTest {
mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
}
+ @Ignore
@Test
public void handleSetStreamPath_underCurrentDevice() {
assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0);
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
+ // TODO(amyjojo): Move set and get LocalActivePath to Control Service.
assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(1);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index d914b9a090a2..bd297eecf25c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -26,8 +26,10 @@ import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +51,9 @@ public class SystemAudioInitiationActionFromAvrTest {
private int mMsgRequestActiveSourceCount;
private int mMsgSetSystemAudioModeCount;
private int mQueryTvSystemAudioModeSupportCount;
+ private boolean mArcEnabled;
+ private boolean mIsPlaybackDevice;
+ private boolean mBroadcastActiveSource;
@Before
public void SetUp() {
@@ -80,6 +85,8 @@ public class SystemAudioInitiationActionFromAvrTest {
callback.onSendCompleted(SendMessageResult.NACK);
}
break;
+ case Constants.MESSAGE_INITIATE_ARC:
+ break;
default:
throw new IllegalArgumentException("Unexpected message");
}
@@ -132,6 +139,17 @@ public class SystemAudioInitiationActionFromAvrTest {
int getPhysicalAddress() {
return 0;
}
+
+ @Override
+ boolean isPlaybackDevice() {
+ return mIsPlaybackDevice;
+ }
+
+ @Override
+ public void setAndBroadcastActiveSourceFromOneDeviceType(
+ int sourceAddress, int physicalAddress) {
+ mBroadcastActiveSource = true;
+ }
};
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@@ -148,6 +166,11 @@ public class SystemAudioInitiationActionFromAvrTest {
HdmiDeviceInfo getDeviceInfo() {
return mDeviceInfoForTests;
}
+
+ @Override
+ void setArcStatus(boolean enabled) {
+ mArcEnabled = enabled;
+ }
};
mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
@@ -159,7 +182,7 @@ public class SystemAudioInitiationActionFromAvrTest {
resetTestVariables();
mShouldDispatchActiveSource = false;
- assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
.isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
@@ -171,7 +194,7 @@ public class SystemAudioInitiationActionFromAvrTest {
assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(0);
assertFalse(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
- assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
.isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
}
@@ -206,14 +229,15 @@ public class SystemAudioInitiationActionFromAvrTest {
assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
- assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress).isEqualTo(1002);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
+ .isEqualTo(1002);
}
@Test
public void testKnownActiveSource() {
resetTestVariables();
mTvSystemAudioModeSupport = true;
- mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress = 1001;
+ mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress = 1001;
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
@@ -233,7 +257,7 @@ public class SystemAudioInitiationActionFromAvrTest {
mTryCountBeforeSucceed = 3;
assertThat(mTryCountBeforeSucceed)
.isAtMost(SystemAudioInitiationActionFromAvr.MAX_RETRY_COUNT);
- assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress)
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
.isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
@@ -246,12 +270,32 @@ public class SystemAudioInitiationActionFromAvrTest {
assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
}
+ @Test
+ public void testIsPlaybackDevice_cannotReceiveActiveSource() {
+ resetTestVariables();
+ mIsPlaybackDevice = true;
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress)
+ .isEqualTo(Constants.INVALID_PHYSICAL_ADDRESS);
+
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+ new SystemAudioInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem));
+ mTestLooper.dispatchAll();
+
+ assertThat(mMsgRequestActiveSourceCount).isEqualTo(1);
+ assertThat(mMsgSetSystemAudioModeCount).isEqualTo(1);
+ assertThat(mQueryTvSystemAudioModeSupportCount).isEqualTo(1);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
+ assertThat(mBroadcastActiveSource).isTrue();
+ }
+
private void resetTestVariables() {
mMsgRequestActiveSourceCount = 0;
mMsgSetSystemAudioModeCount = 0;
mQueryTvSystemAudioModeSupportCount = 0;
mTryCountBeforeSucceed = 0;
- mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress =
+ mIsPlaybackDevice = false;
+ mBroadcastActiveSource = false;
+ mHdmiCecLocalDeviceAudioSystem.getActiveSource().physicalAddress =
Constants.INVALID_PHYSICAL_ADDRESS;
}
}