diff --git a/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java new file mode 100644 index 0000000000000..03efd8c4d1624 --- /dev/null +++ b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java @@ -0,0 +1,415 @@ +/* + * 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 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. + *

+ * Timer info contains all timing information used for recording. It consists of the following + * values. + *

+ *

+ * Record source containers all program information used for recording. + * For more details, look at {@link HdmiRecordSources}. + *

+ * Usage + *

+ * 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);
+ * 
+ * + * @hide + */ +@SystemApi +public class HdmiTimerRecordSources { + private static final String TAG = "HdmiTimerRecordingSources"; + + private HdmiTimerRecordSources() {} + + /** + * Create {@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); + } + + /** + * Create {@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); + } + + /** + * Create {@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)); + } + + /** + * Create {@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."); + } + } + + /** + * Create {@link Duration} for time value. + * + * @param hour hour in range of [0, 24] + * @param minute minute in range of [0, 60] + * @return {@link Duration} + * @throw IllegalArgumentException if hour or minute is out of range + */ + public static Time ofTime(int hour, int minute) { + checkTimeValue(hour, minute); + return new Time(hour, minute); + } + + private static void checkTimeValue(int hour, int minute) { + if (hour < 0 || hour > 24) { + throw new IllegalArgumentException("Hour should be in rage of [0, 24]:" + hour); + } + if (minute < 0 || minute > 60) { + throw new IllegalArgumentException("Minute should be in rage of [0, 60]:" + minute); + } + } + + /** + * Create {@link Duration} for duration value. + * + * @param hour hour in range of [0, 90] + * @param minute minute in range of [0, 60] + * @return {@link Duration} + * @throw IllegalArgumentException if hour or minute is out of range + */ + public static Duration ofDuration(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 > 60) { + throw new IllegalArgumentException("minute should be in rage of [0, 60]:" + minute); + } + } + + private static class TimeUnit { + protected final int mHour; + protected final int mMinute; + + protected TimeUnit(int hour, int minute) { + mHour = hour; + mMinute = minute; + } + + protected int toByteArray(byte[] data, int index) { + data[index] = toBcdByte(mHour); + data[index + 1] = toBcdByte(mMinute); + return 2; + } + + protected static byte toBcdByte(int value) { + int digitOfTen = (value / 10) % 10; + int digitOfOne = value % 10; + return (byte) ((digitOfTen << 4) | digitOfOne); + } + } + + /** + * Place holder for time value. + */ + public static class Time extends TimeUnit { + private Time(int hour, int minute) { + super(hour, minute); + } + } + + /** + * Place holder for duration value. + */ + public static 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); + + /** + * Create {@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}. + * @throw 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. + * + */ + public static 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. + *

+ * In order to create this from each source type, use one of helper method. + *

+ */ + public static 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); + } + } +} diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java index 6d64a1da4f34a..c02ff8ae653f1 100644 --- a/core/java/android/hardware/hdmi/HdmiTvClient.java +++ b/core/java/android/hardware/hdmi/HdmiTvClient.java @@ -168,29 +168,56 @@ public final class HdmiTvClient extends HdmiClient { /** * Set {@link RecordRequestListener} to hdmi control service. */ - public void setRecordRequestListener(RecordRequestListener listener) { + public void setOneTouchRecordRequestListener(RecordRequestListener listener) { try { - mService.setRecordRequestListener(getCallbackWrapper(listener)); + mService.setOneTouchRecordRequestListener(getCallbackWrapper(listener)); } catch (RemoteException e) { Log.e(TAG, "failed to set record request listener: ", e); } } /** - * Start recording with the given recorder address and recorder source. - *

Usage + * Start one touch recording with the given recorder address and recorder source. + *

+ * Usage *

      * HdmiTvClient tvClient = ....;
      * // for own source.
      * OwnSource ownSource = ownHdmiRecordSources.ownSource();
-     * tvClient.startRecord(recorderAddress, ownSource);
+     * tvClient.startOneTouchRecord(recorderAddress, ownSource);
      * 
*/ - public void startRecord(int recorderAddress, HdmiRecordSources.RecordSource source) { + public void startOneTouchRecord(int recorderAddress, HdmiRecordSources.RecordSource source) { try { byte[] data = new byte[source.getDataSize(true)]; source.toByteArray(true, data, 0); - mService.startRecord(recorderAddress, data); + mService.startOneTouchRecord(recorderAddress, data); + } catch (RemoteException e) { + Log.e(TAG, "failed to start record: ", e); + } + } + + /** + * Start timer recording with the given recoder address and recorder source. + *

+ * Usage + *

+     * HdmiTvClient tvClient = ....;
+     * // create timer info
+     * TimerInfo timerInfo = HdmiTimerRecourdSources.timerInfoOf(...);
+     * // for digital source.
+     * DigitalServiceSource recordSource = HdmiRecordSources.ofDigitalService(...);
+     * // create timer recording source.
+     * TimerRecordSource source = HdmiTimerRecourdSources.ofDigitalSource(timerInfo, recordSource);
+     * tvClient.startTimerRecording(recorderAddress, source);
+     * 
+ */ + public void startTimerRecording(int recorderAddress, + HdmiTimerRecordSources.TimerRecordSource source) { + try { + byte[] data = new byte[source.getDataSize()]; + source.toByteArray(data, 0); + mService.startTimerRecording(recorderAddress, data); } catch (RemoteException e) { Log.e(TAG, "failed to start record: ", e); } diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 6f3763b2d7597..53b8b3f153eb6 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -55,13 +55,14 @@ interface IHdmiControlService { void setArcMode(boolean enabled); void setOption(int option, int value); void setProhibitMode(boolean enabled); - oneway void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex); - oneway void setSystemAudioMute(boolean mute); + void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex); + void setSystemAudioMute(boolean mute); void setInputChangeListener(IHdmiInputChangeListener listener); List getInputDevices(); void sendVendorCommand(int deviceType, int targetAddress, in byte[] params, boolean hasVendorId); void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType); - void setRecordRequestListener(IHdmiRecordRequestListener listener); - void startRecord(int recorderAddress, in byte[] recordSource); + void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener); + void startOneTouchRecord(int recorderAddress, in byte[] recordSource); + void startTimerRecording(int recorderAddress, in byte[] recordSource); } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 560ee84fb7d0e..95cd7ef6038e3 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1013,12 +1013,17 @@ public final class HdmiControlService extends SystemService { } @Override - public void setRecordRequestListener(IHdmiRecordRequestListener listener) { + public void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) { // TODO: implement this. } @Override - public void startRecord(int recorderAddress, byte[] recordSource) { + public void startOneTouchRecord(int recorderAddress, byte[] recordSource) { + // TODO: implement this. + } + + @Override + public void startTimerRecording(int recorderAddress, byte[] recordSource) { // TODO: implement this. } }