HdmiUtils.java revision c0c20d0522d7756d80f011e7a54bf3b51c78df41
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
17package com.android.server.hdmi;
18
19import android.hardware.hdmi.HdmiCecDeviceInfo;
20import android.util.Slog;
21import android.util.SparseArray;
22
23import java.util.ArrayList;
24import java.util.Collections;
25import java.util.List;
26
27/**
28 * Various utilities to handle HDMI CEC messages.
29 */
30final class HdmiUtils {
31
32    private static final int[] ADDRESS_TO_TYPE = {
33        HdmiCecDeviceInfo.DEVICE_TV,  // ADDR_TV
34        HdmiCecDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_1
35        HdmiCecDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_2
36        HdmiCecDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_1
37        HdmiCecDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_1
38        HdmiCecDeviceInfo.DEVICE_AUDIO_SYSTEM,  // ADDR_AUDIO_SYSTEM
39        HdmiCecDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_2
40        HdmiCecDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_3
41        HdmiCecDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_2
42        HdmiCecDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_3
43        HdmiCecDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_4
44        HdmiCecDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_3
45        HdmiCecDeviceInfo.DEVICE_RESERVED,
46        HdmiCecDeviceInfo.DEVICE_RESERVED,
47        HdmiCecDeviceInfo.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
68    private HdmiUtils() { /* cannot be instantiated */ }
69
70    /**
71     * Check if the given type is valid. A valid type is one of the actual
72     * logical device types defined in the standard ({@link #DEVICE_TV},
73     * {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER},
74     * and {@link #DEVICE_AUDIO_SYSTEM}).
75     *
76     * @param type device type
77     * @return true if the given type is valid
78     */
79    static boolean isValidType(int type) {
80        return (HdmiCecDeviceInfo.DEVICE_TV <= type && type <= HdmiCecDeviceInfo.DEVICE_AUDIO_SYSTEM)
81                && type != HdmiCecDeviceInfo.DEVICE_RESERVED;
82    }
83
84    /**
85     * Check if the given logical address is valid. A logical address is valid
86     * if it is one allocated for an actual device which allows communication
87     * with other logical devices.
88     *
89     * @param address logical address
90     * @return true if the given address is valid
91     */
92    static boolean isValidAddress(int address) {
93        return (Constants.ADDR_TV <= address && address <= Constants.ADDR_SPECIFIC_USE);
94    }
95
96    /**
97     * Return the device type for the given logical address.
98     *
99     * @param address logical address
100     * @return device type for the given logical address; DEVICE_INACTIVE
101     *         if the address is not valid.
102     */
103    static int getTypeFromAddress(int address) {
104        if (isValidAddress(address)) {
105            return ADDRESS_TO_TYPE[address];
106        }
107        return HdmiCecDeviceInfo.DEVICE_INACTIVE;
108    }
109
110    /**
111     * Return the default device name for a logical address. This is the name
112     * by which the logical device is known to others until a name is
113     * set explicitly using HdmiCecService.setOsdName.
114     *
115     * @param address logical address
116     * @return default device name; empty string if the address is not valid
117     */
118    static String getDefaultDeviceName(int address) {
119        if (isValidAddress(address)) {
120            return DEFAULT_NAMES[address];
121        }
122        return "";
123    }
124
125    /**
126     * Verify if the given address is for the given device type.  If not it will throw
127     * {@link IllegalArgumentException}.
128     *
129     * @param logicalAddress the logical address to verify
130     * @param deviceType the device type to check
131     * @throw IllegalArgumentException
132     */
133    static void verifyAddressType(int logicalAddress, int deviceType) {
134        int actualDeviceType = getTypeFromAddress(logicalAddress);
135        if (actualDeviceType != deviceType) {
136            throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
137                    + ", Actual:" + actualDeviceType);
138        }
139    }
140
141    /**
142     * Check if the given CEC message come from the given address.
143     *
144     * @param cmd the CEC message to check
145     * @param expectedAddress the expected source address of the given message
146     * @param tag the tag of caller module (for log message)
147     * @return true if the CEC message comes from the given address
148     */
149    static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) {
150        int src = cmd.getSource();
151        if (src != expectedAddress) {
152            Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]");
153            return false;
154        }
155        return true;
156    }
157
158    /**
159     * Parse the parameter block of CEC message as [System Audio Status].
160     *
161     * @param cmd the CEC message to parse
162     * @return true if the given parameter has [ON] value
163     */
164    static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) {
165        // TODO: Handle the exception when the length is wrong.
166        return cmd.getParams().length > 0
167                && cmd.getParams()[0] == Constants.SYSTEM_AUDIO_STATUS_ON;
168    }
169
170    /**
171     * Convert integer array to list of {@link Integer}.
172     *
173     * <p>The result is immutable.
174     *
175     * @param is integer array
176     * @return {@link List} instance containing the elements in the given array
177     */
178    static List<Integer> asImmutableList(final int[] is) {
179        ArrayList<Integer> list = new ArrayList<>(is.length);
180        for (int type : is) {
181            list.add(type);
182        }
183        return Collections.unmodifiableList(list);
184    }
185
186    /**
187     * Assemble two bytes into single integer value.
188     *
189     * @param data to be assembled
190     * @return assembled value
191     */
192    static int twoBytesToInt(byte[] data) {
193        return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
194    }
195
196    /**
197     * Assemble two bytes into single integer value.
198     *
199     * @param data to be assembled
200     * @param offset offset to the data to convert in the array
201     * @return assembled value
202     */
203    static int twoBytesToInt(byte[] data, int offset) {
204        return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
205    }
206
207    /**
208     * Assemble three bytes into single integer value.
209     *
210     * @param data to be assembled
211     * @return assembled value
212     */
213    static int threeBytesToInt(byte[] data) {
214        return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
215    }
216
217    static <T> List<T> sparseArrayToList(SparseArray<T> array) {
218        ArrayList<T> list = new ArrayList<>();
219        for (int i = 0; i < array.size(); ++i) {
220            list.add(array.valueAt(i));
221        }
222        return list;
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     */
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     */
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