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