Add HdmiCecMessageValidator to verify the incoming messages.

- Remove all param's length-check-logics in the package.

Bug: 16051295, Bug: 16117332, Bug: 15841545
Change-Id: If48ad9731f4f4613fd22aa3d9ada7ba3142bc999
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 4da781f..0e0664f 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -141,12 +141,15 @@
     static final int MESSAGE_VENDOR_COMMAND_WITH_ID = 0xA0;
     static final int MESSAGE_CLEAR_EXTERNAL_TIMER = 0xA1;
     static final int MESSAGE_SET_EXTERNAL_TIMER = 0xA2;
+    static final int MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR = 0xA3;
+    static final int MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR = 0xA4;
     static final int MESSAGE_INITIATE_ARC = 0xC0;
     static final int MESSAGE_REPORT_ARC_INITIATED = 0xC1;
     static final int MESSAGE_REPORT_ARC_TERMINATED = 0xC2;
     static final int MESSAGE_REQUEST_ARC_INITIATION = 0xC3;
     static final int MESSAGE_REQUEST_ARC_TERMINATION = 0xC4;
     static final int MESSAGE_TERMINATE_ARC = 0xC5;
+    static final int MESSAGE_CDC_MESSAGE = 0xF8;
     static final int MESSAGE_ABORT = 0xFF;
 
     static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
@@ -168,6 +171,7 @@
     // Bit mask used to get the routing path of the top level device.
     // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0.
     static final int ROUTING_PATH_TOP_MASK = 0xF000;
+    static final int ROUTING_PATH_TOP_SHIFT = 12;
 
     static final int INVALID_PORT_ID = -1;
     static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index 6577406..3c699bc 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -253,18 +253,11 @@
         }
 
         byte params[] = cmd.getParams();
-        if (params.length == 3) {
-            current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
-            current.mDeviceType = params[2] & 0xFF;
+        current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
+        current.mDeviceType = params[2] & 0xFF;
 
-            increaseProcessedDeviceCount();
-            checkAndProceedStage();
-        } else {
-            // Physical address is a critical element in device info.
-            // If failed, remove device from device list and proceed to the next device.
-            removeDevice(mProcessedDeviceCount);
-            checkAndProceedStage();
-        }
+        increaseProcessedDeviceCount();
+        checkAndProceedStage();
     }
 
     private void handleSetOsdName(HdmiCecMessage cmd) {
@@ -301,12 +294,8 @@
         }
 
         byte[] params = cmd.getParams();
-        if (params.length == 3) {
-            int vendorId = HdmiUtils.threeBytesToInt(params);
-            current.mVendorId = vendorId;
-        } else {
-            Slog.w(TAG, "Invalid vendor id: " + cmd.toString());
-        }
+        int vendorId = HdmiUtils.threeBytesToInt(params);
+        current.mVendorId = vendorId;
         increaseProcessedDeviceCount();
         checkAndProceedStage();
     }
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index 47386a2..9767d21 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -124,12 +124,12 @@
 
         switch (mState) {
             case STATE_WAIT_FOR_REPORT_POWER_STATUS:
-                if (opcode == Constants.MESSAGE_REPORT_POWER_STATUS && params.length == 1) {
+                if (opcode == Constants.MESSAGE_REPORT_POWER_STATUS) {
                     return handleReportPowerStatus(params[0]);
                 }
                 return false;
             case STATE_WAIT_FOR_ACTIVE_SOURCE:
-                if (opcode == Constants.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
+                if (opcode == Constants.MESSAGE_ACTIVE_SOURCE) {
                     int activePath = HdmiUtils.twoBytesToInt(params);
                     ActiveSourceHandler
                             .create((HdmiCecLocalDeviceTv) localDevice(), mCallback)
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index c5169b9..0cae459 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -306,7 +306,6 @@
     private static boolean isPowerOnOrToggleCommand(HdmiCecMessage message) {
         byte[] params = message.getParams();
         return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
-                && params.length == 1
                 && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
                         || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_ON_FUNCTION
                         || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
@@ -315,7 +314,6 @@
     private static boolean isPowerOffOrToggleCommand(HdmiCecMessage message) {
         byte[] params = message.getParams();
         return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
-                && params.length == 1
                 && (params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER
                         || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_OFF_FUNCTION
                         || params[0] == HdmiCecKeycode.CEC_KEYCODE_POWER_TOGGLE_FUNCTION);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 02b5344..4185d25 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -390,10 +390,6 @@
         assertRunOnServiceThread();
         // Seq #21
         byte[] params = message.getParams();
-        if (params.length != 4) {
-            Slog.w(TAG, "Wrong parameter: " + message);
-            return true;
-        }
         int currentPath = HdmiUtils.twoBytesToInt(params);
         if (HdmiUtils.isAffectingActiveRoutingPath(getActivePath(), currentPath)) {
             int newPath = HdmiUtils.twoBytesToInt(params, 2);
@@ -410,10 +406,6 @@
         assertRunOnServiceThread();
 
         byte params[] = message.getParams();
-        if (params.length < 1) {
-            Slog.w(TAG, "Invalide <Report Audio Status> message:" + message);
-            return true;
-        }
         int mute = params[0] & 0x80;
         int volume = params[0] & 0x7F;
         setAudioStatus(mute == 0x80, volume);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
new file mode 100644
index 0000000..c96d0c8d
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.util.Slog;
+import android.util.SparseArray;
+
+/**
+ * A helper class to validates {@link HdmiCecMessage}.
+ */
+public final class HdmiCecMessageValidator {
+    private static final String TAG = "HdmiCecMessageValidator";
+
+    private final HdmiControlService mService;
+
+    interface ParameterValidator {
+        boolean isValid(byte[] params);
+    }
+
+    final SparseArray<ParameterValidator> mValidators = new SparseArray<>();
+
+    public HdmiCecMessageValidator(HdmiControlService service) {
+        mService = service;
+
+        // Messages related to the physical address.
+        PhysicalAddressValidator physicalAddressValidator = new PhysicalAddressValidator();
+        mValidators.append(Constants.MESSAGE_ACTIVE_SOURCE, physicalAddressValidator);
+        mValidators.append(Constants.MESSAGE_INACTIVE_SOURCE, physicalAddressValidator);
+        mValidators.append(Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
+                new ReportPhysicalAddressValidator());
+        mValidators.append(Constants.MESSAGE_ROUTING_CHANGE, new RoutingChangeValidator());
+        mValidators.append(Constants.MESSAGE_ROUTING_INFORMATION, physicalAddressValidator);
+        mValidators.append(Constants.MESSAGE_SET_STREAM_PATH, physicalAddressValidator);
+        mValidators.append(Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST, physicalAddressValidator);
+
+        // Messages have no parameter.
+        FixedLengthValidator noneValidator = new FixedLengthValidator(0);
+        mValidators.append(Constants.MESSAGE_ABORT, noneValidator);
+        mValidators.append(Constants.MESSAGE_GET_CEC_VERSION, noneValidator);
+        mValidators.append(Constants.MESSAGE_GET_MENU_LANGUAGE, noneValidator);
+        mValidators.append(Constants.MESSAGE_GIVE_AUDIO_STATUS, noneValidator);
+        mValidators.append(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, noneValidator);
+        mValidators.append(Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID, noneValidator);
+        mValidators.append(Constants.MESSAGE_GIVE_OSD_NAME, noneValidator);
+        mValidators.append(Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS, noneValidator);
+        mValidators.append(Constants.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS, noneValidator);
+        mValidators.append(Constants.MESSAGE_IMAGE_VIEW_ON, noneValidator);
+        mValidators.append(Constants.MESSAGE_INITIATE_ARC, noneValidator);
+        mValidators.append(Constants.MESSAGE_RECORD_OFF, noneValidator);
+        mValidators.append(Constants.MESSAGE_RECORD_TV_SCREEN, noneValidator);
+        mValidators.append(Constants.MESSAGE_REPORT_ARC_INITIATED, noneValidator);
+        mValidators.append(Constants.MESSAGE_REPORT_ARC_TERMINATED, noneValidator);
+        mValidators.append(Constants.MESSAGE_REQUEST_ARC_INITIATION, noneValidator);
+        mValidators.append(Constants.MESSAGE_REQUEST_ARC_TERMINATION, noneValidator);
+        mValidators.append(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, noneValidator);
+        mValidators.append(Constants.MESSAGE_STANDBY, noneValidator);
+        mValidators.append(Constants.MESSAGE_TERMINATE_ARC, noneValidator);
+        mValidators.append(Constants.MESSAGE_TEXT_VIEW_ON, noneValidator);
+        mValidators.append(Constants.MESSAGE_TUNER_STEP_DECREMENT, noneValidator);
+        mValidators.append(Constants.MESSAGE_TUNER_STEP_INCREMENT, noneValidator);
+        mValidators.append(Constants.MESSAGE_USER_CONTROL_RELEASED, noneValidator);
+        mValidators.append(Constants.MESSAGE_VENDOR_REMOTE_BUTTON_UP, noneValidator);
+
+        // TODO: Validate more than length for the following messages.
+
+        // Messages for the One Touch Record.
+        FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
+        mValidators.append(Constants.MESSAGE_RECORD_ON, new VariableLengthValidator(1, 8));
+        mValidators.append(Constants.MESSAGE_RECORD_STATUS, oneByteValidator);
+
+        // TODO: Handle messages for the Timer Programming.
+
+        // Messages for the System Information.
+        mValidators.append(Constants.MESSAGE_CEC_VERSION, oneByteValidator);
+        mValidators.append(Constants.MESSAGE_SET_MENU_LANGUAGE, new FixedLengthValidator(3));
+
+        // TODO: Handle messages for the Deck Control.
+
+        // TODO: Handle messages for the Tuner Control.
+
+        // Messages for the Vendor Specific Commands.
+        VariableLengthValidator maxLengthValidator = new VariableLengthValidator(0, 14);
+        mValidators.append(Constants.MESSAGE_DEVICE_VENDOR_ID, new FixedLengthValidator(3));
+        mValidators.append(Constants.MESSAGE_VENDOR_COMMAND, maxLengthValidator);
+        mValidators.append(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, maxLengthValidator);
+        mValidators.append(Constants.MESSAGE_VENDOR_REMOTE_BUTTON_DOWN, maxLengthValidator);
+
+        // Messages for the OSD.
+        mValidators.append(Constants.MESSAGE_SET_OSD_STRING, maxLengthValidator);
+        mValidators.append(Constants.MESSAGE_SET_OSD_NAME, maxLengthValidator);
+
+        // TODO: Handle messages for the Device Menu Control.
+
+        // Messages for the Remote Control Passthrough.
+        // TODO: Parse the first parameter and determine if it can have the next parameter.
+        mValidators.append(Constants.MESSAGE_USER_CONTROL_PRESSED,
+                new VariableLengthValidator(1, 2));
+
+        // Messages for the Power Status.
+        mValidators.append(Constants.MESSAGE_REPORT_POWER_STATUS, oneByteValidator);
+
+        // Messages for the General Protocol.
+        mValidators.append(Constants.MESSAGE_FEATURE_ABORT, new FixedLengthValidator(2));
+
+        // Messages for the System Audio Control.
+        mValidators.append(Constants.MESSAGE_REPORT_AUDIO_STATUS, oneByteValidator);
+        mValidators.append(Constants.MESSAGE_REPORT_SHORT_AUDIO_DESCRIPTOR,
+                new FixedLengthValidator(3));
+        mValidators.append(Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, oneByteValidator);
+        mValidators.append(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, oneByteValidator);
+        mValidators.append(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, oneByteValidator);
+
+        // Messages for the Audio Rate Control.
+        mValidators.append(Constants.MESSAGE_SET_AUDIO_RATE, oneByteValidator);
+
+        // All Messages for the ARC have no parameters.
+
+        // Messages for the Capability Discovery and Control.
+        mValidators.append(Constants.MESSAGE_CDC_MESSAGE, maxLengthValidator);
+    }
+
+    boolean isValid(HdmiCecMessage message) {
+        int opcode = message.getOpcode();
+        ParameterValidator validator = mValidators.get(opcode);
+        if (validator == null) {
+            Slog.i(TAG, "No validator for the message: " + message);
+            return true;
+        }
+        return validator.isValid(message.getParams());
+    }
+
+    private static class FixedLengthValidator implements ParameterValidator {
+        private final int mLength;
+
+        public FixedLengthValidator(int length) {
+            mLength = length;
+        }
+
+        @Override
+        public boolean isValid(byte[] params) {
+            return params.length == mLength;
+        }
+    }
+
+    private static class VariableLengthValidator implements ParameterValidator {
+        private final int mMinLength;
+        private final int mMaxLength;
+
+        public VariableLengthValidator(int minLength, int maxLength) {
+            mMinLength = minLength;
+            mMaxLength = maxLength;
+        }
+
+        @Override
+        public boolean isValid(byte[] params) {
+            return params.length >= mMinLength && params.length <= mMaxLength;
+        }
+    }
+
+    private boolean isValidPhysicalAddress(byte[] params, int offset) {
+        int path = HdmiUtils.twoBytesToInt(params, offset);
+        int portId = mService.pathToPortId(path);
+        if (portId == Constants.INVALID_PORT_ID) {
+            return false;
+        }
+        // TODO: Add more logic like validating 1.0.1.0.
+        return true;
+    }
+
+    /**
+     * Check if the given type is valid. A valid type is one of the actual
+     * logical device types defined in the standard ({@link #DEVICE_TV},
+     * {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER},
+     * and {@link #DEVICE_AUDIO_SYSTEM}).
+     *
+     * @param type device type
+     * @return true if the given type is valid
+     */
+    static boolean isValidType(int type) {
+        return (HdmiCecDeviceInfo.DEVICE_TV <= type
+                && type <= HdmiCecDeviceInfo.DEVICE_VIDEO_PROCESSOR)
+                && type != HdmiCecDeviceInfo.DEVICE_RESERVED;
+    }
+
+    private class PhysicalAddressValidator implements ParameterValidator {
+        @Override
+        public boolean isValid(byte[] params) {
+            if (params.length != 2) {
+                return false;
+            }
+            return isValidPhysicalAddress(params, 0);
+        }
+    }
+
+    private class ReportPhysicalAddressValidator implements ParameterValidator {
+        @Override
+        public boolean isValid(byte[] params) {
+            if (params.length != 3) {
+                return false;
+            }
+            return isValidPhysicalAddress(params, 0) && isValidType(params[2]);
+        }
+    }
+
+    private class RoutingChangeValidator implements ParameterValidator {
+        @Override
+        public boolean isValid(byte[] params) {
+            if (params.length != 4) {
+                return false;
+            }
+            return isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c32f312..d35f03d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -183,6 +183,8 @@
     // from being modified.
     private List<HdmiPortInfo> mPortInfo;
 
+    private HdmiCecMessageValidator mMessageValidator;
+
     private final PowerStateReceiver mPowerStateReceiver = new PowerStateReceiver();
 
     @ServiceThreadOnly
@@ -220,6 +222,7 @@
             Slog.i(TAG, "Device does not support MHL-control.");
         }
         mPortInfo = initPortInfo();
+        mMessageValidator = new HdmiCecMessageValidator(this);
         publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
 
         // Register broadcast receiver for power state change.
@@ -473,6 +476,9 @@
     @ServiceThreadOnly
     boolean handleCecCommand(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        if (!mMessageValidator.isValid(message)) {
+            return false;
+        }
         return dispatchMessageToLocalDevice(message);
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 3a43f83..b92e823 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -68,20 +68,6 @@
     private HdmiUtils() { /* cannot be instantiated */ }
 
     /**
-     * Check if the given type is valid. A valid type is one of the actual
-     * logical device types defined in the standard ({@link #DEVICE_TV},
-     * {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER},
-     * and {@link #DEVICE_AUDIO_SYSTEM}).
-     *
-     * @param type device type
-     * @return true if the given type is valid
-     */
-    static boolean isValidType(int type) {
-        return (HdmiCecDeviceInfo.DEVICE_TV <= type && type <= HdmiCecDeviceInfo.DEVICE_AUDIO_SYSTEM)
-                && type != HdmiCecDeviceInfo.DEVICE_RESERVED;
-    }
-
-    /**
      * Check if the given logical address is valid. A logical address is valid
      * if it is one allocated for an actual device which allows communication
      * with other logical devices.
@@ -162,9 +148,7 @@
      * @return true if the given parameter has [ON] value
      */
     static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) {
-        // TODO: Handle the exception when the length is wrong.
-        return cmd.getParams().length > 0
-                && cmd.getParams()[0] == Constants.SYSTEM_AUDIO_STATUS_ON;
+        return cmd.getParams()[0] == Constants.SYSTEM_AUDIO_STATUS_ON;
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 9f7bb60..a950afb 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -127,11 +127,7 @@
             }
         } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
             if (opcode == Constants.MESSAGE_DEVICE_VENDOR_ID) {
-                if (params.length == 3) {
-                    mVendorId = HdmiUtils.threeBytesToInt(params);
-                } else {
-                    Slog.e(TAG, "Failed to get device vendor ID: ");
-                }
+                mVendorId = HdmiUtils.threeBytesToInt(params);
                 addDeviceInfo();
                 finish();
                 return true;
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
index 941033f..ce9cc5c 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -94,21 +94,15 @@
 
     private void handleReportAudioStatus(HdmiCecMessage cmd) {
         byte[] params = cmd.getParams();
-        if (params.length > 0) {
-            boolean mute = (params[0] & 0x80) == 0x80;
-            int volume = params[0] & 0x7F;
-            tv().setAudioStatus(mute, volume);
+        boolean mute = (params[0] & 0x80) == 0x80;
+        int volume = params[0] & 0x7F;
+        tv().setAudioStatus(mute, volume);
 
-            if ((tv().getSystemAudioMode() && mute) || (!tv().getSystemAudioMode() && !mute)) {
-                // Toggle AVR's mute status to match with the system audio status.
-                sendUserControlPressedAndReleased(mAvrAddress, HdmiCecKeycode.CEC_KEYCODE_MUTE);
-            }
-            finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
-        } else {
-            Slog.e(TAG, "Invalid <Report Audio Status> message:" + cmd);
-            handleSendGiveAudioStatusFailure();
-            return;
+        if ((tv().getSystemAudioMode() && mute) || (!tv().getSystemAudioMode() && !mute)) {
+            // Toggle AVR's mute status to match with the system audio status.
+            sendUserControlPressedAndReleased(mAvrAddress, HdmiCecKeycode.CEC_KEYCODE_MUTE);
         }
+        finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
     }
 
     private void finishWithCallback(int returnCode) {
diff --git a/services/core/java/com/android/server/hdmi/VolumeControlAction.java b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
index 3701f88..e32b792 100644
--- a/services/core/java/com/android/server/hdmi/VolumeControlAction.java
+++ b/services/core/java/com/android/server/hdmi/VolumeControlAction.java
@@ -18,8 +18,6 @@
 
 import static com.android.server.hdmi.Constants.IRT_MS;
 
-import android.util.Slog;
-
 import com.android.internal.util.Preconditions;
 
 /**
@@ -168,11 +166,6 @@
 
     private void handleReportAudioStatus(HdmiCecMessage cmd) {
         byte[] params = cmd.getParams();
-        if (params.length != 1) {
-            Slog.e(TAG, "Invalid <Report Audio Status> message:" + cmd);
-            return;
-        }
-
         int volume = params[0] & 0x7F;
         // Update volume with new value.
         // Note that it will affect system volume change.