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 &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    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 &lt;= Hour &lt;= 24, 0 &lt;= Minute &lt;= 60 in BCD format.
317         */
318        private final Time mStartTime;
319        /**
320         * Duration. [Hour][Minute].
321         * 0 &lt;= Hour &lt;= 99, 0 &lt;= Minute &lt;= 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 &lt;Set Digital
364     * Timer&gt;, &lt;Set Analogue Timer&gt;, and &lt;Set External Timer&gt; 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