Merge "CEC: Change vendor command handling" am: 134f59f851

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1807376

Change-Id: Ia4d1dda5058b8fa8ec452a3556cf5b76cf7f642e
This commit is contained in:
Nathalie Le Clair
2022-02-01 13:03:01 +00:00
committed by Automerger Merge Worker
8 changed files with 156 additions and 29 deletions

View File

@@ -2954,6 +2954,7 @@ package android.hardware.hdmi {
method public void sendKeyEvent(int, boolean); method public void sendKeyEvent(int, boolean);
method public void sendVendorCommand(int, byte[], 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);
method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener, int);
} }
public final class HdmiControlManager { public final class HdmiControlManager {

View File

@@ -17,6 +17,8 @@ import android.util.Log;
public abstract class HdmiClient { public abstract class HdmiClient {
private static final String TAG = "HdmiClient"; private static final String TAG = "HdmiClient";
private static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
/* package */ final IHdmiControlService mService; /* package */ final IHdmiControlService mService;
private IHdmiVendorCommandListener mIHdmiVendorCommandListener; 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 * @param listener listener object
*/ */
public void setVendorCommandListener(@NonNull VendorCommandListener listener) { 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) { if (listener == null) {
throw new IllegalArgumentException("listener cannot be null"); throw new IllegalArgumentException("listener cannot be null");
} }
@@ -107,7 +123,7 @@ public abstract class HdmiClient {
} }
try { try {
IHdmiVendorCommandListener wrappedListener = getListenerWrapper(listener); IHdmiVendorCommandListener wrappedListener = getListenerWrapper(listener);
mService.addVendorCommandListener(wrappedListener, getDeviceType()); mService.addVendorCommandListener(wrappedListener, vendorId);
mIHdmiVendorCommandListener = wrappedListener; mIHdmiVendorCommandListener = wrappedListener;
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "failed to set vendor command listener: ", e); Log.e(TAG, "failed to set vendor command listener: ", e);

View File

@@ -221,8 +221,8 @@ public final class HdmiControlServiceWrapper {
} }
@Override @Override
public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
HdmiControlServiceWrapper.this.addVendorCommandListener(listener, deviceType); HdmiControlServiceWrapper.this.addVendorCommandListener(listener, vendorId);
} }
@Override @Override
@@ -471,7 +471,7 @@ public final class HdmiControlServiceWrapper {
boolean hasVendorId) {} boolean hasVendorId) {}
/** @hide */ /** @hide */
public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {} public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {}
/** @hide */ /** @hide */
public void sendStandby(int deviceType, int deviceId) {} public void sendStandby(int deviceType, int deviceId) {}

View File

@@ -76,7 +76,7 @@ interface IHdmiControlService {
void askRemoteDeviceToBecomeActiveSource(int physicalAddress); void askRemoteDeviceToBecomeActiveSource(int physicalAddress);
void sendVendorCommand(int deviceType, int targetAddress, in byte[] params, void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
boolean hasVendorId); boolean hasVendorId);
void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType); void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId);
void sendStandby(int deviceType, int deviceId); void sendStandby(int deviceType, int deviceId);
void setHdmiRecordListener(IHdmiRecordListener callback); void setHdmiRecordListener(IHdmiRecordListener callback);
void startOneTouchRecord(int recorderAddress, in byte[] recordSource); void startOneTouchRecord(int recorderAddress, in byte[] recordSource);

View File

@@ -288,9 +288,8 @@ public class HdmiAudioSystemClientTest {
} }
@Override @Override
public void addVendorCommandListener(final IHdmiVendorCommandListener listener, public void addVendorCommandListener(
final int deviceType) { final IHdmiVendorCommandListener listener, final int vendorId) {}
}
@Override @Override
public void sendVendorCommand(final int deviceType, final int targetAddress, public void sendVendorCommand(final int deviceType, final int targetAddress,

View File

@@ -829,17 +829,12 @@ abstract class HdmiCecLocalDevice {
protected int handleVendorCommandWithId(HdmiCecMessage message) { protected int handleVendorCommandWithId(HdmiCecMessage message) {
byte[] params = message.getParams(); byte[] params = message.getParams();
int vendorId = HdmiUtils.threeBytesToInt(params); int vendorId = HdmiUtils.threeBytesToInt(params);
if (vendorId == mService.getVendorId()) { if (message.getDestination() == Constants.ADDR_BROADCAST
if (!mService.invokeVendorCommandListenersOnReceived( || message.getSource() == Constants.ADDR_UNREGISTERED) {
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 {
Slog.v(TAG, "Wrong broadcast vendor command. Ignoring"); 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; return Constants.HANDLED;
} }

View File

@@ -1582,11 +1582,11 @@ public class HdmiControlService extends SystemService {
class VendorCommandListenerRecord implements IBinder.DeathRecipient { class VendorCommandListenerRecord implements IBinder.DeathRecipient {
private final IHdmiVendorCommandListener mListener; 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; mListener = listener;
mDeviceType = deviceType; mVendorId = vendorId;
} }
@Override @Override
@@ -2077,10 +2077,10 @@ public class HdmiControlService extends SystemService {
} }
@Override @Override
public void addVendorCommandListener(final IHdmiVendorCommandListener listener, public void addVendorCommandListener(
final int deviceType) { final IHdmiVendorCommandListener listener, final int vendorId) {
initBinderCall(); initBinderCall();
HdmiControlService.this.addVendorCommandListener(listener, deviceType); HdmiControlService.this.addVendorCommandListener(listener, vendorId);
} }
@Override @Override
@@ -3219,8 +3219,9 @@ public class HdmiControlService extends SystemService {
} }
} }
private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) { @VisibleForTesting
VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType); void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
try { try {
listener.asBinder().linkToDeath(record, 0); listener.asBinder().linkToDeath(record, 0);
} catch (RemoteException e) { } catch (RemoteException e) {
@@ -3239,8 +3240,14 @@ public class HdmiControlService extends SystemService {
return false; return false;
} }
for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) { for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
if (record.mDeviceType != deviceType) { if (hasVendorId) {
continue; int vendorId =
((params[0] & 0xFF) << 16)
+ ((params[1] & 0xFF) << 8)
+ (params[2] & 0xFF);
if (record.mVendorId != vendorId) {
continue;
}
} }
try { try {
record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId); 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.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener; import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener; import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.os.Binder; import android.os.Binder;
import android.os.IPowerManager; import android.os.IPowerManager;
import android.os.IThermalService; import android.os.IThermalService;
@@ -885,6 +886,114 @@ public class HdmiControlServiceTest {
.isEqualTo(Constants.ABORT_REFUSED); .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 @Test
public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() { public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby( HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby(