1 /*
2  * Copyright (C) 2015 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 com.android.tv.tuner;
18 
19 import android.content.Context;
20 import android.util.Log;
21 import com.android.tv.common.BuildConfig;
22 import com.android.tv.common.compat.TvInputConstantCompat;
23 import com.android.tv.tuner.api.Tuner;
24 import com.android.tv.common.annotation.UsedByNative;
25 import java.util.Objects;
26 
27 /** A base class to handle a hardware tuner device. */
28 public abstract class TunerHal implements Tuner {
29     private static final String TAG = "TunerHal";
30 
31     private static final int PID_PAT = 0;
32     private static final int PID_ATSC_SI_BASE = 0x1ffb;
33     private static final int PID_DVB_SDT = 0x0011;
34     private static final int PID_DVB_EIT = 0x0012;
35     private static final int DEFAULT_VSB_TUNE_TIMEOUT_MS = 2000;
36     private static final int DEFAULT_QAM_TUNE_TIMEOUT_MS = 4000; // Some device takes time for
37 
38     @DeliverySystemType private int mDeliverySystemType;
39     @DeliverySystemType private int[] mDeliverySystemTypes;
40     private boolean mIsStreaming;
41     private int mFrequency;
42     private String mModulation;
43 
44     static {
45         if (!BuildConfig.NO_JNI_TEST) {
46             System.loadLibrary("tunertvinput_jni");
47         }
48     }
49 
TunerHal(Context context)50     protected TunerHal(Context context) {
51         mIsStreaming = false;
52         mFrequency = -1;
53         mModulation = null;
54     }
55 
isStreaming()56     protected boolean isStreaming() {
57         return mIsStreaming;
58     }
59 
getDeliverySystemTypeFromDevice()60     protected void getDeliverySystemTypeFromDevice() {
61         getDeliverySystemTypesFromDevice();
62     }
63 
getDeliverySystemTypesFromDevice()64     protected void getDeliverySystemTypesFromDevice() {
65         if (mDeliverySystemType == DELIVERY_SYSTEM_UNDEFINED) {
66             mDeliverySystemType = nativeGetDeliverySystemType(getDeviceId());
67         }
68         if (mDeliverySystemTypes == null) {
69             mDeliverySystemTypes = nativeGetDeliverySystemTypes(getDeviceId());
70         }
71     }
72 
73     /**
74      * Returns {@code true} if this tuner HAL can be reused to save tuning time between channels of
75      * the same frequency.
76      */
77     @Override
isReusable()78     public boolean isReusable() {
79         return true;
80     }
81 
82     @Override
finalize()83     protected void finalize() throws Throwable {
84         super.finalize();
85         close();
86     }
87 
nativeFinalize(long deviceId)88     protected native void nativeFinalize(long deviceId);
89 
90     @Override
tune( int frequency, @ModulationType String modulation, String channelNumber)91     public synchronized boolean tune(
92                 int frequency, @ModulationType String modulation,
93             String channelNumber) {
94         return tuneInternal(mDeliverySystemType, frequency, modulation, channelNumber);
95     }
96 
97     @Override
tune( int deliverySystemType, int frequency, @ModulationType String modulation, String channelNumber)98     public synchronized boolean tune(
99             int deliverySystemType, int frequency, @ModulationType String modulation,
100             String channelNumber) {
101         return tuneInternal(deliverySystemType, frequency, modulation, channelNumber);
102     }
103 
104     /**
105      * Sets the tuner channel. This should be called after acquiring a tuner device.
106      *
107      * @param deliverySystemType a system delivery type of the channel to tune to
108      * @param frequency a frequency of the channel to tune to
109      * @param modulation a modulation method of the channel to tune to
110      * @param channelNumber channel number when channel number is already known. Some tuner HAL may
111      *     use channelNumber instead of frequency for tune.
112      * @return {@code true} if the operation was successful, {@code false} otherwise
113      */
tuneInternal( int deliverySystemType, int frequency, @ModulationType String modulation, String channelNumber)114     protected boolean tuneInternal(
115         int deliverySystemType, int frequency, @ModulationType String modulation,
116         String channelNumber) {
117 
118         if (!isDeviceOpen()) {
119             Log.e(TAG, "There's no available device");
120             return false;
121         }
122         if (mIsStreaming) {
123             nativeCloseAllPidFilters(getDeviceId());
124             mIsStreaming = false;
125         }
126         if (mDeliverySystemTypes != null) {
127             int i;
128             for (i = 0; i < mDeliverySystemTypes.length; i++) {
129                 if (deliverySystemType == mDeliverySystemTypes[i]) {
130                     break;
131                 }
132             }
133 
134             if (i == mDeliverySystemTypes.length) {
135                 Log.e(TAG, "Unsupported delivery system type for device");
136                 return false;
137             }
138         }
139 
140         // When tuning to a new channel in the same frequency, there's no need to stop current tuner
141         // device completely and the only thing necessary for tuning is reopening pid filters.
142         if (mFrequency == frequency && Objects.equals(mModulation, modulation)) {
143             addPidFilter(PID_PAT, FILTER_TYPE_OTHER);
144             addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER);
145             if (Tuner.isDvbDeliverySystem(deliverySystemType)) {
146                 addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER);
147                 addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER);
148             }
149             mIsStreaming = true;
150             return true;
151         }
152 
153         int timeout_ms =
154                 modulation.equals(MODULATION_8VSB)
155                         ? DEFAULT_VSB_TUNE_TIMEOUT_MS
156                         : DEFAULT_QAM_TUNE_TIMEOUT_MS;
157 
158         boolean tuneStatus;
159         switch(deliverySystemType) {
160             case DELIVERY_SYSTEM_UNDEFINED:
161             case DELIVERY_SYSTEM_ATSC:
162                 tuneStatus = nativeTune(getDeviceId(), frequency, modulation, timeout_ms);
163                 break;
164             case DELIVERY_SYSTEM_DVBT:
165             case DELIVERY_SYSTEM_DVBT2:
166                 tuneStatus = nativeTune(getDeviceId(), deliverySystemType, frequency, modulation,
167                         timeout_ms);
168                 break;
169             default:
170                 Log.e(TAG, "Unsupported delivery system type for device");
171                 return false;
172         }
173 
174         if (tuneStatus == true) {
175             addPidFilter(PID_PAT, FILTER_TYPE_OTHER);
176             addPidFilter(PID_ATSC_SI_BASE, FILTER_TYPE_OTHER);
177             if (Tuner.isDvbDeliverySystem(deliverySystemType)) {
178                 addPidFilter(PID_DVB_SDT, FILTER_TYPE_OTHER);
179                 addPidFilter(PID_DVB_EIT, FILTER_TYPE_OTHER);
180             }
181             mFrequency = frequency;
182             mModulation = modulation;
183             mIsStreaming = true;
184         }
185 
186         return tuneStatus;
187     }
188 
nativeTune( long deviceId, int frequency, @ModulationType String modulation, int timeout_ms)189     protected native boolean nativeTune(
190             long deviceId, int frequency,
191             @ModulationType String modulation, int timeout_ms);
192 
nativeTune( long deviceId, int deliverySystemType, int frequency, @ModulationType String modulation, int timeout_ms)193     protected native boolean nativeTune(
194             long deviceId, int deliverySystemType, int frequency,
195             @ModulationType String modulation, int timeout_ms);
196 
197     /**
198      * Sets a pid filter. This should be set after setting a channel.
199      *
200      * @param pid a pid number to be added to filter list
201      * @param filterType a type of pid. Must be one of (FILTER_TYPE_XXX)
202      * @return {@code true} if the operation was successful, {@code false} otherwise
203      */
204     @Override
addPidFilter(int pid, @FilterType int filterType)205     public synchronized boolean addPidFilter(int pid, @FilterType int filterType) {
206         if (!isDeviceOpen()) {
207             Log.e(TAG, "There's no available device");
208             return false;
209         }
210         if (pid >= 0 && pid <= 0x1fff) {
211             nativeAddPidFilter(getDeviceId(), pid, filterType);
212             return true;
213         }
214         return false;
215     }
216 
nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType)217     protected native void nativeAddPidFilter(long deviceId, int pid, @FilterType int filterType);
218 
nativeCloseAllPidFilters(long deviceId)219     protected native void nativeCloseAllPidFilters(long deviceId);
220 
nativeSetHasPendingTune(long deviceId, boolean hasPendingTune)221     protected native void nativeSetHasPendingTune(long deviceId, boolean hasPendingTune);
222 
nativeGetDeliverySystemType(long deviceId)223     protected native int nativeGetDeliverySystemType(long deviceId);
224 
nativeGetDeliverySystemTypes(long deviceId)225     protected native int[] nativeGetDeliverySystemTypes(long deviceId);
226 
nativeGetSignalStrength(long deviceId)227     protected native int nativeGetSignalStrength(long deviceId);
228 
229     /**
230      * Stops current tuning. The tuner device and pid filters will be reset by this call and make
231      * the tuner ready to accept another tune request.
232      */
233     @Override
stopTune()234     public synchronized void stopTune() {
235         if (isDeviceOpen()) {
236             if (mIsStreaming) {
237                 nativeCloseAllPidFilters(getDeviceId());
238             }
239             nativeStopTune(getDeviceId());
240         }
241         mIsStreaming = false;
242         mFrequency = -1;
243         mModulation = null;
244     }
245 
246     @Override
setHasPendingTune(boolean hasPendingTune)247     public void setHasPendingTune(boolean hasPendingTune) {
248         nativeSetHasPendingTune(getDeviceId(), hasPendingTune);
249     }
250 
251     @Override
getDeliverySystemType()252     public int getDeliverySystemType() {
253         return mDeliverySystemType;
254     }
255 
256     @Override
getDeliverySystemTypes()257     public int[] getDeliverySystemTypes() {
258         return mDeliverySystemTypes;
259     }
260 
nativeStopTune(long deviceId)261     protected native void nativeStopTune(long deviceId);
262 
263     /**
264      * This method must be called after {@link #tune(int, String, String)} and before {@link
265      * #stopTune()}. Writes at most maxSize TS frames in a buffer provided by the user. The frames
266      * employ MPEG encoding.
267      *
268      * @param javaBuffer a buffer to write the video data in
269      * @param javaBufferSize the max amount of bytes to write in this buffer. Usually this number
270      *     should be equal to the length of the buffer.
271      * @return the amount of bytes written in the buffer. Note that this value could be 0 if no new
272      *     frames have been obtained since the last call.
273      */
274     @Override
readTsStream(byte[] javaBuffer, int javaBufferSize)275     public synchronized int readTsStream(byte[] javaBuffer, int javaBufferSize) {
276         if (isDeviceOpen()) {
277             return nativeWriteInBuffer(getDeviceId(), javaBuffer, javaBufferSize);
278         } else {
279             return 0;
280         }
281     }
282 
283     /**
284      * This method gets signal strength for currently tuned channel.
285      * Each specific tuner should implement its own method.
286      *
287      * @return {@link TvInputConstantCompat#SIGNAL_STRENGTH_NOT_USED
288      *          when signal check is not supported from tuner.
289      *          {@link TvInputConstantCompat#SIGNAL_STRENGTH_ERROR}
290      *          when signal returned is not valid.
291      *          0 - 100 representing strength from low to high. Curve raw data if necessary.
292      */
293     @Override
getSignalStrength()294     public int getSignalStrength() {
295         return TvInputConstantCompat.SIGNAL_STRENGTH_NOT_USED;
296     }
297 
nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize)298     protected native int nativeWriteInBuffer(long deviceId, byte[] javaBuffer, int javaBufferSize);
299 
300     /**
301      * Opens Linux DVB frontend device. This method is called from native JNI and used only for
302      * DvbTunerHal.
303      */
304     @UsedByNative("DvbManager.cpp")
openDvbFrontEndFd()305     protected int openDvbFrontEndFd() {
306         return -1;
307     }
308 
309     /**
310      * Opens Linux DVB demux device. This method is called from native JNI and used only for
311      * DvbTunerHal.
312      */
313     @UsedByNative("DvbManager.cpp")
openDvbDemuxFd()314     protected int openDvbDemuxFd() {
315         return -1;
316     }
317 
318     /**
319      * Opens Linux DVB dvr device. This method is called from native JNI and used only for
320      * DvbTunerHal.
321      */
322     @UsedByNative("DvbManager.cpp")
openDvbDvrFd()323     protected int openDvbDvrFd() {
324         return -1;
325     }
326 }
327