HdmiTimerRecordSources.java revision 28d2a693efaa10e9b71d679919566cab640f73e0
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 &lt;Set Digital
71     * Timer&gt;.
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 &lt;Set Analogue
86     * Timer&gt;.
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 &lt;Set External
101     * Timer&gt;.
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 &lt;Set
116     * External Timer&gt;.
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 &lt;= Hour &lt;= 24, 0 &lt;= Minute &lt;= 60 in BCD format.
322         */
323        private final Time mStartTime;
324        /**
325         * Duration. [Hour][Minute].
326         * 0 &lt;= Hour &lt;= 99, 0 &lt;= Minute &lt;= 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 &lt;Set Digital
369     * Timer&gt;, &lt;Set Analogue Timer&gt;, and &lt;Set External Timer&gt; 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