Add Device OSD Name Transfer Tests

Adds three tests in the set -
1. Check that the DUT responds with a <SET_OSD_NAME> message when a
<GIVE_OSD_NAME> message is sent from the TV. Also checks that the name
matches the global setting "device_name".
2. Update the global setting "device_name" and check that the DUT
sends a <SET_OSD_NAME> with the updated device name in reponse to a
<GIVE_OSD_NAME>
3. Ensure that the device does not respond to a <GIVE_OSD_NAME> from an
unregistered device. This test tests for PLAYBACK_1 and BROADCAST.

Bug: 141606867
Test: run cts -m CtsHdmiCecHostTestCases -t android.hdmicec.cts.HdmiCecDeviceOsdNameTest
Change-Id: Ib1a6c36b04915de5ccf6a8788e68ce708bb239c0
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
index 85dce04..7c95f7e 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/CecMessage.java
@@ -25,6 +25,8 @@
     STANDBY(0x36),
     USER_CONTROL_PRESSED(0x44),
     USER_CONTROL_RELEASED(0x45),
+    GIVE_OSD_NAME(0x46),
+    SET_OSD_NAME(0x47),
     GIVE_SYSTEM_AUDIO_MODE_STATUS(0x7d),
     ACTIVE_SOURCE(0x82),
     GIVE_PHYSICAL_ADDRESS(0x83),
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
index 3df185b..f4c662d 100644
--- a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecClientWrapper.java
@@ -281,6 +281,47 @@
         throw new Exception("Could not find message " + expectedMessage.name());
     }
 
+    /**
+     * Looks for the CEC message incorrectMessage sent to CEC device toDevice on the cec-client
+     * communication channel and throws an exception if it finds the line that contains the message
+     * within the default timeout. If the CEC message is not found within the timeout, function
+     * returns without error.
+     */
+    public void checkOutputDoesNotContainMessage(CecDevice toDevice,
+            CecMessage incorrectMessage) throws Exception {
+        checkOutputDoesNotContainMessage(toDevice, incorrectMessage, DEFAULT_TIMEOUT);
+     }
+
+    /**
+     * Looks for the CEC message incorrectMessage sent to CEC device toDevice on the cec-client
+     * communication channel and throws an exception if it finds the line that contains the message
+     * within timeoutMillis. If the CEC message is not found within the timeout, function returns
+     * without error.
+     */
+    public void checkOutputDoesNotContainMessage(CecDevice toDevice, CecMessage incorrectMessage,
+            long timeoutMillis) throws Exception {
+
+        checkCecClient();
+        long startTime = System.currentTimeMillis();
+        long endTime = startTime;
+        Pattern pattern = Pattern.compile("(.*>>)(.*?)" +
+                                          "(" + targetDevice + toDevice + "):" +
+                                          "(" + incorrectMessage + ")(.*)",
+                                          Pattern.CASE_INSENSITIVE);
+
+        while ((endTime - startTime <= timeoutMillis)) {
+            if (mInputConsole.ready()) {
+                String line = mInputConsole.readLine();
+                if (pattern.matcher(line).matches()) {
+                    CLog.v("Found " + incorrectMessage.name() + " in " + line);
+                    throw new Exception("Found " + incorrectMessage.name() + " to " + toDevice +
+                            " with params " + getParamsFromMessage(line));
+                }
+            }
+            endTime = System.currentTimeMillis();
+        }
+     }
+
     /** Gets the hexadecimal ASCII character values of a string. */
     public String getHexAsciiString(String string) {
         String asciiString = "";
@@ -331,6 +372,17 @@
         return Integer.parseInt(message, HEXADECIMAL_RADIX);
     }
 
+    public String getAsciiStringFromMessage(String message) {
+        String params = getNibbles(message).substring(4);
+        StringBuilder builder = new StringBuilder();
+
+        for (int i = 2; i <= params.length(); i += 2) {
+            builder.append((char) hexStringToInt(params.substring(i - 2, i)));
+        }
+
+        return builder.toString();
+    }
+
     /**
      * Gets the params from a CEC message.
      */
diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecDeviceOsdNameTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecDeviceOsdNameTest.java
new file mode 100644
index 0000000..46306f1
--- /dev/null
+++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/HdmiCecDeviceOsdNameTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 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 android.hdmicec.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+/** HDMI CEC tests related to the device reporting the device OSD name (Section 11.2.11) */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public final class HdmiCecDeviceOsdNameTest extends BaseHostJUnit4Test {
+    private static final CecDevice PLAYBACK_DEVICE = CecDevice.PLAYBACK_1;
+
+    @Rule
+    public HdmiCecClientWrapper hdmiCecClient =
+        new HdmiCecClientWrapper(CecDevice.PLAYBACK_1, this);
+
+    /**
+     * Test 11.2.11-1a
+     * Tests that the device responds to a <GIVE_OSD_NAME> with a <SET_OSD_NAME> that has the
+     * correct device name in the parameters.
+     */
+    @Test
+    public void cect_11_2_11_1a_GiveOsdNameTest() throws Exception {
+        /* The params for <SET_OSD_NAME> only allow for 14 characters */
+        final int nameLength = 14;
+        ITestDevice device = getDevice();
+        String deviceName = device.executeShellCommand("settings get global device_name").trim();
+        if (deviceName.length() > nameLength) {
+            deviceName = deviceName.substring(0, nameLength).trim();
+        }
+        hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_OSD_NAME);
+        String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV, CecMessage.SET_OSD_NAME);
+        assertEquals(deviceName, hdmiCecClient.getAsciiStringFromMessage(message));
+    }
+
+    /**
+     * Test 11.2.11-1b
+     * Test updates the device_name in global properties and checks that the device responds to a
+     * <GIVE_OSD_NAME> with a <SET_OSD_NAME> that has the updated device name in the parameters.
+     */
+    @Test
+    public void cect_11_2_11_1b_UpdateAndGiveOsdNameTest() throws Exception {
+        final String testName = "test_name";
+        ITestDevice device = getDevice();
+        String originalName = device.executeShellCommand("settings get global device_name").trim();
+        try {
+            device.executeShellCommand("settings put global device_name '" + testName + "'");
+            hdmiCecClient.sendCecMessage(CecDevice.TV, CecMessage.GIVE_OSD_NAME);
+            String message = hdmiCecClient.checkExpectedOutput(CecDevice.TV,
+                    CecMessage.SET_OSD_NAME);
+            assertEquals(testName, hdmiCecClient.getAsciiStringFromMessage(message));
+        } finally {
+            device.executeShellCommand("settings put global device_name '" + originalName + "'");
+        }
+    }
+
+    /**
+     * Test 11.2.11-2
+     * Tests that the device does not respond to a <GIVE_OSD_NAME> from an unregistered device.
+     */
+    @Test
+    public void cect_11_2_11_2_UnregisteredDeviceGiveOsdNameTest() throws Exception {
+        hdmiCecClient.sendCecMessage(CecDevice.PLAYBACK_1, CecMessage.GIVE_OSD_NAME);
+        hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.PLAYBACK_1,
+                CecMessage.SET_OSD_NAME);
+        hdmiCecClient.sendCecMessage(CecDevice.BROADCAST, CecMessage.GIVE_OSD_NAME);
+        hdmiCecClient.checkOutputDoesNotContainMessage(CecDevice.BROADCAST,
+                CecMessage.SET_OSD_NAME);
+    }
+}