Prevent sending <Feature Abort> for some messages.

This change fixes the following cases.
- <Report Power Status>(0x90)
-  When TV receives <Report Power Status> as a reply of <Give Device Power Status>, TV does not send <Feature Abort>. But if a device sends <Report Power Status> actively, TV sends <Feature Abort>.
-  <Set System Audio Mode>(0x72)
-  <System Audio Mode Status>(0x7E) Directly address message is also defined.
-  <Record Status>(0x0A) (We will support One-touch record function at least in Japan)
-  <Timer Status>(0x35) (We will support Timer programming function at least in Japan)
-  No response for <Record TV Screen>(0x0F)

Bug: 17382769
Change-Id: I8ae355337757710d54b788edfdf37293f96cfa97
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index aa1310d..6ce235b 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -159,6 +159,8 @@
     static final int TRUE = 1;
     static final int FALSE = 0;
 
+    // Internal abort error code. It's the same as success.
+    static final int ABORT_NO_ERROR = -1;
     // Constants related to operands of HDMI CEC commands.
     // Refer to CEC Table 29 in HDMI Spec v1.4b.
     // [Abort Reason]
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 04e38dc..c00c5d0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -274,6 +274,12 @@
                 return handleRecordTvScreen(message);
             case Constants.MESSAGE_TIMER_CLEARED_STATUS:
                 return handleTimerClearedStatus(message);
+            case Constants.MESSAGE_REPORT_POWER_STATUS:
+                return handleReportPowerStatus(message);
+            case Constants.MESSAGE_TIMER_STATUS:
+                return handleTimerStatus(message);
+            case Constants.MESSAGE_RECORD_STATUS:
+                return handleRecordStatus(message);
             default:
                 return false;
         }
@@ -541,6 +547,18 @@
         return false;
     }
 
+    protected boolean handleReportPowerStatus(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleTimerStatus(HdmiCecMessage message) {
+        return false;
+    }
+
+    protected boolean handleRecordStatus(HdmiCecMessage message) {
+        return false;
+    }
+
     @ServiceThreadOnly
     final void handleAddressAllocated(int logicalAddress, int reason) {
         assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 5994c76..58ccbdb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -474,6 +474,25 @@
         return true;
     }
 
+    @Override
+    protected boolean handleReportPowerStatus(HdmiCecMessage command) {
+        int newStatus = command.getParams()[0] & 0xFF;
+        updateDevicePowerStatus(command.getSource(), newStatus);
+        return true;
+    }
+
+    @Override
+    protected boolean handleTimerStatus(HdmiCecMessage message) {
+        // Do nothing.
+        return true;
+    }
+
+    @Override
+    protected boolean handleRecordStatus(HdmiCecMessage message) {
+        // Do nothing.
+        return true;
+    }
+
     boolean updateCecSwitchInfo(int address, int type, int path) {
         if (address == Constants.ADDR_UNREGISTERED
                 && type == HdmiDeviceInfo.DEVICE_PURE_CEC_SWITCH) {
@@ -937,7 +956,8 @@
         assertRunOnServiceThread();
         if (!isMessageForSystemAudio(message)) {
             HdmiLogger.warning("Invalid <Set System Audio Mode> message:" + message);
-            return false;
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
         }
         SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
                 message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message), null);
@@ -951,7 +971,8 @@
         assertRunOnServiceThread();
         if (!isMessageForSystemAudio(message)) {
             HdmiLogger.warning("Invalid <System Audio Mode Status> message:" + message);
-            return false;
+            // Ignore this message.
+            return true;
         }
         setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message), true);
         return true;
@@ -974,7 +995,10 @@
 
         int recorderAddress = message.getSource();
         byte[] recordSource = mService.invokeRecordRequestListener(recorderAddress);
-        startOneTouchRecord(recorderAddress, recordSource);
+        int reason = startOneTouchRecord(recorderAddress, recordSource);
+        if (reason != Constants.ABORT_NO_ERROR) {
+            mService.maySendFeatureAbortCommand(message, reason);
+        }
         return true;
     }
 
@@ -999,7 +1023,8 @@
     }
 
     private boolean isMessageForSystemAudio(HdmiCecMessage message) {
-        return message.getSource() == Constants.ADDR_AUDIO_SYSTEM
+        return mService.isControlEnabled()
+                && message.getSource() == Constants.ADDR_AUDIO_SYSTEM
                 && (message.getDestination() == Constants.ADDR_TV
                         || message.getDestination() == Constants.ADDR_BROADCAST)
                 && getAvrDeviceInfo() != null;
@@ -1454,29 +1479,30 @@
 
     // Seq #54 and #55
     @ServiceThreadOnly
-    void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
+    int startOneTouchRecord(int recorderAddress, byte[] recordSource) {
         assertRunOnServiceThread();
         if (!mService.isControlEnabled()) {
             Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
             announceOneTouchRecordResult(ONE_TOUCH_RECORD_CEC_DISABLED);
-            return;
+            return Constants.ABORT_NOT_IN_CORRECT_MODE;
         }
 
         if (!checkRecorder(recorderAddress)) {
             Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
             announceOneTouchRecordResult(ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
-            return;
+            return Constants.ABORT_NOT_IN_CORRECT_MODE;
         }
 
         if (!checkRecordSource(recordSource)) {
             Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
             announceOneTouchRecordResult(ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN);
-            return;
+            return Constants.ABORT_UNABLE_TO_DETERMINE;
         }
 
         addAndStartAction(new OneTouchRecordAction(this, recorderAddress, recordSource));
         Slog.i(TAG, "Start new [One Touch Record]-Target:" + recorderAddress + ", recordSource:"
                 + Arrays.toString(recordSource));
+        return Constants.ABORT_NO_ERROR;
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index 03fbb95..1e29fd6 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -77,7 +77,7 @@
             // if no device exists for incoming message, hands it over to other actions.
             return false;
         }
-        int newStatus = cmd.getParams()[0];
+        int newStatus = cmd.getParams()[0] & 0xFF;
         updatePowerStatus(sourceAddress, newStatus, true);
         return true;
     }