1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hdmicec.cts;
18 
19 import java.util.regex.Matcher;
20 import java.util.regex.Pattern;
21 
22 public class CecMessage {
23 
24     private static final int HEXADECIMAL_RADIX = 16;
25 
26     /** Gets the hexadecimal ASCII character values of a string. */
getHexAsciiString(String string)27     public static String getHexAsciiString(String string) {
28         String asciiString = "";
29         byte[] ascii = string.trim().getBytes();
30 
31         for (byte b : ascii) {
32             asciiString.concat(Integer.toHexString(b));
33         }
34 
35         return asciiString;
36     }
37 
formatParams(String rawParams)38     public static String formatParams(String rawParams) {
39         StringBuilder params = new StringBuilder("");
40         int position = 0;
41         int endPosition = 2;
42 
43         do {
44             params.append(":" + rawParams.substring(position, endPosition));
45             position = endPosition;
46             endPosition += 2;
47         } while (endPosition <= rawParams.length());
48         return params.toString();
49     }
50 
formatParams(long rawParam)51     public static String formatParams(long rawParam) {
52         StringBuilder params = new StringBuilder("");
53 
54         do {
55             params.insert(0, ":" + String.format("%02x", rawParam % 256));
56             rawParam >>= 8;
57         } while (rawParam > 0);
58 
59         return params.toString();
60     }
61 
62     /**
63      * Formats the rawParam into CEC message parameters. The parameters will be at least
64      * minimumNibbles long.
65      */
formatParams(long rawParam, int minimumNibbles)66     public static String formatParams(long rawParam, int minimumNibbles) {
67         StringBuilder params = new StringBuilder("");
68 
69         do {
70             params.insert(0, ":" + String.format("%02x", rawParam % 256));
71             rawParam >>= 8;
72             minimumNibbles -= 2;
73         } while (rawParam > 0 || minimumNibbles > 0);
74 
75         return params.toString();
76     }
77 
hexStringToInt(String message)78     public static int hexStringToInt(String message) {
79         return Integer.parseInt(message, HEXADECIMAL_RADIX);
80     }
81 
getAsciiString(String message)82     public static String getAsciiString(String message) {
83         String params = getNibbles(message).substring(4);
84         StringBuilder builder = new StringBuilder();
85 
86         for (int i = 2; i <= params.length(); i += 2) {
87             builder.append((char) hexStringToInt(params.substring(i - 2, i)));
88         }
89 
90         return builder.toString();
91     }
92 
getParamsAsString(String message)93     public static String getParamsAsString(String message) {
94         return getNibbles(message).substring(4);
95     }
96 
97     /** Gets the params from a CEC message. */
getParams(String message)98     public static int getParams(String message) {
99         return hexStringToInt(getNibbles(message).substring(4));
100     }
101 
102     /** Gets the first 'numNibbles' number of param nibbles from a CEC message. */
getParams(String message, int numNibbles)103     public static int getParams(String message, int numNibbles) {
104         int paramStart = 4;
105         int end = numNibbles + paramStart;
106         return hexStringToInt(getNibbles(message).substring(paramStart, end));
107     }
108 
109     /**
110      * From the params of a CEC message, gets the nibbles from position start to position end.
111      * The start and end are relative to the beginning of the params. For example, in the following
112      * message - 4F:82:10:00:04, getParamsFromMessage(message, 0, 4) will return 0x1000 and
113      * getParamsFromMessage(message, 4, 6) will return 0x04.
114      */
getParams(String message, int start, int end)115     public static int getParams(String message, int start, int end) {
116         return hexStringToInt(getNibbles(message).substring(4).substring(start, end));
117     }
118 
119     /**
120      * Gets the source logical address from a CEC message.
121      */
getSource(String message)122     public static LogicalAddress getSource(String message) {
123         String param = getNibbles(message).substring(0, 1);
124         return LogicalAddress.getLogicalAddress(hexStringToInt(param));
125     }
126 
127     /** Gets the destination logical address from a CEC message. */
getDestination(String message)128     public static LogicalAddress getDestination(String message) {
129         String param = getNibbles(message).substring(1, 2);
130         return LogicalAddress.getLogicalAddress(hexStringToInt(param));
131     }
132 
133     /** Gets the operand from a CEC message. */
getOperand(String message)134     public static CecOperand getOperand(String message) {
135         String param = getNibbles(message).substring(2, 4);
136         return CecOperand.getOperand(hexStringToInt(param));
137     }
138 
139     /**
140      * Converts ascii characters to hexadecimal numbers that can be appended to a CEC message as
141      * params. For example, "spa" will be converted to ":73:70:61"
142      */
convertStringToHexParams(String rawParams)143     public static String convertStringToHexParams(String rawParams) {
144         StringBuilder params = new StringBuilder("");
145         for (int i = 0; i < rawParams.length(); i++) {
146             params.append(String.format(":%02x", (int) rawParams.charAt(i)));
147         }
148         return params.toString();
149     }
150 
getNibbles(String message)151     private static String getNibbles(String message) {
152         final String tag1 = "group1";
153         final String tag2 = "group2";
154         String paramsPattern = "(?:.*[>>|<<].*?)" +
155                 "(?<" + tag1 + ">[\\p{XDigit}{2}:]+)" +
156                 "(?<" + tag2 + ">\\p{XDigit}{2})" +
157                 "(?:.*?)";
158         String nibbles = "";
159 
160         Pattern p = Pattern.compile(paramsPattern);
161         Matcher m = p.matcher(message);
162         if (m.matches()) {
163             nibbles = m.group(tag1).replace(":", "") + m.group(tag2);
164         }
165         return nibbles;
166     }
167 }
168