CEC: Change vendor command handling

If a vendor command without vendor ID is received, the message is now
forwarded to all registered listeners, irrespective of the device type.

If a vendor command with ID is received, the message is forwarded to all
listeners registered with that vendor ID

Bug: 177061176
Test: atest com.android.server.hdmi.HdmiControlServiceTest#addVendorCommandListener_noCallback_VendorCmdDiffIdTest
  atest com.android.server.hdmi.HdmiControlServiceTest#addVendorCommandListener_receiveCallback_VendorCmdNoIdTest
  atest com.android.server.hdmi.HdmiControlServiceTest#addVendorCommandListener_receiveCallback_VendorCmdWithIdTest
Change-Id: I84e010ccbe58ff090348babba05db0c31add1136
This commit is contained in:
Venkatarama NG. Avadhani
2021-08-23 17:13:27 +05:30
parent d5bd847275
commit 6c65342c8d
8 changed files with 156 additions and 29 deletions

View File

@@ -3345,6 +3345,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 {

View File

@@ -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);

View File

@@ -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) {}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
@@ -885,6 +886,114 @@ public class HdmiControlServiceTest {
.isEqualTo(Constants.ABORT_REFUSED);
}
@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(