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