Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cfd1fa63 authored by Jungshik Jang's avatar Jungshik Jang Committed by Android Git Automerger
Browse files

am c1d403f6: am da251030: Add startTimerRecording api to IHdmiControlService.

* commit 'c1d403f661b6c7b9339e983e7f89a4c7e287e72b':
  Add startTimerRecording api to IHdmiControlService.
parents 74065729 ee4ed4e3
Loading
Loading
Loading
Loading
+415 −0
Original line number Diff line number Diff line
/*
 * 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.
 * <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() {}

    /**
     * Create {@link TimerRecordSource} for digital source which is used for &lt;Set Digital
     * Timer&gt;.
     *
     * @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 &lt;Set Analogue
     * Timer&gt;.
     *
     * @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 &lt;Set External
     * Timer&gt;.
     *
     * @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 &lt;Set
     * External Timer&gt;.
     *
     * @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.
     * <ul>
     * <li>[Day of Month]
     * <li>[Month of Year]
     * <li>[Start Time]
     * <li>[Duration]
     * <li>[Recording Sequence]
     * </ul>
     */
    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 &lt;= Hour &lt;= 24, 0 &lt;= Minute &lt;= 60 in BCD format.
         */
        private final Time mStartTime;
        /**
         * Duration. [Hour][Minute].
         * 0 &lt;= Hour &lt;= 99, 0 &lt;= Minute &lt;= 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 &lt;Set Digital
     * Timer&gt;, &lt;Set Analogue Timer&gt;, and &lt;Set External Timer&gt; 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>
     */
    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);
        }
    }
}
+34 −7
Original line number Diff line number Diff line
@@ -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.
     * <p>Usage
     * Start one touch recording with the given recorder address and recorder source.
     * <p>
     * Usage
     * <pre>
     * HdmiTvClient tvClient = ....;
     * // for own source.
     * OwnSource ownSource = ownHdmiRecordSources.ownSource();
     * tvClient.startRecord(recorderAddress, ownSource);
     * tvClient.startOneTouchRecord(recorderAddress, ownSource);
     * </pre>
     */
    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.
     * <p>
     * Usage
     * <pre>
     * 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);
     * </pre>
     */
    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);
        }
+5 −4
Original line number Diff line number Diff line
@@ -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<HdmiCecDeviceInfo> 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);
}
+7 −2
Original line number Diff line number Diff line
@@ -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.
        }
    }