| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.hardware.hdmi; |
| |
| import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_ANALOGUE; |
| import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL; |
| import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL; |
| |
| import android.annotation.SystemApi; |
| import android.hardware.hdmi.HdmiRecordSources.AnalogueServiceSource; |
| import android.hardware.hdmi.HdmiRecordSources.DigitalServiceSource; |
| import android.hardware.hdmi.HdmiRecordSources.ExternalPhysicalAddress; |
| import android.hardware.hdmi.HdmiRecordSources.ExternalPlugData; |
| import android.hardware.hdmi.HdmiRecordSources.RecordSource; |
| import android.util.Log; |
| |
| /** |
| * Container for timer record source used for timer recording. Timer source consists of two parts, |
| * timer info and record source. |
| * <p> |
| * Timer info contains all timing information used for recording. It consists of the following |
| * values. |
| * <ul> |
| * <li>[Day of Month] |
| * <li>[Month of Year] |
| * <li>[Start Time] |
| * <li>[Duration] |
| * <li>[Recording Sequence] |
| * </ul> |
| * <p> |
| * Record source containers all program information used for recording. |
| * For more details, look at {@link HdmiRecordSources}. |
| * <p> |
| * Usage |
| * <pre> |
| * TimeOrDuration startTime = HdmiTimerRecordSources.ofTime(18, 00); // 6PM. |
| * TimeOrDuration duration = HdmiTimerRecordSource.ofDuration(1, 00); // 1 hour duration. |
| * // For 1 hour from 6PM, August 10th every SaturDay and Sunday. |
| * TimerInfo timerInfo = HdmiTimerRecordSource.timerInfoOf(10, 8, starTime, duration, |
| * HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SATURDAY | |
| * HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SUNDAY); |
| * // create digital source. |
| * DigitalServiceSource recordSource = HdmiRecordSource.ofDvb(...); |
| * TimerRecordSource source = ofDigitalSource(timerInfo, recordSource); |
| * </pre> |
| * |
| * @hide |
| */ |
| @SystemApi |
| public class HdmiTimerRecordSources { |
| private static final String TAG = "HdmiTimerRecordingSources"; |
| |
| private HdmiTimerRecordSources() {} |
| |
| /** |
| * Creates {@link TimerRecordSource} for digital source which is used for <Set Digital |
| * Timer>. |
| * |
| * @param timerInfo timer info used for timer recording |
| * @param source digital source used for timer recording |
| * @return {@link TimerRecordSource} |
| * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null |
| */ |
| public static TimerRecordSource ofDigitalSource(TimerInfo timerInfo, |
| DigitalServiceSource source) { |
| checkTimerRecordSourceInputs(timerInfo, source); |
| return new TimerRecordSource(timerInfo, source); |
| } |
| |
| /** |
| * Creates {@link TimerRecordSource} for analogue source which is used for <Set Analogue |
| * Timer>. |
| * |
| * @param timerInfo timer info used for timer recording |
| * @param source digital source used for timer recording |
| * @return {@link TimerRecordSource} |
| * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null |
| */ |
| public static TimerRecordSource ofAnalogueSource(TimerInfo timerInfo, |
| AnalogueServiceSource source) { |
| checkTimerRecordSourceInputs(timerInfo, source); |
| return new TimerRecordSource(timerInfo, source); |
| } |
| |
| /** |
| * Creates {@link TimerRecordSource} for external plug which is used for <Set External |
| * Timer>. |
| * |
| * @param timerInfo timer info used for timer recording |
| * @param source digital source used for timer recording |
| * @return {@link TimerRecordSource} |
| * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null |
| */ |
| public static TimerRecordSource ofExternalPlug(TimerInfo timerInfo, ExternalPlugData source) { |
| checkTimerRecordSourceInputs(timerInfo, source); |
| return new TimerRecordSource(timerInfo, |
| new ExternalSourceDecorator(source, EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG)); |
| } |
| |
| /** |
| * Creates {@link TimerRecordSource} for external physical address which is used for <Set |
| * External Timer>. |
| * |
| * @param timerInfo timer info used for timer recording |
| * @param source digital source used for timer recording |
| * @return {@link TimerRecordSource} |
| * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null |
| */ |
| public static TimerRecordSource ofExternalPhysicalAddress(TimerInfo timerInfo, |
| ExternalPhysicalAddress source) { |
| checkTimerRecordSourceInputs(timerInfo, source); |
| return new TimerRecordSource(timerInfo, |
| new ExternalSourceDecorator(source, |
| EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS)); |
| } |
| |
| private static void checkTimerRecordSourceInputs(TimerInfo timerInfo, RecordSource source) { |
| if (timerInfo == null) { |
| Log.w(TAG, "TimerInfo should not be null."); |
| throw new IllegalArgumentException("TimerInfo should not be null."); |
| } |
| if (source == null) { |
| Log.w(TAG, "source should not be null."); |
| throw new IllegalArgumentException("source should not be null."); |
| } |
| } |
| |
| /** |
| * Creates {@link Duration} for time value. |
| * |
| * @param hour hour in range of [0, 23] |
| * @param minute minute in range of [0, 60] |
| * @return {@link Duration} |
| * @throws IllegalArgumentException if hour or minute is out of range |
| */ |
| public static Time timeOf(int hour, int minute) { |
| checkTimeValue(hour, minute); |
| return new Time(hour, minute); |
| } |
| |
| private static void checkTimeValue(int hour, int minute) { |
| if (hour < 0 || hour > 23) { |
| throw new IllegalArgumentException("Hour should be in rage of [0, 23]:" + hour); |
| } |
| if (minute < 0 || minute > 59) { |
| throw new IllegalArgumentException("Minute should be in rage of [0, 59]:" + minute); |
| } |
| } |
| |
| /** |
| * Creates {@link Duration} for duration value. |
| * |
| * @param hour hour in range of [0, 99] |
| * @param minute minute in range of [0, 59] |
| * @return {@link Duration} |
| * @throws IllegalArgumentException if hour or minute is out of range |
| */ |
| public static Duration durationOf(int hour, int minute) { |
| checkDurationValue(hour, minute); |
| return new Duration(hour, minute); |
| } |
| |
| private static void checkDurationValue(int hour, int minute) { |
| if (hour < 0 || hour > 99) { |
| throw new IllegalArgumentException("Hour should be in rage of [0, 99]:" + hour); |
| } |
| if (minute < 0 || minute > 59) { |
| throw new IllegalArgumentException("minute should be in rage of [0, 59]:" + minute); |
| } |
| } |
| |
| private static class TimeUnit { |
| /* package */ final int mHour; |
| /* package */ final int mMinute; |
| |
| /* package */ TimeUnit(int hour, int minute) { |
| mHour = hour; |
| mMinute = minute; |
| } |
| |
| /* package */ int toByteArray(byte[] data, int index) { |
| data[index] = toBcdByte(mHour); |
| data[index + 1] = toBcdByte(mMinute); |
| return 2; |
| } |
| |
| /* package */ static byte toBcdByte(int value) { |
| int digitOfTen = (value / 10) % 10; |
| int digitOfOne = value % 10; |
| return (byte) ((digitOfTen << 4) | digitOfOne); |
| } |
| } |
| |
| /** |
| * Place holder for time value. |
| * @hide |
| */ |
| @SystemApi |
| public static final class Time extends TimeUnit { |
| private Time(int hour, int minute) { |
| super(hour, minute); |
| } |
| } |
| |
| /** |
| * Place holder for duration value. |
| * @hide |
| */ |
| @SystemApi |
| public static final class Duration extends TimeUnit { |
| private Duration(int hour, int minute) { |
| super(hour, minute); |
| } |
| } |
| |
| /** |
| * Fields for recording sequence. |
| * The following can be merged by OR(|) operation. |
| */ |
| public static final int RECORDING_SEQUENCE_REPEAT_ONCE_ONLY = 0; |
| public static final int RECORDING_SEQUENCE_REPEAT_SUNDAY = 1 << 0; |
| public static final int RECORDING_SEQUENCE_REPEAT_MONDAY = 1 << 1; |
| public static final int RECORDING_SEQUENCE_REPEAT_TUESDAY = 1 << 2; |
| public static final int RECORDING_SEQUENCE_REPEAT_WEDNESDAY = 1 << 3; |
| public static final int RECORDING_SEQUENCE_REPEAT_THURSDAY = 1 << 4; |
| public static final int RECORDING_SEQUENCE_REPEAT_FRIDAY = 1 << 5; |
| public static final int RECORDING_SEQUENCE_REPEAT_SATUREDAY = 1 << 6; |
| |
| private static final int RECORDING_SEQUENCE_REPEAT_MASK = |
| (RECORDING_SEQUENCE_REPEAT_SUNDAY | RECORDING_SEQUENCE_REPEAT_MONDAY | |
| RECORDING_SEQUENCE_REPEAT_TUESDAY | RECORDING_SEQUENCE_REPEAT_WEDNESDAY | |
| RECORDING_SEQUENCE_REPEAT_THURSDAY | RECORDING_SEQUENCE_REPEAT_FRIDAY | |
| RECORDING_SEQUENCE_REPEAT_SATUREDAY); |
| |
| /** |
| * Creates {@link TimerInfo} with the given information. |
| * |
| * @param dayOfMonth day of month |
| * @param monthOfYear month of year |
| * @param startTime start time in {@link Time} |
| * @param duration duration in {@link Duration} |
| * @param recordingSequence recording sequence. Use RECORDING_SEQUENCE_REPEAT_ONCE_ONLY for no |
| * repeat. Otherwise use combination of {@link #RECORDING_SEQUENCE_REPEAT_SUNDAY}, |
| * {@link #RECORDING_SEQUENCE_REPEAT_MONDAY}, |
| * {@link #RECORDING_SEQUENCE_REPEAT_TUESDAY}, |
| * {@link #RECORDING_SEQUENCE_REPEAT_WEDNESDAY}, |
| * {@link #RECORDING_SEQUENCE_REPEAT_THURSDAY}, |
| * {@link #RECORDING_SEQUENCE_REPEAT_FRIDAY}, |
| * {@link #RECORDING_SEQUENCE_REPEAT_SATUREDAY}. |
| * @return {@link TimerInfo}. |
| * @throws IllegalArgumentException if input value is invalid |
| */ |
| public static TimerInfo timerInfoOf(int dayOfMonth, int monthOfYear, Time startTime, |
| Duration duration, int recordingSequence) { |
| if (dayOfMonth < 0 || dayOfMonth > 31) { |
| throw new IllegalArgumentException( |
| "Day of month should be in range of [0, 31]:" + dayOfMonth); |
| } |
| if (monthOfYear < 1 || monthOfYear > 12) { |
| throw new IllegalArgumentException( |
| "Month of year should be in range of [1, 12]:" + monthOfYear); |
| } |
| checkTimeValue(startTime.mHour, startTime.mMinute); |
| checkDurationValue(duration.mHour, duration.mMinute); |
| // Recording sequence should use least 7 bits or no bits. |
| if ((recordingSequence != 0) |
| && ((recordingSequence & ~RECORDING_SEQUENCE_REPEAT_MASK) != 0)) { |
| throw new IllegalArgumentException( |
| "Invalid reecording sequence value:" + recordingSequence); |
| } |
| |
| return new TimerInfo(dayOfMonth, monthOfYear, startTime, duration, recordingSequence); |
| } |
| |
| /** |
| * Container basic timer information. It consists of the following fields. |
| * <ul> |
| * <li>[Day of Month] |
| * <li>[Month of Year] |
| * <li>[Start Time] |
| * <li>[Duration] |
| * <li>[Recording Sequence] |
| * </ul> |
| * @hide |
| */ |
| @SystemApi |
| public static final class TimerInfo { |
| private static final int DAY_OF_MONTH_SIZE = 1; |
| private static final int MONTH_OF_YEAR_SIZE = 1; |
| private static final int START_TIME_SIZE = 2; // 1byte for hour and 1byte for minute. |
| private static final int DURATION_SIZE = 2; // 1byte for hour and 1byte for minute. |
| private static final int RECORDING_SEQUENCE_SIZE = 1; |
| private static final int BASIC_INFO_SIZE = DAY_OF_MONTH_SIZE + MONTH_OF_YEAR_SIZE |
| + START_TIME_SIZE + DURATION_SIZE + RECORDING_SEQUENCE_SIZE; |
| |
| /** Day of month. */ |
| private final int mDayOfMonth; |
| /** Month of year. */ |
| private final int mMonthOfYear; |
| /** |
| * Time of day. |
| * [Hour][Minute]. 0 <= Hour <= 24, 0 <= Minute <= 60 in BCD format. |
| */ |
| private final Time mStartTime; |
| /** |
| * Duration. [Hour][Minute]. |
| * 0 <= Hour <= 99, 0 <= Minute <= 60 in BCD format. |
| * */ |
| private final Duration mDuration; |
| /** |
| * Indicates if recording is repeated and, if so, on which days. For repeated recordings, |
| * the recording sequence value is the bitwise OR of the days when recordings are required. |
| * [Recording Sequence] shall be set to 0x00 when the recording is not repeated. Bit 7 is |
| * reserved and shall be set to zero. |
| */ |
| private final int mRecordingSequence; |
| |
| private TimerInfo(int dayOfMonth, int monthOfYear, Time startTime, |
| Duration duration, int recordingSequence) { |
| mDayOfMonth = dayOfMonth; |
| mMonthOfYear = monthOfYear; |
| mStartTime = startTime; |
| mDuration = duration; |
| mRecordingSequence = recordingSequence; |
| } |
| |
| int toByteArray(byte[] data, int index) { |
| // [Day of Month] |
| data[index] = (byte) mDayOfMonth; |
| index += DAY_OF_MONTH_SIZE; |
| // [Month of Year] |
| data[index] = (byte) mMonthOfYear; |
| index += MONTH_OF_YEAR_SIZE; |
| // [Start Time] |
| index += mStartTime.toByteArray(data, index); |
| index += mDuration.toByteArray(data, index); |
| // [Duration] |
| // [Recording Sequence] |
| data[index] = (byte) mRecordingSequence; |
| return getDataSize(); |
| } |
| |
| int getDataSize() { |
| return BASIC_INFO_SIZE; |
| } |
| } |
| |
| /** |
| * Record source container for timer record. This is used to set parameter for <Set Digital |
| * Timer>, <Set Analogue Timer>, and <Set External Timer> message. |
| * <p> |
| * In order to create this from each source type, use one of helper method. |
| * <ul> |
| * <li>{@link #ofDigitalSource} for digital source |
| * <li>{@link #ofAnalogueSource} for analogue source |
| * <li>{@link #ofExternalPlug} for external plug type |
| * <li>{@link #ofExternalPhysicalAddress} for external physical address type. |
| * </ul> |
| * @hide |
| */ |
| @SystemApi |
| public static final class TimerRecordSource { |
| private final RecordSource mRecordSource; |
| private final TimerInfo mTimerInfo; |
| |
| private TimerRecordSource(TimerInfo timerInfo, RecordSource recordSource) { |
| mTimerInfo = timerInfo; |
| mRecordSource = recordSource; |
| } |
| |
| int getDataSize() { |
| return mTimerInfo.getDataSize() + mRecordSource.getDataSize(false); |
| } |
| |
| int toByteArray(byte[] data, int index) { |
| // Basic infos including [Day of Month] [Month of Year] [Start Time] [Duration] |
| // [Recording Sequence] |
| index += mTimerInfo.toByteArray(data, index); |
| // [Record Source] |
| mRecordSource.toByteArray(false, data, index); |
| return getDataSize(); |
| } |
| } |
| |
| /** |
| * External source specifier types. |
| */ |
| private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG = 4; |
| private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS = 5; |
| |
| /** |
| * Decorator for external source. Beside digital or analogue source, external source starts with |
| * [External Source Specifier] because it covers both external plug type and external specifier. |
| */ |
| private static class ExternalSourceDecorator extends RecordSource { |
| private final RecordSource mRecordSource; |
| private final int mExternalSourceSpecifier; |
| |
| private ExternalSourceDecorator(RecordSource recordSource, int externalSourceSpecifier) { |
| // External source has one byte field for [External Source Specifier]. |
| super(recordSource.mSourceType, recordSource.getDataSize(false) + 1); |
| mRecordSource = recordSource; |
| mExternalSourceSpecifier = externalSourceSpecifier; |
| } |
| |
| @Override |
| int extraParamToByteArray(byte[] data, int index) { |
| data[index] = (byte) mExternalSourceSpecifier; |
| mRecordSource.toByteArray(false, data, index + 1); |
| return getDataSize(false); |
| } |
| } |
| |
| /** |
| * Checks the byte array of timer record source. |
| * @param sourcetype |
| * @param recordSource |
| * @hide |
| */ |
| @SystemApi |
| public static boolean checkTimerRecordSource(int sourcetype, byte[] recordSource) { |
| int recordSourceSize = recordSource.length - TimerInfo.BASIC_INFO_SIZE; |
| switch (sourcetype) { |
| case TIMER_RECORDING_TYPE_DIGITAL: |
| return DigitalServiceSource.EXTRA_DATA_SIZE == recordSourceSize; |
| case TIMER_RECORDING_TYPE_ANALOGUE: |
| return AnalogueServiceSource.EXTRA_DATA_SIZE == recordSourceSize; |
| case TIMER_RECORDING_TYPE_EXTERNAL: |
| int specifier = recordSource[TimerInfo.BASIC_INFO_SIZE]; |
| if (specifier == EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG) { |
| // One byte for specifier. |
| return ExternalPlugData.EXTRA_DATA_SIZE + 1 == recordSourceSize; |
| } else if (specifier == EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS) { |
| // One byte for specifier. |
| return ExternalPhysicalAddress.EXTRA_DATA_SIZE + 1 == recordSourceSize; |
| } else { |
| // Invalid specifier. |
| return false; |
| } |
| default: |
| return false; |
| } |
| } |
| } |