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.hardware.tv.tuner.V1_0.Constants;
24 import android.media.tv.tuner.Tuner;
25 import android.media.tv.tuner.Tuner.Result;
26 import android.media.tv.tuner.TunerUtils;
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 = Constants.PlaybackStatus.SPACE_EMPTY;
60     /**
61      * The space of the playback is almost empty.
62      *
63      * <p> the threshold is set in {@link DvrSettings}.
64      */
65     public static final int PLAYBACK_STATUS_ALMOST_EMPTY =
66             Constants.PlaybackStatus.SPACE_ALMOST_EMPTY;
67     /**
68      * The space of the playback is almost full.
69      *
70      * <p> the threshold is set in {@link DvrSettings}.
71      */
72     public static final int PLAYBACK_STATUS_ALMOST_FULL =
73             Constants.PlaybackStatus.SPACE_ALMOST_FULL;
74     /**
75      * The space of the playback is full.
76      */
77     public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
78 
79     private static final String TAG = "TvTunerPlayback";
80 
81     private long mNativeContext;
82     private OnPlaybackStatusChangedListener mListener;
83     private Executor mExecutor;
84     private int mUserId;
85     private static int sInstantId = 0;
86     private int mSegmentId = 0;
87     private int mUnderflow;
88     private final Object mListenerLock = new Object();
89 
nativeAttachFilter(Filter filter)90     private native int nativeAttachFilter(Filter filter);
nativeDetachFilter(Filter filter)91     private native int nativeDetachFilter(Filter filter);
nativeConfigureDvr(DvrSettings settings)92     private native int nativeConfigureDvr(DvrSettings settings);
nativeStartDvr()93     private native int nativeStartDvr();
nativeStopDvr()94     private native int nativeStopDvr();
nativeFlushDvr()95     private native int nativeFlushDvr();
nativeClose()96     private native int nativeClose();
nativeSetFileDescriptor(int fd)97     private native void nativeSetFileDescriptor(int fd);
nativeRead(long size)98     private native long nativeRead(long size);
nativeRead(byte[] bytes, long offset, long size)99     private native long nativeRead(byte[] bytes, long offset, long size);
100 
DvrPlayback()101     private DvrPlayback() {
102         mUserId = Process.myUid();
103         mSegmentId = (sInstantId & 0x0000ffff) << 16;
104         sInstantId++;
105     }
106 
107     /** @hide */
setListener( @onNull Executor executor, @NonNull OnPlaybackStatusChangedListener listener)108     public void setListener(
109             @NonNull Executor executor, @NonNull OnPlaybackStatusChangedListener listener) {
110         synchronized (mListenerLock) {
111             mExecutor = executor;
112             mListener = listener;
113         }
114     }
115 
onPlaybackStatusChanged(int status)116     private void onPlaybackStatusChanged(int status) {
117         if (status == PLAYBACK_STATUS_EMPTY) {
118             mUnderflow++;
119         }
120         synchronized (mListenerLock) {
121             if (mExecutor != null && mListener != null) {
122                 mExecutor.execute(() -> mListener.onPlaybackStatusChanged(status));
123             }
124         }
125     }
126 
127 
128     /**
129      * Attaches a filter to DVR interface for playback.
130      *
131      * @deprecated attaching filters is not valid in Dvr Playback use case. This API is a no-op.
132      *             Filters opened by {@link Tuner#openFilter} are used for DVR playback.
133      *
134      * @param filter the filter to be attached.
135      * @return result status of the operation.
136      */
137     @Result
138     @Deprecated
attachFilter(@onNull Filter filter)139     public int attachFilter(@NonNull Filter filter) {
140         // no-op
141         return Tuner.RESULT_UNAVAILABLE;
142     }
143 
144     /**
145      * Detaches a filter from DVR interface.
146      *
147      * @deprecated detaching filters is not valid in Dvr Playback use case. This API is a no-op.
148      *             Filters opened by {@link Tuner#openFilter} are used for DVR playback.
149      *
150      * @param filter the filter to be detached.
151      * @return result status of the operation.
152      */
153     @Result
154     @Deprecated
detachFilter(@onNull Filter filter)155     public int detachFilter(@NonNull Filter filter) {
156         // no-op
157         return Tuner.RESULT_UNAVAILABLE;
158     }
159 
160     /**
161      * Configures the DVR.
162      *
163      * @param settings the settings of the DVR interface.
164      * @return result status of the operation.
165      */
166     @Result
configure(@onNull DvrSettings settings)167     public int configure(@NonNull DvrSettings settings) {
168         return nativeConfigureDvr(settings);
169     }
170 
171     /**
172      * Starts DVR.
173      *
174      * <p>Starts consuming playback data or producing data for recording.
175      *
176      * @return result status of the operation.
177      */
178     @Result
start()179     public int start() {
180         mSegmentId =  (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
181         mUnderflow = 0;
182         Log.d(TAG, "Write Stats Log for Playback.");
183         FrameworkStatsLog
184                 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
185                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
186                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
187         return nativeStartDvr();
188     }
189 
190     /**
191      * Stops DVR.
192      *
193      * <p>Stops consuming playback data or producing data for recording.
194      * <p>Does nothing if the filter is stopped or not started.</p>
195      *
196      * @return result status of the operation.
197      */
198     @Result
stop()199     public int stop() {
200         Log.d(TAG, "Write Stats Log for Playback.");
201         FrameworkStatsLog
202                 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
203                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__PLAYBACK,
204                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mUnderflow);
205         return nativeStopDvr();
206     }
207 
208     /**
209      * Flushed DVR data.
210      *
211      * <p>The data in DVR buffer is cleared.
212      *
213      * @return result status of the operation.
214      */
215     @Result
flush()216     public int flush() {
217         return nativeFlushDvr();
218     }
219 
220     /**
221      * Closes the DVR instance to release resources.
222      */
223     @Override
close()224     public void close() {
225         int res = nativeClose();
226         if (res != Tuner.RESULT_SUCCESS) {
227             TunerUtils.throwExceptionForResult(res, "failed to close DVR playback");
228         }
229     }
230 
231     /**
232      * Sets file descriptor to read data.
233      *
234      * <p>When a read operation of the filter object is happening, this method should not be
235      * called.
236      *
237      * @param fd the file descriptor to read data.
238      * @see #read(long)
239      * @see #read(byte[], long, long)
240      */
setFileDescriptor(@onNull ParcelFileDescriptor fd)241     public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
242         nativeSetFileDescriptor(fd.getFd());
243     }
244 
245     /**
246      * Reads data from the file for DVR playback.
247      *
248      * @param size the maximum number of bytes to read.
249      * @return the number of bytes read.
250      */
251     @BytesLong
read(@ytesLong long size)252     public long read(@BytesLong long size) {
253         return nativeRead(size);
254     }
255 
256     /**
257      * Reads data from the buffer for DVR playback and copies to the given byte array.
258      *
259      * @param bytes the byte array to store the data.
260      * @param offset the index of the first byte in {@code bytes} to copy to.
261      * @param size the maximum number of bytes to read.
262      * @return the number of bytes read.
263      */
264     @BytesLong
read(@onNull byte[] bytes, @BytesLong long offset, @BytesLong long size)265     public long read(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
266         if (size + offset > bytes.length) {
267             throw new ArrayIndexOutOfBoundsException(
268                     "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
269         }
270         return nativeRead(bytes, offset, size);
271     }
272 }
273