Merge "Cache ITelephony service."
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index f0fab26..fe97f70 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -122,7 +122,6 @@
         addValidationInfo(Constants.MESSAGE_RECORD_STATUS,
                 new RecordStatusInfoValidator(), DEST_DIRECT);
 
-        // TODO: Handle messages for the Timer Programming.
         addValidationInfo(
                 Constants.MESSAGE_CLEAR_ANALOG_TIMER, new AnalogueTimerValidator(), DEST_DIRECT);
         addValidationInfo(
@@ -141,6 +140,7 @@
                 Constants.MESSAGE_TIMER_CLEARED_STATUS,
                 new TimerClearedStatusValidator(),
                 DEST_DIRECT);
+        addValidationInfo(Constants.MESSAGE_TIMER_STATUS, new TimerStatusValidator(), DEST_DIRECT);
 
         // Messages for the System Information.
         FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
@@ -592,6 +592,46 @@
         return false;
     }
 
+    private boolean isValidProgrammedInfo(int programedInfo) {
+        return (isWithinRange(programedInfo, 0x00, 0x0B));
+    }
+
+    private boolean isValidNotProgrammedErrorInfo(int nonProgramedErrorInfo) {
+        return (isWithinRange(nonProgramedErrorInfo, 0x00, 0x0E));
+    }
+
+    private boolean isValidTimerStatusData(byte[] params, int offset) {
+        int programedIndicator = params[offset] & 0x10;
+        boolean durationAvailable = false;
+        if (programedIndicator == 0x10) {
+            // Programmed
+            int programedInfo = params[offset] & 0x0F;
+            if (isValidProgrammedInfo(programedInfo)) {
+                if (programedInfo == 0x09 || programedInfo == 0x0B) {
+                    durationAvailable = true;
+                } else {
+                    return true;
+                }
+            }
+        } else {
+            // Non programmed
+            int nonProgramedErrorInfo = params[offset] & 0x0F;
+            if (isValidNotProgrammedErrorInfo(nonProgramedErrorInfo)) {
+                if (nonProgramedErrorInfo == 0x0E) {
+                    durationAvailable = true;
+                } else {
+                    return true;
+                }
+            }
+        }
+        offset = offset + 1;
+        // Duration Available (2 bytes)
+        if (durationAvailable && params.length - offset >= 2) {
+            return (isValidDurationHours(params[offset]) && isValidMinute(params[offset + 1]));
+        }
+        return false;
+    }
+
     private class PhysicalAddressValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
@@ -809,4 +849,18 @@
             return toErrorCode(isWithinRange(params[0], 0x00, 0x02) || (params[0] & 0xFF) == 0x80);
         }
     }
+
+    /**
+     * Check if the given timer status data parameter is valid. A valid parameter should lie within
+     * the range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     */
+    private class TimerStatusValidator implements ParameterValidator {
+        @Override
+        public int isValid(byte[] params) {
+            if (params.length < 1) {
+                return ERROR_PARAMETER_SHORT;
+            }
+            return toErrorCode(isValidTimerStatusData(params, 0));
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index c84e10b..553df3b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -329,6 +329,30 @@
         assertMessageValidity("40:43:03").isEqualTo(ERROR_PARAMETER);
     }
 
+    @Test
+    public void isValid_timerStatus() {
+        // Programmed - Space available
+        assertMessageValidity("40:35:58").isEqualTo(OK);
+        // Programmed - Not enough space available
+        assertMessageValidity("40:35:B9:32:1C:4F").isEqualTo(OK);
+        // Not programmed - Date out of range
+        assertMessageValidity("40:35:82:3B").isEqualTo(OK);
+        // Not programmed - Duplicate
+        assertMessageValidity("40:35:EE:52:0C").isEqualTo(OK);
+
+        assertMessageValidity("4F:35:58").isEqualTo(ERROR_DESTINATION);
+        assertMessageValidity("F0:35:82").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("40:35").isEqualTo(ERROR_PARAMETER_SHORT);
+        // Programmed - Invalid programmed info
+        assertMessageValidity("40:35:BD").isEqualTo(ERROR_PARAMETER);
+        // Non programmed - Invalid not programmed error info
+        assertMessageValidity("40:35:DE").isEqualTo(ERROR_PARAMETER);
+        // Programmed - Might not be enough space available - Invalid duration hours
+        assertMessageValidity("40:35:BB:96:1C").isEqualTo(ERROR_PARAMETER);
+        // Not programmed - Duplicate - Invalid duration minutes
+        assertMessageValidity("40:35:EE:52:4A").isEqualTo(ERROR_PARAMETER);
+    }
+
     private IntegerSubject assertMessageValidity(String message) {
         return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message)));
     }