/* * Copyright (C) 2014 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 com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecMessage; import android.util.Slog; import android.util.SparseArray; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Various utilities to handle HDMI CEC messages. */ final class HdmiUtils { private HdmiUtils() { /* cannot be instantiated */ } /** * Verify if the given address is for the given device type. If not it will throw * {@link IllegalArgumentException}. * * @param logicalAddress the logical address to verify * @param deviceType the device type to check * @throw IllegalArgumentException */ static void verifyAddressType(int logicalAddress, int deviceType) { int actualDeviceType = HdmiCec.getTypeFromAddress(logicalAddress); if (actualDeviceType != deviceType) { throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType + ", Actual:" + actualDeviceType); } } /** * Check if the given CEC message come from the given address. * * @param cmd the CEC message to check * @param expectedAddress the expected source address of the given message * @param tag the tag of caller module (for log message) * @return true if the CEC message comes from the given address */ static boolean checkCommandSource(HdmiCecMessage cmd, int expectedAddress, String tag) { int src = cmd.getSource(); if (src != expectedAddress) { Slog.w(tag, "Invalid source [Expected:" + expectedAddress + ", Actual:" + src + "]"); return false; } return true; } /** * Parse the parameter block of CEC message as [System Audio Status]. * * @param cmd the CEC message to parse * @return true if the given parameter has [ON] value */ static boolean parseCommandParamSystemAudioStatus(HdmiCecMessage cmd) { // TODO: Handle the exception when the length is wrong. return cmd.getParams().length > 0 && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON; } /** * Convert integer array to list of {@link Integer}. * *

The result is immutable. * * @param is integer array * @return {@link List} instance containing the elements in the given array */ static List asImmutableList(final int[] is) { ArrayList list = new ArrayList<>(is.length); for (int type : is) { list.add(type); } return Collections.unmodifiableList(list); } /** * Assemble two bytes into single integer value. * * @param data to be assembled * @return assembled value */ static int twoBytesToInt(byte[] data) { return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF); } /** * Assemble two bytes into single integer value. * * @param data to be assembled * @param offset offset to the data to convert in the array * @return assembled value */ static int twoBytesToInt(byte[] data, int offset) { return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); } /** * Assemble three bytes into single integer value. * * @param data to be assembled * @return assembled value */ static int threeBytesToInt(byte[] data) { return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); } static List sparseArrayToList(SparseArray array) { ArrayList list = new ArrayList<>(); for (int i = 0; i < array.size(); ++i) { list.add(array.valueAt(i)); } return list; } /** * See if the new path is affecting the active path. * * @param activePath current active path * @param newPath new path * @return true if the new path changes the current active path */ static boolean isAffectingActiveRoutingPath(int activePath, int newPath) { // The new path affects the current active path if the parent of the new path // is an ancestor of the active path. // (1.1.0.0, 2.0.0.0) -> true, new path alters the parent // (1.1.0.0, 1.2.0.0) -> true, new path is a sibling // (1.1.0.0, 1.2.1.0) -> false, new path is a descendant of a sibling // (1.0.0.0, 3.2.0.0) -> false, in a completely different path // Get the parent of the new path by clearing the least significant // non-zero nibble. for (int i = 0; i <= 12; i += 4) { int nibble = (newPath >> i) & 0xF; if (nibble != 0) { int mask = 0xFFF0 << i; newPath &= mask; break; } } if (newPath == 0x0000) { return true; // Top path always affects the active path } return isInActiveRoutingPath(activePath, newPath); } /** * See if the new path is in the active path. * * @param activePath current active path * @param newPath new path * @return true if the new path in the active routing path */ static boolean isInActiveRoutingPath(int activePath, int newPath) { // Check each nibble of the currently active path and the new path till the position // where the active nibble is not zero. For (activePath, newPath), // (1.1.0.0, 1.0.0.0) -> true, new path is a parent // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling // (1.0.0.0, 2.0.0.0) -> false, in a completely different path for (int i = 12; i >= 0; i -= 4) { int nibbleActive = (activePath >> i) & 0xF; if (nibbleActive == 0) { break; } int nibbleNew = (newPath >> i) & 0xF; if (nibbleNew == 0) { break; } if (nibbleActive != nibbleNew) { return false; } } return true; } }