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 android.hardware.hdmi; 18 19 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE; 20 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; 21 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; 22 23 import android.annotation.SystemApi; 24 import android.hardware.hdmi.HdmiRecordSources.AnalogueServiceSource; 25 import android.hardware.hdmi.HdmiRecordSources.DigitalServiceSource; 26 import android.hardware.hdmi.HdmiRecordSources.ExternalPhysicalAddress; 27 import android.hardware.hdmi.HdmiRecordSources.ExternalPlugData; 28 import android.hardware.hdmi.HdmiRecordSources.RecordSource; 29 import android.util.Log; 30 31 /** 32 * Container for timer record source used for timer recording. Timer source consists of two parts, 33 * timer info and record source. 34 * <p> 35 * Timer info contains all timing information used for recording. It consists of the following 36 * values. 37 * <ul> 38 * <li>[Day of Month] 39 * <li>[Month of Year] 40 * <li>[Start Time] 41 * <li>[Duration] 42 * <li>[Recording Sequence] 43 * </ul> 44 * <p> 45 * Record source containers all program information used for recording. 46 * For more details, look at {@link HdmiRecordSources}. 47 * <p> 48 * Usage 49 * <pre> 50 * TimeOrDuration startTime = HdmiTimerRecordSources.ofTime(18, 00); // 6PM. 51 * TimeOrDuration duration = HdmiTimerRecordSource.ofDuration(1, 00); // 1 hour duration. 52 * // For 1 hour from 6PM, August 10th every SaturDay and Sunday. 53 * TimerInfo timerInfo = HdmiTimerRecordSource.timerInfoOf(10, 8, starTime, duration, 54 * HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SATURDAY | 55 * HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SUNDAY); 56 * // create digital source. 57 * DigitalServiceSource recordSource = HdmiRecordSource.ofDvb(...); 58 * TimerRecordSource source = ofDigitalSource(timerInfo, recordSource); 59 * </pre> 60 * 61 * @hide 62 */ 63 @SystemApi 64 public class HdmiTimerRecordSources { 65 private static final String TAG = "HdmiTimerRecordingSources"; 66 HdmiTimerRecordSources()67 private HdmiTimerRecordSources() {} 68 69 /** 70 * Creates {@link TimerRecordSource} for digital source which is used for <Set Digital 71 * Timer>. 72 * 73 * @param timerInfo timer info used for timer recording 74 * @param source digital source used for timer recording 75 * @return {@link TimerRecordSource} 76 * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null 77 */ ofDigitalSource(TimerInfo timerInfo, DigitalServiceSource source)78 public static TimerRecordSource ofDigitalSource(TimerInfo timerInfo, 79 DigitalServiceSource source) { 80 checkTimerRecordSourceInputs(timerInfo, source); 81 return new TimerRecordSource(timerInfo, source); 82 } 83 84 /** 85 * Creates {@link TimerRecordSource} for analogue source which is used for <Set Analogue 86 * Timer>. 87 * 88 * @param timerInfo timer info used for timer recording 89 * @param source digital source used for timer recording 90 * @return {@link TimerRecordSource} 91 * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null 92 */ ofAnalogueSource(TimerInfo timerInfo, AnalogueServiceSource source)93 public static TimerRecordSource ofAnalogueSource(TimerInfo timerInfo, 94 AnalogueServiceSource source) { 95 checkTimerRecordSourceInputs(timerInfo, source); 96 return new TimerRecordSource(timerInfo, source); 97 } 98 99 /** 100 * Creates {@link TimerRecordSource} for external plug which is used for <Set External 101 * Timer>. 102 * 103 * @param timerInfo timer info used for timer recording 104 * @param source digital source used for timer recording 105 * @return {@link TimerRecordSource} 106 * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null 107 */ ofExternalPlug(TimerInfo timerInfo, ExternalPlugData source)108 public static TimerRecordSource ofExternalPlug(TimerInfo timerInfo, ExternalPlugData source) { 109 checkTimerRecordSourceInputs(timerInfo, source); 110 return new TimerRecordSource(timerInfo, 111 new ExternalSourceDecorator(source, EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG)); 112 } 113 114 /** 115 * Creates {@link TimerRecordSource} for external physical address which is used for <Set 116 * External Timer>. 117 * 118 * @param timerInfo timer info used for timer recording 119 * @param source digital source used for timer recording 120 * @return {@link TimerRecordSource} 121 * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null 122 */ ofExternalPhysicalAddress(TimerInfo timerInfo, ExternalPhysicalAddress source)123 public static TimerRecordSource ofExternalPhysicalAddress(TimerInfo timerInfo, 124 ExternalPhysicalAddress source) { 125 checkTimerRecordSourceInputs(timerInfo, source); 126 return new TimerRecordSource(timerInfo, 127 new ExternalSourceDecorator(source, 128 EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS)); 129 } 130 checkTimerRecordSourceInputs(TimerInfo timerInfo, RecordSource source)131 private static void checkTimerRecordSourceInputs(TimerInfo timerInfo, RecordSource source) { 132 if (timerInfo == null) { 133 Log.w(TAG, "TimerInfo should not be null."); 134 throw new IllegalArgumentException("TimerInfo should not be null."); 135 } 136 if (source == null) { 137 Log.w(TAG, "source should not be null."); 138 throw new IllegalArgumentException("source should not be null."); 139 } 140 } 141 142 /** 143 * Creates {@link Duration} for time value. 144 * 145 * @param hour hour in range of [0, 23] 146 * @param minute minute in range of [0, 60] 147 * @return {@link Duration} 148 * @throws IllegalArgumentException if hour or minute is out of range 149 */ timeOf(int hour, int minute)150 public static Time timeOf(int hour, int minute) { 151 checkTimeValue(hour, minute); 152 return new Time(hour, minute); 153 } 154 checkTimeValue(int hour, int minute)155 private static void checkTimeValue(int hour, int minute) { 156 if (hour < 0 || hour > 23) { 157 throw new IllegalArgumentException("Hour should be in rage of [0, 23]:" + hour); 158 } 159 if (minute < 0 || minute > 59) { 160 throw new IllegalArgumentException("Minute should be in rage of [0, 59]:" + minute); 161 } 162 } 163 164 /** 165 * Creates {@link Duration} for duration value. 166 * 167 * @param hour hour in range of [0, 99] 168 * @param minute minute in range of [0, 59] 169 * @return {@link Duration} 170 * @throws IllegalArgumentException if hour or minute is out of range 171 */ durationOf(int hour, int minute)172 public static Duration durationOf(int hour, int minute) { 173 checkDurationValue(hour, minute); 174 return new Duration(hour, minute); 175 } 176 checkDurationValue(int hour, int minute)177 private static void checkDurationValue(int hour, int minute) { 178 if (hour < 0 || hour > 99) { 179 throw new IllegalArgumentException("Hour should be in rage of [0, 99]:" + hour); 180 } 181 if (minute < 0 || minute > 59) { 182 throw new IllegalArgumentException("minute should be in rage of [0, 59]:" + minute); 183 } 184 } 185 186 /** 187 * Base class for time-related information. 188 * @hide 189 */ 190 /* package */ static class TimeUnit { 191 /* package */ final int mHour; 192 /* package */ final int mMinute; 193 TimeUnit(int hour, int minute)194 /* package */ TimeUnit(int hour, int minute) { 195 mHour = hour; 196 mMinute = minute; 197 } 198 toByteArray(byte[] data, int index)199 /* package */ int toByteArray(byte[] data, int index) { 200 data[index] = toBcdByte(mHour); 201 data[index + 1] = toBcdByte(mMinute); 202 return 2; 203 } 204 toBcdByte(int value)205 /* package */ static byte toBcdByte(int value) { 206 int digitOfTen = (value / 10) % 10; 207 int digitOfOne = value % 10; 208 return (byte) ((digitOfTen << 4) | digitOfOne); 209 } 210 } 211 212 /** 213 * Place holder for time value. 214 * @hide 215 */ 216 @SystemApi 217 public static final class Time extends TimeUnit { Time(int hour, int minute)218 private Time(int hour, int minute) { 219 super(hour, minute); 220 } 221 } 222 223 /** 224 * Place holder for duration value. 225 * @hide 226 */ 227 @SystemApi 228 public static final class Duration extends TimeUnit { Duration(int hour, int minute)229 private Duration(int hour, int minute) { 230 super(hour, minute); 231 } 232 } 233 234 /** 235 * Fields for recording sequence. 236 * The following can be merged by OR(|) operation. 237 */ 238 public static final int RECORDING_SEQUENCE_REPEAT_ONCE_ONLY = 0; 239 public static final int RECORDING_SEQUENCE_REPEAT_SUNDAY = 1 << 0; 240 public static final int RECORDING_SEQUENCE_REPEAT_MONDAY = 1 << 1; 241 public static final int RECORDING_SEQUENCE_REPEAT_TUESDAY = 1 << 2; 242 public static final int RECORDING_SEQUENCE_REPEAT_WEDNESDAY = 1 << 3; 243 public static final int RECORDING_SEQUENCE_REPEAT_THURSDAY = 1 << 4; 244 public static final int RECORDING_SEQUENCE_REPEAT_FRIDAY = 1 << 5; 245 public static final int RECORDING_SEQUENCE_REPEAT_SATUREDAY = 1 << 6; 246 247 private static final int RECORDING_SEQUENCE_REPEAT_MASK = 248 (RECORDING_SEQUENCE_REPEAT_SUNDAY | RECORDING_SEQUENCE_REPEAT_MONDAY | 249 RECORDING_SEQUENCE_REPEAT_TUESDAY | RECORDING_SEQUENCE_REPEAT_WEDNESDAY | 250 RECORDING_SEQUENCE_REPEAT_THURSDAY | RECORDING_SEQUENCE_REPEAT_FRIDAY | 251 RECORDING_SEQUENCE_REPEAT_SATUREDAY); 252 253 /** 254 * Creates {@link TimerInfo} with the given information. 255 * 256 * @param dayOfMonth day of month 257 * @param monthOfYear month of year 258 * @param startTime start time in {@link Time} 259 * @param duration duration in {@link Duration} 260 * @param recordingSequence recording sequence. Use RECORDING_SEQUENCE_REPEAT_ONCE_ONLY for no 261 * repeat. Otherwise use combination of {@link #RECORDING_SEQUENCE_REPEAT_SUNDAY}, 262 * {@link #RECORDING_SEQUENCE_REPEAT_MONDAY}, 263 * {@link #RECORDING_SEQUENCE_REPEAT_TUESDAY}, 264 * {@link #RECORDING_SEQUENCE_REPEAT_WEDNESDAY}, 265 * {@link #RECORDING_SEQUENCE_REPEAT_THURSDAY}, 266 * {@link #RECORDING_SEQUENCE_REPEAT_FRIDAY}, 267 * {@link #RECORDING_SEQUENCE_REPEAT_SATUREDAY}. 268 * @return {@link TimerInfo}. 269 * @throws IllegalArgumentException if input value is invalid 270 */ timerInfoOf(int dayOfMonth, int monthOfYear, Time startTime, Duration duration, int recordingSequence)271 public static TimerInfo timerInfoOf(int dayOfMonth, int monthOfYear, Time startTime, 272 Duration duration, int recordingSequence) { 273 if (dayOfMonth < 0 || dayOfMonth > 31) { 274 throw new IllegalArgumentException( 275 "Day of month should be in range of [0, 31]:" + dayOfMonth); 276 } 277 if (monthOfYear < 1 || monthOfYear > 12) { 278 throw new IllegalArgumentException( 279 "Month of year should be in range of [1, 12]:" + monthOfYear); 280 } 281 checkTimeValue(startTime.mHour, startTime.mMinute); 282 checkDurationValue(duration.mHour, duration.mMinute); 283 // Recording sequence should use least 7 bits or no bits. 284 if ((recordingSequence != 0) 285 && ((recordingSequence & ~RECORDING_SEQUENCE_REPEAT_MASK) != 0)) { 286 throw new IllegalArgumentException( 287 "Invalid reecording sequence value:" + recordingSequence); 288 } 289 290 return new TimerInfo(dayOfMonth, monthOfYear, startTime, duration, recordingSequence); 291 } 292 293 /** 294 * Container basic timer information. It consists of the following fields. 295 * <ul> 296 * <li>[Day of Month] 297 * <li>[Month of Year] 298 * <li>[Start Time] 299 * <li>[Duration] 300 * <li>[Recording Sequence] 301 * </ul> 302 * @hide 303 */ 304 @SystemApi 305 public static final class TimerInfo { 306 private static final int DAY_OF_MONTH_SIZE = 1; 307 private static final int MONTH_OF_YEAR_SIZE = 1; 308 private static final int START_TIME_SIZE = 2; // 1byte for hour and 1byte for minute. 309 private static final int DURATION_SIZE = 2; // 1byte for hour and 1byte for minute. 310 private static final int RECORDING_SEQUENCE_SIZE = 1; 311 private static final int BASIC_INFO_SIZE = DAY_OF_MONTH_SIZE + MONTH_OF_YEAR_SIZE 312 + START_TIME_SIZE + DURATION_SIZE + RECORDING_SEQUENCE_SIZE; 313 314 /** Day of month. */ 315 private final int mDayOfMonth; 316 /** Month of year. */ 317 private final int mMonthOfYear; 318 /** 319 * Time of day. 320 * [Hour][Minute]. 0 <= Hour <= 24, 0 <= Minute <= 60 in BCD format. 321 */ 322 private final Time mStartTime; 323 /** 324 * Duration. [Hour][Minute]. 325 * 0 <= Hour <= 99, 0 <= Minute <= 60 in BCD format. 326 * */ 327 private final Duration mDuration; 328 /** 329 * Indicates if recording is repeated and, if so, on which days. For repeated recordings, 330 * the recording sequence value is the bitwise OR of the days when recordings are required. 331 * [Recording Sequence] shall be set to 0x00 when the recording is not repeated. Bit 7 is 332 * reserved and shall be set to zero. 333 */ 334 private final int mRecordingSequence; 335 TimerInfo(int dayOfMonth, int monthOfYear, Time startTime, Duration duration, int recordingSequence)336 private TimerInfo(int dayOfMonth, int monthOfYear, Time startTime, 337 Duration duration, int recordingSequence) { 338 mDayOfMonth = dayOfMonth; 339 mMonthOfYear = monthOfYear; 340 mStartTime = startTime; 341 mDuration = duration; 342 mRecordingSequence = recordingSequence; 343 } 344 toByteArray(byte[] data, int index)345 int toByteArray(byte[] data, int index) { 346 // [Day of Month] 347 data[index] = (byte) mDayOfMonth; 348 index += DAY_OF_MONTH_SIZE; 349 // [Month of Year] 350 data[index] = (byte) mMonthOfYear; 351 index += MONTH_OF_YEAR_SIZE; 352 // [Start Time] 353 index += mStartTime.toByteArray(data, index); 354 index += mDuration.toByteArray(data, index); 355 // [Duration] 356 // [Recording Sequence] 357 data[index] = (byte) mRecordingSequence; 358 return getDataSize(); 359 } 360 getDataSize()361 int getDataSize() { 362 return BASIC_INFO_SIZE; 363 } 364 } 365 366 /** 367 * Record source container for timer record. This is used to set parameter for <Set Digital 368 * Timer>, <Set Analogue Timer>, and <Set External Timer> message. 369 * <p> 370 * In order to create this from each source type, use one of helper method. 371 * <ul> 372 * <li>{@link #ofDigitalSource} for digital source 373 * <li>{@link #ofAnalogueSource} for analogue source 374 * <li>{@link #ofExternalPlug} for external plug type 375 * <li>{@link #ofExternalPhysicalAddress} for external physical address type. 376 * </ul> 377 * @hide 378 */ 379 @SystemApi 380 public static final class TimerRecordSource { 381 private final RecordSource mRecordSource; 382 private final TimerInfo mTimerInfo; 383 TimerRecordSource(TimerInfo timerInfo, RecordSource recordSource)384 private TimerRecordSource(TimerInfo timerInfo, RecordSource recordSource) { 385 mTimerInfo = timerInfo; 386 mRecordSource = recordSource; 387 } 388 getDataSize()389 int getDataSize() { 390 return mTimerInfo.getDataSize() + mRecordSource.getDataSize(false); 391 } 392 toByteArray(byte[] data, int index)393 int toByteArray(byte[] data, int index) { 394 // Basic infos including [Day of Month] [Month of Year] [Start Time] [Duration] 395 // [Recording Sequence] 396 index += mTimerInfo.toByteArray(data, index); 397 // [Record Source] 398 mRecordSource.toByteArray(false, data, index); 399 return getDataSize(); 400 } 401 } 402 403 /** 404 * External source specifier types. 405 */ 406 private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG = 4; 407 private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS = 5; 408 409 /** 410 * Decorator for external source. Beside digital or analogue source, external source starts with 411 * [External Source Specifier] because it covers both external plug type and external specifier. 412 */ 413 private static class ExternalSourceDecorator extends RecordSource { 414 private final RecordSource mRecordSource; 415 private final int mExternalSourceSpecifier; 416 ExternalSourceDecorator(RecordSource recordSource, int externalSourceSpecifier)417 private ExternalSourceDecorator(RecordSource recordSource, int externalSourceSpecifier) { 418 // External source has one byte field for [External Source Specifier]. 419 super(recordSource.mSourceType, recordSource.getDataSize(false) + 1); 420 mRecordSource = recordSource; 421 mExternalSourceSpecifier = externalSourceSpecifier; 422 } 423 424 @Override extraParamToByteArray(byte[] data, int index)425 int extraParamToByteArray(byte[] data, int index) { 426 data[index] = (byte) mExternalSourceSpecifier; 427 mRecordSource.toByteArray(false, data, index + 1); 428 return getDataSize(false); 429 } 430 } 431 432 /** 433 * Checks the byte array of timer record source. 434 * @param sourcetype 435 * @param recordSource 436 * @hide 437 */ 438 @SystemApi checkTimerRecordSource(int sourcetype, byte[] recordSource)439 public static boolean checkTimerRecordSource(int sourcetype, byte[] recordSource) { 440 int recordSourceSize = recordSource.length - TimerInfo.BASIC_INFO_SIZE; 441 switch (sourcetype) { 442 case TIMER_RECORDING_TYPE_DIGITAL: 443 return DigitalServiceSource.EXTRA_DATA_SIZE == recordSourceSize; 444 case TIMER_RECORDING_TYPE_ANALOGUE: 445 return AnalogueServiceSource.EXTRA_DATA_SIZE == recordSourceSize; 446 case TIMER_RECORDING_TYPE_EXTERNAL: 447 int specifier = recordSource[TimerInfo.BASIC_INFO_SIZE]; 448 if (specifier == EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG) { 449 // One byte for specifier. 450 return ExternalPlugData.EXTRA_DATA_SIZE + 1 == recordSourceSize; 451 } else if (specifier == EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS) { 452 // One byte for specifier. 453 return ExternalPhysicalAddress.EXTRA_DATA_SIZE + 1 == recordSourceSize; 454 } else { 455 // Invalid specifier. 456 return false; 457 } 458 default: 459 return false; 460 } 461 } 462 } 463