diff options
8 files changed, 156 insertions, 29 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 7c8868e4e570..845020d61896 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2954,6 +2954,7 @@ package android.hardware.hdmi { method public void sendKeyEvent(int, boolean); method public void sendVendorCommand(int, byte[], boolean); method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener); + method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener, int); } public final class HdmiControlManager { diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java index 0c21746ea412..066de11d0623 100644 --- a/core/java/android/hardware/hdmi/HdmiClient.java +++ b/core/java/android/hardware/hdmi/HdmiClient.java @@ -17,6 +17,8 @@ import android.util.Log; public abstract class HdmiClient { private static final String TAG = "HdmiClient"; + private static final int UNKNOWN_VENDOR_ID = 0xFFFFFF; + /* package */ final IHdmiControlService mService; private IHdmiVendorCommandListener mIHdmiVendorCommandListener; @@ -94,11 +96,25 @@ public abstract class HdmiClient { } /** - * Sets a listener used to receive incoming vendor-specific command. + * Sets a listener used to receive incoming vendor-specific command. This listener will only + * receive {@code <Vendor Command>} but will not receive any {@code <Vendor Command with ID>} + * messages. * * @param listener listener object */ public void setVendorCommandListener(@NonNull VendorCommandListener listener) { + // Set the vendor ID to INVALID_VENDOR_ID. + setVendorCommandListener(listener, UNKNOWN_VENDOR_ID); + } + + /** + * Sets a listener used to receive incoming vendor-specific command. + * + * @param listener listener object + * @param vendorId The listener is interested in {@code <Vendor Command with ID>} received with + * this vendorId and all {@code <Vendor Command>} messages. + */ + public void setVendorCommandListener(@NonNull VendorCommandListener listener, int vendorId) { if (listener == null) { throw new IllegalArgumentException("listener cannot be null"); } @@ -107,7 +123,7 @@ public abstract class HdmiClient { } try { IHdmiVendorCommandListener wrappedListener = getListenerWrapper(listener); - mService.addVendorCommandListener(wrappedListener, getDeviceType()); + mService.addVendorCommandListener(wrappedListener, vendorId); mIHdmiVendorCommandListener = wrappedListener; } catch (RemoteException e) { Log.e(TAG, "failed to set vendor command listener: ", e); diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java index 9a9e945a91cf..45b0689a90d4 100644 --- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java +++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java @@ -221,8 +221,8 @@ public final class HdmiControlServiceWrapper { } @Override - public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { - HdmiControlServiceWrapper.this.addVendorCommandListener(listener, deviceType); + public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) { + HdmiControlServiceWrapper.this.addVendorCommandListener(listener, vendorId); } @Override @@ -471,7 +471,7 @@ public final class HdmiControlServiceWrapper { boolean hasVendorId) {} /** @hide */ - public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {} + public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {} /** @hide */ public void sendStandby(int deviceType, int deviceId) {} diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 7f0e53ea2e68..48177e1726c4 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -76,7 +76,7 @@ interface IHdmiControlService { void askRemoteDeviceToBecomeActiveSource(int physicalAddress); void sendVendorCommand(int deviceType, int targetAddress, in byte[] params, boolean hasVendorId); - void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType); + void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId); void sendStandby(int deviceType, int deviceId); void setHdmiRecordListener(IHdmiRecordListener callback); void startOneTouchRecord(int recorderAddress, in byte[] recordSource); diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java index 597ea14df56d..12e52c62b7c7 100644 --- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java @@ -288,9 +288,8 @@ public class HdmiAudioSystemClientTest { } @Override - public void addVendorCommandListener(final IHdmiVendorCommandListener listener, - final int deviceType) { - } + public void addVendorCommandListener( + final IHdmiVendorCommandListener listener, final int vendorId) {} @Override public void sendVendorCommand(final int deviceType, final int targetAddress, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index c27293c3cc09..27f22c4f2e88 100755 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -829,17 +829,12 @@ abstract class HdmiCecLocalDevice { protected int handleVendorCommandWithId(HdmiCecMessage message) { byte[] params = message.getParams(); int vendorId = HdmiUtils.threeBytesToInt(params); - if (vendorId == mService.getVendorId()) { - if (!mService.invokeVendorCommandListenersOnReceived( - mDeviceType, message.getSource(), message.getDestination(), params, true)) { - return Constants.ABORT_REFUSED; - } - } else if (message.getDestination() != Constants.ADDR_BROADCAST - && message.getSource() != Constants.ADDR_UNREGISTERED) { - Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>"); - return Constants.ABORT_UNRECOGNIZED_OPCODE; - } else { + if (message.getDestination() == Constants.ADDR_BROADCAST + || message.getSource() == Constants.ADDR_UNREGISTERED) { Slog.v(TAG, "Wrong broadcast vendor command. Ignoring"); + } else if (!mService.invokeVendorCommandListenersOnReceived( + mDeviceType, message.getSource(), message.getDestination(), params, true)) { + return Constants.ABORT_REFUSED; } return Constants.HANDLED; } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index a571dc06db97..8dadf5a8d20d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1582,11 +1582,11 @@ public class HdmiControlService extends SystemService { class VendorCommandListenerRecord implements IBinder.DeathRecipient { private final IHdmiVendorCommandListener mListener; - private final int mDeviceType; + private final int mVendorId; - public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) { + VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId) { mListener = listener; - mDeviceType = deviceType; + mVendorId = vendorId; } @Override @@ -2077,10 +2077,10 @@ public class HdmiControlService extends SystemService { } @Override - public void addVendorCommandListener(final IHdmiVendorCommandListener listener, - final int deviceType) { + public void addVendorCommandListener( + final IHdmiVendorCommandListener listener, final int vendorId) { initBinderCall(); - HdmiControlService.this.addVendorCommandListener(listener, deviceType); + HdmiControlService.this.addVendorCommandListener(listener, vendorId); } @Override @@ -3219,8 +3219,9 @@ public class HdmiControlService extends SystemService { } } - private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { - VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType); + @VisibleForTesting + void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) { + VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId); try { listener.asBinder().linkToDeath(record, 0); } catch (RemoteException e) { @@ -3239,8 +3240,14 @@ public class HdmiControlService extends SystemService { return false; } for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) { - if (record.mDeviceType != deviceType) { - continue; + if (hasVendorId) { + int vendorId = + ((params[0] & 0xFF) << 16) + + ((params[1] & 0xFF) << 8) + + (params[2] & 0xFF); + if (record.mVendorId != vendorId) { + continue; + } } try { record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 01bd04c6f06b..4c39768a8de7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -43,6 +43,7 @@ import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener; import android.hardware.hdmi.IHdmiControlStatusChangeListener; +import android.hardware.hdmi.IHdmiVendorCommandListener; import android.os.Binder; import android.os.IPowerManager; import android.os.IThermalService; @@ -886,6 +887,114 @@ public class HdmiControlServiceTest { } @Test + public void addVendorCommandListener_receiveCallback_VendorCmdNoIdTest() { + int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(); + int sourceAddress = Constants.ADDR_TV; + byte[] params = {0x00, 0x01, 0x02, 0x03}; + int vendorId = 0x123456; + + VendorCommandListener vendorCmdListener = + new VendorCommandListener(sourceAddress, destAddress, params, vendorId); + mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId); + mTestLooper.dispatchAll(); + + HdmiCecMessage vendorCommandNoId = + HdmiCecMessageBuilder.buildVendorCommand(sourceAddress, destAddress, params); + mNativeWrapper.onCecMessage(vendorCommandNoId); + mTestLooper.dispatchAll(); + assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue(); + assertThat(vendorCmdListener.mParamsCorrect).isTrue(); + assertThat(vendorCmdListener.mHasVendorId).isFalse(); + } + + @Test + public void addVendorCommandListener_receiveCallback_VendorCmdWithIdTest() { + int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(); + int sourceAddress = Constants.ADDR_TV; + byte[] params = {0x00, 0x01, 0x02, 0x03}; + int vendorId = 0x123456; + + VendorCommandListener vendorCmdListener = + new VendorCommandListener(sourceAddress, destAddress, params, vendorId); + mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId); + mTestLooper.dispatchAll(); + + HdmiCecMessage vendorCommandWithId = + HdmiCecMessageBuilder.buildVendorCommandWithId( + sourceAddress, destAddress, vendorId, params); + mNativeWrapper.onCecMessage(vendorCommandWithId); + mTestLooper.dispatchAll(); + assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue(); + assertThat(vendorCmdListener.mParamsCorrect).isTrue(); + assertThat(vendorCmdListener.mHasVendorId).isTrue(); + } + + @Test + public void addVendorCommandListener_noCallback_VendorCmdDiffIdTest() { + int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress(); + int sourceAddress = Constants.ADDR_TV; + byte[] params = {0x00, 0x01, 0x02, 0x03}; + int vendorId = 0x123456; + int diffVendorId = 0x345678; + + VendorCommandListener vendorCmdListener = + new VendorCommandListener(sourceAddress, destAddress, params, vendorId); + mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId); + mTestLooper.dispatchAll(); + + HdmiCecMessage vendorCommandWithDiffId = + HdmiCecMessageBuilder.buildVendorCommandWithId( + sourceAddress, destAddress, diffVendorId, params); + mNativeWrapper.onCecMessage(vendorCommandWithDiffId); + mTestLooper.dispatchAll(); + assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isFalse(); + } + + private static class VendorCommandListener extends IHdmiVendorCommandListener.Stub { + boolean mVendorCommandCallbackReceived = false; + boolean mParamsCorrect = false; + boolean mHasVendorId = false; + + int mSourceAddress; + int mDestAddress; + byte[] mParams; + int mVendorId; + + VendorCommandListener(int sourceAddress, int destAddress, byte[] params, int vendorId) { + this.mSourceAddress = sourceAddress; + this.mDestAddress = destAddress; + this.mParams = params.clone(); + this.mVendorId = vendorId; + } + + @Override + public void onReceived( + int sourceAddress, int destAddress, byte[] params, boolean hasVendorId) { + mVendorCommandCallbackReceived = true; + if (mSourceAddress == sourceAddress && mDestAddress == destAddress) { + byte[] expectedParams; + if (hasVendorId) { + // If the command has vendor ID, we have to add it to mParams. + expectedParams = new byte[params.length]; + expectedParams[0] = (byte) ((mVendorId >> 16) & 0xFF); + expectedParams[1] = (byte) ((mVendorId >> 8) & 0xFF); + expectedParams[2] = (byte) (mVendorId & 0xFF); + System.arraycopy(mParams, 0, expectedParams, 3, mParams.length); + } else { + expectedParams = params.clone(); + } + if (Arrays.equals(expectedParams, params)) { + mParamsCorrect = true; + } + } + mHasVendorId = hasVendorId; + } + + @Override + public void onControlStateChanged(boolean enabled, int reason) {} + } + + @Test public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() { HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby( Constants.ADDR_TV, |