HdmiUtils.java revision 61f4fbd2e8436a1ecd478c2a1f516d064a24d43b
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.HdmiDeviceInfo; 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 197 static int threeBytesToInt(byte[] data) { 198 return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); 199 } 200 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 209 /** 210 * See if the new path is affecting the active path. 211 * 212 * @param activePath current active path 213 * @param newPath new path 214 * @return true if the new path changes the current active path 215 */ 216 static boolean isAffectingActiveRoutingPath(int activePath, int newPath) { 217 // The new path affects the current active path if the parent of the new path 218 // is an ancestor of the active path. 219 // (1.1.0.0, 2.0.0.0) -> true, new path alters the parent 220 // (1.1.0.0, 1.2.0.0) -> true, new path is a sibling 221 // (1.1.0.0, 1.2.1.0) -> false, new path is a descendant of a sibling 222 // (1.0.0.0, 3.2.0.0) -> false, in a completely different path 223 224 // Get the parent of the new path by clearing the least significant 225 // non-zero nibble. 226 for (int i = 0; i <= 12; i += 4) { 227 int nibble = (newPath >> i) & 0xF; 228 if (nibble != 0) { 229 int mask = 0xFFF0 << i; 230 newPath &= mask; 231 break; 232 } 233 } 234 if (newPath == 0x0000) { 235 return true; // Top path always affects the active path 236 } 237 return isInActiveRoutingPath(activePath, newPath); 238 } 239 240 /** 241 * See if the new path is in the active path. 242 * 243 * @param activePath current active path 244 * @param newPath new path 245 * @return true if the new path in the active routing path 246 */ 247 static boolean isInActiveRoutingPath(int activePath, int newPath) { 248 // Check each nibble of the currently active path and the new path till the position 249 // where the active nibble is not zero. For (activePath, newPath), 250 // (1.1.0.0, 1.0.0.0) -> true, new path is a parent 251 // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant 252 // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling 253 // (1.0.0.0, 2.0.0.0) -> false, in a completely different path 254 for (int i = 12; i >= 0; i -= 4) { 255 int nibbleActive = (activePath >> i) & 0xF; 256 if (nibbleActive == 0) { 257 break; 258 } 259 int nibbleNew = (newPath >> i) & 0xF; 260 if (nibbleNew == 0) { 261 break; 262 } 263 if (nibbleActive != nibbleNew) { 264 return false; 265 } 266 } 267 return true; 268 } 269} 270