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 17 package com.android.server.hdmi; 18 19 import android.hardware.hdmi.HdmiDeviceInfo; 20 import android.util.Slog; 21 import android.util.SparseArray; 22 23 import java.util.ArrayList; 24 import java.util.Collections; 25 import java.util.List; 26 27 /** 28 * Various utilities to handle HDMI CEC messages. 29 */ 30 final 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 static <T> List<T> mergeToUnmodifiableList(List<T> a, List<T> b) { 210 if (a.isEmpty() && b.isEmpty()) { 211 return Collections.emptyList(); 212 } 213 if (a.isEmpty()) { 214 return Collections.unmodifiableList(b); 215 } 216 if (b.isEmpty()) { 217 return Collections.unmodifiableList(a); 218 } 219 List<T> newList = new ArrayList<>(); 220 newList.addAll(a); 221 newList.addAll(b); 222 return Collections.unmodifiableList(newList); 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 /** 287 * Clone {@link HdmiDeviceInfo} with new power status. 288 */ 289 static HdmiDeviceInfo cloneHdmiDeviceInfo(HdmiDeviceInfo info, int newPowerStatus) { 290 return new HdmiDeviceInfo(info.getLogicalAddress(), 291 info.getPhysicalAddress(), info.getPortId(), info.getDeviceType(), 292 info.getVendorId(), info.getDisplayName(), newPowerStatus); 293 } 294 295 } 296