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