• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.filter;
18 
19 import android.annotation.BytesLong;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.hardware.tv.tuner.DemuxFilterMainType;
25 import android.hardware.tv.tuner.DemuxFilterMonitorEventType;
26 import android.hardware.tv.tuner.DemuxFilterStatus;
27 import android.media.tv.tuner.Tuner;
28 import android.media.tv.tuner.Tuner.Result;
29 import android.media.tv.tuner.TunerUtils;
30 import android.media.tv.tuner.TunerVersionChecker;
31 import android.util.Log;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.concurrent.Executor;
36 
37 /**
38  * Tuner data filter.
39  *
40  * <p>This class is used to filter wanted data according to the filter's configuration.
41  *
42  * @hide
43  */
44 @SystemApi
45 public class Filter implements AutoCloseable {
46     /** @hide */
47     @IntDef(prefix = "TYPE_",
48             value = {TYPE_TS, TYPE_MMTP, TYPE_IP, TYPE_TLV, TYPE_ALP})
49     @Retention(RetentionPolicy.SOURCE)
50     public @interface Type {}
51 
52     /**
53      * Undefined filter type.
54      */
55     public static final int TYPE_UNDEFINED = 0;
56     /**
57      * TS filter type.
58      */
59     public static final int TYPE_TS = DemuxFilterMainType.TS;
60     /**
61      * MMTP filter type.
62      */
63     public static final int TYPE_MMTP = DemuxFilterMainType.MMTP;
64     /**
65      * IP filter type.
66      */
67     public static final int TYPE_IP = DemuxFilterMainType.IP;
68     /**
69      * TLV filter type.
70      */
71     public static final int TYPE_TLV = DemuxFilterMainType.TLV;
72     /**
73      * ALP filter type.
74      */
75     public static final int TYPE_ALP = DemuxFilterMainType.ALP;
76 
77     /** @hide */
78     @IntDef(prefix = "SUBTYPE_",
79             value = {SUBTYPE_UNDEFINED, SUBTYPE_SECTION, SUBTYPE_PES, SUBTYPE_AUDIO, SUBTYPE_VIDEO,
80                     SUBTYPE_DOWNLOAD, SUBTYPE_RECORD, SUBTYPE_TS, SUBTYPE_PCR, SUBTYPE_TEMI,
81                     SUBTYPE_MMTP, SUBTYPE_NTP, SUBTYPE_IP_PAYLOAD, SUBTYPE_IP,
82                     SUBTYPE_PAYLOAD_THROUGH, SUBTYPE_TLV, SUBTYPE_PTP, })
83     @Retention(RetentionPolicy.SOURCE)
84     public @interface Subtype {}
85     /**
86      * Filter subtype undefined.
87      */
88     public static final int SUBTYPE_UNDEFINED = 0;
89     /**
90      * Section filter subtype.
91      */
92     public static final int SUBTYPE_SECTION = 1;
93     /**
94      * PES filter subtype.
95      */
96     public static final int SUBTYPE_PES = 2;
97     /**
98      * Audio filter subtype.
99      */
100     public static final int SUBTYPE_AUDIO = 3;
101     /**
102      * Video filter subtype.
103      */
104     public static final int SUBTYPE_VIDEO = 4;
105     /**
106      * Download filter subtype.
107      */
108     public static final int SUBTYPE_DOWNLOAD = 5;
109     /**
110      * Record filter subtype.
111      */
112     public static final int SUBTYPE_RECORD = 6;
113     /**
114      * TS filter subtype.
115      */
116     public static final int SUBTYPE_TS = 7;
117     /**
118      * PCR filter subtype.
119      */
120     public static final int SUBTYPE_PCR = 8;
121     /**
122      * TEMI filter subtype.
123      */
124     public static final int SUBTYPE_TEMI = 9;
125     /**
126      * MMTP filter subtype.
127      */
128     public static final int SUBTYPE_MMTP = 10;
129     /**
130      * NTP filter subtype.
131      */
132     public static final int SUBTYPE_NTP = 11;
133     /**
134      * Payload filter subtype.
135      */
136     public static final int SUBTYPE_IP_PAYLOAD = 12;
137     /**
138      * IP filter subtype.
139      */
140     public static final int SUBTYPE_IP = 13;
141     /**
142      * Payload through filter subtype.
143      */
144     public static final int SUBTYPE_PAYLOAD_THROUGH = 14;
145     /**
146      * TLV filter subtype.
147      */
148     public static final int SUBTYPE_TLV = 15;
149     /**
150      * PTP filter subtype.
151      */
152     public static final int SUBTYPE_PTP = 16;
153 
154 
155     /** @hide */
156     @IntDef(prefix = "STATUS_",
157             value = {STATUS_DATA_READY, STATUS_LOW_WATER, STATUS_HIGH_WATER, STATUS_OVERFLOW})
158     @Retention(RetentionPolicy.SOURCE)
159     public @interface Status {}
160 
161     /**
162      * The status of a filter that the data in the filter buffer is ready to be read. It can also be
163      * used to know the STC (System Time Clock) ready status if it's PCR filter.
164      */
165     public static final int STATUS_DATA_READY = DemuxFilterStatus.DATA_READY;
166     /**
167      * The status of a filter that the amount of available data in the filter buffer is at low
168      * level.
169      *
170      * The value is set to 25 percent of the buffer size by default. It can be changed when
171      * configuring the filter.
172      */
173     public static final int STATUS_LOW_WATER = DemuxFilterStatus.LOW_WATER;
174     /**
175      * The status of a filter that the amount of available data in the filter buffer is at high
176      * level.
177      * The value is set to 75 percent of the buffer size by default. It can be changed when
178      * configuring the filter.
179      */
180     public static final int STATUS_HIGH_WATER = DemuxFilterStatus.HIGH_WATER;
181     /**
182      * The status of a filter that the filter buffer is full and newly filtered data is being
183      * discarded.
184      */
185     public static final int STATUS_OVERFLOW = DemuxFilterStatus.OVERFLOW;
186 
187     /** @hide */
188     @IntDef(prefix = "SCRAMBLING_STATUS_",
189             value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED,
190                     SCRAMBLING_STATUS_SCRAMBLED})
191     @Retention(RetentionPolicy.SOURCE)
192     public @interface ScramblingStatus {}
193 
194     /**
195      * Content’s scrambling status is unknown
196      */
197     public static final int SCRAMBLING_STATUS_UNKNOWN =
198             android.hardware.tv.tuner.ScramblingStatus.UNKNOWN;
199     /**
200      * Content is not scrambled.
201      */
202     public static final int SCRAMBLING_STATUS_NOT_SCRAMBLED =
203             android.hardware.tv.tuner.ScramblingStatus.NOT_SCRAMBLED;
204     /**
205      * Content is scrambled.
206      */
207     public static final int SCRAMBLING_STATUS_SCRAMBLED =
208             android.hardware.tv.tuner.ScramblingStatus.SCRAMBLED;
209 
210     /** @hide */
211     @IntDef(prefix = "MONITOR_EVENT_",
212             value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE})
213     @Retention(RetentionPolicy.SOURCE)
214     public @interface MonitorEventMask {}
215 
216     /**
217      * Monitor scrambling status change.
218      */
219     public static final int MONITOR_EVENT_SCRAMBLING_STATUS =
220             DemuxFilterMonitorEventType.SCRAMBLING_STATUS;
221     /**
222      * Monitor ip cid change.
223      */
224     public static final int MONITOR_EVENT_IP_CID_CHANGE = DemuxFilterMonitorEventType.IP_CID_CHANGE;
225 
226     private static final String TAG = "Filter";
227 
228     private long mNativeContext;
229     private FilterCallback mCallback;
230     private Executor mExecutor;
231     private final Object mCallbackLock = new Object();
232     private final long mId;
233     private int mMainType;
234     private int mSubtype;
235     private Filter mSource;
236     private boolean mStarted;
237     private boolean mIsClosed = false;
238     private boolean mIsStarted = false;
239     private boolean mIsShared = false;
240     private final Object mLock = new Object();
241 
nativeConfigureFilter( int type, int subType, FilterConfiguration settings)242     private native int nativeConfigureFilter(
243             int type, int subType, FilterConfiguration settings);
nativeGetId()244     private native int nativeGetId();
nativeGetId64Bit()245     private native long nativeGetId64Bit();
nativeConfigureMonitorEvent(int monitorEventMask)246     private native int nativeConfigureMonitorEvent(int monitorEventMask);
nativeSetDataSource(Filter source)247     private native int nativeSetDataSource(Filter source);
nativeStartFilter()248     private native int nativeStartFilter();
nativeStopFilter()249     private native int nativeStopFilter();
nativeFlushFilter()250     private native int nativeFlushFilter();
nativeRead(byte[] buffer, long offset, long size)251     private native int nativeRead(byte[] buffer, long offset, long size);
nativeClose()252     private native int nativeClose();
nativeAcquireSharedFilterToken()253     private native String nativeAcquireSharedFilterToken();
nativeFreeSharedFilterToken(String token)254     private native void nativeFreeSharedFilterToken(String token);
nativeSetTimeDelayHint(int timeDelayInMs)255     private native int nativeSetTimeDelayHint(int timeDelayInMs);
nativeSetDataSizeDelayHint(int dataSizeDelayInBytes)256     private native int nativeSetDataSizeDelayHint(int dataSizeDelayInBytes);
257 
258     // Called by JNI
Filter(long id)259     private Filter(long id) {
260         mId = id;
261     }
262 
onFilterStatus(int status)263     private void onFilterStatus(int status) {
264         synchronized (mCallbackLock) {
265             if (mCallback != null && mExecutor != null) {
266                 mExecutor.execute(() -> {
267                     synchronized (mCallbackLock) {
268                         if (mCallback != null) {
269                             mCallback.onFilterStatusChanged(this, status);
270                         }
271                     }
272                 });
273             }
274         }
275     }
276 
onFilterEvent(FilterEvent[] events)277     private void onFilterEvent(FilterEvent[] events) {
278         synchronized (mCallbackLock) {
279             if (mCallback != null && mExecutor != null) {
280                 mExecutor.execute(() -> {
281                     synchronized (mCallbackLock) {
282                         if (mCallback != null) {
283                             mCallback.onFilterEvent(this, events);
284                         } else {
285                             for (FilterEvent event : events) {
286                                 if (event instanceof MediaEvent) {
287                                     ((MediaEvent)event).release();
288                                 }
289                             }
290                         }
291                     }
292                 });
293             } else {
294                 for (FilterEvent event : events) {
295                     if (event instanceof MediaEvent) {
296                         ((MediaEvent)event).release();
297                     }
298                 }
299             }
300         }
301     }
302 
303     /** @hide */
setType(@ype int mainType, @Subtype int subtype)304     public void setType(@Type int mainType, @Subtype int subtype) {
305         mMainType = mainType;
306         mSubtype = TunerUtils.getFilterSubtype(mainType, subtype);
307     }
308 
309     /** @hide */
setCallback(FilterCallback cb, Executor executor)310     public void setCallback(FilterCallback cb, Executor executor) {
311         synchronized (mCallbackLock) {
312             mCallback = cb;
313             mExecutor = executor;
314         }
315     }
316 
317     /** @hide */
getCallback()318     public FilterCallback getCallback() {
319         synchronized (mCallbackLock) {
320             return mCallback;
321         }
322     }
323 
324     /**
325      * Configures the filter.
326      *
327      * <p>Recofiguring must happen after stopping the filter.
328      *
329      * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
330      * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
331      * using the events from the previous configuration.
332      *
333      * <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
334      *
335      * @param config the configuration of the filter.
336      * @return result status of the operation.
337      */
338     @Result
configure(@onNull FilterConfiguration config)339     public int configure(@NonNull FilterConfiguration config) {
340         synchronized (mLock) {
341             TunerUtils.checkResourceState(TAG, mIsClosed);
342             if (mIsShared) {
343                 return Tuner.RESULT_INVALID_STATE;
344             }
345             Settings s = config.getSettings();
346             int subType = (s == null) ? mSubtype : s.getType();
347             if (mMainType != config.getType() || mSubtype != subType) {
348                 throw new IllegalArgumentException("Invalid filter config. filter main type="
349                         + mMainType + ", filter subtype=" + mSubtype + ". config main type="
350                         + config.getType() + ", config subtype=" + subType);
351             }
352             return nativeConfigureFilter(config.getType(), subType, config);
353         }
354     }
355 
356     /**
357      * Gets the filter Id in 32-bit. For any Tuner SoC that supports 64-bit filter architecture,
358      * use {@link #getIdLong()}.
359      * @deprecated Use {@link #getIdLong()} for both 32-bit and 64-bit filter architectures.
360      */
getId()361     public int getId() {
362         synchronized (mLock) {
363             TunerUtils.checkResourceState(TAG, mIsClosed);
364             return nativeGetId();
365         }
366     }
367 
368     /**
369      * Gets the filter Id.
370      */
getIdLong()371     public long getIdLong() {
372         synchronized (mLock) {
373             TunerUtils.checkResourceState(TAG, mIsClosed);
374             return nativeGetId64Bit();
375         }
376     }
377 
378     /**
379      * Configure the Filter to monitor scrambling status and ip cid change. Set corresponding bit
380      * to monitor the change. Reset to stop monitoring.
381      *
382      * <p>{@link ScramblingStatusEvent} should be sent at the following two scenarios:
383      * <ul>
384      *   <li>When this method is called with {@link #MONITOR_EVENT_SCRAMBLING_STATUS}, the first
385      *       detected scrambling status should be sent.
386      *   <li>When the Scrambling status transits into different status, event should be sent.
387      *     <ul/>
388      *
389      * <p>{@link IpCidChangeEvent} should be sent at the following two scenarios:
390      * <ul>
391      *   <li>When this method is called with {@link #MONITOR_EVENT_IP_CID_CHANGE}, the first
392      *       detected CID for the IP should be sent.
393      *   <li>When the CID is changed to different value for the IP filter, event should be sent.
394      *     <ul/>
395      *
396      * <p>This configuration is only supported in Tuner 1.1 or higher version. Unsupported version
397      * will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version
398      * information.
399      *
400      * <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
401      *
402      * @param monitorEventMask Types of event to be monitored. Set corresponding bit to
403      *                         monitor it. Reset to stop monitoring.
404      * @return result status of the operation.
405      */
406     @Result
setMonitorEventMask(@onitorEventMask int monitorEventMask)407     public int setMonitorEventMask(@MonitorEventMask int monitorEventMask) {
408         synchronized (mLock) {
409             TunerUtils.checkResourceState(TAG, mIsClosed);
410             if (mIsShared) {
411                 return Tuner.RESULT_INVALID_STATE;
412             }
413             if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
414                     TunerVersionChecker.TUNER_VERSION_1_1, "setMonitorEventMask")) {
415                 return Tuner.RESULT_UNAVAILABLE;
416             }
417             return nativeConfigureMonitorEvent(monitorEventMask);
418         }
419     }
420 
421     /**
422      * Sets the filter's data source.
423      *
424      * A filter uses demux as data source by default. If the data was packetized
425      * by multiple protocols, multiple filters may need to work together to
426      * extract all protocols' header. Then a filter's data source can be output
427      * from another filter.
428      *
429      * <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
430      *
431      * @param source the filter instance which provides data input. Switch to
432      * use demux as data source if the filter instance is NULL.
433      * @return result status of the operation.
434      * @throws IllegalStateException if the data source has been set.
435      */
436     @Result
setDataSource(@ullable Filter source)437     public int setDataSource(@Nullable Filter source) {
438         synchronized (mLock) {
439             TunerUtils.checkResourceState(TAG, mIsClosed);
440             if (mIsShared) {
441                 return Tuner.RESULT_INVALID_STATE;
442             }
443             if (mSource != null) {
444                 throw new IllegalStateException("Data source is existing");
445             }
446             int res = nativeSetDataSource(source);
447             if (res == Tuner.RESULT_SUCCESS) {
448                 mSource = source;
449             }
450             return res;
451         }
452     }
453 
454     /**
455      * Starts filtering data.
456      *
457      * <p>Does nothing if the filter is already started.
458      *
459      * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
460      * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
461      * using the events from the previous configuration.
462      *
463      * <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
464      *
465      * @return result status of the operation.
466      */
467     @Result
start()468     public int start() {
469         synchronized (mLock) {
470             TunerUtils.checkResourceState(TAG, mIsClosed);
471             if (mIsShared) {
472                 return Tuner.RESULT_INVALID_STATE;
473             }
474             int res = nativeStartFilter();
475             if (res == Tuner.RESULT_SUCCESS) {
476                 mIsStarted = true;
477             }
478             return res;
479         }
480     }
481 
482     /**
483      * Stops filtering data.
484      *
485      * <p>Does nothing if the filter is stopped or not started.
486      *
487      * <p>Filter must be stopped to reconfigure.
488      *
489      * <p>When stopping, reconfiguring and restarting the filter, the client should discard all
490      * coming events until it receives {@link RestartEvent} through {@link FilterCallback} to avoid
491      * using the events from the previous configuration.
492      *
493      * <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
494      *
495      * @return result status of the operation.
496      */
497     @Result
stop()498     public int stop() {
499         synchronized (mLock) {
500             TunerUtils.checkResourceState(TAG, mIsClosed);
501             if (mIsShared) {
502                 return Tuner.RESULT_INVALID_STATE;
503             }
504             int res = nativeStopFilter();
505             if (res == Tuner.RESULT_SUCCESS) {
506                 mIsStarted = false;
507             }
508             return res;
509         }
510     }
511 
512     /**
513      * Flushes the filter.
514      *
515      * <p>The data which is already produced by filter but not consumed yet will
516      * be cleared.
517      *
518      * <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
519      *
520      * @return result status of the operation.
521      */
522     @Result
flush()523     public int flush() {
524         synchronized (mLock) {
525             TunerUtils.checkResourceState(TAG, mIsClosed);
526             if (mIsShared) {
527                 return Tuner.RESULT_INVALID_STATE;
528             }
529             return nativeFlushFilter();
530         }
531     }
532 
533     /**
534      * Copies filtered data from filter output to the given byte array.
535      *
536      * <p>If this filter is shared, do nothing and just return {@link Tuner#RESULT_INVALID_STATE}.
537      *
538      * @param buffer the buffer to store the filtered data.
539      * @param offset the index of the first byte in {@code buffer} to write.
540      * @param size the maximum number of bytes to read.
541      * @return the number of bytes read.
542      */
read(@onNull byte[] buffer, @BytesLong long offset, @BytesLong long size)543     public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
544         synchronized (mLock) {
545             TunerUtils.checkResourceState(TAG, mIsClosed);
546             if (mIsShared) {
547                 return 0;
548             }
549             size = Math.min(size, buffer.length - offset);
550             return nativeRead(buffer, offset, size);
551         }
552     }
553 
554     /**
555      * Stops filtering data and releases the Filter instance.
556      *
557      * <p>If this filter is shared, this filter will be closed and a
558      * {@link SharedFilterCallback#STATUS_INACCESSIBLE} event will be sent to shared filter before
559      * closing.
560      */
561     @Override
close()562     public void close() {
563         synchronized (mCallbackLock) {
564             mCallback = null;
565             mExecutor = null;
566         }
567 
568         synchronized (mLock) {
569             if (mIsClosed) {
570                 return;
571             }
572             int res = nativeClose();
573             if (res != Tuner.RESULT_SUCCESS) {
574                 TunerUtils.throwExceptionForResult(res, "Failed to close filter.");
575             } else {
576                 mIsStarted = false;
577                 mIsClosed = true;
578             }
579         }
580     }
581 
582     /**
583      * Acquires a shared filter token.
584      *
585      * @return a string shared filter token.
586      */
587     @Nullable
acquireSharedFilterToken()588     public String acquireSharedFilterToken() {
589         synchronized (mLock) {
590             TunerUtils.checkResourceState(TAG, mIsClosed);
591             if (mIsStarted || mIsShared) {
592                 Log.d(TAG, "Acquire shared filter in a wrong state, started: " +
593                      mIsStarted + "shared: " + mIsShared);
594                 return null;
595             }
596             String token = nativeAcquireSharedFilterToken();
597             if (token != null) {
598                 mIsShared = true;
599             }
600             return token;
601         }
602     }
603 
604     /**
605      * Frees a shared filter token.
606      *
607      * @param filterToken the token of the shared filter being released.
608      */
freeSharedFilterToken(@onNull String filterToken)609     public void freeSharedFilterToken(@NonNull String filterToken) {
610         synchronized (mLock) {
611             TunerUtils.checkResourceState(TAG, mIsClosed);
612             if (!mIsShared) {
613                 return;
614             }
615             nativeFreeSharedFilterToken(filterToken);
616             mIsShared = false;
617         }
618     }
619 
620     /**
621      * Set filter time delay.
622      *
623      * <p> Setting a time delay instructs the filter to delay its event callback invocation until
624      * the specified amount of time has passed. The default value (delay disabled) is {@code 0}.
625      *
626      * <p>This functionality is only available in Tuner version 2.0 and higher and will otherwise
627      * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
628      *
629      * @param durationInMs specifies the duration of the delay in milliseconds.
630      * @return one of the following results: {@link Tuner#RESULT_SUCCESS},
631      * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED},
632      * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT},
633      * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}.
634      */
delayCallbackForDurationMillis(long durationInMs)635     public int delayCallbackForDurationMillis(long durationInMs) {
636         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
637                   TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) {
638             return Tuner.RESULT_UNAVAILABLE;
639         }
640 
641         if (durationInMs >= 0 && durationInMs <= Integer.MAX_VALUE) {
642             synchronized (mLock) {
643                 return nativeSetTimeDelayHint((int) durationInMs);
644             }
645         }
646         return Tuner.RESULT_INVALID_ARGUMENT;
647     }
648 
649     /**
650      * Set filter data size delay.
651      *
652      * <p> Setting a data size delay instructs the filter to delay its event callback invocation
653      * until a specified amount of data has accumulated. The default value (delay disabled) is
654      * {@code 0}.
655      *
656      * <p>This functionality is only available in Tuner version 2.0 and higher and will otherwise
657      * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
658      *
659      * @param bytesAccumulated specifies the delay condition in bytes.
660      * @return one of the following results: {@link Tuner#RESULT_SUCCESS},
661      * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED},
662      * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT},
663      * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}.
664      */
delayCallbackUntilBytesAccumulated(int bytesAccumulated)665     public int delayCallbackUntilBytesAccumulated(int bytesAccumulated) {
666         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
667                   TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) {
668             return Tuner.RESULT_UNAVAILABLE;
669         }
670 
671         synchronized (mLock) {
672             return nativeSetDataSizeDelayHint(bytesAccumulated);
673         }
674     }
675 }
676