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.NonNull;
21 import android.annotation.SystemApi;
22 import android.app.ActivityManager;
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.filter.Filter;
27 import android.os.ParcelFileDescriptor;
28 import android.util.Log;
29 
30 import com.android.internal.util.FrameworkStatsLog;
31 
32 import java.util.concurrent.Executor;
33 
34 
35 /**
36  * Digital Video Record (DVR) recorder class which provides record control on Demux's output buffer.
37  *
38  * @hide
39  */
40 @SystemApi
41 public class DvrRecorder implements AutoCloseable {
42     private static final String TAG = "TvTunerRecord";
43     private long mNativeContext;
44     private OnRecordStatusChangedListener mListener;
45     private Executor mExecutor;
46     private int mUserId;
47     private static int sInstantId = 0;
48     private int mSegmentId = 0;
49     private int mOverflow;
50 
nativeAttachFilter(Filter filter)51     private native int nativeAttachFilter(Filter filter);
nativeDetachFilter(Filter filter)52     private native int nativeDetachFilter(Filter filter);
nativeConfigureDvr(DvrSettings settings)53     private native int nativeConfigureDvr(DvrSettings settings);
nativeStartDvr()54     private native int nativeStartDvr();
nativeStopDvr()55     private native int nativeStopDvr();
nativeFlushDvr()56     private native int nativeFlushDvr();
nativeClose()57     private native int nativeClose();
nativeSetFileDescriptor(int fd)58     private native void nativeSetFileDescriptor(int fd);
nativeWrite(long size)59     private native long nativeWrite(long size);
nativeWrite(byte[] bytes, long offset, long size)60     private native long nativeWrite(byte[] bytes, long offset, long size);
61 
DvrRecorder()62     private DvrRecorder() {
63         mUserId = ActivityManager.getCurrentUser();
64         mSegmentId = (sInstantId & 0x0000ffff) << 16;
65         sInstantId++;
66     }
67 
68     /** @hide */
setListener( @onNull Executor executor, @NonNull OnRecordStatusChangedListener listener)69     public void setListener(
70             @NonNull Executor executor, @NonNull OnRecordStatusChangedListener listener) {
71         mExecutor = executor;
72         mListener = listener;
73     }
74 
onRecordStatusChanged(int status)75     private void onRecordStatusChanged(int status) {
76         if (status == Filter.STATUS_OVERFLOW) {
77             mOverflow++;
78         }
79         if (mExecutor != null && mListener != null) {
80             mExecutor.execute(() -> mListener.onRecordStatusChanged(status));
81         }
82     }
83 
84 
85     /**
86      * Attaches a filter to DVR interface for recording.
87      *
88      * <p>There can be multiple filters attached. Attached filters are independent, so the order
89      * doesn't matter.
90      *
91      * @param filter the filter to be attached.
92      * @return result status of the operation.
93      */
94     @Result
attachFilter(@onNull Filter filter)95     public int attachFilter(@NonNull Filter filter) {
96         return nativeAttachFilter(filter);
97     }
98 
99     /**
100      * Detaches a filter from DVR interface.
101      *
102      * @param filter the filter to be detached.
103      * @return result status of the operation.
104      */
105     @Result
detachFilter(@onNull Filter filter)106     public int detachFilter(@NonNull Filter filter) {
107         return nativeDetachFilter(filter);
108     }
109 
110     /**
111      * Configures the DVR.
112      *
113      * @param settings the settings of the DVR interface.
114      * @return result status of the operation.
115      */
116     @Result
configure(@onNull DvrSettings settings)117     public int configure(@NonNull DvrSettings settings) {
118         return nativeConfigureDvr(settings);
119     }
120 
121     /**
122      * Starts DVR.
123      *
124      * <p>Starts consuming playback data or producing data for recording.
125      * <p>Does nothing if the filter is stopped or not started.</p>
126      *
127      * @return result status of the operation.
128      */
129     @Result
start()130     public int start() {
131         mSegmentId =  (mSegmentId & 0xffff0000) | (((mSegmentId & 0x0000ffff) + 1) & 0x0000ffff);
132         mOverflow = 0;
133         Log.d(TAG, "Write Stats Log for Record.");
134         FrameworkStatsLog
135                 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
136                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
137                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STARTED, mSegmentId, 0);
138         return nativeStartDvr();
139     }
140 
141     /**
142      * Stops DVR.
143      *
144      * <p>Stops consuming playback data or producing data for recording.
145      *
146      * @return result status of the operation.
147      */
148     @Result
stop()149     public int stop() {
150         Log.d(TAG, "Write Stats Log for Playback.");
151         FrameworkStatsLog
152                 .write(FrameworkStatsLog.TV_TUNER_DVR_STATUS, mUserId,
153                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__TYPE__RECORD,
154                     FrameworkStatsLog.TV_TUNER_DVR_STATUS__STATE__STOPPED, mSegmentId, mOverflow);
155         return nativeStopDvr();
156     }
157 
158     /**
159      * Flushed DVR data.
160      *
161      * <p>The data in DVR buffer is cleared.
162      *
163      * @return result status of the operation.
164      */
165     @Result
flush()166     public int flush() {
167         return nativeFlushDvr();
168     }
169 
170     /**
171      * Closes the DVR instance to release resources.
172      */
173     @Override
close()174     public void close() {
175         int res = nativeClose();
176         if (res != Tuner.RESULT_SUCCESS) {
177             TunerUtils.throwExceptionForResult(res, "failed to close DVR recorder");
178         }
179     }
180 
181     /**
182      * Sets file descriptor to write data.
183      *
184      * <p>When a write operation of the filter object is happening, this method should not be
185      * called.
186      *
187      * @param fd the file descriptor to write data.
188      * @see #write(long)
189      * @see #write(byte[], long, long)
190      */
setFileDescriptor(@onNull ParcelFileDescriptor fd)191     public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
192         nativeSetFileDescriptor(fd.getFd());
193     }
194 
195     /**
196      * Writes recording data to file.
197      *
198      * @param size the maximum number of bytes to write.
199      * @return the number of bytes written.
200      */
201     @BytesLong
write(@ytesLong long size)202     public long write(@BytesLong long size) {
203         return nativeWrite(size);
204     }
205 
206     /**
207      * Writes recording data to buffer.
208      *
209      * @param bytes the byte array stores the data to be written to DVR.
210      * @param offset the index of the first byte in {@code bytes} to be written to DVR.
211      * @param size the maximum number of bytes to write.
212      * @return the number of bytes written.
213      */
214     @BytesLong
write(@onNull byte[] bytes, @BytesLong long offset, @BytesLong long size)215     public long write(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
216         if (size + offset > bytes.length) {
217             throw new ArrayIndexOutOfBoundsException(
218                     "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
219         }
220         return nativeWrite(bytes, offset, size);
221     }
222 }
223