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