• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 com.android.server.hdmi;
18 
19 import android.hardware.hdmi.HdmiDeviceInfo;
20 import android.util.Slog;
21 import android.util.SparseArray;
22 
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 
27 /**
28  * Various utilities to handle HDMI CEC messages.
29  */
30 final class HdmiUtils {
31 
32     private static final int[] ADDRESS_TO_TYPE = {
33         HdmiDeviceInfo.DEVICE_TV,  // ADDR_TV
34         HdmiDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_1
35         HdmiDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_2
36         HdmiDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_1
37         HdmiDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_1
38         HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,  // ADDR_AUDIO_SYSTEM
39         HdmiDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_2
40         HdmiDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_3
41         HdmiDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_2
42         HdmiDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_3
43         HdmiDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_4
44         HdmiDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_3
45         HdmiDeviceInfo.DEVICE_RESERVED,
46         HdmiDeviceInfo.DEVICE_RESERVED,
47         HdmiDeviceInfo.DEVICE_TV,  // ADDR_SPECIFIC_USE
48     };
49 
50     private static final String[] DEFAULT_NAMES = {
51         "TV",
52         "Recorder_1",
53         "Recorder_2",
54         "Tuner_1",
55         "Playback_1",
56         "AudioSystem",
57         "Tuner_2",
58         "Tuner_3",
59         "Playback_2",
60         "Recorder_3",
61         "Tuner_4",
62         "Playback_3",
63         "Reserved_1",
64         "Reserved_2",
65         "Secondary_TV",
66     };
67 
HdmiUtils()68     private HdmiUtils() { /* cannot be instantiated */ }
69 
70     /**
71      * Check if the given logical address is valid. A logical address is valid
72      * if it is one allocated for an actual device which allows communication
73      * with other logical devices.
74      *
75      * @param address logical address
76      * @return true if the given address is valid
77      */
isValidAddress(int address)78     static boolean isValidAddress(int address) {
79         return (Constants.ADDR_TV <= address && address <= Constants.ADDR_SPECIFIC_USE);
80     }
81 
82     /**
83      * Return the device type for the given logical address.
84      *
85      * @param address logical address
86      * @return device type for the given logical address; DEVICE_INACTIVE
87      *         if the address is not valid.
88      */
getTypeFromAddress(int address)89     static int getTypeFromAddress(int address) {
90         if (isValidAddress(address)) {
91             return ADDRESS_TO_TYPE[address];
92         }
93         return HdmiDeviceInfo.DEVICE_INACTIVE;
94     }
95 
96     /**
97      * Return the default device name for a logical address. This is the name
98      * by which the logical device is known to others until a name is
99      * set explicitly using HdmiCecService.setOsdName.
100      *
101      * @param address logical address
102      * @return default device name; empty string if the address is not valid
103      */
getDefaultDeviceName(int address)104     static String getDefaultDeviceName(int address) {
105         if (isValidAddress(address)) {
106             return DEFAULT_NAMES[address];
107         }
108         return "";
109     }
110 
111     /**
112      * Verify if the given address is for the given device type.  If not it will throw
113      * {@link IllegalArgumentException}.
114      *
115      * @param logicalAddress the logical address to verify
116      * @param deviceType the device type to check
117      * @throw IllegalArgumentException
118      */
verifyAddressType(int logicalAddress, int deviceType)119     static void verifyAddressType(int logicalAddress, int deviceType) {
120         int actualDeviceType = getTypeFromAddress(logicalAddress);
121         if (actualDeviceType != deviceType) {
122             throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
123                     + ", Actual:" + actualDeviceType);
124         }
125     }
126 
127     /**
128      * Check if the given CEC message come from the given address.
129      *
130      * @param cmd the CEC message to check
131      * @param expectedAddress the expected source address of the given message
132      * @param tag the tag of caller module (for log message)
133      * @return true if the CEC message comes from the given address
134      */
checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag)135     static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) {
136         int src = cmd.getSource();
137         if (src != expectedAddress) {
138             Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]");
139             return false;
140         }
141         return true;
142     }
143 
144     /**
145      * Parse the parameter block of CEC message as [System Audio Status].
146      *
147      * @param cmd the CEC message to parse
148      * @return true if the given parameter has [ON] value
149      */
parseCommandParamSystemAudioStatus(HdmiCecMessage cmd)150     static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) {
151         return cmd.getParams()[0] == Constants.SYSTEM_AUDIO_STATUS_ON;
152     }
153 
154     /**
155      * Convert integer array to list of {@link Integer}.
156      *
157      * <p>The result is immutable.
158      *
159      * @param is integer array
160      * @return {@link List} instance containing the elements in the given array
161      */
asImmutableList(final int[] is)162     static List<Integer> asImmutableList(final int[] is) {
163         ArrayList<Integer> list = new ArrayList<>(is.length);
164         for (int type : is) {
165             list.add(type);
166         }
167         return Collections.unmodifiableList(list);
168     }
169 
170     /**
171      * Assemble two bytes into single integer value.
172      *
173      * @param data to be assembled
174      * @return assembled value
175      */
twoBytesToInt(byte[] data)176     static int twoBytesToInt(byte[] data) {
177         return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
178     }
179 
180     /**
181      * Assemble two bytes into single integer value.
182      *
183      * @param data to be assembled
184      * @param offset offset to the data to convert in the array
185      * @return assembled value
186      */
twoBytesToInt(byte[] data, int offset)187     static int twoBytesToInt(byte[] data, int offset) {
188         return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
189     }
190 
191     /**
192      * Assemble three bytes into single integer value.
193      *
194      * @param data to be assembled
195      * @return assembled value
196      */
threeBytesToInt(byte[] data)197     static int threeBytesToInt(byte[] data) {
198         return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
199     }
200 
sparseArrayToList(SparseArray<T> array)201     static <T> List<T> sparseArrayToList(SparseArray<T> array) {
202         ArrayList<T> list = new ArrayList<>();
203         for (int i = 0; i < array.size(); ++i) {
204             list.add(array.valueAt(i));
205         }
206         return list;
207     }
208 
mergeToUnmodifiableList(List<T> a, List<T> b)209     static <T> List<T> mergeToUnmodifiableList(List<T> a, List<T> b) {
210         if (a.isEmpty() && b.isEmpty()) {
211             return Collections.emptyList();
212         }
213         if (a.isEmpty()) {
214             return Collections.unmodifiableList(b);
215         }
216         if (b.isEmpty()) {
217             return Collections.unmodifiableList(a);
218         }
219         List<T> newList = new ArrayList<>();
220         newList.addAll(a);
221         newList.addAll(b);
222         return Collections.unmodifiableList(newList);
223     }
224 
225     /**
226      * See if the new path is affecting the active path.
227      *
228      * @param activePath current active path
229      * @param newPath new path
230      * @return true if the new path changes the current active path
231      */
isAffectingActiveRoutingPath(int activePath, int newPath)232     static boolean isAffectingActiveRoutingPath(int activePath, int newPath) {
233         // The new path affects the current active path if the parent of the new path
234         // is an ancestor of the active path.
235         // (1.1.0.0, 2.0.0.0) -> true, new path alters the parent
236         // (1.1.0.0, 1.2.0.0) -> true, new path is a sibling
237         // (1.1.0.0, 1.2.1.0) -> false, new path is a descendant of a sibling
238         // (1.0.0.0, 3.2.0.0) -> false, in a completely different path
239 
240         // Get the parent of the new path by clearing the least significant
241         // non-zero nibble.
242         for (int i = 0; i <= 12; i += 4) {
243             int nibble = (newPath >> i) & 0xF;
244             if (nibble != 0) {
245                 int mask = 0xFFF0 << i;
246                 newPath &= mask;
247                 break;
248             }
249         }
250         if (newPath == 0x0000) {
251             return true;  // Top path always affects the active path
252         }
253         return isInActiveRoutingPath(activePath, newPath);
254     }
255 
256     /**
257      * See if the new path is in the active path.
258      *
259      * @param activePath current active path
260      * @param newPath new path
261      * @return true if the new path in the active routing path
262      */
isInActiveRoutingPath(int activePath, int newPath)263     static boolean isInActiveRoutingPath(int activePath, int newPath) {
264         // Check each nibble of the currently active path and the new path till the position
265         // where the active nibble is not zero. For (activePath, newPath),
266         // (1.1.0.0, 1.0.0.0) -> true, new path is a parent
267         // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant
268         // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling
269         // (1.0.0.0, 2.0.0.0) -> false, in a completely different path
270         for (int i = 12; i >= 0; i -= 4) {
271             int nibbleActive = (activePath >> i) & 0xF;
272             if (nibbleActive == 0) {
273                 break;
274             }
275             int nibbleNew = (newPath >> i) & 0xF;
276             if (nibbleNew == 0) {
277                 break;
278             }
279             if (nibbleActive != nibbleNew) {
280                 return false;
281             }
282         }
283         return true;
284     }
285 
286     /**
287      * Clone {@link HdmiDeviceInfo} with new power status.
288      */
cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus)289     static HdmiDeviceInfo cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus) {
290         return new HdmiDeviceInfo(info.getLogicalAddress(),
291                 info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
292                 info.getVendorId(), info.getDisplayName(), newPowerStatus);
293     }
294 
295     /**
296      * Convert 3 byte-long language code in string to integer representation.
297      * English(eng), for example, is converted to 0x656e67.
298      *
299      * @param language language code in string
300      * @return language code in integer representation
301      */
languageToInt(String language)302     static int languageToInt(String language) {
303         String normalized = language.toLowerCase();
304         return ((normalized.charAt(0) & 0xFF) << 16)
305                 | ((normalized.charAt(1) & 0xFF) << 8)
306                 | (normalized.charAt(2) & 0xFF);
307     }
308 }
309