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