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 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 */ 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 */ 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 */ 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 */ 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 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 */ 150 public static Time timeOf(int hour, int minute) { 151 checkTimeValue(hour, minute); 152 return new Time(hour, minute); 153 } 154 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 */ 172 public static Duration durationOf(int hour, int minute) { 173 checkDurationValue(hour, minute); 174 return new Duration(hour, minute); 175 } 176 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 @SystemApi 191 /* package */ static class TimeUnit { 192 /* package */ final int mHour; 193 /* package */ final int mMinute; 194 195 /* package */ TimeUnit(int hour, int minute) { 196 mHour = hour; 197 mMinute = minute; 198 } 199 200 /* package */ int toByteArray(byte[] data, int index) { 201 data[index] = toBcdByte(mHour); 202 data[index + 1] = toBcdByte(mMinute); 203 return 2; 204 } 205 206 /* package */ static byte toBcdByte(int value) { 207 int digitOfTen = (value / 10) % 10; 208 int digitOfOne = value % 10; 209 return (byte) ((digitOfTen << 4) | digitOfOne); 210 } 211 } 212 213 /** 214 * Place holder for time value. 215 * @hide 216 */ 217 @SystemApi 218 public static final class Time extends TimeUnit { 219 private Time(int hour, int minute) { 220 super(hour, minute); 221 } 222 } 223 224 /** 225 * Place holder for duration value. 226 * @hide 227 */ 228 @SystemApi 229 public static final class Duration extends TimeUnit { 230 private Duration(int hour, int minute) { 231 super(hour, minute); 232 } 233 } 234 235 /** 236 * Fields for recording sequence. 237 * The following can be merged by OR(|) operation. 238 */ 239 public static final int RECORDING_SEQUENCE_REPEAT_ONCE_ONLY = 0; 240 public static final int RECORDING_SEQUENCE_REPEAT_SUNDAY = 1 << 0; 241 public static final int RECORDING_SEQUENCE_REPEAT_MONDAY = 1 << 1; 242 public static final int RECORDING_SEQUENCE_REPEAT_TUESDAY = 1 << 2; 243 public static final int RECORDING_SEQUENCE_REPEAT_WEDNESDAY = 1 << 3; 244 public static final int RECORDING_SEQUENCE_REPEAT_THURSDAY = 1 << 4; 245 public static final int RECORDING_SEQUENCE_REPEAT_FRIDAY = 1 << 5; 246 public static final int RECORDING_SEQUENCE_REPEAT_SATUREDAY = 1 << 6; 247 248 private static final int RECORDING_SEQUENCE_REPEAT_MASK = 249 (RECORDING_SEQUENCE_REPEAT_SUNDAY | RECORDING_SEQUENCE_REPEAT_MONDAY | 250 RECORDING_SEQUENCE_REPEAT_TUESDAY | RECORDING_SEQUENCE_REPEAT_WEDNESDAY | 251 RECORDING_SEQUENCE_REPEAT_THURSDAY | RECORDING_SEQUENCE_REPEAT_FRIDAY | 252 RECORDING_SEQUENCE_REPEAT_SATUREDAY); 253 254 /** 255 * Creates {@link TimerInfo} with the given information. 256 * 257 * @param dayOfMonth day of month 258 * @param monthOfYear month of year 259 * @param startTime start time in {@link Time} 260 * @param duration duration in {@link Duration} 261 * @param recordingSequence recording sequence. Use RECORDING_SEQUENCE_REPEAT_ONCE_ONLY for no 262 * repeat. Otherwise use combination of {@link #RECORDING_SEQUENCE_REPEAT_SUNDAY}, 263 * {@link #RECORDING_SEQUENCE_REPEAT_MONDAY}, 264 * {@link #RECORDING_SEQUENCE_REPEAT_TUESDAY}, 265 * {@link #RECORDING_SEQUENCE_REPEAT_WEDNESDAY}, 266 * {@link #RECORDING_SEQUENCE_REPEAT_THURSDAY}, 267 * {@link #RECORDING_SEQUENCE_REPEAT_FRIDAY}, 268 * {@link #RECORDING_SEQUENCE_REPEAT_SATUREDAY}. 269 * @return {@link TimerInfo}. 270 * @throws IllegalArgumentException if input value is invalid 271 */ 272 public static TimerInfo timerInfoOf(int dayOfMonth, int monthOfYear, Time startTime, 273 Duration duration, int recordingSequence) { 274 if (dayOfMonth < 0 || dayOfMonth > 31) { 275 throw new IllegalArgumentException( 276 "Day of month should be in range of [0, 31]:" + dayOfMonth); 277 } 278 if (monthOfYear < 1 || monthOfYear > 12) { 279 throw new IllegalArgumentException( 280 "Month of year should be in range of [1, 12]:" + monthOfYear); 281 } 282 checkTimeValue(startTime.mHour, startTime.mMinute); 283 checkDurationValue(duration.mHour, duration.mMinute); 284 // Recording sequence should use least 7 bits or no bits. 285 if ((recordingSequence != 0) 286 && ((recordingSequence & ~RECORDING_SEQUENCE_REPEAT_MASK) != 0)) { 287 throw new IllegalArgumentException( 288 "Invalid reecording sequence value:" + recordingSequence); 289 } 290 291 return new TimerInfo(dayOfMonth, monthOfYear, startTime, duration, recordingSequence); 292 } 293 294 /** 295 * Container basic timer information. It consists of the following fields. 296 * <ul> 297 * <li>[Day of Month] 298 * <li>[Month of Year] 299 * <li>[Start Time] 300 * <li>[Duration] 301 * <li>[Recording Sequence] 302 * </ul> 303 * @hide 304 */ 305 @SystemApi 306 public static final class TimerInfo { 307 private static final int DAY_OF_MONTH_SIZE = 1; 308 private static final int MONTH_OF_YEAR_SIZE = 1; 309 private static final int START_TIME_SIZE = 2; // 1byte for hour and 1byte for minute. 310 private static final int DURATION_SIZE = 2; // 1byte for hour and 1byte for minute. 311 private static final int RECORDING_SEQUENCE_SIZE = 1; 312 private static final int BASIC_INFO_SIZE = DAY_OF_MONTH_SIZE + MONTH_OF_YEAR_SIZE 313 + START_TIME_SIZE + DURATION_SIZE + RECORDING_SEQUENCE_SIZE; 314 315 /** Day of month. */ 316 private final int mDayOfMonth; 317 /** Month of year. */ 318 private final int mMonthOfYear; 319 /** 320 * Time of day. 321 * [Hour][Minute]. 0 <= Hour <= 24, 0 <= Minute <= 60 in BCD format. 322 */ 323 private final Time mStartTime; 324 /** 325 * Duration. [Hour][Minute]. 326 * 0 <= Hour <= 99, 0 <= Minute <= 60 in BCD format. 327 * */ 328 private final Duration mDuration; 329 /** 330 * Indicates if recording is repeated and, if so, on which days. For repeated recordings, 331 * the recording sequence value is the bitwise OR of the days when recordings are required. 332 * [Recording Sequence] shall be set to 0x00 when the recording is not repeated. Bit 7 is 333 * reserved and shall be set to zero. 334 */ 335 private final int mRecordingSequence; 336 337 private TimerInfo(int dayOfMonth, int monthOfYear, Time startTime, 338 Duration duration, int recordingSequence) { 339 mDayOfMonth = dayOfMonth; 340 mMonthOfYear = monthOfYear; 341 mStartTime = startTime; 342 mDuration = duration; 343 mRecordingSequence = recordingSequence; 344 } 345 346 int toByteArray(byte[] data, int index) { 347 // [Day of Month] 348 data[index] = (byte) mDayOfMonth; 349 index += DAY_OF_MONTH_SIZE; 350 // [Month of Year] 351 data[index] = (byte) mMonthOfYear; 352 index += MONTH_OF_YEAR_SIZE; 353 // [Start Time] 354 index += mStartTime.toByteArray(data, index); 355 index += mDuration.toByteArray(data, index); 356 // [Duration] 357 // [Recording Sequence] 358 data[index] = (byte) mRecordingSequence; 359 return getDataSize(); 360 } 361 362 int getDataSize() { 363 return BASIC_INFO_SIZE; 364 } 365 } 366 367 /** 368 * Record source container for timer record. This is used to set parameter for <Set Digital 369 * Timer>, <Set Analogue Timer>, and <Set External Timer> message. 370 * <p> 371 * In order to create this from each source type, use one of helper method. 372 * <ul> 373 * <li>{@link #ofDigitalSource} for digital source 374 * <li>{@link #ofAnalogueSource} for analogue source 375 * <li>{@link #ofExternalPlug} for external plug type 376 * <li>{@link #ofExternalPhysicalAddress} for external physical address type. 377 * </ul> 378 * @hide 379 */ 380 @SystemApi 381 public static final class TimerRecordSource { 382 private final RecordSource mRecordSource; 383 private final TimerInfo mTimerInfo; 384 385 private TimerRecordSource(TimerInfo timerInfo, RecordSource recordSource) { 386 mTimerInfo = timerInfo; 387 mRecordSource = recordSource; 388 } 389 390 int getDataSize() { 391 return mTimerInfo.getDataSize() + mRecordSource.getDataSize(false); 392 } 393 394 int toByteArray(byte[] data, int index) { 395 // Basic infos including [Day of Month] [Month of Year] [Start Time] [Duration] 396 // [Recording Sequence] 397 index += mTimerInfo.toByteArray(data, index); 398 // [Record Source] 399 mRecordSource.toByteArray(false, data, index); 400 return getDataSize(); 401 } 402 } 403 404 /** 405 * External source specifier types. 406 */ 407 private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG = 4; 408 private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS = 5; 409 410 /** 411 * Decorator for external source. Beside digital or analogue source, external source starts with 412 * [External Source Specifier] because it covers both external plug type and external specifier. 413 */ 414 private static class ExternalSourceDecorator extends RecordSource { 415 private final RecordSource mRecordSource; 416 private final int mExternalSourceSpecifier; 417 418 private ExternalSourceDecorator(RecordSource recordSource, int externalSourceSpecifier) { 419 // External source has one byte field for [External Source Specifier]. 420 super(recordSource.mSourceType, recordSource.getDataSize(false) + 1); 421 mRecordSource = recordSource; 422 mExternalSourceSpecifier = externalSourceSpecifier; 423 } 424 425 @Override 426 int extraParamToByteArray(byte[] data, int index) { 427 data[index] = (byte) mExternalSourceSpecifier; 428 mRecordSource.toByteArray(false, data, index + 1); 429 return getDataSize(false); 430 } 431 } 432 433 /** 434 * Checks the byte array of timer record source. 435 * @param sourcetype 436 * @param recordSource 437 * @hide 438 */ 439 @SystemApi 440 public static boolean checkTimerRecordSource(int sourcetype, byte[] recordSource) { 441 int recordSourceSize = recordSource.length - TimerInfo.BASIC_INFO_SIZE; 442 switch (sourcetype) { 443 case TIMER_RECORDING_TYPE_DIGITAL: 444 return DigitalServiceSource.EXTRA_DATA_SIZE == recordSourceSize; 445 case TIMER_RECORDING_TYPE_ANALOGUE: 446 return AnalogueServiceSource.EXTRA_DATA_SIZE == recordSourceSize; 447 case TIMER_RECORDING_TYPE_EXTERNAL: 448 int specifier = recordSource[TimerInfo.BASIC_INFO_SIZE]; 449 if (specifier == EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG) { 450 // One byte for specifier. 451 return ExternalPlugData.EXTRA_DATA_SIZE + 1 == recordSourceSize; 452 } else if (specifier == EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS) { 453 // One byte for specifier. 454 return ExternalPhysicalAddress.EXTRA_DATA_SIZE + 1 == recordSourceSize; 455 } else { 456 // Invalid specifier. 457 return false; 458 } 459 default: 460 return false; 461 } 462 } 463 } 464