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