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