CEC: Make HDMI CEC volume control configurable

Add API (backed by Setting) to enable/disable volume changes via HDMI
CEC for HDMI CEC source devices.
This state is persisted via Settings.

If volume control is disabled, no incoming HDMI CEC commands related to
volume will be processed. If disabled, no HDMI CEC volume control
messages will be sent by the device.

Test: atest com.android.server.hdmi
Bug: 149800547

Merged-In: I83ae9b423122b540b9adb156fb1c6f5964dd6105
Change-Id: I83ae9b423122b540b9adb156fb1c6f5964dd6105
This commit is contained in:
Marvin Ramin
2020-03-16 14:17:58 +01:00
parent a2807f2859
commit da665a67f1
13 changed files with 452 additions and 6 deletions

View File

@@ -649,6 +649,68 @@ public final class HdmiControlManager {
} }
} }
/**
* Controls whether volume control commands via HDMI CEC are enabled.
*
* <p>When disabled:
* <ul>
* <li>the device will not send any HDMI CEC audio messages
* <li>received HDMI CEC audio messages are responded to with {@code <Feature Abort>}
* </ul>
*
* <p>Effects on different device types:
* <table>
* <tr><th>HDMI CEC device type</th><th>enabled</th><th>disabled</th></tr>
* <tr>
* <td>TV (type: 0)</td>
* <td>Per CEC specification.</td>
* <td>TV changes system volume. TV no longer reacts to incoming volume changes via
* {@code <User Control Pressed>}. TV no longer handles {@code <Report Audio Status>}
* .</td>
* </tr>
* <tr>
* <td>Playback device (type: 4)</td>
* <td>Device sends volume commands to TV/Audio system via {@code <User Control
* Pressed>}</td><td>Device does not send volume commands via {@code <User Control
* Pressed>}.</td>
* </tr>
* <tr>
* <td>Audio device (type: 5)</td>
* <td>Full "System Audio Control" capabilities.</td>
* <td>Audio device no longer reacts to incoming {@code <User Control Pressed>}
* volume commands. Audio device no longer reports volume changes via {@code <Report
* Audio Status>}.</td>
* </tr>
* </table>
*
* <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged.
*
* @param isHdmiCecVolumeControlEnabled target state of HDMI CEC volume control.
* @see Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED
* @hide
*/
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
try {
mService.setHdmiCecVolumeControlEnabled(isHdmiCecVolumeControlEnabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns whether volume changes via HDMI CEC are enabled.
* @hide
*/
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public boolean isHdmiCecVolumeControlEnabled() {
try {
return mService.isHdmiCecVolumeControlEnabled();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** /**
* Gets whether the system is in system audio mode. * Gets whether the system is in system audio mode.
* *

View File

@@ -80,6 +80,8 @@ interface IHdmiControlService {
void sendMhlVendorCommand(int portId, int offset, int length, in byte[] data); void sendMhlVendorCommand(int portId, int offset, int length, in byte[] data);
void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener); void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener);
void setStandbyMode(boolean isStandbyModeOn); void setStandbyMode(boolean isStandbyModeOn);
void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled);
boolean isHdmiCecVolumeControlEnabled();
void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute); void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
void setSystemAudioModeOnForAudioOnlySource(); void setSystemAudioModeOnForAudioOnlySource();
} }

View File

@@ -9604,6 +9604,43 @@ public final class Settings {
*/ */
public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled"; public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
/**
* Controls whether volume control commands via HDMI CEC are enabled. (0 = false, 1 =
* true).
*
* <p>Effects on different device types:
* <table>
* <tr><th>HDMI CEC device type</th><th>0: disabled</th><th>1: enabled</th></tr>
* <tr>
* <td>TV (type: 0)</td>
* <td>Per CEC specification.</td>
* <td>TV changes system volume. TV no longer reacts to incoming volume changes
* via {@code <User Control Pressed>}. TV no longer handles {@code <Report Audio
* Status>}.</td>
* </tr>
* <tr>
* <td>Playback device (type: 4)</td>
* <td>Device sends volume commands to TV/Audio system via {@code <User Control
* Pressed>}</td>
* <td>Device does not send volume commands via {@code <User Control Pressed>}.</td>
* </tr>
* <tr>
* <td>Audio device (type: 5)</td>
* <td>Full "System Audio Control" capabilities.</td>
* <td>Audio device no longer reacts to incoming {@code <User Control Pressed>}
* volume commands. Audio device no longer reports volume changes via {@code
* <Report Audio Status>}.</td>
* </tr>
* </table>
*
* <p> Due to the resulting behavior, usage on TV and Audio devices is discouraged.
*
* @hide
* @see android.hardware.hdmi.HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)
*/
public static final String HDMI_CONTROL_VOLUME_CONTROL_ENABLED =
"hdmi_control_volume_control_enabled";
/** /**
* Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on * Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on
* system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will * system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will

View File

@@ -354,6 +354,15 @@ public class HdmiAudioSystemClientTest {
@Override @Override
public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) { public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) {
} }
@Override
public void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
}
@Override
public boolean isHdmiCecVolumeControlEnabled() {
return true;
}
} }
} }

View File

@@ -296,6 +296,7 @@ public class SettingsBackupTest {
Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
Settings.Global.HDMI_CONTROL_ENABLED, Settings.Global.HDMI_CONTROL_ENABLED,
Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
Settings.Global.HIDDEN_API_POLICY, Settings.Global.HIDDEN_API_POLICY,

View File

@@ -32,6 +32,7 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent; import android.view.KeyEvent;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.Constants.LocalActivePort; import com.android.server.hdmi.Constants.LocalActivePort;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
@@ -144,7 +145,8 @@ abstract class HdmiCecLocalDevice {
// A collection of FeatureAction. // A collection of FeatureAction.
// Note that access to this collection should happen in service thread. // Note that access to this collection should happen in service thread.
private final ArrayList<HdmiCecFeatureAction> mActions = new ArrayList<>(); @VisibleForTesting
final ArrayList<HdmiCecFeatureAction> mActions = new ArrayList<>();
private final Handler mHandler = private final Handler mHandler =
new Handler() { new Handler() {
@@ -544,6 +546,8 @@ abstract class HdmiCecLocalDevice {
} else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) { } else if (mService.isPowerStandbyOrTransient() && isPowerOnOrToggleCommand(message)) {
mService.wakeUp(); mService.wakeUp();
return true; return true;
} else if (!mService.isHdmiCecVolumeControlEnabled() && isVolumeOrMuteCommand(message)) {
return false;
} }
final long downTime = SystemClock.uptimeMillis(); final long downTime = SystemClock.uptimeMillis();
@@ -618,6 +622,16 @@ abstract class HdmiCecLocalDevice {
|| params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION); || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
} }
static boolean isVolumeOrMuteCommand(HdmiCecMessage message) {
byte[] params = message.getParams();
return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
&& (params[0] == HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN
|| params[0] == HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
|| params[0] == HdmiCecKeycode.CEC_KEYCODE_MUTE
|| params[0] == HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION
|| params[0] == HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
}
protected boolean handleTextViewOn(HdmiCecMessage message) { protected boolean handleTextViewOn(HdmiCecMessage message) {
return false; return false;
} }
@@ -1038,6 +1052,9 @@ abstract class HdmiCecLocalDevice {
@ServiceThreadOnly @ServiceThreadOnly
protected void sendVolumeKeyEvent(int keyCode, boolean isPressed) { protected void sendVolumeKeyEvent(int keyCode, boolean isPressed) {
assertRunOnServiceThread(); assertRunOnServiceThread();
if (!mService.isHdmiCecVolumeControlEnabled()) {
return;
}
if (!HdmiCecKeycode.isVolumeKeycode(keyCode)) { if (!HdmiCecKeycode.isVolumeKeycode(keyCode)) {
Slog.w(TAG, "Not a volume key: " + keyCode); Slog.w(TAG, "Not a volume key: " + keyCode);
return; return;

View File

@@ -575,7 +575,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
@ServiceThreadOnly @ServiceThreadOnly
protected boolean handleGiveAudioStatus(HdmiCecMessage message) { protected boolean handleGiveAudioStatus(HdmiCecMessage message) {
assertRunOnServiceThread(); assertRunOnServiceThread();
if (isSystemAudioControlFeatureEnabled()) { if (isSystemAudioControlFeatureEnabled() && mService.isHdmiCecVolumeControlEnabled()) {
reportAudioStatus(message.getSource()); reportAudioStatus(message.getSource());
} else { } else {
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED); mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
@@ -930,6 +930,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
void reportAudioStatus(int source) { void reportAudioStatus(int source) {
assertRunOnServiceThread(); assertRunOnServiceThread();
if (!mService.isHdmiCecVolumeControlEnabled()) {
return;
}
int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC); int volume = mService.getAudioManager().getStreamVolume(AudioManager.STREAM_MUSIC);
boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC); boolean mute = mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);

View File

@@ -678,6 +678,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly @ServiceThreadOnly
protected boolean handleReportAudioStatus(HdmiCecMessage message) { protected boolean handleReportAudioStatus(HdmiCecMessage message) {
assertRunOnServiceThread(); assertRunOnServiceThread();
if (!mService.isHdmiCecVolumeControlEnabled()) {
return false;
}
boolean mute = HdmiUtils.isAudioStatusMute(message); boolean mute = HdmiUtils.isAudioStatusMute(message);
int volume = HdmiUtils.getAudioStatusVolume(message); int volume = HdmiUtils.getAudioStatusVolume(message);
@@ -987,7 +990,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
} }
void setAudioStatus(boolean mute, int volume) { void setAudioStatus(boolean mute, int volume) {
if (!isSystemAudioActivated()) { if (!isSystemAudioActivated() || !mService.isHdmiCecVolumeControlEnabled()) {
return; return;
} }
synchronized (mLock) { synchronized (mLock) {
@@ -1009,7 +1012,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
// On initialization process, getAvrDeviceInfo() may return null and cause exception // On initialization process, getAvrDeviceInfo() may return null and cause exception
return; return;
} }
if (delta == 0 || !isSystemAudioActivated()) { if (delta == 0 || !isSystemAudioActivated() || !mService.isHdmiCecVolumeControlEnabled()) {
return; return;
} }
@@ -1038,7 +1041,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
@ServiceThreadOnly @ServiceThreadOnly
void changeMute(boolean mute) { void changeMute(boolean mute) {
assertRunOnServiceThread(); assertRunOnServiceThread();
if (getAvrDeviceInfo() == null) { if (getAvrDeviceInfo() == null || !mService.isHdmiCecVolumeControlEnabled()) {
// On initialization process, getAvrDeviceInfo() may return null and cause exception // On initialization process, getAvrDeviceInfo() may return null and cause exception
return; return;
} }

View File

@@ -177,6 +177,10 @@ public class HdmiControlService extends SystemService {
@GuardedBy("mLock") @GuardedBy("mLock")
protected final ActiveSource mActiveSource = new ActiveSource(); protected final ActiveSource mActiveSource = new ActiveSource();
// Whether HDMI CEC volume control is enabled or not.
@GuardedBy("mLock")
private boolean mHdmiCecVolumeControlEnabled;
// Whether System Audio Mode is activated or not. // Whether System Audio Mode is activated or not.
@GuardedBy("mLock") @GuardedBy("mLock")
private boolean mSystemAudioActivated = false; private boolean mSystemAudioActivated = false;
@@ -497,6 +501,8 @@ public class HdmiControlService extends SystemService {
mPowerStatus = getInitialPowerStatus(); mPowerStatus = getInitialPowerStatus();
mProhibitMode = false; mProhibitMode = false;
mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true); mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
mHdmiCecVolumeControlEnabled = readBooleanSetting(
Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true);
mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true); mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
if (mCecController == null) { if (mCecController == null) {
@@ -646,6 +652,7 @@ public class HdmiControlService extends SystemService {
ContentResolver resolver = getContext().getContentResolver(); ContentResolver resolver = getContext().getContentResolver();
String[] settings = new String[] { String[] settings = new String[] {
Global.HDMI_CONTROL_ENABLED, Global.HDMI_CONTROL_ENABLED,
Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
@@ -674,6 +681,9 @@ public class HdmiControlService extends SystemService {
case Global.HDMI_CONTROL_ENABLED: case Global.HDMI_CONTROL_ENABLED:
setControlEnabled(enabled); setControlEnabled(enabled);
break; break;
case Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED:
setHdmiCecVolumeControlEnabled(enabled);
break;
case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED: case Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED:
if (isTvDeviceEnabled()) { if (isTvDeviceEnabled()) {
tv().setAutoWakeup(enabled); tv().setAutoWakeup(enabled);
@@ -1273,7 +1283,9 @@ public class HdmiControlService extends SystemService {
} }
void setAudioStatus(boolean mute, int volume) { void setAudioStatus(boolean mute, int volume) {
if (!isTvDeviceEnabled() || !tv().isSystemAudioActivated()) { if (!isTvDeviceEnabled()
|| !tv().isSystemAudioActivated()
|| !isHdmiCecVolumeControlEnabled()) {
return; return;
} }
AudioManager audioManager = getAudioManager(); AudioManager audioManager = getAudioManager();
@@ -2186,6 +2198,24 @@ public class HdmiControlService extends SystemService {
}); });
} }
@Override
public boolean isHdmiCecVolumeControlEnabled() {
enforceAccessPermission();
return HdmiControlService.this.isHdmiCecVolumeControlEnabled();
}
@Override
public void setHdmiCecVolumeControlEnabled(final boolean isHdmiCecVolumeControlEnabled) {
enforceAccessPermission();
runOnServiceThread(new Runnable() {
@Override
public void run() {
HdmiControlService.this.setHdmiCecVolumeControlEnabled(
isHdmiCecVolumeControlEnabled);
}
});
}
@Override @Override
public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume, public void reportAudioStatus(final int deviceType, final int volume, final int maxVolume,
final boolean isMute) { final boolean isMute) {
@@ -2250,6 +2280,7 @@ public class HdmiControlService extends SystemService {
pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled); pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled); pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
pw.println("mSystemAudioActivated: " + isSystemAudioActivated()); pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
pw.println("mHdmiCecVolumeControlEnabled " + mHdmiCecVolumeControlEnabled);
pw.decreaseIndent(); pw.decreaseIndent();
pw.println("mMhlController: "); pw.println("mMhlController: ");
@@ -2982,6 +3013,29 @@ public class HdmiControlService extends SystemService {
} }
} }
void setHdmiCecVolumeControlEnabled(boolean isHdmiCecVolumeControlEnabled) {
assertRunOnServiceThread();
synchronized (mLock) {
mHdmiCecVolumeControlEnabled = isHdmiCecVolumeControlEnabled;
boolean storedValue = readBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
true);
if (storedValue != isHdmiCecVolumeControlEnabled) {
HdmiLogger.debug("Changing HDMI CEC volume control feature state: %s",
isHdmiCecVolumeControlEnabled);
writeBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED,
isHdmiCecVolumeControlEnabled);
}
}
}
boolean isHdmiCecVolumeControlEnabled() {
assertRunOnServiceThread();
synchronized (mLock) {
return mHdmiCecVolumeControlEnabled;
}
}
boolean isProhibitMode() { boolean isProhibitMode() {
synchronized (mLock) { synchronized (mLock) {
return mProhibitMode; return mProhibitMode;
@@ -3022,8 +3076,12 @@ public class HdmiControlService extends SystemService {
if (enabled) { if (enabled) {
enableHdmiControlService(); enableHdmiControlService();
setHdmiCecVolumeControlEnabled(
readBooleanSetting(Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, true));
return; return;
} }
setHdmiCecVolumeControlEnabled(false);
// Call the vendor handler before the service is disabled. // Call the vendor handler before the service is disabled.
invokeVendorCommandListenersOnControlStateChanged(false, invokeVendorCommandListenersOnControlStateChanged(false,
HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING); HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING);

View File

@@ -24,6 +24,7 @@ import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2; import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
import static com.android.server.hdmi.Constants.ADDR_TUNER_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.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.MESSAGE_GIVE_AUDIO_STATUS;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC; import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
@@ -47,6 +48,7 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4; import org.junit.runners.JUnit4;
import java.util.ArrayList; import java.util.ArrayList;
@SmallTest @SmallTest
@RunWith(JUnit4.class) @RunWith(JUnit4.class)
/** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */ /** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */
@@ -167,6 +169,8 @@ public class HdmiCecLocalDeviceAudioSystemTest {
} }
}; };
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mMyLooper = mTestLooper.getLooper(); mMyLooper = mTestLooper.getLooper();
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService); mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) { mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
@@ -717,4 +721,112 @@ public class HdmiCecLocalDeviceAudioSystemTest {
mHdmiCecLocalDeviceAudioSystem.onHotplug(0, false); mHdmiCecLocalDeviceAudioSystem.onHotplug(0, false);
assertThat(mWokenUp).isFalse(); assertThat(mWokenUp).isFalse();
} }
@Test
public void giveAudioStatus_volumeEnabled() {
mMusicVolume = 50;
mMusicMaxVolume = 100;
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
int volume = mHdmiControlService.getAudioManager()
.getStreamVolume(AudioManager.STREAM_MUSIC);
boolean mute = mHdmiControlService.getAudioManager()
.isStreamMute(AudioManager.STREAM_MUSIC);
int maxVolume = mHdmiControlService.getAudioManager()
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
HdmiCecMessage expected = HdmiCecMessageBuilder.buildReportAudioStatus(ADDR_AUDIO_SYSTEM,
ADDR_TV, scaledVolume, mute);
HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
ADDR_AUDIO_SYSTEM, ADDR_TV, MESSAGE_GIVE_AUDIO_STATUS, Constants.ABORT_REFUSED);
HdmiCecMessage giveAudioStatus = HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV,
ADDR_AUDIO_SYSTEM);
mNativeWrapper.clearResultMessages();
boolean handled = mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(giveAudioStatus);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expected);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbort);
assertThat(handled).isTrue();
}
@Test
public void giveAudioStatus_volumeDisabled() {
mMusicVolume = 50;
mMusicMaxVolume = 100;
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
int volume = mHdmiControlService.getAudioManager()
.getStreamVolume(AudioManager.STREAM_MUSIC);
boolean mute = mHdmiControlService.getAudioManager()
.isStreamMute(AudioManager.STREAM_MUSIC);
int maxVolume = mHdmiControlService.getAudioManager()
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
HdmiCecMessage unexpected = HdmiCecMessageBuilder.buildReportAudioStatus(ADDR_AUDIO_SYSTEM,
ADDR_TV, scaledVolume, mute);
HdmiCecMessage featureAbort = HdmiCecMessageBuilder.buildFeatureAbortCommand(
ADDR_AUDIO_SYSTEM, ADDR_TV, MESSAGE_GIVE_AUDIO_STATUS, Constants.ABORT_REFUSED);
HdmiCecMessage giveAudioStatus = HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV,
ADDR_AUDIO_SYSTEM);
mNativeWrapper.clearResultMessages();
boolean handled = mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(giveAudioStatus);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(featureAbort);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(unexpected);
assertThat(handled).isTrue();
}
@Test
public void reportAudioStatus_volumeEnabled() {
mMusicVolume = 50;
mMusicMaxVolume = 100;
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
int volume = mHdmiControlService.getAudioManager()
.getStreamVolume(AudioManager.STREAM_MUSIC);
boolean mute = mHdmiControlService.getAudioManager()
.isStreamMute(AudioManager.STREAM_MUSIC);
int maxVolume = mHdmiControlService.getAudioManager()
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
HdmiCecMessage expected = HdmiCecMessageBuilder.buildReportAudioStatus(ADDR_AUDIO_SYSTEM,
ADDR_TV, scaledVolume, mute);
mNativeWrapper.clearResultMessages();
mHdmiCecLocalDeviceAudioSystem.reportAudioStatus(ADDR_TV);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expected);
}
@Test
public void reportAudioStatus_volumeDisabled() {
mMusicVolume = 50;
mMusicMaxVolume = 100;
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(true);
int volume = mHdmiControlService.getAudioManager()
.getStreamVolume(AudioManager.STREAM_MUSIC);
boolean mute = mHdmiControlService.getAudioManager()
.isStreamMute(AudioManager.STREAM_MUSIC);
int maxVolume = mHdmiControlService.getAudioManager()
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int scaledVolume = VolumeControlAction.scaleToCecVolume(volume, maxVolume);
HdmiCecMessage unexpected = HdmiCecMessageBuilder.buildReportAudioStatus(ADDR_AUDIO_SYSTEM,
ADDR_TV, scaledVolume, mute);
mNativeWrapper.clearResultMessages();
mHdmiCecLocalDeviceAudioSystem.reportAudioStatus(ADDR_TV);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(unexpected);
}
} }

View File

@@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.os.Looper; import android.os.Looper;
import android.os.test.TestLooper; import android.os.test.TestLooper;
import android.view.KeyEvent;
import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@@ -153,4 +154,75 @@ public class HdmiCecLocalDevicePlaybackTest {
mHdmiCecLocalDevicePlayback.onHotplug(0, false); mHdmiCecLocalDevicePlayback.onHotplug(0, false);
assertThat(mWokenUp).isFalse(); assertThat(mWokenUp).isFalse();
} }
@Test
@Ignore("b/151147315")
public void sendVolumeKeyEvent_up_volumeEnabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
assertThat(hasSendKeyAction()).isTrue();
}
@Test
@Ignore("b/151147315")
public void sendVolumeKeyEvent_down_volumeEnabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
assertThat(hasSendKeyAction()).isTrue();
}
@Test
@Ignore("b/151147315")
public void sendVolumeKeyEvent_mute_volumeEnabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false);
assertThat(hasSendKeyAction()).isTrue();
}
@Test
@Ignore("b/151147315")
public void sendVolumeKeyEvent_up_volumeDisabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
assertThat(hasSendKeyAction()).isFalse();
}
@Test
@Ignore("b/151147315")
public void sendVolumeKeyEvent_down_volumeDisabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
assertThat(hasSendKeyAction()).isFalse();
}
@Test
@Ignore("b/151147315")
public void sendVolumeKeyEvent_mute_volumeDisabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, true);
mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE, false);
assertThat(hasSendKeyAction()).isFalse();
}
private boolean hasSendKeyAction() {
boolean match = false;
for (HdmiCecFeatureAction action : mHdmiCecLocalDevicePlayback.mActions) {
if (action instanceof SendKeyAction) {
match = true;
break;
}
}
return match;
}
} }

View File

@@ -19,6 +19,7 @@ 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_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST; 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_TV; import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED; import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID; import static com.android.server.hdmi.Constants.MESSAGE_DEVICE_VENDOR_ID;
@@ -185,4 +186,64 @@ public class HdmiCecLocalDeviceTest {
HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM)); HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM));
assertTrue(mStandbyMessageReceived); assertTrue(mStandbyMessageReceived);
} }
@Test
public void handleUserControlPressed_volumeUp() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
boolean result = mHdmiLocalDevice.handleUserControlPressed(
HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP));
assertTrue(result);
}
@Test
public void handleUserControlPressed_volumeDown() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
boolean result = mHdmiLocalDevice.handleUserControlPressed(
HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN));
assertTrue(result);
}
@Test
public void handleUserControlPressed_volumeMute() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
boolean result = mHdmiLocalDevice.handleUserControlPressed(
HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
HdmiCecKeycode.CEC_KEYCODE_MUTE));
assertTrue(result);
}
@Test
public void handleUserControlPressed_volumeUp_disabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
boolean result = mHdmiLocalDevice.handleUserControlPressed(
HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP));
assertFalse(result);
}
@Test
public void handleUserControlPressed_volumeDown_disabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
boolean result = mHdmiLocalDevice.handleUserControlPressed(
HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN));
assertFalse(result);
}
@Test
public void handleUserControlPressed_volumeMute_disabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
boolean result = mHdmiLocalDevice.handleUserControlPressed(
HdmiCecMessageBuilder.buildUserControlPressed(ADDR_PLAYBACK_1, ADDR_TV,
HdmiCecKeycode.CEC_KEYCODE_MUTE));
assertFalse(result);
}
} }

View File

@@ -252,4 +252,13 @@ public class HdmiControlServiceTest {
assertThat(mHdmiControlService.getPowerStatus()).isEqualTo( assertThat(mHdmiControlService.getPowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_STANDBY); HdmiControlManager.POWER_STATUS_STANDBY);
} }
@Test
public void setAndGetCecVolumeControlEnabled_isApi() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isFalse();
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isTrue();
}
} }