1 /*
2  * Copyright 2019 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 
17 package android.media.tv.tuner.dvr;
18 
19 import android.annotation.BytesLong;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.SystemApi;
23 import android.media.tv.tuner.Tuner;
24 import android.media.tv.tuner.Tuner.Result;
25 import android.media.tv.tuner.TunerUtils;
26 import android.media.tv.tuner.TunerVersionChecker;
27 import android.media.tv.tuner.filter.Filter;
28 import android.os.ParcelFileDescriptor;
29 import android.os.Process;
30 import android.util.Log;
31 
32 import com.android.internal.util.FrameworkStatsLog;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.util.concurrent.Executor;
37 
38 /**
39  * Digital Video Record (DVR) class which provides playback control on Demux's input buffer.
40  *
41  * <p>It's used to play recorded programs.
42  *
43  * @hide
44  */
45 @SystemApi
46 public class DvrPlayback implements AutoCloseable {
47 
48 
49     /** @hide */
50     @Retention(RetentionPolicy.SOURCE)
51     @IntDef(prefix = "PLAYBACK_STATUS_",
52             value = {PLAYBACK_STATUS_EMPTY, PLAYBACK_STATUS_ALMOST_EMPTY,
53                     PLAYBACK_STATUS_ALMOST_FULL, PLAYBACK_STATUS_FULL})
54     @interface PlaybackStatus {}
55 
56     /**
57      * The space of the playback is empty.
58      */
59     public static final int PLAYBACK_STATUS_EMPTY =
60             android.hardware.tv.tuner.PlaybackStatus.SPACE_EMPTY;
61     /**
62      * The space of the playback is almost empty.
63      *
64      * <p> the threshold is set in {@link DvrSettings}.
65      */
66     public static final int PLAYBACK_STATUS_ALMOST_EMPTY =
67             android.hardware.tv.tuner.PlaybackStatus.SPACE_ALMOST_EMPTY;
68     /**
69      * The space of the playback is almost full.
70      *
71      * <p> the threshold is set in {@link DvrSettings}.
72      */
73     public static final int PLAYBACK_STATUS_ALMOST_FULL =
74             android.hardware.tv.tuner.PlaybackStatus.SPACE_ALMOST_FULL;
75     /**
76      * The space of the playback is full.
77      */
78     public static final int PLAYBACK_STATUS_FULL =
79             android.hardware.tv.tuner.PlaybackStatus.SPACE_FULL;
80 
81     private static final String TAG = "TvTunerPlayback";
82 
83     private long mNativeContext;
84     private OnPlaybackStatusChangedListener mListener;
85     private Executor mExecutor;
86     private int mUserId;
87     private static int sInstantId = 0;
88     private int mSegmentId = 0;
89     private int mUnderflow;
90     private final Object mListenerLock = new Object();
91 
nativeAttachFilter(Filter filter)92     private native int nativeAttachFilter(Filter filter);
nativeDetachFilter(Filter filter)93     private native int nativeDetachFilter(Filter filter);
nativeConfigureDvr(DvrSettings settings)94     private native int nativeConfigureDvr(DvrSettings settings);
nativeSetStatusCheckIntervalHint(long durationInMs)95     private native int nativeSetStatusCheckIntervalHint(long durationInMs);
nativeStartDvr()96     private native int nativeStartDvr();
nativeStopDvr()97     private native int nativeStopDvr();
nativeFlushDvr()98     private native int nativeFlushDvr();
nativeClose()99     private native int nativeClose();
nativeSetFileDescriptor(int fd)100     private native void nativeSetFileDescriptor(int fd);
nativeRead(long size)101     private native long nativeRead(long size);
nativeRead(byte[] bytes, long offset, long size)102     private native long nativeRead(byte[] bytes, long offset, long size);
nativeSeek(long pos)103     private native long nativeSeek(long pos);
104 
DvrPlayback()105     private DvrPlayback() {
106         mUserId = Process.myUid();
107         mSegmentId = (sInstantId & 0x0000ffff) << 16;
108         sInstantId++;
109     }
110 
111     /** @hide */
setListener( @onNull Executor executor, @NonNull OnPlaybackStatusChangedListener listener)112     public void setListener(
113             @NonNull Executor executor, @NonNull OnPlaybackStatusChangedListener listener) {
114         synchronized (mListenerLock) {
115             mExecutor = executor;
116             mListener = listener;
117         }
118     }
119 
onPlaybackStatusChanged(int status)120     private void onPlaybackStatusChanged(int status) {
121         if (status == PLAYBACK_STATUS_EMPTY) {
122             mUnderflow++;
123         }
124         synchronized (mListenerLock) {
125             if (mExecutor != null && mListener != null) {
126                 mExecutor.execute(() -> {
127                     synchronized (mListenerLock) {
128                         if (mListener != null) {
129                             mListener.onPlaybackStatusChanged(status);
130                         }
131                     }
132                 });
133             }
134         }
135     }
136 
137 
138     /**
139      * Attaches a filter to DVR interface for playback.
140      *
141      * @deprecated attaching filters is not valid in Dvr Playback use case. This API is a no-op.
142      *             Filters opened by {@link Tuner#openFilter} are used for DVR playback.
143      *
144      * @param filter the filter to be attached.
145      * @return result status of the operation.
146      */
147     @Result
148     @Deprecated
attachFilter(@onNull Filter filter)149     public int attachFilter(@NonNull Filter filter) {
150         // no-op
151         return Tuner.RESULT_UNAVAILABLE;
152     }
153 
154     /**
155      * Detaches a filter from DVR interface.
156      *
157      * @deprecated detaching filters is not valid in Dvr Playback use case. This API is a no-op.
158      *             Filters opened by {@link Tuner#openFilter} are used for DVR playback.
159      *
160      * @param filter the filter to be detached.
161      * @return result status of the operation.
162      */
163     @Result
164     @Deprecated
detachFilter(@onNull Filter filter)165     public int detachFilter(@NonNull Filter filter) {
166         // no-op
167         return Tuner.RESULT_UNAVAILABLE;
168     }
169 
170     /**
171      * Configures the DVR.
172      *
173      * @param settings the settings of the DVR interface.
174      * @return result status of the operation.
175      */
176     @Result
configure(@onNull DvrSettings settings)177     public int configure(@NonNull DvrSettings settings) {
178         return nativeConfigureDvr(settings);
179     }
180 
181     /**
182      * Set playback buffer status check time interval.
183      *
184      * This status check time interval will be used by the Dvr to decide how often to evaluate
185      * data. The default value will be decided by HAL if it’s not set.
186      *
187      * <p>This functionality is only available in Tuner version 3.0 and higher and will otherwise
188      * return a {@link Tuner#RESULT_UNAVAILABLE}. Use {@link TunerVersionChecker#getTunerVersion()}
189      * to get the version information.
190      *
191      * @param durationInMs specifies the duration of the delay in milliseconds.
192      *
193      * @return one of the following results:
194      * {@link Tuner#RESULT_SUCCESS} if succeed,
195      * {@link Tuner#RESULT_UNAVAILABLE} if Dvr is unavailable or unsupported HAL versions,
196      * {@link Tuner#RESULT_NOT_INITIALIZED} if Dvr is not initialized,
197      * {@link Tuner#RESULT_INVALID_STATE} if Dvr is in a wrong state,
198      * {@link Tuner#RESULT_INVALID_ARGUMENT}  if the input parameter is invalid.
199      */
200     @Result
setPlaybackBufferStatusCheckIntervalHint(long durationInMs)201     public int setPlaybackBufferStatusCheckIntervalHint(long durationInMs) {
202         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
203                 TunerVersionChecker.TUNER_VERSION_3_0, "Set status check interval hint")) {
204             // no-op
205             return Tuner.RESULT_UNAVAILABLE;
206         }
207         return nativeSetStatusCheckIntervalHint(durationInMs);
208     }
209 
210     /**
211      * Starts DVR.
212      *
213      * <p>Starts consuming playback data or producing data for recording.
214      *
215      * @return result status of the operation.
216      */
217     @Result
start()218     public int start() {
219         mSegmentId =  (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
220         mUnderflow = 0;
221         Log.d(TAG, "Write Stats Log for Playback.");
222         FrameworkStatsLog
223                 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
224                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
225                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
226         return nativeStartDvr();
227     }
228 
229     /**
230      * Stops DVR.
231      *
232      * <p>Stops consuming playback data or producing data for recording.
233      * <p>Does nothing if the filter is stopped or not started.</p>
234      *
235      * @return result status of the operation.
236      */
237     @Result
stop()238     public int stop() {
239         Log.d(TAG, "Write Stats Log for Playback.");
240         FrameworkStatsLog
241                 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
242                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
243                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mUnderflow);
244         return nativeStopDvr();
245     }
246 
247     /**
248      * Flushed DVR data.
249      *
250      * <p>The data in DVR buffer is cleared.
251      *
252      * @return result status of the operation.
253      */
254     @Result
flush()255     public int flush() {
256         return nativeFlushDvr();
257     }
258 
259     /**
260      * Closes the DVR instance to release resources.
261      */
262     @Override
close()263     public void close() {
264         int res = nativeClose();
265         if (res != Tuner.RESULT_SUCCESS) {
266             TunerUtils.throwExceptionForResult(res, "failed to close DVR playback");
267         }
268     }
269 
270     /**
271      * Sets file descriptor to read data.
272      *
273      * <p>When a read operation of the filter object is happening, this method should not be
274      * called.
275      *
276      * @param fd the file descriptor to read data.
277      * @see #read(long)
278      * @see #seek(long)
279      */
setFileDescriptor(@onNull ParcelFileDescriptor fd)280     public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
281         nativeSetFileDescriptor(fd.getFd());
282     }
283 
284     /**
285      * Reads data from the file for DVR playback.
286      *
287      * @param size the maximum number of bytes to read.
288      * @return the number of bytes read.
289      */
290     @BytesLong
read(@ytesLong long size)291     public long read(@BytesLong long size) {
292         return nativeRead(size);
293     }
294 
295     /**
296      * Reads data from the buffer for DVR playback.
297      *
298      * @param buffer the byte array where DVR reads data from.
299      * @param offset the index of the first byte in {@code buffer} to read.
300      * @param size the maximum number of bytes to read.
301      * @return the number of bytes read.
302      */
303     @BytesLong
read(@onNull byte[] buffer, @BytesLong long offset, @BytesLong long size)304     public long read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
305         if (size + offset > buffer.length) {
306             throw new ArrayIndexOutOfBoundsException(
307                     "Array length=" + buffer.length + ", offset=" + offset + ", size=" + size);
308         }
309         return nativeRead(buffer, offset, size);
310     }
311 
312     /**
313      * Sets the file pointer offset of the file descriptor.
314      *
315      * @param position the offset position, measured in bytes from the beginning of the file.
316      * @return the new offset position. On error, {@code -1} is returned.
317      */
318     @BytesLong
seek(@ytesLong long position)319     public long seek(@BytesLong long position) {
320         return nativeSeek(position);
321     }
322 }
323