1 /* 2 * Copyright (C) 2021 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 android.hardware.hdmi; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 23 /** 24 * Immutable class that stores support status for features in the [Device Features] operand. 25 * Each feature may be supported, be not supported, or have an unknown support status. 26 * 27 * @hide 28 */ 29 public class DeviceFeatures { 30 31 @IntDef({ 32 FEATURE_NOT_SUPPORTED, 33 FEATURE_SUPPORTED, 34 FEATURE_SUPPORT_UNKNOWN 35 }) 36 public @interface FeatureSupportStatus {}; 37 38 public static final int FEATURE_NOT_SUPPORTED = 0; 39 public static final int FEATURE_SUPPORTED = 1; 40 public static final int FEATURE_SUPPORT_UNKNOWN = 2; 41 42 /** 43 * Instance representing no knowledge of any feature's support. 44 */ 45 @NonNull 46 public static final DeviceFeatures ALL_FEATURES_SUPPORT_UNKNOWN = 47 new Builder(FEATURE_SUPPORT_UNKNOWN).build(); 48 49 /** 50 * Instance representing no support for any feature. 51 */ 52 @NonNull 53 public static final DeviceFeatures NO_FEATURES_SUPPORTED = 54 new Builder(FEATURE_NOT_SUPPORTED).build(); 55 56 @FeatureSupportStatus private final int mRecordTvScreenSupport; 57 @FeatureSupportStatus private final int mSetOsdStringSupport; 58 @FeatureSupportStatus private final int mDeckControlSupport; 59 @FeatureSupportStatus private final int mSetAudioRateSupport; 60 @FeatureSupportStatus private final int mArcTxSupport; 61 @FeatureSupportStatus private final int mArcRxSupport; 62 @FeatureSupportStatus private final int mSetAudioVolumeLevelSupport; 63 DeviceFeatures(@onNull Builder builder)64 private DeviceFeatures(@NonNull Builder builder) { 65 this.mRecordTvScreenSupport = builder.mRecordTvScreenSupport; 66 this.mSetOsdStringSupport = builder.mOsdStringSupport; 67 this.mDeckControlSupport = builder.mDeckControlSupport; 68 this.mSetAudioRateSupport = builder.mSetAudioRateSupport; 69 this.mArcTxSupport = builder.mArcTxSupport; 70 this.mArcRxSupport = builder.mArcRxSupport; 71 this.mSetAudioVolumeLevelSupport = builder.mSetAudioVolumeLevelSupport; 72 } 73 74 /** 75 * Converts an instance to a builder. 76 */ toBuilder()77 public Builder toBuilder() { 78 return new Builder(this); 79 } 80 81 /** 82 * Constructs an instance from a [Device Features] operand. 83 * 84 * Bit 7 of [Device Features] is currently ignored. It indicates whether the operand spans more 85 * than one byte, but only the first byte contains information as of CEC 2.0. 86 * 87 * @param deviceFeaturesOperand The [Device Features] operand to parse. 88 * @return Instance representing the [Device Features] operand. 89 */ 90 @NonNull fromOperand(@onNull byte[] deviceFeaturesOperand)91 public static DeviceFeatures fromOperand(@NonNull byte[] deviceFeaturesOperand) { 92 Builder builder = new Builder(FEATURE_SUPPORT_UNKNOWN); 93 94 // Read feature support status from the bits of [Device Features] 95 if (deviceFeaturesOperand.length >= 1) { 96 byte b = deviceFeaturesOperand[0]; 97 builder 98 .setRecordTvScreenSupport(bitToFeatureSupportStatus(((b >> 6) & 1) == 1)) 99 .setSetOsdStringSupport(bitToFeatureSupportStatus(((b >> 5) & 1) == 1)) 100 .setDeckControlSupport(bitToFeatureSupportStatus(((b >> 4) & 1) == 1)) 101 .setSetAudioRateSupport(bitToFeatureSupportStatus(((b >> 3) & 1) == 1)) 102 .setArcTxSupport(bitToFeatureSupportStatus(((b >> 2) & 1) == 1)) 103 .setArcRxSupport(bitToFeatureSupportStatus(((b >> 1) & 1) == 1)) 104 .setSetAudioVolumeLevelSupport(bitToFeatureSupportStatus((b & 1) == 1)); 105 } 106 return builder.build(); 107 } 108 109 /** 110 * Returns the input that is not {@link #FEATURE_SUPPORT_UNKNOWN}. If neither is equal to 111 * {@link #FEATURE_SUPPORT_UNKNOWN}, returns the second input. 112 */ updateFeatureSupportStatus( @eatureSupportStatus int oldStatus, @FeatureSupportStatus int newStatus)113 private static @FeatureSupportStatus int updateFeatureSupportStatus( 114 @FeatureSupportStatus int oldStatus, @FeatureSupportStatus int newStatus) { 115 if (newStatus == FEATURE_SUPPORT_UNKNOWN) { 116 return oldStatus; 117 } else { 118 return newStatus; 119 } 120 } 121 122 /** 123 * Returns the [Device Features] operand corresponding to this instance. 124 * {@link #FEATURE_SUPPORT_UNKNOWN} maps to 0, indicating no support. 125 * 126 * As of CEC 2.0, the returned byte array will always be of length 1. 127 */ 128 @NonNull toOperand()129 public byte[] toOperand() { 130 byte result = 0; 131 132 if (mRecordTvScreenSupport == FEATURE_SUPPORTED) result |= (byte) (1 << 6); 133 if (mSetOsdStringSupport == FEATURE_SUPPORTED) result |= (byte) (1 << 5); 134 if (mDeckControlSupport == FEATURE_SUPPORTED) result |= (byte) (1 << 4); 135 if (mSetAudioRateSupport == FEATURE_SUPPORTED) result |= (byte) (1 << 3); 136 if (mArcTxSupport == FEATURE_SUPPORTED) result |= (byte) (1 << 2); 137 if (mArcRxSupport == FEATURE_SUPPORTED) result |= (byte) (1 << 1); 138 if (mSetAudioVolumeLevelSupport == FEATURE_SUPPORTED) result |= (byte) 1; 139 140 return new byte[]{ result }; 141 } 142 143 @FeatureSupportStatus bitToFeatureSupportStatus(boolean bit)144 private static int bitToFeatureSupportStatus(boolean bit) { 145 return bit ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED; 146 } 147 148 /** 149 * Returns whether the device is a TV that supports <Record TV Screen>. 150 */ 151 @FeatureSupportStatus getRecordTvScreenSupport()152 public int getRecordTvScreenSupport() { 153 return mRecordTvScreenSupport; 154 } 155 156 /** 157 * Returns whether the device is a TV that supports <Set OSD String>. 158 */ 159 @FeatureSupportStatus getSetOsdStringSupport()160 public int getSetOsdStringSupport() { 161 return mSetOsdStringSupport; 162 } 163 164 /** 165 * Returns whether the device supports being controlled by Deck Control. 166 */ 167 @FeatureSupportStatus getDeckControlSupport()168 public int getDeckControlSupport() { 169 return mDeckControlSupport; 170 } 171 172 /** 173 * Returns whether the device is a Source that supports <Set Audio Rate>. 174 */ 175 @FeatureSupportStatus getSetAudioRateSupport()176 public int getSetAudioRateSupport() { 177 return mSetAudioRateSupport; 178 } 179 180 /** 181 * Returns whether the device is a Sink that supports ARC Tx. 182 */ 183 @FeatureSupportStatus getArcTxSupport()184 public int getArcTxSupport() { 185 return mArcTxSupport; 186 } 187 188 /** 189 * Returns whether the device is a Source that supports ARC Rx. 190 */ 191 @FeatureSupportStatus getArcRxSupport()192 public int getArcRxSupport() { 193 return mArcRxSupport; 194 } 195 196 /** 197 * Returns whether the device supports <Set Audio Volume Level>. 198 */ 199 @FeatureSupportStatus getSetAudioVolumeLevelSupport()200 public int getSetAudioVolumeLevelSupport() { 201 return mSetAudioVolumeLevelSupport; 202 } 203 204 @Override equals(@ullable Object obj)205 public boolean equals(@Nullable Object obj) { 206 if (!(obj instanceof DeviceFeatures)) { 207 return false; 208 } 209 210 DeviceFeatures other = (DeviceFeatures) obj; 211 return mRecordTvScreenSupport == other.mRecordTvScreenSupport 212 && mSetOsdStringSupport == other.mSetOsdStringSupport 213 && mDeckControlSupport == other.mDeckControlSupport 214 && mSetAudioRateSupport == other.mSetAudioRateSupport 215 && mArcTxSupport == other.mArcTxSupport 216 && mArcRxSupport == other.mArcRxSupport 217 && mSetAudioVolumeLevelSupport == other.mSetAudioVolumeLevelSupport; 218 } 219 220 @Override hashCode()221 public int hashCode() { 222 return java.util.Objects.hash( 223 mRecordTvScreenSupport, 224 mSetOsdStringSupport, 225 mDeckControlSupport, 226 mSetAudioRateSupport, 227 mArcTxSupport, 228 mArcRxSupport, 229 mSetAudioVolumeLevelSupport 230 ); 231 } 232 233 @NonNull 234 @Override toString()235 public String toString() { 236 StringBuilder s = new StringBuilder(); 237 s.append("Device features: "); 238 s.append("record_tv_screen: ") 239 .append(featureSupportStatusToString(mRecordTvScreenSupport)).append(" "); 240 s.append("set_osd_string: ") 241 .append(featureSupportStatusToString(mSetOsdStringSupport)).append(" "); 242 s.append("deck_control: ") 243 .append(featureSupportStatusToString(mDeckControlSupport)).append(" "); 244 s.append("set_audio_rate: ") 245 .append(featureSupportStatusToString(mSetAudioRateSupport)).append(" "); 246 s.append("arc_tx: ") 247 .append(featureSupportStatusToString(mArcTxSupport)).append(" "); 248 s.append("arc_rx: ") 249 .append(featureSupportStatusToString(mArcRxSupport)).append(" "); 250 s.append("set_audio_volume_level: ") 251 .append(featureSupportStatusToString(mSetAudioVolumeLevelSupport)).append(" "); 252 return s.toString(); 253 } 254 255 @NonNull featureSupportStatusToString(@eatureSupportStatus int status)256 private static String featureSupportStatusToString(@FeatureSupportStatus int status) { 257 switch (status) { 258 case FEATURE_SUPPORTED: 259 return "Y"; 260 case FEATURE_NOT_SUPPORTED: 261 return "N"; 262 case FEATURE_SUPPORT_UNKNOWN: 263 default: 264 return "?"; 265 } 266 } 267 268 /** 269 * Builder for {@link DeviceFeatures} instances. 270 */ 271 public static final class Builder { 272 @FeatureSupportStatus private int mRecordTvScreenSupport; 273 @FeatureSupportStatus private int mOsdStringSupport; 274 @FeatureSupportStatus private int mDeckControlSupport; 275 @FeatureSupportStatus private int mSetAudioRateSupport; 276 @FeatureSupportStatus private int mArcTxSupport; 277 @FeatureSupportStatus private int mArcRxSupport; 278 @FeatureSupportStatus private int mSetAudioVolumeLevelSupport; 279 Builder(@eatureSupportStatus int defaultFeatureSupportStatus)280 private Builder(@FeatureSupportStatus int defaultFeatureSupportStatus) { 281 mRecordTvScreenSupport = defaultFeatureSupportStatus; 282 mOsdStringSupport = defaultFeatureSupportStatus; 283 mDeckControlSupport = defaultFeatureSupportStatus; 284 mSetAudioRateSupport = defaultFeatureSupportStatus; 285 mArcTxSupport = defaultFeatureSupportStatus; 286 mArcRxSupport = defaultFeatureSupportStatus; 287 mSetAudioVolumeLevelSupport = defaultFeatureSupportStatus; 288 } 289 Builder(DeviceFeatures info)290 private Builder(DeviceFeatures info) { 291 mRecordTvScreenSupport = info.getRecordTvScreenSupport(); 292 mOsdStringSupport = info.getSetOsdStringSupport(); 293 mDeckControlSupport = info.getDeckControlSupport(); 294 mSetAudioRateSupport = info.getSetAudioRateSupport(); 295 mArcTxSupport = info.getArcTxSupport(); 296 mArcRxSupport = info.getArcRxSupport(); 297 mSetAudioVolumeLevelSupport = info.getSetAudioVolumeLevelSupport(); 298 } 299 300 /** 301 * Creates a new {@link DeviceFeatures} object. 302 */ build()303 public DeviceFeatures build() { 304 return new DeviceFeatures(this); 305 } 306 307 /** 308 * Sets the value for {@link #getRecordTvScreenSupport()}. 309 */ 310 @NonNull setRecordTvScreenSupport(@eatureSupportStatus int recordTvScreenSupport)311 public Builder setRecordTvScreenSupport(@FeatureSupportStatus int recordTvScreenSupport) { 312 mRecordTvScreenSupport = recordTvScreenSupport; 313 return this; 314 } 315 316 /** 317 * Sets the value for {@link #getSetOsdStringSupport()}. 318 */ 319 @NonNull setSetOsdStringSupport(@eatureSupportStatus int setOsdStringSupport)320 public Builder setSetOsdStringSupport(@FeatureSupportStatus int setOsdStringSupport) { 321 mOsdStringSupport = setOsdStringSupport; 322 return this; 323 } 324 325 /** 326 * Sets the value for {@link #getDeckControlSupport()}. 327 */ 328 @NonNull setDeckControlSupport(@eatureSupportStatus int deckControlSupport)329 public Builder setDeckControlSupport(@FeatureSupportStatus int deckControlSupport) { 330 mDeckControlSupport = deckControlSupport; 331 return this; 332 } 333 334 /** 335 * Sets the value for {@link #getSetAudioRateSupport()}. 336 */ 337 @NonNull setSetAudioRateSupport(@eatureSupportStatus int setAudioRateSupport)338 public Builder setSetAudioRateSupport(@FeatureSupportStatus int setAudioRateSupport) { 339 mSetAudioRateSupport = setAudioRateSupport; 340 return this; 341 } 342 343 /** 344 * Sets the value for {@link #getArcTxSupport()}. 345 */ 346 @NonNull setArcTxSupport(@eatureSupportStatus int arcTxSupport)347 public Builder setArcTxSupport(@FeatureSupportStatus int arcTxSupport) { 348 mArcTxSupport = arcTxSupport; 349 return this; 350 } 351 352 /** 353 * Sets the value for {@link #getArcRxSupport()}. 354 */ 355 @NonNull setArcRxSupport(@eatureSupportStatus int arcRxSupport)356 public Builder setArcRxSupport(@FeatureSupportStatus int arcRxSupport) { 357 mArcRxSupport = arcRxSupport; 358 return this; 359 } 360 361 /** 362 * Sets the value for {@link #getSetAudioRateSupport()}. 363 */ 364 @NonNull setSetAudioVolumeLevelSupport( @eatureSupportStatus int setAudioVolumeLevelSupport)365 public Builder setSetAudioVolumeLevelSupport( 366 @FeatureSupportStatus int setAudioVolumeLevelSupport) { 367 mSetAudioVolumeLevelSupport = setAudioVolumeLevelSupport; 368 return this; 369 } 370 371 /** 372 * Updates all fields with those in a 'new' instance of {@link DeviceFeatures}. 373 * All fields are replaced with those in the new instance, except when the field is 374 * {@link #FEATURE_SUPPORT_UNKNOWN} in the new instance. 375 */ 376 @NonNull update(DeviceFeatures newDeviceFeatures)377 public Builder update(DeviceFeatures newDeviceFeatures) { 378 mRecordTvScreenSupport = updateFeatureSupportStatus(mRecordTvScreenSupport, 379 newDeviceFeatures.getRecordTvScreenSupport()); 380 mOsdStringSupport = updateFeatureSupportStatus(mOsdStringSupport, 381 newDeviceFeatures.getSetOsdStringSupport()); 382 mDeckControlSupport = updateFeatureSupportStatus(mDeckControlSupport, 383 newDeviceFeatures.getDeckControlSupport()); 384 mSetAudioRateSupport = updateFeatureSupportStatus(mSetAudioRateSupport, 385 newDeviceFeatures.getSetAudioRateSupport()); 386 mArcTxSupport = updateFeatureSupportStatus(mArcTxSupport, 387 newDeviceFeatures.getArcTxSupport()); 388 mArcRxSupport = updateFeatureSupportStatus(mArcRxSupport, 389 newDeviceFeatures.getArcRxSupport()); 390 mSetAudioVolumeLevelSupport = updateFeatureSupportStatus(mSetAudioVolumeLevelSupport, 391 newDeviceFeatures.getSetAudioVolumeLevelSupport()); 392 return this; 393 } 394 } 395 } 396