From 6f031afecf1e73ee2d41262d009cf9143db95dc0 Mon Sep 17 00:00:00 2001 From: Amy Date: Tue, 30 Oct 2018 16:38:33 -0700 Subject: [PATCH] Add APIs to expose some cec control to other services. ag/5398143 We exposed Power on/Power off/Device select/Connected device list query from HdmiControlManager. Test: local tested Bug: 117775357 Change-Id: Iee495e7131f44282a60e83ad827faa1431a30389 --- .../hardware/hdmi/HdmiControlManager.java | 68 ++++++++++++++++ .../hardware/hdmi/IHdmiControlService.aidl | 3 + .../hdmi/HdmiAudioSystemClientTest.java | 12 +++ .../server/hdmi/HdmiControlService.java | 81 ++++++++++++++++++- 4 files changed, 161 insertions(+), 3 deletions(-) diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index be8009e6a9661..a7734f5446076 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -33,6 +33,8 @@ import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Log; +import java.util.List; + /** * The {@link HdmiControlManager} class is used to send HDMI control messages * to attached CEC devices. @@ -403,6 +405,72 @@ public final class HdmiControlManager { return (HdmiSwitchClient) getClient(HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH); } + /** + * Get a snapshot of the real-time status of the remote devices. + * + * @return a list of {@link HdmiDeviceInfo} of the devices connected to the current device. + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public List getConnectedDevicesList() { + try { + return mService.getDeviceList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Power off the target device. + * + * @param deviceInfo HdmiDeviceInfo of the device to be powered off + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public void powerOffRemoteDevice(HdmiDeviceInfo deviceInfo) { + try { + mService.powerOffRemoteDevice( + deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Power on the target device. + * + * @param deviceInfo HdmiDeviceInfo of the device to be powered on + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public void powerOnRemoteDevice(HdmiDeviceInfo deviceInfo) { + try { + mService.powerOnRemoteDevice( + deviceInfo.getLogicalAddress(), deviceInfo.getDevicePowerStatus()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Ask the target device to be the new Active Source. + * + * @param deviceInfo HdmiDeviceInfo of the target device + * + * TODO(b/110094868): unhide for Q + * @hide + */ + public void askRemoteDeviceToBecomeActiveSource(HdmiDeviceInfo deviceInfo) { + try { + mService.askRemoteDeviceToBecomeActiveSource(deviceInfo.getPhysicalAddress()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Controls standby mode of the system. It will also try to turn on/off the connected devices if * necessary. diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 66bb084d5482f..1cd9920aa250f 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -61,6 +61,9 @@ interface IHdmiControlService { void setInputChangeListener(IHdmiInputChangeListener listener); List getInputDevices(); List getDeviceList(); + void powerOffRemoteDevice(int logicalAddress, int powerStatus); + void powerOnRemoteDevice(int logicalAddress, int powerStatus); + void askRemoteDeviceToBecomeActiveSource(int physicalAddress); void sendVendorCommand(int deviceType, int targetAddress, in byte[] params, boolean hasVendorId); void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType); diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index 64b3ba04e841d..28a8afe434e4d 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -327,6 +327,18 @@ public class HdmiAudioSystemClientTest { public int getPhysicalAddress() { return 0x0000; } + + @Override + public void powerOffRemoteDevice(int logicalAddress, int powerStatus) { + } + + @Override + public void powerOnRemoteDevice(int logicalAddress, int powerStatus) { + } + + @Override + public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) { + } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index aabe1ad659d6a..d390d860aeeae 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -20,6 +20,7 @@ import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE; import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE; import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH; +import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED; import static com.android.server.hdmi.Constants.DISABLED; import static com.android.server.hdmi.Constants.ENABLED; import static com.android.server.hdmi.Constants.OPTION_MHL_ENABLE; @@ -1615,13 +1616,64 @@ public class HdmiControlService extends SystemService { public List getDeviceList() { enforceAccessPermission(); HdmiCecLocalDeviceTv tv = tv(); - synchronized (mLock) { - return (tv == null) + if (tv != null) { + synchronized (mLock) { + return tv.getSafeCecDevicesLocked(); + } + } else { + HdmiCecLocalDeviceAudioSystem audioSystem = audioSystem(); + synchronized (mLock) { + return (audioSystem == null) ? Collections.emptyList() - : tv.getSafeCecDevicesLocked(); + : audioSystem.getSafeCecDevicesLocked(); + } } } + @Override + public void powerOffRemoteDevice(int logicalAddress, int powerStatus) { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + if (powerStatus == HdmiControlManager.POWER_STATUS_ON + || powerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { + sendCecCommand(HdmiCecMessageBuilder.buildStandby( + getRemoteControlSourceAddress(), logicalAddress)); + } else { + Slog.w(TAG, "Device " + logicalAddress + " is already off " + powerStatus); + } + } + }); + } + + @Override + public void powerOnRemoteDevice(int logicalAddress, int powerStatus) { + // TODO(amyjojo): implement the method + } + + @Override + // TODO(AMYJOJO): add a result callback + public void askRemoteDeviceToBecomeActiveSource(int physicalAddress) { + enforceAccessPermission(); + runOnServiceThread(new Runnable() { + @Override + public void run() { + HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath( + getRemoteControlSourceAddress(), physicalAddress); + if (pathToPortId(physicalAddress) != Constants.INVALID_PORT_ID) { + if (getSwitchDevice() != null) { + getSwitchDevice().handleSetStreamPath(setStreamPath); + } else { + Slog.e(TAG, "Can't get the correct local device to handle routing."); + } + } else { + sendCecCommand(setStreamPath); + } + } + }); + } + @Override public void setSystemAudioVolume(final int oldIndex, final int newIndex, final int maxIndex) { @@ -1917,6 +1969,29 @@ public class HdmiControlService extends SystemService { } } + // Get the source address to send out commands to devices connected to the current device + // when other services interact with HdmiControlService. + private int getRemoteControlSourceAddress() { + if (isAudioSystemDevice()) { + return audioSystem().getDeviceInfo().getLogicalAddress(); + } else if (isPlaybackDevice()) { + return playback().getDeviceInfo().getLogicalAddress(); + } + return ADDR_UNREGISTERED; + } + + // Get the switch device to do CEC routing control + @Nullable + private HdmiCecLocalDeviceSource getSwitchDevice() { + if (isAudioSystemDevice()) { + return audioSystem(); + } + if (isPlaybackDevice()) { + return playback(); + } + return null; + } + @ServiceThreadOnly private void oneTouchPlay(final IHdmiControlCallback callback) { assertRunOnServiceThread();