163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo/*
263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * Copyright (C) 2014 The Android Open Source Project
363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo *
463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * Licensed under the Apache License, Version 2.0 (the "License");
563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * you may not use this file except in compliance with the License.
663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * You may obtain a copy of the License at
763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo *
863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo *      http://www.apache.org/licenses/LICENSE-2.0
963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo *
1063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * Unless required by applicable law or agreed to in writing, software
1163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * distributed under the License is distributed on an "AS IS" BASIS,
1263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * See the License for the specific language governing permissions and
1463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * limitations under the License.
1563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo */
1663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heopackage com.android.server.hdmi;
1863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
1961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jangimport android.hardware.hdmi.HdmiDeviceInfo;
2063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heoimport android.util.Slog;
2179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jangimport android.util.SparseArray;
2263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
230340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.ArrayList;
240340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.Collections;
250340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kimimport java.util.List;
260340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
2763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo/**
2863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo * Various utilities to handle HDMI CEC messages.
2963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo */
3063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heofinal class HdmiUtils {
3163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
32c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private static final int[] ADDRESS_TO_TYPE = {
3361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_TV,  // ADDR_TV
3461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_1
3561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_2
3661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_1
3761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_1
3861f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,  // ADDR_AUDIO_SYSTEM
3961f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_2
4061f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_3
4161f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_2
4261f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_RECORDER,  // ADDR_RECORDER_3
4361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_TUNER,  // ADDR_TUNER_4
4461f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_PLAYBACK,  // ADDR_PLAYBACK_3
4561f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_RESERVED,
4661f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_RESERVED,
4761f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        HdmiDeviceInfo.DEVICE_TV,  // ADDR_SPECIFIC_USE
48c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    };
49c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim
50c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    private static final String[] DEFAULT_NAMES = {
51c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "TV",
52c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Recorder_1",
53c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Recorder_2",
54c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Tuner_1",
55c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Playback_1",
56c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "AudioSystem",
57c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Tuner_2",
58c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Tuner_3",
59c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Playback_2",
60c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Recorder_3",
61c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Tuner_4",
62c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Playback_3",
63c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Reserved_1",
64c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Reserved_2",
65c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        "Secondary_TV",
66c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    };
67c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim
6863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    private HdmiUtils() { /* cannot be instantiated */ }
6963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
7063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    /**
71c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * Check if the given logical address is valid. A logical address is valid
72c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * if it is one allocated for an actual device which allows communication
73c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * with other logical devices.
74c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     *
75c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @param address logical address
76c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @return true if the given address is valid
77c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     */
78c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    static boolean isValidAddress(int address) {
79c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return (Constants.ADDR_TV <= address && address <= Constants.ADDR_SPECIFIC_USE);
80c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    }
81c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim
82c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    /**
83c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * Return the device type for the given logical address.
84c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     *
85c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @param address logical address
86c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @return device type for the given logical address; DEVICE_INACTIVE
87c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     *         if the address is not valid.
88c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     */
89c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    static int getTypeFromAddress(int address) {
90c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (isValidAddress(address)) {
91c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return ADDRESS_TO_TYPE[address];
92c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        }
9361f4fbd2e8436a1ecd478c2a1f516d064a24d43bJungshik Jang        return HdmiDeviceInfo.DEVICE_INACTIVE;
94c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    }
95c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim
96c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    /**
97c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * Return the default device name for a logical address. This is the name
98c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * by which the logical device is known to others until a name is
99c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * set explicitly using HdmiCecService.setOsdName.
100c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     *
101c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @param address logical address
102c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     * @return default device name; empty string if the address is not valid
103c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim     */
104c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    static String getDefaultDeviceName(int address) {
105c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        if (isValidAddress(address)) {
106c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim            return DEFAULT_NAMES[address];
107c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        }
108c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        return "";
109c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    }
110c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim
111c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim    /**
11263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * Verify if the given address is for the given device type.  If not it will throw
11363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * {@link IllegalArgumentException}.
11463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     *
11563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @param logicalAddress the logical address to verify
11663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @param deviceType the device type to check
11763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @throw IllegalArgumentException
11863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     */
11963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    static void verifyAddressType(int logicalAddress, int deviceType) {
120c0c20d0522d7756d80f011e7a54bf3b51c78df41Jinsuk Kim        int actualDeviceType = getTypeFromAddress(logicalAddress);
12163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (actualDeviceType != deviceType) {
12263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
12363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo                    + ", Actual:" + actualDeviceType);
12463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
12563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
12663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
12763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    /**
12863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * Check if the given CEC message come from the given address.
12963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     *
13063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @param cmd the CEC message to check
13163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @param expectedAddress the expected source address of the given message
13263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @param tag the tag of caller module (for log message)
13363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @return true if the CEC message comes from the given address
13463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     */
13563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) {
13663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        int src = cmd.getSource();
13763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        if (src != expectedAddress) {
13863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]");
13963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo            return false;
14063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        }
14163a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo        return true;
14263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
14363a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo
14463a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    /**
14563a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * Parse the parameter block of CEC message as [System Audio Status].
14663a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     *
14763a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @param cmd the CEC message to parse
14863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     * @return true if the given parameter has [ON] value
14963a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo     */
15063a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) {
15175a77e7d6cbfc287c6126efd28b338b48b7ea70cYuncheol Heo        return cmd.getParams()[0] == Constants.SYSTEM_AUDIO_STATUS_ON;
15263a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo    }
153092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
154092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
1550340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * Convert integer array to list of {@link Integer}.
1560340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
1570340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * <p>The result is immutable.
1580340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     *
1590340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @param is integer array
1600340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     * @return {@link List} instance containing the elements in the given array
1610340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim     */
1620340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    static List<Integer> asImmutableList(final int[] is) {
1630340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        ArrayList<Integer> list = new ArrayList<>(is.length);
1640340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        for (int type : is) {
1650340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim            list.add(type);
1660340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        }
1670340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim        return Collections.unmodifiableList(list);
1680340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    }
1690340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim
1700340bbc89f8162f9c2a298c98b03bfcdd1bc6e87Jinsuk Kim    /**
171092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Assemble two bytes into single integer value.
172092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
173092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param data to be assembled
174092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @return assembled value
175092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
176092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    static int twoBytesToInt(byte[] data) {
177092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
178092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
179092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang
180092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    /**
18192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * Assemble two bytes into single integer value.
18292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     *
18392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @param data to be assembled
18492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @param offset offset to the data to convert in the array
18592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @return assembled value
18692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     */
18792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    static int twoBytesToInt(byte[] data, int offset) {
18892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
18992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
19092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
19192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    /**
192092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * Assemble three bytes into single integer value.
193092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     *
194092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @param data to be assembled
195092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     * @return assembled value
196092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang     */
197092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    static int threeBytesToInt(byte[] data) {
198092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang        return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
199092b445ef898e3c1e5b2918b554480940f0f5a28Jungshik Jang    }
20079c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
20179c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    static <T> List<T> sparseArrayToList(SparseArray<T> array) {
20279c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        ArrayList<T> list = new ArrayList<>();
20379c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        for (int i = 0; i < array.size(); ++i) {
20479c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang            list.add(array.valueAt(i));
20579c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        }
20679c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang        return list;
20779c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang    }
20879c58a4b97f27ede6a1b680d2fece9c2a0edf7b7Jungshik Jang
209ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    static <T> List<T> mergeToUnmodifiableList(List<T> a, List<T> b) {
210ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        if (a.isEmpty() && b.isEmpty()) {
211ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            return Collections.emptyList();
212ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
213ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        if (a.isEmpty()) {
214ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            return Collections.unmodifiableList(b);
215ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
216ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        if (b.isEmpty()) {
217ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim            return Collections.unmodifiableList(a);
218ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        }
219ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        List<T> newList = new ArrayList<>();
220ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        newList.addAll(a);
221ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        newList.addAll(b);
222ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim        return Collections.unmodifiableList(newList);
223ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim    }
224ed0864557b3340ab7db00e2dc95b29c4b8bb485dJinsuk Kim
22592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    /**
22692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * See if the new path is affecting the active path.
22792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     *
22892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @param activePath current active path
22992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @param newPath new path
23092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @return true if the new path changes the current active path
23192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     */
23292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    static boolean isAffectingActiveRoutingPath(int activePath, int newPath) {
23392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // The new path affects the current active path if the parent of the new path
23492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // is an ancestor of the active path.
23592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // (1.1.0.0, 2.0.0.0) -> true, new path alters the parent
23692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // (1.1.0.0, 1.2.0.0) -> true, new path is a sibling
23792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // (1.1.0.0, 1.2.1.0) -> false, new path is a descendant of a sibling
23892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // (1.0.0.0, 3.2.0.0) -> false, in a completely different path
23992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
24092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // Get the parent of the new path by clearing the least significant
24192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // non-zero nibble.
24292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        for (int i = 0; i <= 12; i += 4) {
24392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            int nibble = (newPath >> i) & 0xF;
24492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            if (nibble != 0) {
24592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                int mask = 0xFFF0 << i;
24692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                newPath &= mask;
24792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                break;
24892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            }
24992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
25092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        if (newPath == 0x0000) {
25192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            return true;  // Top path always affects the active path
25292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
25392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        return isInActiveRoutingPath(activePath, newPath);
25492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
25592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim
25692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    /**
25792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * See if the new path is in the active path.
25892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     *
25992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @param activePath current active path
26092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @param newPath new path
26192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     * @return true if the new path in the active routing path
26292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim     */
26392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    static boolean isInActiveRoutingPath(int activePath, int newPath) {
26492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // Check each nibble of the currently active path and the new path till the position
26592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // where the active nibble is not zero. For (activePath, newPath),
26692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // (1.1.0.0, 1.0.0.0) -> true, new path is a parent
26792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant
26892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling
26992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        // (1.0.0.0, 2.0.0.0) -> false, in a completely different path
27092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        for (int i = 12; i >= 0; i -= 4) {
27192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            int nibbleActive = (activePath >> i) & 0xF;
27292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            if (nibbleActive == 0) {
27392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                break;
27492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            }
27592b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            int nibbleNew = (newPath >> i) & 0xF;
27692b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            if (nibbleNew == 0) {
27792b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                break;
27892b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            }
27992b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            if (nibbleActive != nibbleNew) {
28092b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim                return false;
28192b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim            }
28292b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        }
28392b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim        return true;
28492b77cf9cbf512e7141cad6fef5a38d0682dde43Jinsuk Kim    }
285410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang
286410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    /**
287410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang     * Clone {@link HdmiDeviceInfo} with new power status.
288410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang     */
289410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    static HdmiDeviceInfo cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus) {
290410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang        return new HdmiDeviceInfo(info.getLogicalAddress(),
291410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(),
292410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang                info.getVendorId(), info.getDisplayName(), newPowerStatus);
293410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang    }
294410ca9c7a4a2d69af5c81e76320433bfda05cafeJungshik Jang
2955b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim    /**
2965b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim     * Convert 3 byte-long language code in string to integer representation.
2975b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim     * English(eng), for example, is converted to 0x656e67.
2985b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim     *
2995b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim     * @param language language code in string
3005b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim     * @return language code in integer representation
3015b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim     */
3025b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim    static int languageToInt(String language) {
3035b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim        String normalized = language.toLowerCase();
3045b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim        return ((normalized.charAt(0) & 0xFF) << 16)
3055b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim                | ((normalized.charAt(1) & 0xFF) << 8)
3065b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim                | (normalized.charAt(2) & 0xFF);
3075b8cb00b8a302329b98a5528eaa7934d0f5c3e65Jinsuk Kim    }
30863a2e0696ce2a04fbe0f1f00cfe9c93189f944daYuncheol Heo}
309