Refactor <Feature Abort> logic to concentrate it in one place.
- Don't reply from the unregistered address.
- Use "unrecognized opcode" as the default reason.
Bug: 16799466, Bug: 16798785
Change-Id: I7c2ece6436f7ebd59986d2baf4f45cd86e6622d9
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index cbccc1d..c33b35f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -502,24 +502,30 @@
@ServiceThreadOnly
private void onReceiveCommand(HdmiCecMessage message) {
assertRunOnServiceThread();
- if (isAcceptableAddress(message.getDestination())
- && mService.handleCecCommand(message)) {
+ if (isAcceptableAddress(message.getDestination()) && mService.handleCecCommand(message)) {
return;
}
- if (message.getDestination() == Constants.ADDR_BROADCAST) {
- return;
- }
- if (message.getOpcode() == Constants.MESSAGE_FEATURE_ABORT) {
- Slog.v(TAG, "Unhandled <Feature Abort> message:" + message);
- return;
- }
+ // Not handled message, so we will reply it with <Feature Abort>.
+ maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
+ }
- int sourceAddress = message.getDestination();
- // Reply <Feature Abort> to initiator (source) for all requests.
- HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
- sourceAddress, message.getSource(), message.getOpcode(),
- Constants.ABORT_REFUSED);
- sendCommand(cecMessage);
+ @ServiceThreadOnly
+ void maySendFeatureAbortCommand(HdmiCecMessage message, int reason) {
+ assertRunOnServiceThread();
+ // Swap the source and the destination.
+ int src = message.getDestination();
+ int dest = message.getSource();
+ if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_UNREGISTERED) {
+ // Don't reply <Feature Abort> from the unregistered devices or for the broadcasted
+ // messages. See CEC 12.2 Protocol General Rules for detail.
+ return;
+ }
+ int originalOpcode = message.getOpcode();
+ if (originalOpcode == Constants.MESSAGE_FEATURE_ABORT) {
+ return;
+ }
+ sendCommand(
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(src, dest, originalOpcode, reason));
}
@ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 55f6ade..cf16fa3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -308,11 +308,8 @@
protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
assertRunOnServiceThread();
Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
- message.getSource(), Constants.MESSAGE_GET_MENU_LANGUAGE,
- Constants.ABORT_UNRECOGNIZED_OPCODE));
- return true;
+ // 'return false' will cause to reply with <Feature Abort>.
+ return false;
}
@ServiceThreadOnly
@@ -431,9 +428,7 @@
} else if (message.getDestination() != Constants.ADDR_BROADCAST &&
message.getSource() != Constants.ADDR_UNREGISTERED) {
Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
- mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
- message.getSource(), Constants.MESSAGE_VENDOR_COMMAND_WITH_ID,
- Constants.ABORT_UNRECOGNIZED_OPCODE));
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
} else {
Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
}
@@ -448,8 +443,7 @@
protected boolean handleRecordTvScreen(HdmiCecMessage message) {
// The default behavior of <Record TV Screen> is replying <Feature Abort> with
// "Cannot provide source".
- mService.sendCecCommand(HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
- message.getSource(), message.getOpcode(), Constants.ABORT_CANNOT_PROVIDE_SOURCE));
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_CANNOT_PROVIDE_SOURCE);
return true;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index c653125..8c40424 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -48,16 +48,7 @@
}
@Override
public boolean handle(HdmiCecMessage message) {
- int src = message.getSource();
- int dest = message.getDestination();
- if (src == Constants.ADDR_BROADCAST || dest == Constants.ADDR_BROADCAST) {
- // Do not send <Feature Abort> on the message from the unassigned device
- // or the broadcasted message.
- return true;
- }
- HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
- dest, src, message.getOpcode(), mReason);
- mService.sendCecCommand(cecMessage);
+ mService.maySendFeatureAbortCommand(message, mReason);
return true;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e0673e9..fe55b01 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -532,6 +532,18 @@
mCecController.sendCommand(command, null);
}
+ /**
+ * Send <Feature Abort> command on the given CEC message if possible.
+ * If the aborted message is invalid, then it wont send the message.
+ * @param command original command to be aborted
+ * @param reason reason of feature abort
+ */
+ @ServiceThreadOnly
+ void maySendFeatureAbortCommand(HdmiCecMessage command, int reason) {
+ assertRunOnServiceThread();
+ mCecController.maySendFeatureAbortCommand(command, reason);
+ }
+
@ServiceThreadOnly
boolean handleCecCommand(HdmiCecMessage message) {
assertRunOnServiceThread();